diff --git a/bandit/plugins/general_hardcoded_password.py b/bandit/plugins/general_hardcoded_password.py index 12f629074..4e6071d34 100644 --- a/bandit/plugins/general_hardcoded_password.py +++ b/bandit/plugins/general_hardcoded_password.py @@ -79,69 +79,112 @@ def hardcoded_password_string(context): """ node = context.node + if isinstance(node._bandit_parent, ast.Assign): - # looks for "candidate='some_string'" + # multiple targets? "a = b.d = c[e] = ..." for targ in node._bandit_parent.targets: if isinstance(targ, ast.Name) and RE_CANDIDATES.search(targ.id): + # looks for "candidate='this_string'" return _report(node.value) elif isinstance(targ, ast.Attribute) and RE_CANDIDATES.search( targ.attr ): + # looks for "o.candidate='this_string'" + return _report(node.value) + elif isinstance(targ, ast.Subscript): + if ( + isinstance(targ.slice, ast.Constant) + and isinstance(targ.slice.value, str) + and RE_CANDIDATES.search(targ.slice.value) + ): + # looks for "d['candidate'] = 'this_string'" + return _report(node.value) + elif isinstance(targ.slice, ast.Name) and RE_CANDIDATES.search( + targ.slice.id + ): + # looks for "d[candidate] = 'this_string'" + return _report(node.value) + + elif isinstance(node._bandit_parent, ast.AnnAssign): + # skip if current node is an annotation string + # e.g. password: 'this_string' = 'other_string' + if node._bandit_parent.annotation == node: + return None + + target_node = node._bandit_parent.target + + if isinstance(target_node, ast.Name) and RE_CANDIDATES.search( + target_node.id + ): + # looks for "candidate: str = 'this_str'" + return _report(node.value) + elif isinstance(target_node, ast.Attribute) and RE_CANDIDATES.search( + target_node.attr + ): + # looks for "o.candidate: str = 'this_str'" + return _report(node.value) + elif isinstance(target_node, ast.Subscript): + if ( + isinstance(target_node.slice, ast.Constant) + and isinstance(target_node.slice.value, str) + and RE_CANDIDATES.search(target_node.slice.value) + ): + # looks for "d['candidate']: str = 'this_str'" + return _report(node.value) + elif isinstance( + target_node.slice, ast.Name + ) and RE_CANDIDATES.search(target_node.slice.id): + # looks for "d[candidate]: str = 'some_str'" return _report(node.value) - elif ( - isinstance(node._bandit_parent, ast.Dict) - and node in node._bandit_parent.keys - and RE_CANDIDATES.search(node.value) - ): - # looks for "{'candidate': 'some_string'}" + elif isinstance(node._bandit_parent, ast.Dict): + # skip if current node is a dictionary key + if node in node._bandit_parent.keys: + return None + dict_node = node._bandit_parent - pos = dict_node.keys.index(node) - value_node = dict_node.values[pos] - if isinstance(value_node, ast.Constant): - return _report(value_node.value) - - elif isinstance( - node._bandit_parent, ast.Subscript - ) and RE_CANDIDATES.search(node.value): - # 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.Constant) - and isinstance(assign.value.value, str) + pos = dict_node.values.index(node) + key_node = dict_node.keys[pos] + + if isinstance(key_node, ast.Constant) and RE_CANDIDATES.search( + key_node.value ): - return _report(assign.value.value) - - elif isinstance(node._bandit_parent, ast.Index) and RE_CANDIDATES.search( - node.value - ): - # looks for "dict[candidate]='some_string'" - # assign -> subscript -> index -> string - assign = node._bandit_parent._bandit_parent._bandit_parent - if ( - isinstance(assign, ast.Assign) - and isinstance(assign.value, ast.Constant) - and isinstance(assign.value.value, str) + # looks for "{'candidate': 'some_string'}" + return _report(node.value) + elif isinstance(key_node, ast.Name) and RE_CANDIDATES.search( + key_node.id ): - return _report(assign.value.value) + # looks for "{candidate: 'some_string'}" + return _report(node.value) elif isinstance(node._bandit_parent, ast.Compare): # looks for "candidate == 'some_string'" - comp = node._bandit_parent - if isinstance(comp.left, ast.Name): - if RE_CANDIDATES.search(comp.left.id): - if isinstance( - comp.comparators[0], ast.Constant - ) and isinstance(comp.comparators[0].value, str): - return _report(comp.comparators[0].value) - elif isinstance(comp.left, ast.Attribute): - if RE_CANDIDATES.search(comp.left.attr): - if isinstance( - comp.comparators[0], ast.Constant - ) and isinstance(comp.comparators[0].value, str): - return _report(comp.comparators[0].value) + comp_node = node._bandit_parent + operand_nodes = [comp_node.left] + comp_node.comparators + for operand_node in operand_nodes: + if isinstance(operand_node, ast.Name) and RE_CANDIDATES.search( + operand_node.id + ): + # looks for "password == 'this_string'" + return _report(node.value) + elif isinstance( + operand_node, ast.Attribute + ) and RE_CANDIDATES.search(operand_node.attr): + # looks for "o.password == 'this_string'" + return _report(node.value) + elif isinstance(operand_node, ast.Subscript): + if ( + isinstance(operand_node.slice, ast.Constant) + and isinstance(operand_node.slice.value, str) + and RE_CANDIDATES.search(operand_node.slice.value) + ): + # looks for "d["candidate"] == 'this_str'" + return _report(node.value) + elif isinstance( + operand_node.slice, ast.Name + ) and RE_CANDIDATES.search(operand_node.slice.id): + # looks for "d[candidate] == 'some_str'" + return _report(node.value) @test.checks("Call") diff --git a/examples/hardcoded-passwords.py b/examples/hardcoded-passwords.py index b1665ae7b..76eb1d4f5 100644 --- a/examples/hardcoded-passwords.py +++ b/examples/hardcoded-passwords.py @@ -1,57 +1,405 @@ -# Possible hardcoded password: 'class_password' +#----------------------------------------------------------------------------- +# ASSIGNMENTS +#----------------------------------------------------------------------------- + +# Possible hardcoded password: 'this_string' +# Severity: Low Confidence: Medium +password = "this_string" + +# not! +a = "password" + + +# not! +password.a = "this_string" + +# Possible hardcoded password: 'this_string' +# Severity: Low Confidence: Medium +a.password = "this_string" + +# not! +a.b = "password" + +# not! +password['b'] = "this_string" + +# Possible hardcoded password: 'this_string' +# Severity: Low Confidence: Medium +a['password'] = "this_string" + +# not! +a['b'] = "password" + + +# not! +password[b] = "this_string" + +# Possible hardcoded password: 'this_string' +# Severity: Low Confidence: Medium +a[password] = "this_string" + +# not! +a[b] = "password" + + +# Possible hardcoded password: 'this_string' +# Severity: Low Confidence: Medium +password = b.c = d['e'] = f[g] = "this_string" + +# not! +a = password.c = d['e'] = f[g] = "this_string" + +# Possible hardcoded password: 'this_string' +# Severity: Low Confidence: Medium +a = b.password = d['e'] = f[g] = "this_string" + +# not! +a = b.c = password['e'] = f[g] = "this_string" + +# Possible hardcoded password: 'this_string' +# Severity: Low Confidence: Medium +a = b.c = d['password'] = f[g] = "this_string" + +# not! +a = b.c = d['e'] = password[g] = "this_string" + +# Possible hardcoded password: 'this_string' +# Severity: Low Confidence: Medium +a = b.c = d['e'] = f[password] = "this_string" + +# not! +a = b.c = d['e'] = f[g] = "password" + + +# Possible hardcoded password: 'this_string' # Severity: Low Confidence: Medium class SomeClass: - password = "class_password" + password = "this_string" + + +#----------------------------------------------------------------------------- +# ANNOTATED ASSIGNMENTS +#----------------------------------------------------------------------------- + +# Possible hardcoded password: 'this_string' +# Severity: Low Confidence: Medium +password: str = "this_string" + +# Possible hardcoded password: 'this_string' +# Severity: Low Confidence: Medium +password: "str" = "this_string" + +# not! +a: password = "this_string" + +# not! +a: "password" = "this_string" + +# not! +a: str = "password" + +# not! +a: "str" = "password" + + +# not! +password.b: str = "this_string" + +# not! +password.b: "str" = "this_string" + +# Possible hardcoded password: 'this_string' +# Severity: Low Confidence: Medium +a.password: str = "this_string" + +# Possible hardcoded password: 'this_string' +# Severity: Low Confidence: Medium +a.password: "str" = "this_string" + +# not! +a.b: password = "this_string" + +# not! +a.b: "password" = "this_string" + +# not! +a.b: str = "password" + +# not! +a.b: "str" = "password" + + +# not! +password["b"]: str = "this_string" + +# not! +password["b"]: "str" = "this_string" + +# Possible hardcoded password: 'this_string' +# Severity: Low Confidence: Medium +a["password"]: str = "this_string" + +# Possible hardcoded password: 'this_string' +# Severity: Low Confidence: Medium +a["password"]: "str" = "this_string" + +# not! +a["b"]: password = "this_string" + +# not! +a["b"]: "password" = "this_string" + +# not! +a["b"]: str = "password" + +# not! +a["b"]: "str" = "password" + + +# not! +password[b]: str = "this_string" + +# not! +password[b]: "str" = "this_string" + +# Possible hardcoded password: 'this_string' +# Severity: Low Confidence: Medium +a[password]: str = "this_string" + +# Possible hardcoded password: 'this_string' +# Severity: Low Confidence: Medium +a[password]: "str" = "this_string" + +# not! +a[b]: password = "this_string" + +# not! +a[b]: "password" = "this_string" + +# not! +a[b]: str = "password" + +# not! +a[b]: "str" = "password" + + +#----------------------------------------------------------------------------- +# DICTIONARIES +#----------------------------------------------------------------------------- + +# Possible hardcoded password: 'this_string' +# Severity: Low Confidence: Medium +{"password": "this_string"} + +# not! +{"a": "password"} + + +# Possible hardcoded password: 'this_string' +# Severity: Low Confidence: Medium +{password: "this_string"} + +# not! +{a: "password"} + + +#----------------------------------------------------------------------------- +# COMPARISONS +#----------------------------------------------------------------------------- + +# Possible hardcoded password: 'this_string' +# Severity: Low Confidence: Medium +password == "this_string" + +# not! +a == "password" + + +# not! +password.b == "this_string" + +# Possible hardcoded password: 'this_string' +# Severity: Low Confidence: Medium +a.password == "this_string" + +# not! +a.b == "password" + + +# not! +password["b"] == "this_string" + +# Possible hardcoded password: 'this_string' +# Severity: Low Confidence: Medium +a["password"] == "this_string" + +# not! +a["b"] == "password" + + +# not! +password[b] == "this_string" + +# Possible hardcoded password: 'this_string' +# Severity: Low Confidence: Medium +a[password] == "this_string" + +# not! +a[b] == "password" + + +# Possible hardcoded password: 'this_string' +# Severity: Low Confidence: Medium +password != "this_string" + +# not! +a != "password" + + +# Possible hardcoded password: 'this_string' +# Severity: Low Confidence: Medium +"this_string" == password + +# not! +"password" == b + + +# not! +password == b == c + +# not! +a == password == c + +# not! +a == b == password + +# Possible hardcoded password: 'third_string' +# Severity: Low Confidence: Medium +password == b == "third_string" + +# Possible hardcoded password: 'third_string' +# Severity: Low Confidence: Medium +a == password == "third_string" + +# not! +a == b == "password" + +# Possible hardcoded password: 'other_string' +# Severity: Low Confidence: Medium +password == "other_string" == c + +# not! +a == "password" == c + +# Possible hardcoded password: 'other_string' +# Severity: Low Confidence: Medium +a == "other_string" == password + +# Possible hardcoded password: 'other_string' +# Severity: Low Confidence: Medium +# Possible hardcoded password: 'third_string' +# Severity: Low Confidence: Medium +password == "other_string" == "third_string" + +# not! +a == "password" == "third_string" + +# not! +a == "other_string" == "password" + +# not! +"password" == b == c + +# Possible hardcoded password: 'this_string' +# Severity: Low Confidence: Medium +"this_string" == password == c + +# Possible hardcoded password: 'this_string' +# Severity: Low Confidence: Medium +"this_string" == b == password + +# not! +"password" == b == "third_string" + +# Possible hardcoded password: 'this_string' +# Severity: Low Confidence: Medium +# Possible hardcoded password: 'third_string' +# Severity: Low Confidence: Medium +"this_string" == password == "third_string" + +# not! +"this_string" == b == "password" + +# not! +"password" == "other_string" == c + +# not! +"this_string" == "password" == c + +# Possible hardcoded password: 'this_string' +# Severity: Low Confidence: Medium +# Possible hardcoded password: 'other_string' +# Severity: Low Confidence: Medium +"this_string" == "other_string" == password + +# not! +"password" == "other_string" == "third_string" + +# not! +"this_string" == "password" == "third_string" + +# not! +"this_string" == "other_string" == "password" + + +#----------------------------------------------------------------------------- +# FUNCTION CALLS +#----------------------------------------------------------------------------- + +# Possible hardcoded password: 'blerg' +# Severity: Low Confidence: Medium +doLogin(password="blerg") + + +#----------------------------------------------------------------------------- +# FUNCTION DEFINITIONS +#----------------------------------------------------------------------------- # 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!") - -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"): pass + def NoMatch3(a, b): pass -# Possible hardcoded password: 'blerg' -# Severity: Low Confidence: Medium -doLogin(password="blerg") -# Possible hardcoded password: 'blerg' +#----------------------------------------------------------------------------- +# REPORTED ISSUES +#----------------------------------------------------------------------------- + +# https://github.com/PyCQA/bandit/issues/313 + +# Possible hardcoded password: 'pass' # Severity: Low Confidence: Medium -password = "blerg" +log({"server": server, "password": 'pass', "user": user}) -# Possible hardcoded password: 'blerg' +# not! +log({"server": server, "password": password, "user": user}) + +# Possible hardcoded password: 'pass' # Severity: Low Confidence: Medium -password["password"] = "blerg" +log(password='pass') + + +# https://github.com/PyCQA/bandit/issues/386 # Possible hardcoded password: 'secret' # Severity: Low Confidence: Medium @@ -61,14 +409,61 @@ def NoMatch3(a, b): # Severity: Low Confidence: Medium email_pwd = 'emails_secret' -# Possible hardcoded password: 'd6s$f9g!j8mg7hw?n&2' + +# https://github.com/PyCQA/bandit/issues/551 + +# Possible hardcoded password: 'aaaaaaa' +# Severity: Low Confidence: Medium +app.config['SECRET_KEY'] = 'aaaaaaa' + + +# https://github.com/PyCQA/bandit/issues/605 + +# Possible hardcoded password: 'root' +# Severity: Low Confidence: Medium +def fooBar(password): + if password == "root": + print("OK, logged in") + + +# https://github.com/PyCQA/bandit/issues/639 + +# Possible hardcoded password: '1238aoufhz8xyf3jr;' +# Severity: Low Confidence: Medium +password = "1238aoufhz8xyf3jr;" + + +# https://github.com/PyCQA/bandit/issues/642 + +# Possible hardcoded password: 'password' +# Severity: Low Confidence: Medium +class MyConfig: + my_password: str = 'password' + + +# https://github.com/PyCQA/bandit/issues/759 + +# Possible hardcoded password: '12123123' # Severity: Low Confidence: Medium -my_secret_password_for_email = 'd6s$f9g!j8mg7hw?n&2' +password = "12123123" -# Possible hardcoded password: '1234' +# Possible hardcoded password: '12123123' # Severity: Low Confidence: Medium -passphrase='1234' +self.password = "12123123" + + +# https://github.com/PyCQA/bandit/issues/1267 + +# Possible hardcoded password: '12345' +# Severity: Low Confidence: Medium +{"password": "12345"} + +#----------------------------------------------------------------------------- +# OTHER +#----------------------------------------------------------------------------- + +# TODO: what's the purpose of this example? # Possible hardcoded password: None # Severity: High Confidence: High def __init__(self, auth_scheme, auth_token=None, auth_username=None, auth_password=None, auth_link=None, **kwargs): @@ -79,6 +474,7 @@ def __init__(self, auth_scheme, auth_token=None, auth_username=None, auth_passwo self.auth_link = auth_link self.kwargs = kwargs +# TODO: what's the purpose of this example? # Possible hardcoded password: None # Severity: High Confidence: High from oslo_config import cfg @@ -87,19 +483,3 @@ def __init__(self, auth_scheme, auth_token=None, auth_username=None, auth_passwo default='', secret=True, ) - -# Possible hardcoded password: 'pass' -# Severity: Low Confidence: Medium -# https://github.com/PyCQA/bandit/issues/313 -log({"server": server, "password": 'pass', "user": user}) - -# ... but not: -log({"server": server, "password": password, "user": user}) - -# Possible hardcoded password: '12345' -# Severity: Low Confidence: Medium -# https://github.com/PyCQA/bandit/issues/1267 -info = {"password": "12345"} - -# ... but not: -info = {"password": password} diff --git a/tests/functional/test_functional.py b/tests/functional/test_functional.py index 08b1c5c5b..0b8e9006b 100644 --- a/tests/functional/test_functional.py +++ b/tests/functional/test_functional.py @@ -168,8 +168,8 @@ def test_exec(self): def test_hardcoded_passwords(self): """Test for hard-coded passwords.""" expect = { - "SEVERITY": {"UNDEFINED": 0, "LOW": 16, "MEDIUM": 0, "HIGH": 0}, - "CONFIDENCE": {"UNDEFINED": 0, "LOW": 0, "MEDIUM": 16, "HIGH": 0}, + "SEVERITY": {"UNDEFINED": 0, "LOW": 51, "MEDIUM": 0, "HIGH": 0}, + "CONFIDENCE": {"UNDEFINED": 0, "LOW": 0, "MEDIUM": 51, "HIGH": 0}, } self.check_example("hardcoded-passwords.py", expect)