fix: filter structured-text false positives from the generic-secret detector - #29
Merged
Merged
Conversation
…etector
The generic-secret rule matches a keyword (password/secret/api_key/...) plus a
high-entropy value, but its value alphabet and gates (length + entropy + a small
wordlist) did not tell an opaque credential apart from structured text. On
firmware with a web UI it produced almost entirely false positives: printf/URL
format strings, JavaScript object access, and localization strings, all of which
are long and diverse enough to clear the entropy gate.
Add a value-shape filter (generic_value_is_noise) that rejects a value which is
(1) a format string ('%' + a printf conversion), (2) an assignment/query shape
(an interior '=' or any '&'; a trailing '=' run is treated as base64 padding),
or (3) a no-digit word/identifier chain (after trimming non-alphanumeric ends,
the core is only letters and '.'/'_'/'-' separators). Real credential values
carry a digit or base64 density and are kept.
Measured across the corpus: false positives from web/source/localization drop to
zero (e.g. a router web UI 7 -> 0, an NVR web UI 32 -> 0), while real values are
retained (a device's base64-encoded config credentials 29 -> 29, a JWT-shaped
access token kept). The one accepted false negative, documented in the code, is
a purely-alphabetic hardcoded password with no digit, at this pattern tier.
Add test_generic_secret_shape covering the three reject shapes (including the
leading/trailing-punctuation and consecutive-separator variants seen in minified
JS) and two must-keep positives.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
The
generic-secretdetector (keyword likepassword/secret/api_key+ a high-entropy value) produced almost entirely false positives on firmware that ships a web UI. Its value alphabet and gates (length + entropy + a small wordlist) did not distinguish an opaque credential from structured text — printf/URL format strings, JavaScript object access, and localization strings are all long and diverse enough to clear the entropy gate.Fix
Add a value-shape filter (
generic_value_is_noise) applied to the extracted value, rejecting it when it is:%followed by a printf conversion (%s,%02x, …);=or any&(a trailing run of=is treated as base64 padding);./_/-separators (dotted member access, snake/camel identifiers, hyphenated/localization words).Real credential values carry a digit or base64 density and are kept.
Trade-off
Precision-first at the
patterntier: the one accepted false negative (documented in the code) is a purely-alphabetic hardcoded password with no digit.Validation
Measured before/after across a set of real firmware images:
Tests
test_generic_secret_shapecovers the three reject shapes — including the leading/trailing-punctuation and consecutive-separator variants seen in minified JS — plus two must-keep positives (a JWT-shaped token and a digit-bearing password). Full unit suite (1397 checks) and integration suite pass; clean under ASan+UBSan; secrets fuzzer clean.