Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 17 additions & 3 deletions bandit/plugins/general_hardcoded_password.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@
from bandit.core import issue
from bandit.core import test_properties as test

RE_WORDS = "(pas+wo?r?d|pass(phrase)?|pwd|token|secrete?)"
RE_WORDS = "(pas+wo?r?d|pass(phrase)?|pwd|token|secrete?|key)"
RE_CANDIDATES = re.compile(
"(^{0}$|_{0}_|^{0}_|_{0}$)".format(RE_WORDS), re.IGNORECASE
"(^{0}$|_{0}_|^{0}_|_{0}$|{0})".format(RE_WORDS), re.IGNORECASE
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RE_CANDIDATES is now an unbounded substring match (|{0}) and RE_WORDS now includes key. This combination will dramatically increase false positives because it matches credential words inside unrelated identifiers and dict keys (e.g., a dict literal with key 'key' or variables like keyfile, monkey, compass, etc.). Since B105/B106/B107 all share this regex, it will also affect unrelated examples and likely break existing functional expectations.

Suggestion: keep boundary-based matching for common/short terms (especially pass and key), and if you need to catch patterns like DATABASEPASSWORD, add a separate “strong word” substring check (e.g., only for password|passphrase|token|secret) or normalize identifiers (camelCase -> snake_case) and apply the existing boundary regex. Also consider scoping key to B106/B107 only (keyword args/defaults) rather than B105 dict/variable names to reduce noise.

Suggested change
"(^{0}$|_{0}_|^{0}_|_{0}$|{0})".format(RE_WORDS), re.IGNORECASE
"(^{0}$|_{0}_|^{0}_|_{0}$)".format(RE_WORDS), re.IGNORECASE

Copilot uses AI. Check for mistakes.
)


Expand Down Expand Up @@ -83,7 +83,13 @@ def hardcoded_password_string(context):
if isinstance(node._bandit_parent, ast.Assign):
# looks for "candidate='some_string'"
for targ in node._bandit_parent.targets:
if isinstance(targ, ast.Name) and RE_CANDIDATES.search(targ.id):
if isinstance(targ, ast.Name):
normalized = re.sub(
r"(?<=[a-z])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])",
Comment on lines +86 to +88
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This normalized = re.sub(...).lower() line exceeds the repo’s Black limit (pre-commit runs Black with --line-length=79), so it will fail formatting checks. Please reformat it (and ideally extract the normalization regex/pattern into a module-level constant or small helper) to keep the Assign-target logic readable.

Copilot uses AI. Check for mistakes.
"_",
targ.id,
).lower()
if isinstance(targ, ast.Name) and RE_CANDIDATES.search(normalized):
return _report(node.value)
elif isinstance(targ, ast.Attribute) and RE_CANDIDATES.search(
targ.attr
Expand Down Expand Up @@ -143,6 +149,14 @@ def hardcoded_password_string(context):
comp.comparators[0], ast.Constant
) and isinstance(comp.comparators[0].value, str):
return _report(comp.comparators[0].value)
elif isinstance(comp.left, ast.Subscript):
slc = comp.left.slice
if isinstance(slc, ast.Constant) and isinstance(slc.value, str):
if RE_CANDIDATES.search(slc.value):
if isinstance(
comp.comparators[0], ast.Constant
) and isinstance(comp.comparators[0].value, str):
return _report(comp.comparators[0].value)


@test.checks("Call")
Expand Down