Repository navigation
fix(protocol): a hyphen must not decide whether a secret is redacted - #5
Merged
Merged
Conversation
`SENSITIVE_KEY_SUBSTRINGS` carried `api_key` and `apikey` but never `api-key`, and `cookie`
lived only in `SENSITIVE_HEADERS`, which matches exactly and covers the header path alone. So
in a JSON BODY:
{"apiKey": "sk_live_…"} redacted
{"x-api-key": "sk_live_…"} NOT redacted
{"api-key": "sk_live_…"} NOT redacted
{"cookie": "session=…"} NOT redacted
Header-shaped keys appear in bodies constantly — a proxied request, a webhook payload, a
captured config object — and the separator was the only thing deciding it.
Two rules now, because they carry different risks:
- the substring denylist is matched SEPARATOR-INSENSITIVELY. One normalisation beats adding a
hyphenated twin to all 59 entries, which is a list that would have to be edited in pairs for
ever, and it covers `.` and space spellings at the same time.
- the exact HEADER names are consulted as well, for the ones with no substring twin
(`cookie`, `set-cookie`, `x-real-ip`). They are deliberately NOT folded into the substring
list: as a substring, `cookie` would flag `cookieConsent` and `cookie_banner_shown`, which
are ordinary analytics keys. Exact matching costs no false positives.
Found while building source-context scrubbing (closed as a product decision — nobody in the
field scrubs source code). This half stands on its own: it is RUNTIME data, in a place where
we do promise scrubbing, and it is the same class Sentry marks `pii = "true"`.
Broad by design — `isSensitiveKey` decides redaction for JSON bodies, query pairs, span
attributes and local variables — so the whole gate was run with nothing cached, including
e2e: green.
6 mutations, all caught. The last one needed a new test: stripping a LETTER rather than a
separator passes every other case, because the key and the denylist entry are normalised the
same way — while collapsing distinct words together. Drop `a` and `auth` becomes `uth`, which
`south` contains. `south`/`mouth`/`youth` now pin it.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
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.
The defect
SENSITIVE_KEY_SUBSTRINGScarriesapi_keyandapikeybut neverapi-key. Andcookielives only inSENSITIVE_HEADERS, which matches exactly and covers the header path alone. So inside a JSON body:Header-shaped keys appear in bodies constantly — a proxied request, a webhook payload, a captured config object — and a hyphen was the only thing deciding whether a credential shipped.
Two rules, because they carry different risks
The substring denylist is now matched separator-insensitively. One normalisation rather than a hyphenated twin for all 59 entries — a list that would have to be edited in pairs for ever — and it covers
.and space spellings at the same time.The exact header names are consulted too, for the ones with no substring twin:
cookie,set-cookie,x-real-ip. They are deliberately not folded into the substring list — as a substring,cookiewould flagcookieConsentandcookie_banner_shown, which are ordinary analytics keys. Exact matching costs no false positives, and there is a test asserting those three stay unflagged.Scope
Broad by design.
isSensitiveKeydecides redaction for JSON bodies, query pairs, span attributes and local variables, which is why this is its own change rather than a rider on something else. The full gate was run with nothing cached, including e2e — green.Where it came from
Found while building source-context scrubbing, which was closed as a product decision (#4) — no comparable product scrubs source code, and Sentry's split of scrubbing frame
varswhile leavingcontext_linealone is deliberate.This half stands on its own and is on the other side of that line: it is runtime data, in a place where we already promise scrubbing, and it is the same class Sentry marks
pii = "true".Testing
6 mutations, all caught. The last one is worth recording: stripping a letter instead of a separator passes every other test, because the key and the denylist entry are normalised the same way — so
api_keyandx-api-keykeep matching — while quietly collapsing distinct words together. Dropaandauthbecomesuth, whichsouthcontains.south/mouth/youthnow pin that normalisation removes separators and nothing else.🤖 Generated with Claude Code