diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml index 811769722..6f806d514 100644 --- a/.github/workflows/pythonpackage.yml +++ b/.github/workflows/pythonpackage.yml @@ -122,3 +122,23 @@ jobs: pip install tox - name: Run tox run: tox -e py38 + + py39: + runs-on: ubuntu-latest + strategy: + matrix: + python-version: [3.9] + steps: + - uses: actions/checkout@v1 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v1 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -r requirements.txt + pip install -r test-requirements.txt + pip install tox + - name: Run tox + run: tox -e py39 diff --git a/README.rst b/README.rst index b382d52c1..b13bc6cc9 100644 --- a/README.rst +++ b/README.rst @@ -96,167 +96,10 @@ run Bandit with standard input:: cat examples/imports.py | bandit - -Usage:: - - $ bandit -h - usage: bandit [-h] [-r] [-a {file,vuln}] [-n CONTEXT_LINES] [-c CONFIG_FILE] - [-p PROFILE] [-t TESTS] [-s SKIPS] [-l] [-i] - [-f {csv,custom,html,json,screen,txt,xml,yaml}] - [--msg-template MSG_TEMPLATE] [-o [OUTPUT_FILE]] [-v] [-d] [-q] - [--ignore-nosec] [-x EXCLUDED_PATHS] [-b BASELINE] - [--ini INI_PATH] [--exit-zero] [--version] - [targets [targets ...]] - - Bandit - a Python source code security analyzer - - positional arguments: - targets source file(s) or directory(s) to be tested - - optional arguments: - -h, --help show this help message and exit - -r, --recursive find and process files in subdirectories - -a {file,vuln}, --aggregate {file,vuln} - aggregate output by vulnerability (default) or by - filename - -n CONTEXT_LINES, --number CONTEXT_LINES - maximum number of code lines to output for each issue - -c CONFIG_FILE, --configfile CONFIG_FILE - optional config file to use for selecting plugins and - overriding defaults - -p PROFILE, --profile PROFILE - profile to use (defaults to executing all tests) - -t TESTS, --tests TESTS - comma-separated list of test IDs to run - -s SKIPS, --skip SKIPS - comma-separated list of test IDs to skip - -l, --level report only issues of a given severity level or higher - (-l for LOW, -ll for MEDIUM, -lll for HIGH) - -i, --confidence report only issues of a given confidence level or - higher (-i for LOW, -ii for MEDIUM, -iii for HIGH) - -f {csv,custom,html,json,screen,txt,xml,yaml}, --format {csv,custom,html,json,screen,txt,xml,yaml} - specify output format - --msg-template MSG_TEMPLATE - specify output message template (only usable with - --format custom), see CUSTOM FORMAT section for list - of available values - -o [OUTPUT_FILE], --output [OUTPUT_FILE] - write report to filename - -v, --verbose output extra information like excluded and included - files - -d, --debug turn on debug mode - -q, --quiet, --silent - only show output in the case of an error - --ignore-nosec do not skip lines with # nosec comments - -x EXCLUDED_PATHS, --exclude EXCLUDED_PATHS - comma-separated list of paths (glob patterns - supported) to exclude from scan (note that these are - in addition to the excluded paths provided in the - config file) (default: - .svn,CVS,.bzr,.hg,.git,__pycache__,.tox,.eggs,*.egg) - -b BASELINE, --baseline BASELINE - path of a baseline report to compare against (only - JSON-formatted files are accepted) - --ini INI_PATH path to a .bandit file that supplies command line - arguments - --exit-zero exit with 0, even with results found - --version show program's version number and exit - - CUSTOM FORMATTING - ----------------- - - Available tags: - - {abspath}, {relpath}, {line}, {col}, {test_id}, - {severity}, {msg}, {confidence}, {range} - - Example usage: - - Default template: - bandit -r examples/ --format custom --msg-template \ - "{abspath}:{line}: {test_id}[bandit]: {severity}: {msg}" - - Provides same output as: - bandit -r examples/ --format custom - - Tags can also be formatted in python string.format() style: - bandit -r examples/ --format custom --msg-template \ - "{relpath:20.20s}: {line:03}: {test_id:^8}: DEFECT: {msg:>20}" - - See python documentation for more information about formatting style: - https://docs.python.org/3/library/string.html - - The following tests were discovered and loaded: - ----------------------------------------------- - - B101 assert_used - B102 exec_used - B103 set_bad_file_permissions - B104 hardcoded_bind_all_interfaces - B105 hardcoded_password_string - B106 hardcoded_password_funcarg - B107 hardcoded_password_default - B108 hardcoded_tmp_directory - B110 try_except_pass - B112 try_except_continue - B201 flask_debug_true - B301 pickle - B302 marshal - B303 md5 - B304 ciphers - B305 cipher_modes - B306 mktemp_q - B307 eval - B308 mark_safe - B309 httpsconnection - B310 urllib_urlopen - B311 random - B312 telnetlib - B313 xml_bad_cElementTree - B314 xml_bad_ElementTree - B315 xml_bad_expatreader - B316 xml_bad_expatbuilder - B317 xml_bad_sax - B318 xml_bad_minidom - B319 xml_bad_pulldom - B320 xml_bad_etree - B321 ftplib - B323 unverified_context - B324 hashlib_new_insecure_functions - B325 tempnam - B401 import_telnetlib - B402 import_ftplib - B403 import_pickle - B404 import_subprocess - B405 import_xml_etree - B406 import_xml_sax - B407 import_xml_expat - B408 import_xml_minidom - B409 import_xml_pulldom - B410 import_lxml - B411 import_xmlrpclib - B412 import_httpoxy - B413 import_pycrypto - B501 request_with_no_cert_validation - B502 ssl_with_bad_version - B503 ssl_with_bad_defaults - B504 ssl_with_no_version - B505 weak_cryptographic_key - B506 yaml_load - B507 ssh_no_host_key_verification - B601 paramiko_calls - B602 subprocess_popen_with_shell_equals_true - B603 subprocess_without_shell_equals_true - B604 any_other_function_with_shell_equals_true - B605 start_process_with_a_shell - B606 start_process_with_no_shell - B607 start_process_with_partial_path - B608 hardcoded_sql_expressions - B609 linux_commands_wildcard_injection - B610 django_extra_used - B611 django_rawsql_used - B701 jinja2_autoescape_false - B702 use_of_mako_templates - B703 django_mark_safe +For more usage information:: + + bandit -h + Baseline -------- diff --git a/bandit/plugins/general_hardcoded_password.py b/bandit/plugins/general_hardcoded_password.py index dabd8cd47..4a3db14a1 100644 --- a/bandit/plugins/general_hardcoded_password.py +++ b/bandit/plugins/general_hardcoded_password.py @@ -80,6 +80,15 @@ def hardcoded_password_string(context): if isinstance(targ, ast.Name) and RE_CANDIDATES.search(targ.id): return _report(node.s) + elif (isinstance(node._bandit_parent, ast.Subscript) + and RE_CANDIDATES.search(node.s)): + # Py39+: looks for "dict[candidate]='some_string'" + # subscript -> index -> string + assign = node._bandit_parent._bandit_parent + if isinstance(assign, ast.Assign) and isinstance(assign.value, + ast.Str): + return _report(assign.value.s) + elif (isinstance(node._bandit_parent, ast.Index) and RE_CANDIDATES.search(node.s)): # looks for "dict[candidate]='some_string'" diff --git a/examples/hardcoded-passwords.py b/examples/hardcoded-passwords.py index 8225d4551..d7cbdc553 100644 --- a/examples/hardcoded-passwords.py +++ b/examples/hardcoded-passwords.py @@ -1,29 +1,58 @@ +# Possible hardcoded password: 'Admin' +# Severity: Low Confidence: Medium def someFunction(user, password="Admin"): print("Hi " + user) def someFunction2(password): + # Possible hardcoded password: 'root' + # Severity: Low Confidence: Medium if password == "root": print("OK, logged in") def noMatch(password): + # Possible hardcoded password: '' + # Severity: Low Confidence: Medium if password == '': print("No password!") def NoMatch2(password): + # Possible hardcoded password: 'ajklawejrkl42348swfgkg' + # Severity: Low Confidence: Medium if password == "ajklawejrkl42348swfgkg": print("Nice password!") +# Possible hardcoded password: 'blerg' +# Severity: Low Confidence: Medium def doLogin(password="blerg"): pass def NoMatch3(a, b): pass +# Possible hardcoded password: 'blerg' +# Severity: Low Confidence: Medium doLogin(password="blerg") + +# Possible hardcoded password: 'blerg' +# Severity: Low Confidence: Medium password = "blerg" + +# Possible hardcoded password: 'blerg' +# Severity: Low Confidence: Medium d["password"] = "blerg" +# Possible hardcoded password: 'secret' +# Severity: Low Confidence: Medium EMAIL_PASSWORD = "secret" + +# Possible hardcoded password: 'emails_secret' +# Severity: Low Confidence: Medium email_pwd = 'emails_secret' + +# Possible hardcoded password: 'd6s$f9g!j8mg7hw?n&2' +# Severity: Low Confidence: Medium my_secret_password_for_email = 'd6s$f9g!j8mg7hw?n&2' + +# Possible hardcoded password: '1234' +# Severity: Low Confidence: Medium passphrase='1234' diff --git a/setup.cfg b/setup.cfg index 8618f554b..07d494377 100644 --- a/setup.cfg +++ b/setup.cfg @@ -21,6 +21,7 @@ classifier = Programming Language :: Python :: 3.6 Programming Language :: Python :: 3.7 Programming Language :: Python :: 3.8 + Programming Language :: Python :: 3.9 Topic :: Security project_urls = Release notes = https://github.com/PyCQA/bandit/releases diff --git a/tests/functional/test_runtime.py b/tests/functional/test_runtime.py index 6398dc0a7..d0e747c0d 100644 --- a/tests/functional/test_runtime.py +++ b/tests/functional/test_runtime.py @@ -62,18 +62,6 @@ def test_help_arg(self): self.assertIn("optional arguments:", output) self.assertIn("tests were discovered and loaded:", output) - def test_help_in_readme(self): - replace_list = [' ', '\t', '\n'] - (retcode, output) = self._test_runtime(['bandit', '-h']) - for i in replace_list: - output = output.replace(i, '') - output = output.replace("'", "\'") - with open('README.rst') as f: - readme = f.read() - for i in replace_list: - readme = readme.replace(i, '') - self.assertIn(output, readme) - # test examples (use _test_example() to wrap in config location argument def test_example_nonexistent(self): (retcode, output) = self._test_example(