redaction: catch compound secret keys + non-bearer auth schemes; stop scanner over-redaction#249
Conversation
… scanner over-redaction Hardens the secret-redaction last line of defense after an audit of internal/redaction + internal/secrets. 1. Compound secret keys are now redacted. IsSensitiveKey was exact-match only, so db_password, session_secret, stripe_secret_key, auth_token, csrf_token, ssh_private_key, backup_api_key, etc. passed through in cleartext. A new keyLooksSensitive applies conservative structural heuristics (secret / password / passphrase / credential / apikey segments; a singular trailing "_token"; api_key / private_key pairs). Critically, the agent's token-COUNT fields (max_tokens, prompt_tokens, token_count) and ordinary *_key fields (primary_key, public_key, cache_key) are deliberately NOT redacted. 2. Authorization headers with non-bearer/basic schemes were leaking. The header pattern now also covers token, apikey, digest, negotiate, oauth, and aws4-hmac-sha256. 3. The secrets scanner over-redacted ordinary text: its prefixed patterns (sk-, gh*, AIza, AKIA, xox, github_pat, jwt) lacked the word boundaries that internal/redaction already uses, so kebab-case words like "task-management-and-coordination" matched as a fake key. Added \b anchors. Design note: a free-text "key: value" colon pattern was prototyped but dropped -- it mangled "file.go:line: message" output (a filename's "secret" segment looked like a sensitive key), the same over-redaction class fix #3 addresses. The structured paths (RedactValue for maps/structs, plus assign/JSON/query forms in RedactString) are covered; free-text colon-form remains out of scope. Tests: compound-key + token-count-safety matrix, auth-scheme redaction, the file:line guard, and scanner boundary positives/negatives.
ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Free Run ID: 📒 Files selected for processing (4)
WalkthroughRedaction heuristics in ChangesSecret Detection Accuracy Improvements
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Note 🎁 Summarized by CodeRabbit FreeYour organization is on the Free plan. CodeRabbit will generate a high-level summary and a walkthrough for each pull request. For a comprehensive line-by-line review, please upgrade your subscription to CodeRabbit Pro by visiting https://app.coderabbit.ai/login. Comment |
Zero automated PR reviewVerdict: No blockers found Blockers
Validation
ScopeHead: This deterministic review checks validation status and basic diff hygiene. A human reviewer still owns product judgment and design quality. |
anandh8x
left a comment
There was a problem hiding this comment.
High: #249 still leaks credentials for multi-part authorization schemes.
internal/redaction/redaction.go:86 captures only one non-space/comma/semicolon token after Digest or AWS4-HMAC-SHA256, then internal/redaction/redaction.go:180 replaces only that captured token. Real headers such as Authorization: Digest username=..., nonce=..., response=... or AWS SigV4 Credential=..., SignedHeaders=..., Signature=... leave response / Signature visible. Please redact the full auth header value for parameterized schemes and add tests with realistic Digest and AWS headers.
Verification: diff review plus git diff --check passed for this PR.
Vasanthdev2004
left a comment
There was a problem hiding this comment.
APPROVE
Walked the 4 files (216 +, 8 -) at 39045f2 against origin/main. Three independent fixes to the redaction last line of defense, each with a tight test surface. No regressions on the existing 5 redaction tests or 7 secrets tests, and the 7 new tests all pass. The test for the explicit keyLooksSensitive negatives (token-counts, ordinary *_key fields, idempotency_key) is the right discipline for an additive heuristic.
1. Compound secret keys via keyLooksSensitive
internal/redaction/redaction.go:104-145 adds a new structural check layered behind the existing explicit sensitiveKeys map and ExtraSensitiveKeys options. The check is conservative and well-documented:
secretKeySegments(password, passwd, passphrase, secret, credential, credentials, apikey) — fires on any underscore-delimited segment match.<x>_token(singular, trailing only) — catchesauth_token,csrf_token,vault_token; deliberately excludestokens(plural count) andtoken_<x>(prefix form) somax_tokens,prompt_tokens,token_countstay un-redacted. Thei > 0 && i == len(segments)-1guard is the right shape.api_key/private_key(adjacent segments) — narrow switch on the previous segment, not a barekeymatch, soprimary_key,public_key,cache_key,foreign_key,idempotency_keystay un-redacted.
Verified the explicit test list end-to-end. db_password, session_secret, stripe_secret_key, auth_token, csrf_token, ssh_private_key, backup_api_key, aws_credential, gcp_credentials all hit the new check. max_tokens, prompt_tokens, primary_key, public_key, idempotency_key, username, message correctly stay un-redacted. The new check is purely additive — anything the explicit list or ExtraSensitiveKeys flagged before still gets flagged.
2. Auth-scheme expansion in headerPattern
internal/redaction/redaction.go:86 extends the scheme alternation from (bearer|basic) to (bearer|basic|token|apikey|api-key|digest|negotiate|oauth|aws4-hmac-sha256). Catches:
- GitHub's
Authorization: token ghp_...(the most commontokenform, distinct from bearer) - Custom-provider
ApiKey/API-Keyheaders (case-insensitive match on the scheme, soApiKeyandAPI-Keyboth hit) - Proxy / Kerberos
Proxy-Authorization: Digest .../Negotiate ... - SigV4
Authorization: AWS4-HMAC-SHA256 Credential=.../..., SignedHeaders=..., Signature=...(the trailing([^\s,;]+)captures the full credential+signature blob, which is the right thing to redact) Authorization: OAuth ...(custom OAuth realms)
The opaque-value test cases in TestRedactString_AuthHeaderSchemes isolate the scheme fix from the text-pattern fallback (the values are deliberately non-prefixed so only the header/colon logic can catch them). Solid.
3. Word-boundary anchors on scanner patterns
internal/secrets/scanner.go:30-49 adds a leading \b to all 7 prefixed patterns (AWS, GitHub, GitHub PAT, Slack, Google, OpenAI, JWT) and to the private-key block the doc comment is updated. The fix closes the over-redaction class where sk- inside task-management-and-coordination matched as a fake openai_key because the regex fired mid-word. The comment explicitly enumerates the satisfied-by-real-secrets delimiters (space, quote, =, :, start-of-string), so the regression-risk surface is documented.
TestScan_NoOverRedactionOnKebabWords covers five representative over-redaction cases (task-management-and-coordination, ask-the-user-about-this, risk-assessment-and-mitigation, disk-usage-monitoring-and-alerting, desk-booking-and-reservation). TestScan_RealSecretsStillCaught covers five positives (env-export, JSON, mid-sentence, raw AKIA..., raw ghp_...) to confirm no real coverage is lost. The real-secrets test would have caught a regression where the \b anchor is misplaced.
Design call worth flagging
The PR body's "dropped prototype" note — a free-text key: value colon pattern was prototyped and abandoned because it mangled file.go:line: message output. The decision is correct, and TestRedactString_PreservesFileLineColons is exactly the regression guard you'd want to keep that decision honest: a filename containing the literal segment secret plus a colon, line, colon, and a real token on the same line is exactly the test case where a free-text colon pattern would over-reach. Good test to keep.
Non-blocking follow-ups
-
Auth-scheme list isn't exhaustive.
NTLM(Windows),Signature(some custom auth flows), and per-provider idiosyncratic schemes are not covered. The list is right for the well-known cases (the test matrix is the strongest signal of intent); an explicit opt-in viaOptions.ExtraAuthSchemeswould be a clean way to add providers without re-cutting the regex. Not gating; can land as a follow-up. -
keyLooksSensitivewon't catchauth_token_v2or similar future-variants. The trailing-tokenrule requiresi == len(segments)-1, so any suffix breaks the match. The explicit list covers today's common cases, but a similar "trailing-<secret_segment>with any suffix" pattern would be more future-proof. Not a blocker; the test matrix pins today's intent. -
The
headerPatterncapture group is unanchored on the right. The([^\s,;]+)captures the value up to whitespace, comma, or semicolon, so an embedded comma in a JWT-style value (rare but possible in some custom auth schemes) would truncate the redaction. Worth a one-line note in the regex comment so the next person doesn't widen the class silently.
Verification
internal/redactionandinternal/secretstest suites pass cleanly on the branch (16 tests, including the 7 new ones).go test -count=1 ./internal/redaction/... ./internal/secrets/... -vis green.- The new
keyLooksSensitiveis the only behavior change toIsSensitiveKey; the explicit map andExtraSensitiveKeysshort-circuits are unchanged, so existing callers ofRedactString/RedactValuethat rely on the explicit list are byte-identical to before for already-covered keys.
This is a clean hardening pass. The three fixes are independent, the test surface is complete, and the explicit negatives (token-counts, ordinary *_key, kebab-case words, file:line output) are exactly the regression class these heuristics risk introducing. Happy to see it land.
|
Review posted (id Three independent hardening fixes, each with a tight test surface and no regressions on the existing 12 tests:
Verified Three non-blocking follow-ups in the review: auth-scheme list isn't exhaustive ( The |
The broadened header pattern captured only the first token after the scheme, so parameterized schemes (Digest "…, response=…", AWS4-HMAC-SHA256 "…, Signature=…") left the actual secret param visible. Redact the entire value to end of line; the scheme name is kept. Adds tests with realistic Digest + AWS SigV4 headers. Addresses review feedback.
|
Good catch — fixed in f5e1144. The header pattern now redacts the entire credential after the scheme (to end of line), so multi-part schemes are fully covered: |
Audit of
internal/redaction+internal/secretssurfaced three verified gaps in the secret-redaction last line of defense. The configured provider key is already exact-scrubbed; these affect secondary secrets that appear in tool/command output.Fixes
IsSensitiveKeywas exact-match, sodb_password,session_secret,stripe_secret_key,auth_token,csrf_token,ssh_private_key,backup_api_key… passed through. NewkeyLooksSensitiveuses conservative structural rules. Critically token-count-safe:max_tokens/prompt_tokens/token_countand ordinaryprimary_key/public_key/cache_keyare not redacted (tokenonly triggers as a singular trailing segment; barekeynever triggers).Authorization:now redactstoken/apikey/digest/negotiate/oauth/aws4-hmac-sha256, not justbearer/basic.\bto the prefixed patterns so kebab-case words (task-…,ask-…,risk-…) no longer match as a fake key, while real secrets (preceded by space/=/:/quote/start) still match.Deliberately not shipped
A free-text
key: valuecolon pattern — it mangledfile.go:line: messageoutput (a filename'ssecretsegment read as a sensitive key), the same over-redaction class fix #3 addresses. Structured paths (RedactValue, plus assign/JSON/query inRedactString) are covered; free-text colon-form is left as a known limitation, withTestRedactString_PreservesFileLineColonsguarding against re-introduction.Tests
audit_fixes_test.go(compound-key + token-count matrix, auth schemes, file:line guard,RedactValuenesting) andboundary_test.go(scanner over-redaction negatives + real-secret positives).gofmt·go vet·staticcheck·-raceon redaction+secrets+verify · 9 consumer packages — all green.Summary by CodeRabbit
Release Notes
Bug Fixes
Tests