Skip to content

feat(sentry): redact bare rootkey-shaped tokens with an exact-shape base64-22 rule#196

Open
gmaclennan wants to merge 1 commit into
mainfrom
claude/sentry-scrub-bare-rootkey
Open

feat(sentry): redact bare rootkey-shaped tokens with an exact-shape base64-22 rule#196
gmaclennan wants to merge 1 commit into
mainfrom
claude/sentry-scrub-bare-rootkey

Conversation

@gmaclennan

Copy link
Copy Markdown
Member

Closes #77.

PR #111 landed the PII scrubbers with one rule deliberately disabled: a broad "any 22+-char base64 run" meant to catch a bare rootkey with no rootKey= marker. It over-matched 32-hex trace ids, PascalCase exception type names, and error_class metric tags, so bare unmarked rootkeys passed through unredacted.

This enables a much narrower rule built from the rootkey's actual shape. The key crosses the wire as strict base64 of 16 bytes — backend/index.js already enforces /^[A-Za-z0-9+/]{22}==$/ — and any valid 16-byte base64 must end its 22nd char in A/Q/g/w (the final 4 bits are padding zeros). The rule therefore matches exactly 22 base64/base64url chars ending in [AQgw], optionally ==-padded, bounded by non-base64 chars. A padded match is the exact wire shape and always redacts. An unpadded match must additionally show a random-key character mix: both cases, at least one digit or symbol, and not pure hex. That structurally spares trace/span ids (wrong length, hex), exception type names and ERR_* codes (letters-only or single-case), error_class values, and base64-ish ids of other lengths (43-char public keys, 52-char project ids).

Known limits, documented in docs/sentry-integration.md §8: roughly 1% of random rootkeys are all-letters and would slip through in unpadded form (the padded wire form is always caught — verified over 200k random keys: 0 padded misses, 1.29% unpadded), and a 22-char mixed-case identifier containing a digit that happens to end in [AQgw] is falsely redacted — accepted, since false negatives are worse here.

The rule is implemented identically in both hand-mirrored scrubber copies (src/sentry-scrub.ts, backend/before-send.js) and also gates metric tag values in isForbiddenMetric. Shared cases in test-support/scrubber-cases.js (consumed by both the jest and node:test suites) cover: padded and unpadded bare rootkeys redact, JSON-quoted rootkey values redact, and trace/span ids, type names, 22-char PascalCase identifiers, 22-char hex fragments, ERR_* codes, error_class tags, and 43/52-char tokens all survive.

Verified with npm run lint, npm run build, npm run test (79 passed), and npm run backend:test (83 passed).

…ase64-22 rule

Enables the detection deliberately disabled in #111. The rootkey crosses
the wire as strict base64 of 16 bytes (backend/index.js enforces
/^[A-Za-z0-9+/]{22}==$/), and any valid 16-byte base64 ends its 22nd
char in [AQgw], so the rule matches exactly 22 base64/base64url chars
ending there, optionally ==-padded, bounded by non-base64 chars. Padded
matches always redact; unpadded ones also need mixed case plus a
digit/symbol and must not be pure hex, which spares 32-hex trace ids,
16-hex span ids, PascalCase exception type names, ERR_* codes,
error_class tag values, and base64-ish ids of other lengths.

Applied identically to both hand-mirrored scrubber copies and to the
forbidden-metric tag-value gate; shared cases in
test-support/scrubber-cases.js cover both suites.

Closes #77
@github-actions github-actions Bot added the feature New feature (changelog) label Jul 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature New feature (changelog)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Sentry: narrower scrub rule for bare rootkey-shaped tokens (base64-22)

1 participant