From d1622bf0432f0205e0035bcf87f3e22346ccd386 Mon Sep 17 00:00:00 2001 From: Eric Brown Date: Mon, 7 Feb 2022 09:34:40 -0800 Subject: [PATCH 01/13] Add new plugin to check use of pyghmi (#803) * Add new plugin to check use of pyghmi This patch set adds a new bandit plugin to check the use of pyghmi. Signed-off-by: Tin Lam * Fix example and polish te code. Signed-off-by: Tin Lam * Add new plug-in to check pyghmi This patch set adds a new bandit plugin to check the use of the pyghmi library, as the IPMI is known to be an insecured protocol. Closes: #356 Signed-off-by: Tin Lam Co-authored-by: Tin Lam Co-authored-by: Eric Brown --- bandit/blacklists/imports.py | 23 +++++++++++++++++++++++ examples/pyghmi.py | 5 +++++ tests/functional/test_functional.py | 8 ++++++++ 3 files changed, 36 insertions(+) create mode 100644 examples/pyghmi.py diff --git a/bandit/blacklists/imports.py b/bandit/blacklists/imports.py index a123d2b6e..3363c7fa4 100644 --- a/bandit/blacklists/imports.py +++ b/bandit/blacklists/imports.py @@ -212,6 +212,17 @@ | | | - Cryptodome.Util | | +------+---------------------+------------------------------------+-----------+ +B415: import_pyghmi +------------------- +An IPMI-related module is being imported. IPMI is considered insecure. Use +an encrypted protocol. + ++------+---------------------+------------------------------------+-----------+ +| ID | Name | Imports | Severity | ++======+=====================+====================================+===========+ +| B415 | import_pyghmi | - pyghmi | high | ++------+---------------------+------------------------------------+-----------+ + """ from bandit.blacklists import utils from bandit.core import issue @@ -410,4 +421,16 @@ def gen_blacklist(): ) ) + sets.append( + utils.build_conf_dict( + "import_pyghmi", + "B415", + issue.Cwe.CLEARTEXT_TRANSMISSION, + ["pyghmi"], + "An IPMI-related module is being imported. IPMI is considered " + "insecure. Use an encrypted protocol.", + "HIGH", + ) + ) + return {"Import": sets, "ImportFrom": sets, "Call": sets} diff --git a/examples/pyghmi.py b/examples/pyghmi.py new file mode 100644 index 000000000..44eb197ac --- /dev/null +++ b/examples/pyghmi.py @@ -0,0 +1,5 @@ +from pyghmi.ipmi import command + +cmd = command.Command(bmc="bmc", + userid="userid", + password="ZjE4ZjI0NTE4YmI2NGJjZDliOGY3ZmJiY2UyN2IzODQK") diff --git a/tests/functional/test_functional.py b/tests/functional/test_functional.py index bc7f6cfe7..ecfe8780f 100644 --- a/tests/functional/test_functional.py +++ b/tests/functional/test_functional.py @@ -831,6 +831,14 @@ def test_no_blacklist_pycryptodome(self): } self.check_example("pycryptodome.py", expect) + def test_blacklist_pyghmi(self): + """Test calling pyghmi methods""" + expect = { + "SEVERITY": {"UNDEFINED": 0, "LOW": 1, "MEDIUM": 0, "HIGH": 1}, + "CONFIDENCE": {"UNDEFINED": 0, "LOW": 0, "MEDIUM": 1, "HIGH": 1}, + } + self.check_example("pyghmi.py", expect) + def test_snmp_security_check(self): """Test insecure and weak crypto usage of SNMP.""" expect = { From a9eaafa53ae73c1a66172373128cb19bb7b82a32 Mon Sep 17 00:00:00 2001 From: Nick Oliverio Date: Tue, 8 Feb 2022 10:33:11 -0500 Subject: [PATCH 02/13] Check for hardcoded passwords in class attributes (#766) * Check for hardcoded passwords in class attributes B105:hardcoded_password_string currently throws an error for string literal variables, dictionary keys, and comparisons which look like passwords, but does not create an error for class attributes which look like passwords. For example password = "mypassword" and password == "mypassword" would create an error, but my_object.password = "mypassword", and my_object.password == "mypassword" would not. This behavior is unintuitive. Resolves #759 * Add tests for hardcoded passwords in classes * Update general_hardcoded_password.py Co-authored-by: Eric Brown --- bandit/plugins/general_hardcoded_password.py | 9 +++++++++ examples/hardcoded-passwords.py | 12 ++++++++++++ tests/functional/test_functional.py | 4 ++-- 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/bandit/plugins/general_hardcoded_password.py b/bandit/plugins/general_hardcoded_password.py index dbff53ce1..9a162b485 100644 --- a/bandit/plugins/general_hardcoded_password.py +++ b/bandit/plugins/general_hardcoded_password.py @@ -36,6 +36,7 @@ def hardcoded_password_string(context): - assigned to a variable that looks like a password - assigned to a dict key that looks like a password + - assigned to a class attribute that looks like a password - used in a comparison with a variable that looks like a password Variables are considered to look like a password if they have match any one @@ -84,6 +85,10 @@ def hardcoded_password_string(context): for targ in node._bandit_parent.targets: if isinstance(targ, ast.Name) and RE_CANDIDATES.search(targ.id): return _report(node.s) + elif isinstance(targ, ast.Attribute) and RE_CANDIDATES.search( + targ.attr + ): + return _report(node.s) elif isinstance( node._bandit_parent, ast.Subscript @@ -114,6 +119,10 @@ def hardcoded_password_string(context): if RE_CANDIDATES.search(comp.left.id): if isinstance(comp.comparators[0], ast.Str): return _report(comp.comparators[0].s) + elif isinstance(comp.left, ast.Attribute): + if RE_CANDIDATES.search(comp.left.attr): + if isinstance(comp.comparators[0], ast.Str): + return _report(comp.comparators[0].s) @test.checks("Call") diff --git a/examples/hardcoded-passwords.py b/examples/hardcoded-passwords.py index d7cbdc553..2b501c725 100644 --- a/examples/hardcoded-passwords.py +++ b/examples/hardcoded-passwords.py @@ -1,3 +1,8 @@ +# Possible hardcoded password: 'class_password' +# Severity: Low Confidence: Medium +class SomeClass: + password = "class_password" + # Possible hardcoded password: 'Admin' # Severity: Low Confidence: Medium def someFunction(user, password="Admin"): @@ -21,6 +26,13 @@ def NoMatch2(password): if password == "ajklawejrkl42348swfgkg": print("Nice password!") +def noMatchObject(): + obj = SomeClass() + # Possible hardcoded password: 'this cool password' + # Severity: Low Confidence: Medium + if obj.password == "this cool password": + print(obj.password) + # Possible hardcoded password: 'blerg' # Severity: Low Confidence: Medium def doLogin(password="blerg"): diff --git a/tests/functional/test_functional.py b/tests/functional/test_functional.py index ecfe8780f..fe535a4f2 100644 --- a/tests/functional/test_functional.py +++ b/tests/functional/test_functional.py @@ -157,8 +157,8 @@ def test_exec(self): def test_hardcoded_passwords(self): """Test for hard-coded passwords.""" expect = { - "SEVERITY": {"UNDEFINED": 0, "LOW": 12, "MEDIUM": 0, "HIGH": 0}, - "CONFIDENCE": {"UNDEFINED": 0, "LOW": 0, "MEDIUM": 12, "HIGH": 0}, + "SEVERITY": {"UNDEFINED": 0, "LOW": 14, "MEDIUM": 0, "HIGH": 0}, + "CONFIDENCE": {"UNDEFINED": 0, "LOW": 0, "MEDIUM": 14, "HIGH": 0}, } self.check_example("hardcoded-passwords.py", expect) From c4372a095f2ccdb6665bc8f3d555957473aeb8b1 Mon Sep 17 00:00:00 2001 From: Eric Brown Date: Thu, 10 Feb 2022 11:40:35 -0800 Subject: [PATCH 03/13] Better hashlib check for Python 3.9 (#805) * Better hashlib check for Python 3.9 In Python 3.9 and later, the hashlib function has a new keyword argument usedforsecurity to describe the usage of the hash. In that way, we can better identify the severity of the error. Previously, hashlib.md5 and the like were part of the blacklist check. For Python 3.9, it'll be part of the hashlib plugin so it can do more advanced checking of usedforsecurity. Signed-off-by: Eric Brown * Update hashlib_insecure_functions.py --- bandit/blacklists/calls.py | 68 ++++++---- bandit/plugins/hashlib_insecure_functions.py | 118 ++++++++++++++++++ .../plugins/hashlib_new_insecure_functions.py | 85 ------------- examples/crypto-md5.py | 2 + examples/hashlib_new_insecure_functions.py | 6 - setup.cfg | 4 +- tests/functional/test_functional.py | 66 ++++++++-- 7 files changed, 223 insertions(+), 126 deletions(-) create mode 100644 bandit/plugins/hashlib_insecure_functions.py delete mode 100644 bandit/plugins/hashlib_new_insecure_functions.py diff --git a/bandit/blacklists/calls.py b/bandit/blacklists/calls.py index 480f79bb8..65e471d86 100644 --- a/bandit/blacklists/calls.py +++ b/bandit/blacklists/calls.py @@ -313,6 +313,8 @@ +------+---------------------+------------------------------------+-----------+ """ +import sys + from bandit.blacklists import utils from bandit.core import issue @@ -362,28 +364,52 @@ def gen_blacklist(): ) ) - sets.append( - utils.build_conf_dict( - "md5", - "B303", - issue.Cwe.BROKEN_CRYPTO, - [ - "hashlib.md5", - "hashlib.sha1", - "Crypto.Hash.MD2.new", - "Crypto.Hash.MD4.new", - "Crypto.Hash.MD5.new", - "Crypto.Hash.SHA.new", - "Cryptodome.Hash.MD2.new", - "Cryptodome.Hash.MD4.new", - "Cryptodome.Hash.MD5.new", - "Cryptodome.Hash.SHA.new", - "cryptography.hazmat.primitives.hashes.MD5", - "cryptography.hazmat.primitives.hashes.SHA1", - ], - "Use of insecure MD2, MD4, MD5, or SHA1 hash function.", + if sys.version_info >= (3, 9): + sets.append( + utils.build_conf_dict( + "md5", + "B303", + issue.Cwe.BROKEN_CRYPTO, + [ + "Crypto.Hash.MD2.new", + "Crypto.Hash.MD4.new", + "Crypto.Hash.MD5.new", + "Crypto.Hash.SHA.new", + "Cryptodome.Hash.MD2.new", + "Cryptodome.Hash.MD4.new", + "Cryptodome.Hash.MD5.new", + "Cryptodome.Hash.SHA.new", + "cryptography.hazmat.primitives.hashes.MD5", + "cryptography.hazmat.primitives.hashes.SHA1", + ], + "Use of insecure MD2, MD4, MD5, or SHA1 hash function.", + ) + ) + else: + sets.append( + utils.build_conf_dict( + "md5", + "B303", + issue.Cwe.BROKEN_CRYPTO, + [ + "hashlib.md4", + "hashlib.md5", + "hashlib.sha", + "hashlib.sha1", + "Crypto.Hash.MD2.new", + "Crypto.Hash.MD4.new", + "Crypto.Hash.MD5.new", + "Crypto.Hash.SHA.new", + "Cryptodome.Hash.MD2.new", + "Cryptodome.Hash.MD4.new", + "Cryptodome.Hash.MD5.new", + "Cryptodome.Hash.SHA.new", + "cryptography.hazmat.primitives.hashes.MD5", + "cryptography.hazmat.primitives.hashes.SHA1", + ], + "Use of insecure MD2, MD4, MD5, or SHA1 hash function.", + ) ) - ) sets.append( utils.build_conf_dict( diff --git a/bandit/plugins/hashlib_insecure_functions.py b/bandit/plugins/hashlib_insecure_functions.py new file mode 100644 index 000000000..31c18b114 --- /dev/null +++ b/bandit/plugins/hashlib_insecure_functions.py @@ -0,0 +1,118 @@ +# +# SPDX-License-Identifier: Apache-2.0 +r""" +====================================================================== +B324: Test use of insecure md4, md5, or sha1 hash functions in hashlib +====================================================================== + +This plugin checks for the usage of the insecure MD4, MD5, or SHA1 hash +functions in ``hashlib``. The ``hashlib.new`` function provides +the ability to construct a new hashing object using the named algorithm. This +can be used to create insecure hash functions like MD4 and MD5 if they are +passed as algorithm names to this function. + +For Python versions prior to 3.9, this check is similar to B303 blacklist +except that this checks for insecure hash functions created using +``hashlib.new`` function. For Python version 3.9 and later, this check +does additional checking for usage of keyword usedforsecurity on all +function variations of hashlib. + +:Example: + + >> Issue: [B324:hashlib] Use of weak MD2, MD4, MD5, or SHA1 hash for + security. Consider usedforsecurity=False + Severity: High Confidence: High + CWE: CWE-327 (https://cwe.mitre.org/data/definitions/327.html) + Location: examples/hashlib_new_insecure_functions.py:3:0 + More Info: https://bandit.readthedocs.io/en/latest/plugins/b324_hashlib.html + 2 + 3 hashlib.new('md5') + 4 + +.. seealso:: + + - https://cwe.mitre.org/data/definitions/327.html + +.. versionadded:: 1.5.0 + +.. versionchanged:: 1.7.3 + CWE information added + +""" # noqa: E501 +import sys + +import bandit +from bandit.core import issue +from bandit.core import test_properties as test + + +def _hashlib_func(context): + if isinstance(context.call_function_name_qual, str): + qualname_list = context.call_function_name_qual.split(".") + + if "hashlib" in qualname_list: + func = qualname_list[-1] + args = context.call_args + keywords = context.call_keywords + name = args[0] if args else keywords["name"] + + if func in ("md4", "md5", "sha", "sha1"): + if keywords.get("usedforsecurity", "True") == "True": + return bandit.Issue( + severity=bandit.HIGH, + confidence=bandit.HIGH, + cwe=issue.Cwe.BROKEN_CRYPTO, + text="Use of weak MD2, MD4, MD5, or SHA1 hash " + "for security. Consider usedforsecurity=False", + lineno=context.node.lineno, + ) + elif func == "new": + if isinstance(name, str) and name.lower() in ( + "md4", + "md5", + "sha", + "sha1", + ): + if keywords.get("usedforsecurity", "True") == "True": + return bandit.Issue( + severity=bandit.HIGH, + confidence=bandit.HIGH, + cwe=issue.Cwe.BROKEN_CRYPTO, + text="Use of weak MD2, MD4, MD5, or SHA1 hash " + "for security. Consider usedforsecurity=False", + lineno=context.node.lineno, + ) + + +def _hashlib_new(context): + if isinstance(context.call_function_name_qual, str): + qualname_list = context.call_function_name_qual.split(".") + func = qualname_list[-1] + + if "hashlib" in qualname_list and func == "new": + args = context.call_args + keywords = context.call_keywords + name = args[0] if args else keywords["name"] + if isinstance(name, str) and name.lower() in ( + "md4", + "md5", + "sha", + "sha1", + ): + return bandit.Issue( + severity=bandit.MEDIUM, + confidence=bandit.HIGH, + cwe=issue.Cwe.BROKEN_CRYPTO, + text="Use of insecure MD2, MD4, MD5, or SHA1 hash " + "function.", + lineno=context.node.lineno, + ) + + +@test.test_id("B324") +@test.checks("Call") +def hashlib(context): + if sys.version_info >= (3, 9): + return _hashlib_func(context) + else: + return _hashlib_new(context) diff --git a/bandit/plugins/hashlib_new_insecure_functions.py b/bandit/plugins/hashlib_new_insecure_functions.py deleted file mode 100644 index 302a028f1..000000000 --- a/bandit/plugins/hashlib_new_insecure_functions.py +++ /dev/null @@ -1,85 +0,0 @@ -# -# SPDX-License-Identifier: Apache-2.0 -r""" -============================================================================ -B324: Test use of insecure md4, md5, or sha1 hash functions in hashlib.new() -============================================================================ - -This plugin checks for the usage of the insecure MD4, MD5, or SHA1 hash -functions in ``hashlib.new`` function. The ``hashlib.new`` function provides -the ability to construct a new hashing object using the named algorithm. This -can be used to create insecure hash functions like MD4 and MD5 if they are -passed as algorithm names to this function. - -This is similar to B303 blacklist check, except that this checks for insecure -hash functions created using ``hashlib.new`` function. - -:Example: - - >> Issue: [B324:hashlib_new] Use of insecure MD4 or MD5 hash function. - Severity: Medium Confidence: High - CWE: CWE-327 (https://cwe.mitre.org/data/definitions/327.html) - Location: examples/hashlib_new_insecure_funcs.py:3 - 2 - 3 md5_hash = hashlib.new('md5', string='test') - 4 print(md5_hash) - -.. seealso:: - - - https://cwe.mitre.org/data/definitions/327.html - -.. versionadded:: 1.5.0 - -.. versionchanged:: 1.7.3 - CWE information added - -""" -import sys - -import bandit -from bandit.core import issue -from bandit.core import test_properties as test - - -@test.test_id("B324") -@test.checks("Call") -def hashlib_new(context): - if isinstance(context.call_function_name_qual, str): - qualname_list = context.call_function_name_qual.split(".") - func = qualname_list[-1] - if "hashlib" in qualname_list and func == "new": - args = context.call_args - keywords = context.call_keywords - name = args[0] if args else keywords["name"] - if isinstance(name, str) and name.lower() in ( - "md4", - "md5", - "sha", - "sha1", - ): - if sys.version_info >= (3, 9): - # Python 3.9 includes a usedforsecurity argument - usedforsecurity = ( - args[2] - if len(args) > 2 - else keywords.get("usedforsecurity", "True") - ) - - if usedforsecurity == "True": - return bandit.Issue( - severity=bandit.HIGH, - confidence=bandit.HIGH, - cwe=issue.Cwe.BROKEN_CRYPTO, - text="Use of insecure MD2, MD4, MD5, or SHA1 hash " - "function.", - lineno=context.node.lineno, - ) - else: - return bandit.Issue( - severity=bandit.MEDIUM, - confidence=bandit.HIGH, - cwe=issue.Cwe.BROKEN_CRYPTO, - text="Use of insecure MD2, MD4, MD5, or SHA1 hash " - "function.", - lineno=context.node.lineno, - ) diff --git a/examples/crypto-md5.py b/examples/crypto-md5.py index 045740c3b..b78fd4c82 100644 --- a/examples/crypto-md5.py +++ b/examples/crypto-md5.py @@ -18,6 +18,8 @@ hashlib.sha1(1) +hashlib.sha1(usedforsecurity=False) + pycrypto_md2.new() pycrypto_md4.new() pycrypto_md5.new() diff --git a/examples/hashlib_new_insecure_functions.py b/examples/hashlib_new_insecure_functions.py index 96a774ad9..914b3056d 100644 --- a/examples/hashlib_new_insecure_functions.py +++ b/examples/hashlib_new_insecure_functions.py @@ -10,9 +10,6 @@ hashlib.new(string='test', name='MD5') -# 3rd arg only availabe in Python 3.9+ -hashlib.new('md5', b'test', True) - hashlib.new('sha1') hashlib.new(string='test', name='SHA1') @@ -29,8 +26,5 @@ hashlib.new('SHA512') -# 3rd arg only availabe in Python 3.9+ -hashlib.new('md5', b'test', False) - # usedforsecurity arg only availabe in Python 3.9+ hashlib.new(name='sha1', usedforsecurity=False) diff --git a/setup.cfg b/setup.cfg index 5a2ef37b8..6f960c5da 100644 --- a/setup.cfg +++ b/setup.cfg @@ -90,8 +90,8 @@ bandit.plugins = # bandit/plugins/injection_sql.py hardcoded_sql_expressions = bandit.plugins.injection_sql:hardcoded_sql_expressions - # bandit/plugins/hashlib_new_insecure_functions.py - hashlib_new_insecure_functions = bandit.plugins.hashlib_new_insecure_functions:hashlib_new + # bandit/plugins/hashlib_insecure_functions.py + hashlib_insecure_functions = bandit.plugins.hashlib_insecure_functions:hashlib # bandit/plugins/injection_wildcard.py linux_commands_wildcard_injection = bandit.plugins.injection_wildcard:linux_commands_wildcard_injection diff --git a/tests/functional/test_functional.py b/tests/functional/test_functional.py index fe535a4f2..017dc75e7 100644 --- a/tests/functional/test_functional.py +++ b/tests/functional/test_functional.py @@ -108,10 +108,36 @@ def test_binding(self): def test_crypto_md5(self): """Test the `hashlib.md5` example.""" - expect = { - "SEVERITY": {"UNDEFINED": 0, "LOW": 0, "MEDIUM": 15, "HIGH": 4}, - "CONFIDENCE": {"UNDEFINED": 0, "LOW": 0, "MEDIUM": 0, "HIGH": 19}, - } + if sys.version_info >= (3, 9): + expect = { + "SEVERITY": { + "UNDEFINED": 0, + "LOW": 0, + "MEDIUM": 10, + "HIGH": 9, + }, + "CONFIDENCE": { + "UNDEFINED": 0, + "LOW": 0, + "MEDIUM": 0, + "HIGH": 19, + }, + } + else: + expect = { + "SEVERITY": { + "UNDEFINED": 0, + "LOW": 0, + "MEDIUM": 16, + "HIGH": 4, + }, + "CONFIDENCE": { + "UNDEFINED": 0, + "LOW": 0, + "MEDIUM": 0, + "HIGH": 20, + }, + } self.check_example("crypto-md5.py", expect) def test_ciphers(self): @@ -180,10 +206,26 @@ def test_httplib_https(self): def test_imports_aliases(self): """Test the `import X as Y` syntax.""" - expect = { - "SEVERITY": {"UNDEFINED": 0, "LOW": 4, "MEDIUM": 5, "HIGH": 0}, - "CONFIDENCE": {"UNDEFINED": 0, "LOW": 0, "MEDIUM": 0, "HIGH": 9}, - } + if sys.version_info >= (3, 9): + expect = { + "SEVERITY": {"UNDEFINED": 0, "LOW": 4, "MEDIUM": 1, "HIGH": 4}, + "CONFIDENCE": { + "UNDEFINED": 0, + "LOW": 0, + "MEDIUM": 0, + "HIGH": 9, + }, + } + else: + expect = { + "SEVERITY": {"UNDEFINED": 0, "LOW": 4, "MEDIUM": 5, "HIGH": 0}, + "CONFIDENCE": { + "UNDEFINED": 0, + "LOW": 0, + "MEDIUM": 0, + "HIGH": 9, + }, + } self.check_example("imports-aliases.py", expect) def test_imports_from(self): @@ -786,13 +828,13 @@ def test_hashlib_new_insecure_functions(self): "UNDEFINED": 0, "LOW": 0, "MEDIUM": 0, - "HIGH": 11, + "HIGH": 10, }, "CONFIDENCE": { "UNDEFINED": 0, "LOW": 0, "MEDIUM": 0, - "HIGH": 11, + "HIGH": 10, }, } else: @@ -800,14 +842,14 @@ def test_hashlib_new_insecure_functions(self): "SEVERITY": { "UNDEFINED": 0, "LOW": 0, - "MEDIUM": 13, + "MEDIUM": 11, "HIGH": 0, }, "CONFIDENCE": { "UNDEFINED": 0, "LOW": 0, "MEDIUM": 0, - "HIGH": 13, + "HIGH": 11, }, } self.check_example("hashlib_new_insecure_functions.py", expect) From dbefd0428e3ec2ebbbe7b381f3b79d0560782fed Mon Sep 17 00:00:00 2001 From: Eric Brown Date: Sun, 13 Feb 2022 20:40:16 -0800 Subject: [PATCH 04/13] Fix references to the default branch name (#810) The primary branch name has been renamed from master to main. As a result of this, some references in the docs must also be renamed so links are preserved. Signed-off-by: Eric Brown --- CONTRIBUTING.md | 2 +- README.rst | 8 ++++---- bandit/core/manager.py | 2 +- doc/requirements.txt | 2 +- doc/source/conf.py | 4 ++-- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index de636558e..75b6b4c85 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -18,7 +18,7 @@ Here are guidelines and rules that can be helpful if you plan to want to get inv * [Extending Bandit](#extending-bandit) ## Code of Conduct -Everyone who participates in this project is governed by the PyCQA [Code of Conduct](https://github.com/PyCQA/bandit/blob/master/CODE_OF_CONDUCT.md#contributor-covenant-code-of-conduct). +Everyone who participates in this project is governed by the PyCQA [Code of Conduct](https://github.com/PyCQA/bandit/blob/main/CODE_OF_CONDUCT.md#contributor-covenant-code-of-conduct). ## Reporting Bugs If you encounter a bug, please let us know about it. See the guide here [GitHub issues](https://guides.github.com/features/issues/). diff --git a/README.rst b/README.rst index 3c51ca102..bbffffa60 100644 --- a/README.rst +++ b/README.rst @@ -1,4 +1,4 @@ -.. image:: https://github.com/PyCQA/bandit/blob/master/logo/logotype-sm.png +.. image:: https://github.com/PyCQA/bandit/blob/main/logo/logotype-sm.png :alt: Bandit ====== @@ -24,7 +24,7 @@ :alt: Format .. image:: https://img.shields.io/badge/license-Apache%202-blue.svg - :target: https://github.com/PyCQA/bandit/blob/master/LICENSE + :target: https://github.com/PyCQA/bandit/blob/main/LICENSE :alt: License A security linter from PyCQA @@ -33,7 +33,7 @@ A security linter from PyCQA * Documentation: https://bandit.readthedocs.io/en/latest/ * Source: https://github.com/PyCQA/bandit * Bugs: https://github.com/PyCQA/bandit/issues -* Contributing: https://github.com/PyCQA/bandit/blob/master/CONTRIBUTING.md +* Contributing: https://github.com/PyCQA/bandit/blob/main/CONTRIBUTING.md Overview -------- @@ -64,7 +64,7 @@ Contributing ------------ Follow our Contributing file: -https://github.com/PyCQA/bandit/blob/master/CONTRIBUTING.md +https://github.com/PyCQA/bandit/blob/main/CONTRIBUTING.md Reporting Bugs -------------- diff --git a/bandit/core/manager.py b/bandit/core/manager.py index fc6a1eddb..ddfa04573 100644 --- a/bandit/core/manager.py +++ b/bandit/core/manager.py @@ -450,7 +450,7 @@ def _find_candidate_matches(unmatched_issues, results_list): be able to pick out the new one. :param unmatched_issues: List of issues that weren't present before - :param results_list: Master list of current Bandit findings + :param results_list: main list of current Bandit findings :return: A dictionary with a list of candidates for each issue """ diff --git a/doc/requirements.txt b/doc/requirements.txt index 9a5918baf..4a92744bd 100644 --- a/doc/requirements.txt +++ b/doc/requirements.txt @@ -1,5 +1,5 @@ # The order of packages is significant, because pip processes them in the order # of appearance. Changing the order has an impact on the overall integration # process, which may cause wedges in the gate later. -sphinx!=1.6.6,!=1.6.7,>=1.6.2 # BSD +sphinx>=4.0.0 # BSD sphinx-rtd-theme>=0.3.0 diff --git a/doc/source/conf.py b/doc/source/conf.py index 52d490886..cd342ca04 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -21,8 +21,8 @@ # The suffix of source filenames. source_suffix = ".rst" -# The master toctree document. -master_doc = "index" +# The root toctree document. +root_doc = "index" # General information about the project. project = "Bandit" From b8ff685ae805f8fbf677befdc18f585fdfc198c5 Mon Sep 17 00:00:00 2001 From: Eric Brown Date: Mon, 14 Feb 2022 05:09:33 -0800 Subject: [PATCH 05/13] Cleanup the README There's duplicate information in the README regarding where to contribute, where to open bugs, where the documentation lies. This change deletes the dups and also puts the references at the end which is standard for most documents. --- README.rst | 38 ++++++++++++-------------------------- 1 file changed, 12 insertions(+), 26 deletions(-) diff --git a/README.rst b/README.rst index bbffffa60..c4cc80d31 100644 --- a/README.rst +++ b/README.rst @@ -46,32 +46,6 @@ it generates a report. Bandit was originally developed within the OpenStack Security Project and later rehomed to PyCQA. -References ----------- - -Bandit docs: https://bandit.readthedocs.io/en/latest/ - -Python AST module documentation: https://docs.python.org/3/library/ast.html - -Green Tree Snakes - the missing Python AST docs: -https://greentreesnakes.readthedocs.org/en/latest/ - -Documentation of the various types of AST nodes that Bandit currently covers -or could be extended to cover: -https://greentreesnakes.readthedocs.org/en/latest/nodes.html - -Contributing ------------- - -Follow our Contributing file: -https://github.com/PyCQA/bandit/blob/main/CONTRIBUTING.md - -Reporting Bugs --------------- - -Bugs should be reported on github. To file a bug against Bandit, visit: -https://github.com/PyCQA/bandit/issues - Show Your Style --------------- @@ -90,3 +64,15 @@ using RST:: .. image:: https://img.shields.io/badge/security-bandit-yellow.svg :target: https://github.com/PyCQA/bandit :alt: Security Status + +References +---------- + +Python AST module documentation: https://docs.python.org/3/library/ast.html + +Green Tree Snakes - the missing Python AST docs: +https://greentreesnakes.readthedocs.org/en/latest/ + +Documentation of the various types of AST nodes that Bandit currently covers +or could be extended to cover: +https://greentreesnakes.readthedocs.org/en/latest/nodes.html From a3d8b4b275c9f3d227657741f32a449f0edb8b3b Mon Sep 17 00:00:00 2001 From: Eric Brown Date: Tue, 15 Feb 2022 05:04:53 -0800 Subject: [PATCH 06/13] Show usage with no arguments (#814) The current behavior is to display an error when no arguments are given that no files were found. This is a non-standard way for a command line. Rather than an error message, no arguments should display the usage of the command itself. Signed-off-by: Eric Brown --- bandit/cli/main.py | 3 ++- tests/functional/test_runtime.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/bandit/cli/main.py b/bandit/cli/main.py index fd436d1f5..65fd280e2 100644 --- a/bandit/cli/main.py +++ b/bandit/cli/main.py @@ -586,8 +586,9 @@ def main(): ) if not args.targets: - LOG.error("No targets found in CLI or ini files, exiting.") + parser.print_usage() sys.exit(2) + # if the log format string was set in the options, reinitialize if b_conf.get_option("log_format"): log_format = b_conf.get_option("log_format") diff --git a/tests/functional/test_runtime.py b/tests/functional/test_runtime.py index de8236465..a9eb21608 100644 --- a/tests/functional/test_runtime.py +++ b/tests/functional/test_runtime.py @@ -32,7 +32,7 @@ def test_no_arguments(self): ] ) self.assertEqual(2, retcode) - self.assertIn("No targets found in CLI or ini files", output) + self.assertIn("usage: bandit [-h]", output) def test_piped_input(self): with open("examples/imports.py") as infile: From 1691b935fb0b8fabe4d3a0c151ae73e8b16e5cc8 Mon Sep 17 00:00:00 2001 From: Eric Brown Date: Thu, 17 Feb 2022 08:59:52 -0800 Subject: [PATCH 07/13] Respect color environment variables if set (#813) According to command line standards [1], a command line should do its best to honor certain environment variables requesting whether color should be part of the standard output. Two such vars include NO_COLOR (if set) and TERM (if set to dumb), when set tell the CLI to disable color. [1] https://clig.dev/#output Partially-fixes #678 Signed-off-by: Eric Brown --- bandit/cli/main.py | 10 +++++++++- bandit/core/manager.py | 10 +++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/bandit/cli/main.py b/bandit/cli/main.py index 65fd280e2..d5eb57dfd 100644 --- a/bandit/cli/main.py +++ b/bandit/cli/main.py @@ -265,7 +265,15 @@ def main(): ' not be listed in "low".', choices=["all", "low", "medium", "high"], ) - output_format = "screen" if sys.stdout.isatty() else "txt" + output_format = ( + "screen" + if ( + sys.stdout.isatty() + and os.getenv("NO_COLOR") is None + and os.getenv("TERM") != "dumb" + ) + else "txt" + ) parser.add_argument( "-f", "--format", diff --git a/bandit/core/manager.py b/bandit/core/manager.py index ddfa04573..1f9b39e41 100644 --- a/bandit/core/manager.py +++ b/bandit/core/manager.py @@ -163,7 +163,15 @@ def output_results( try: formatters_mgr = extension_loader.MANAGER.formatters_mgr if output_format not in formatters_mgr: - output_format = "screen" if sys.stdout.isatty() else "txt" + output_format = ( + "screen" + if ( + sys.stdout.isatty() + and os.getenv("NO_COLOR") is None + and os.getenv("TERM") != "dumb" + ) + else "txt" + ) formatter = formatters_mgr[output_format] report_func = formatter.plugin From 0f4a4959bcc82665fc6bd7f575a87a12678d4484 Mon Sep 17 00:00:00 2001 From: Tyler Wince Date: Thu, 17 Feb 2022 20:42:04 -0700 Subject: [PATCH 08/13] Cannot seek stdin on pipe (#496) * add namespaces for parent attributes * pylint formatting changes * added _Seeker for running seek on sys.stdin * Update node_visitor.py * Update general_hardcoded_password.py * Update general_hardcoded_password.py * pep8 fixes * added list handling for hard fname swaps * updated manager * maintaining list order * Update manager.py * Update manager.py * Update manager.py * Update issue.py * Update node_visitor.py * Update manager.py * Update issue.py * Update context.py * Update issue.py * Update manager.py * Update node_visitor.py * Update tester.py * Update issue.py * Update manager.py * Update context.py * Update node_visitor.py * Update manager.py Co-authored-by: wxu Co-authored-by: Eric Brown --- bandit/core/context.py | 4 ++++ bandit/core/issue.py | 11 ++++++++++- bandit/core/manager.py | 21 ++++++++++++++++----- bandit/core/node_visitor.py | 8 ++++++-- bandit/core/tester.py | 1 + 5 files changed, 37 insertions(+), 8 deletions(-) diff --git a/bandit/core/context.py b/bandit/core/context.py index a0b2fadf1..801b36466 100644 --- a/bandit/core/context.py +++ b/bandit/core/context.py @@ -315,3 +315,7 @@ def is_module_imported_like(self, module): @property def filename(self): return self._context.get("filename") + + @property + def file_data(self): + return self._context.get("file_data") diff --git a/bandit/core/issue.py b/bandit/core/issue.py index e9727a001..2ef6763c2 100644 --- a/bandit/core/issue.py +++ b/bandit/core/issue.py @@ -93,6 +93,7 @@ def __init__( self.text = text self.ident = ident self.fname = "" + self.fdata = None self.test = "" self.test_id = test_id self.lineno = lineno @@ -171,9 +172,17 @@ def get_code(self, max_lines=3, tabbed=False): lmin = max(1, self.lineno - max_lines // 2) lmax = lmin + len(self.linerange) + max_lines - 1 + if self.fname == "": + self.fdata.seek(0) + for line_num in range(1, lmin): + self.fdata.readline() + tmplt = "%i\t%s" if tabbed else "%i %s" for line in range(lmin, lmax): - text = linecache.getline(self.fname, line) + if self.fname == "": + text = self.fdata.readline() + else: + text = linecache.getline(self.fname, line) if isinstance(text, bytes): text = text.decode("utf-8") diff --git a/bandit/core/manager.py b/bandit/core/manager.py index 1f9b39e41..8a4bdc696 100644 --- a/bandit/core/manager.py +++ b/bandit/core/manager.py @@ -4,6 +4,7 @@ # SPDX-License-Identifier: Apache-2.0 import collections import fnmatch +import io import json import logging import os @@ -277,8 +278,12 @@ def run_tests(self): self._show_progress("%s.. " % count, flush=True) try: if fname == "-": - sys.stdin = os.fdopen(sys.stdin.fileno(), "rb", 0) - self._parse_file("", sys.stdin, new_files_list) + open_fd = os.fdopen(sys.stdin.fileno(), "rb", 0) + fdata = io.BytesIO(open_fd.read()) + new_files_list = [ + "" if x == "-" else x for x in new_files_list + ] + self._parse_file("", fdata, new_files_list) else: with open(fname, "rb") as fdata: self._parse_file(fname, fdata, new_files_list) @@ -333,7 +338,7 @@ def _parse_file(self, fname, fdata, new_files_list): except tokenize.TokenError: pass - score = self._execute_ast_visitor(fname, data, nosec_lines) + score = self._execute_ast_visitor(fname, fdata, data, nosec_lines) self.scores.append(score) self.metrics.count_issues( [ @@ -360,7 +365,7 @@ def _parse_file(self, fname, fdata, new_files_list): LOG.debug(" Exception string: %s", e) LOG.debug(" Exception traceback: %s", traceback.format_exc()) - def _execute_ast_visitor(self, fname, data, nosec_lines): + def _execute_ast_visitor(self, fname, fdata, data, nosec_lines): """Execute AST parse on each file :param fname: The name of the file being parsed @@ -370,7 +375,13 @@ def _execute_ast_visitor(self, fname, data, nosec_lines): """ score = [] res = b_node_visitor.BanditNodeVisitor( - fname, self.b_ma, self.b_ts, self.debug, nosec_lines, self.metrics + fname, + fdata, + self.b_ma, + self.b_ts, + self.debug, + nosec_lines, + self.metrics, ) score = res.process(data) diff --git a/bandit/core/node_visitor.py b/bandit/core/node_visitor.py index 293f18d49..efa491bd2 100644 --- a/bandit/core/node_visitor.py +++ b/bandit/core/node_visitor.py @@ -15,7 +15,9 @@ class BanditNodeVisitor: - def __init__(self, fname, metaast, testset, debug, nosec_lines, metrics): + def __init__( + self, fname, fdata, metaast, testset, debug, nosec_lines, metrics + ): self.debug = debug self.nosec_lines = nosec_lines self.seen = 0 @@ -25,6 +27,7 @@ def __init__(self, fname, metaast, testset, debug, nosec_lines, metrics): } self.depth = 0 self.fname = fname + self.fdata = fdata self.metaast = metaast self.testset = testset self.imports = set() @@ -37,7 +40,7 @@ def __init__(self, fname, metaast, testset, debug, nosec_lines, metrics): try: self.namespace = b_utils.get_module_qualname_from_path(fname) except b_utils.InvalidModulePath: - LOG.info( + LOG.warning( "Unable to find qualified name for module: %s", self.fname ) self.namespace = "" @@ -214,6 +217,7 @@ def pre_visit(self, node): self.context["node"] = node self.context["linerange"] = b_utils.linerange_fix(node) self.context["filename"] = self.fname + self.context["file_data"] = self.fdata self.seen += 1 LOG.debug( diff --git a/bandit/core/tester.py b/bandit/core/tester.py index 6ae22d4ad..834fa37d1 100644 --- a/bandit/core/tester.py +++ b/bandit/core/tester.py @@ -61,6 +61,7 @@ def run_tests(self, raw_context, checktype): result.fname = temp_context["filename"].decode("utf-8") else: result.fname = temp_context["filename"] + result.fdata = temp_context["file_data"] if result.lineno is None: result.lineno = temp_context["lineno"] From e2fa50184e57ffd9da6140cd1b46321669ee1b99 Mon Sep 17 00:00:00 2001 From: Eric Brown Date: Fri, 18 Feb 2022 04:10:36 -0800 Subject: [PATCH 09/13] Test on operating systems we can support (#804) This adds GitHub Action testing on the various operating systems we can potentially support. At the moment that includes Linux, and macOS. Each OS is put into its own job that way the output is clearer. Closes #405 Signed-off-by: Eric Brown --- .github/workflows/pythonpackage.yml | 87 ++++++----------------------- 1 file changed, 17 insertions(+), 70 deletions(-) diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml index 9c5f11d80..80d788442 100644 --- a/.github/workflows/pythonpackage.yml +++ b/.github/workflows/pythonpackage.yml @@ -9,7 +9,8 @@ jobs: matrix: python-version: [3.7] steps: - - uses: actions/checkout@v1 + - name: Checkout repository + uses: actions/checkout@v1 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v1 with: @@ -29,7 +30,8 @@ jobs: matrix: python-version: [3.7] steps: - - uses: actions/checkout@v1 + - name: Checkout repository + uses: actions/checkout@v1 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v1 with: @@ -43,77 +45,22 @@ jobs: - name: Run tox run: tox -e pep8 - py37: - runs-on: ubuntu-latest + tests: strategy: matrix: - python-version: [3.7] + python-version: [ + ["3.7", "37"], ["3.8", "38"], ["3.9", "39"], ["3.10", "310"] + ] + os: [ubuntu-latest, macos-latest] + runs-on: ${{ matrix.os }} + name: ${{ matrix.os }} (${{ matrix.python-version[0] }}) steps: - - uses: actions/checkout@v1 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v1 + - name: Checkout repository + uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version[0] }} + uses: actions/setup-python@v2 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 py37 - - py38: - runs-on: ubuntu-latest - strategy: - matrix: - python-version: [3.8] - 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 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 - - py310: - runs-on: ubuntu-latest - strategy: - matrix: - python-version: ['3.10'] - steps: - - uses: actions/checkout@v1 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v1 - with: - python-version: ${{ matrix.python-version }} + python-version: ${{ matrix.python-version[0] }} - name: Install dependencies run: | python -m pip install --upgrade pip @@ -121,4 +68,4 @@ jobs: pip install -r test-requirements.txt pip install tox - name: Run tox - run: tox -e py310 + run: tox -e py${{ matrix.python-version[1] }} From 78b0bc15da909af2cc4ce85eb6ba29b37cdee265 Mon Sep 17 00:00:00 2001 From: Eric Brown Date: Sat, 19 Feb 2022 16:47:53 -0800 Subject: [PATCH 10/13] Fix up some warnings and errors in docs (#817) * Some lines missing code-block * Missing blank lines for spacing Signed-off-by: Eric Brown --- bandit/plugins/app_debug.py | 2 +- bandit/plugins/snmp_security_check.py | 1 + bandit/plugins/yaml_load.py | 3 ++- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/bandit/plugins/app_debug.py b/bandit/plugins/app_debug.py index fba17052b..3b18996fe 100644 --- a/bandit/plugins/app_debug.py +++ b/bandit/plugins/app_debug.py @@ -33,7 +33,7 @@ .. [1] https://flask.palletsprojects.com/en/1.1.x/quickstart/#debug-mode .. [2] https://werkzeug.palletsprojects.com/en/1.0.x/debug/ .. [3] https://labs.detectify.com/2015/10/02/how-patreon-got-hacked-publicly-exposed-werkzeug-debugger/ - .. [4] https://cwe.mitre.org/data/definitions/94.html + .. https://cwe.mitre.org/data/definitions/94.html .. versionadded:: 0.15.0 diff --git a/bandit/plugins/snmp_security_check.py b/bandit/plugins/snmp_security_check.py index 1d207cdec..a915ed898 100644 --- a/bandit/plugins/snmp_security_check.py +++ b/bandit/plugins/snmp_security_check.py @@ -20,6 +20,7 @@ def snmp_insecure_version_check(context): :Example: .. code-block:: none + >> Issue: [B508:snmp_insecure_version_check] The use of SNMPv1 and SNMPv2 is insecure. You should use SNMPv3 if able. Severity: Medium Confidence: High diff --git a/bandit/plugins/yaml_load.py b/bandit/plugins/yaml_load.py index dec77ee26..acd67d727 100644 --- a/bandit/plugins/yaml_load.py +++ b/bandit/plugins/yaml_load.py @@ -19,6 +19,8 @@ :Example: +.. code-block:: none + >> Issue: [yaml_load] Use of unsafe yaml load. Allows instantiation of arbitrary objects. Consider yaml.safe_load(). Severity: Medium Confidence: High @@ -28,7 +30,6 @@ 5 y = yaml.load(ystr) 6 yaml.dump(y) - .. seealso:: - https://pyyaml.org/wiki/PyYAMLDocumentation#LoadingYAML From 4a18a920a5acd6b019c78b219beac6a172b0f1c4 Mon Sep 17 00:00:00 2001 From: Eric Brown Date: Sat, 19 Feb 2022 18:35:31 -0800 Subject: [PATCH 11/13] Fix root doc for readthedocs (#818) The readthedocs is using Sphinx 1.8.6 which still uses the legacy term master_doc for the root toctree. Signed-off-by: Eric Brown --- doc/source/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/conf.py b/doc/source/conf.py index cd342ca04..b129ec46c 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -22,7 +22,7 @@ source_suffix = ".rst" # The root toctree document. -root_doc = "index" +master_doc = "index" # General information about the project. project = "Bandit" From 7fbf9d517b900936ac97e7debbd16dc7e532bc27 Mon Sep 17 00:00:00 2001 From: Eric Brown Date: Mon, 21 Feb 2022 16:58:04 -0800 Subject: [PATCH 12/13] Use versioned links to docs (#819) In the report of a Bandit run, there are links to the docs as part of the more information. Today, these links are always to the latest docs. So depending on the version of Bandit you're running, these links could contain inaccurate information for that version. That's why this change makes it so a specific version of Bandit is pinned to refer to a specific version of documentation. Signed-off-by: Eric Brown --- bandit/core/docs_utils.py | 12 +++++++----- setup.cfg | 2 +- tests/unit/core/test_docs_util.py | 10 ++++++---- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/bandit/core/docs_utils.py b/bandit/core/docs_utils.py index 6733ad03a..54ca16510 100644 --- a/bandit/core/docs_utils.py +++ b/bandit/core/docs_utils.py @@ -2,11 +2,13 @@ # Copyright 2016 Hewlett-Packard Development Company, L.P. # # SPDX-License-Identifier: Apache-2.0 -# where our docs are hosted -BASE_URL = "https://bandit.readthedocs.io/en/latest/" +import bandit def get_url(bid): + # where our docs are hosted + base_url = f"https://bandit.readthedocs.io/en/{bandit.__version__}/" + # NOTE(tkelsey): for some reason this import can't be found when stevedore # loads up the formatter plugin that imports this file. It is available # later though. @@ -15,7 +17,7 @@ def get_url(bid): info = extension_loader.MANAGER.plugins_by_id.get(bid) if info is not None: return "{}plugins/{}_{}.html".format( - BASE_URL, + base_url, bid.lower(), info.plugin.__name__, ) @@ -51,6 +53,6 @@ def get_url(bid): kind="imports", id=info["id"], name=info["name"] ) - return BASE_URL + ext.lower() + return base_url + ext.lower() - return BASE_URL # no idea, give the docs main page + return base_url # no idea, give the docs main page diff --git a/setup.cfg b/setup.cfg index 6f960c5da..f00237e92 100644 --- a/setup.cfg +++ b/setup.cfg @@ -5,7 +5,7 @@ description_file = README.rst author = PyCQA author_email = code-quality@python.org -home_page = https://bandit.readthedocs.io/en/latest/ +home_page = https://bandit.readthedocs.io/ license = Apache-2.0 license classifier = Development Status :: 5 - Production/Stable diff --git a/tests/unit/core/test_docs_util.py b/tests/unit/core/test_docs_util.py index d1b8fc6a0..2891f7a82 100644 --- a/tests/unit/core/test_docs_util.py +++ b/tests/unit/core/test_docs_util.py @@ -3,26 +3,28 @@ # SPDX-License-Identifier: Apache-2.0 import testtools -from bandit.core.docs_utils import BASE_URL +import bandit from bandit.core.docs_utils import get_url class DocsUtilTests(testtools.TestCase): """This set of tests exercises bandit.core.docs_util functions.""" + BASE_URL = f"https://bandit.readthedocs.io/en/{bandit.__version__}/" + def test_overwrite_bib_info(self): - expected_url = BASE_URL + ( + expected_url = self.BASE_URL + ( "blacklists/blacklist_calls.html" "#b304-b305-ciphers-and-modes" ) self.assertEqual(get_url("B304"), get_url("B305")) self.assertEqual(expected_url, get_url("B304")) def test_plugin_call_bib(self): - expected_url = BASE_URL + "plugins/b101_assert_used.html" + expected_url = self.BASE_URL + "plugins/b101_assert_used.html" self.assertEqual(expected_url, get_url("B101")) def test_import_call_bib(self): - expected_url = BASE_URL + ( + expected_url = self.BASE_URL + ( "blacklists/blacklist_imports.html" "#b413-import-pycrypto" ) self.assertEqual(expected_url, get_url("B413")) From 528c540b3a39cff8b9486606b93967e32bd02674 Mon Sep 17 00:00:00 2001 From: Eric Brown Date: Fri, 25 Feb 2022 05:14:25 -0800 Subject: [PATCH 13/13] Use CWE link in HTML formatter (#825) The CWE link is currently output in plain text. Given this is an HTML outoput formatter, it's only natural to use reference link. Signed-off-by: Eric Brown --- bandit/formatters/html.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/bandit/formatters/html.py b/bandit/formatters/html.py index 48b6ac37f..ff86d8fa4 100644 --- a/bandit/formatters/html.py +++ b/bandit/formatters/html.py @@ -266,8 +266,8 @@ def report(manager, fileobj, sev_level, conf_level, lines=-1): Test ID: {test_id}
Severity: {severity}
Confidence: {confidence}
- CWE: {cwe}
- File: {path}
+ CWE: CWE-{cwe.id}
+ File: {path}
Line number: {line_number}
More info: {url}
{code} @@ -367,6 +367,7 @@ def report(manager, fileobj, sev_level, conf_level, lines=-1): severity=issue.severity, confidence=issue.confidence, cwe=issue.cwe, + cwe_link=issue.cwe.link(), path=issue.fname, code=code, candidates=candidates,