Skip to content

fix(secrets): stop reporting password hashes as hardcoded credentials - #214

Merged
SizzleUnrlsd merged 2 commits into
mainfrom
fix/password-hash-credentials
Oct 4, 2026
Merged

SizzleUnrlsd merged 2 commits into
mainfrom
fix/password-hash-credentials

Conversation

@SizzleUnrlsd

@SizzleUnrlsd SizzleUnrlsd commented Oct 4, 2026 •

Copy link
Copy Markdown
Contributor

Closes #211.

hardcoded-credential reported password hashes as hardcoded passwords: a credential-like name bound to $argon2id$... or $2b$... was judged like a plaintext value, since looks_like_digest only knows hexadecimal digests and subresource-integrity hashes.

Change

  • plugins/secrets.py: is_password_hash recognises a password hash by the shape of its format, and SecretDetector.judge no longer reports such a value as a credential. Formats:
    • modular crypt and PHC strings: $1$, $5$, $6$, $7$, $y$ (yescrypt), $apr1$, $argon2i|d|id$, $pbkdf2[-sha1|256|512]$, $scrypt$, whose identifier must be followed by at least two fields of the hash alphabet; bcrypt ($2a|b|x|y$) with its fixed shape;
    • Werkzeug's scrypt:...$salt$hex and pbkdf2:...$salt$hex;
    • Django's pbkdf2_sha256$..., pbkdf2_sha1$..., argon2$..., bcrypt$..., bcrypt_sha256$..., scrypt$..., md5$..., sha1$....
  • An identifier alone ($argon2id$, as in mitmproxy's invalid-hash test) is an incomplete hash and reveals nothing. An identifier followed by text of another shape ($2y$Summer2024!, $scrypt$MyRealPassw0rd) is a plaintext password and is still reported.
  • A hash value then reaches the high-entropy check, which never matches it ($ is outside the token alphabet), so nothing replaces the finding.
  • The judge is shared by Python literals and configuration values, so the change applies to both, as intended.

The first version recognised the identifier alone; the review showed that it skipped plaintext values opening with an identifier and missed the Werkzeug and Django encodings, the most common in Python code. Both are addressed above.

Validation

  • New tests/test_password_hashes.py (no existing test changed): 20 hash values of every listed format are not reported (all failed before the change); six plaintext values are still reported, including $2y$Summer2024!, $scrypt$MyRealPassw0rd and $5$hunter2-plain. The hash check stays linear on 8 MB adversarial strings.
  • Full suite: 1669 passed, 1 failed. The failure, test_packaging::test_the_version_has_one_source_of_truth, comes from a stale local editable install (metadata 0.8.0 vs 0.21.0) and fails identically on main.
  • ruff check, ruff format --check on the new file and mypy pass.
  • Regression snapshots: unchanged (read-only diff of every repository).
  • mitmproxy at be75825, enterprise plugins at ac35460, [email protected], no baseline, compared with the 87 results of the 2026-10-02 reference: exactly F050 and F051 (test_webaddons.py:37, :48) disappear, nothing else changes. 87 → 85 results, high 18 → 16. Plugin digests are identical.

Not covered

Docker Compose's escaped form ($$2y$$...) is not recognised.

A value in PHC or modular crypt format (argon2, bcrypt, SHA-crypt, PBKDF2,
scrypt) is what an application stores instead of the password and does not
reveal it. It is recognised by its algorithm identifier, so an incomplete
hash is not reported either; plaintext values are still reported.

Closes #211
@SizzleUnrlsd SizzleUnrlsd added the bug Something isn't working label Oct 4, 2026
@SizzleUnrlsd SizzleUnrlsd self-assigned this Oct 4, 2026
Review of the hash check: an algorithm identifier followed by plaintext, such
as $2y$Summer2024!, was skipped, while Werkzeug, Django, md5-crypt, apr1 and
yescrypt hashes were still reported. A value is now a hash when the identifier
is followed by fields of its format's shape, or stands alone as an incomplete
hash.

Refs #211
@SizzleUnrlsd
SizzleUnrlsd merged commit 803de0a into main Oct 4, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

hardcoded-credential: password hashes are reported as plaintext passwords

1 participant