Repository navigation
fix(secrets): redact only the offending span of a multi-line value, keeping entropy coverage - #1753
Conversation
…he whole property The entropy fallback flags any string of 16+ characters containing one of ten common words at 3.8 bits, which ordinary source clears: on one graph 10,598 of 14,765 Class bodies and 19,353 of 98,706 Function bodies were stored as [REDACTED], and a redacted body is also invisible to the full-text index. A multi-line value is now scanned per line. Regex coverage is unchanged: patterns are written with \\s, which matches a newline, so the whole value is still checked and redacted wholesale when a pattern spans lines, whatever the line pass found. Key blocks go BEGIN to END. Three credential names with no name-anchored pattern get one. Nothing replaces entropy for a body, which the pull request states as a reduction.
|
Someone is attempting to deploy a commit to the shashankss1205's projects Team on Vercel. A member of the Team first needs to authorize it. |
Shashankss1205
left a comment
There was a problem hiding this comment.
Do not merge as-is: explicitly removes entropy detection for multiline content and gives up opaque credential coverage. Agree the security tradeoff or use span-based redaction, then test multiline credentials and PEMs. Independent #1754 identity fix is prepared.
The independent identity-redaction defect now has a focused fix in #1760. That change keeps existing content detection and does not decide the multiline coverage reduction in this PR.
|
Fair point, I've reworked it to redact only the matched span instead of dropping entropy. In multi-line |
Shashankss1205
left a comment
There was a problem hiding this comment.
Re-reviewed the new span-based implementation. Multiline source/docstrings retain non-secret code; captured values and complete/truncated PEM blocks are redacted, token-level entropy coverage remains, and merging with #1760 preserves graph identifiers. All 1,652 combined unit tests pass (8 skips), including the new body-redaction regressions; strict parity and platform E2E pass on #1766. This addresses the earlier whole-body/entropy review concerns. Included with original commits in #1766.
Summary
is_likely_secret's entropy fallback flags a string of 16+ characters at 3.8 bits when it containsone of ten hint words, and a whole code body clears that on its own. On one graph, 10,598 of 14,765
Classbodies and 19,353 of 98,706Functionbodies were stored as[REDACTED], which alsoremoved them from the full-text index. Across 1.72M lines of Java, 64–74% of 4 KB body chunks would
be blanked.
This makes redaction of multi-line
source/docstringspan-based instead of whole-value, whilekeeping entropy coverage. Other properties and single-line values keep whole-value redaction.
What changes
Patterns redact the captured value, or the whole match when nothing is captured
(
AKIA…,ghp_…,Bearer …, JWTs, connection URIs). Patterns that span lines still match, sopassword =\n "hunter2"becomespassword =\n "[REDACTED]". An unquoted captured value that isa plain identifier (
accessToken = currentAccessTokenValue) is a variable reference and isskipped unless it also looks opaque.
Private keys are redacted from BEGIN through END (tolerant spacing, CRLF, blocks split over
string concatenation), or to the end of the value when END was cut off by truncation.
Entropy is kept, using
_shannon_entropyand the 3.8-bit threshold, but per candidate tokeninstead of per body. Candidates are base64/base64url runs of 16+ characters (
_MIN_ENTROPY_LEN)inside string literals, or outside them when not a plain identifier. A token must mix letters and
digits, and either:
_KEY_HINT_RE— the existing hint, scoped to the namerather than the whole body, which is what stops
private,tokenorauthServiceelsewherein a method from tripping it; or
character (a capital starting a word not counted). Random base62/base64 tokens of 20+
characters score at or above that about 90% of the time; identifiers, paths and prose score
below it.
Hex cannot exceed 4 bits, so a named hex token is held to 3.0 bits, the detect-secrets/truffleHog
hex limit. The string literal holding the token is redacted, so no fragment of the secret is left.
Three credential names without a name-anchored pattern get one:
client_secret,refresh/id/session/sas_token,connection_string/shared_key/account_key.scan_and_redactandis_likely_secretare unchanged; routing is a two-line change inscan_props_and_redactto the newscan_body_and_redact.Coverage compared with before
An unnamed opaque literal such as
"8f3a1b2c9d4e5f60718293a4zQ"is now caught, which the scalarrule did not catch. Remaining gaps: unnamed hex shorter than about 40 characters (also missed by the
scalar rule) and unquoted, identifier-shaped opaque values. Both are still caught when named.
Measured
78 of 1,719,720 lines of Java (54 of 16,597 files) get any redaction. They are credentials or
opaque fixtures (hashes, IDs), and in each case only the literal is replaced.
Tests
tests/unit/utils/test_secret_scanner_bodies.pycovers unnamed opaque literals (Java, Python,docstring), named key material and named versus unnamed hex, patterns inside bodies (AKIA,
client_secret, a password split across lines, several credentials in one body), PEM blocks (LFand CRLF, doubled spacing, concatenated, missing END), and ordinary code using
private,token,accessKeyandauthServiceleft untouched;sourceanddocstringare parametrised to behavethe same. Regressions keep a multi-line
valueand a single-linesourceredacted whole.tests/unitpasses (1,534 passed, 8 skipped). It merges cleanly with #1760.Disclosure: this change and its description were prepared with AI assistance (Claude Code) and
reviewed by a human before posting.