[FEATURE] Detect and offer to mask secrets in pasted text before it enters the transcript
Summary
When text is pasted into the prompt box, scan it for credential-shaped content and offer to mask it before it is submitted and written to the transcript. Warn-and-offer, not silent rewriting.
Why paste specifically
Pasting a URL, a curl line, or a .env fragment is the single most natural way to hand Claude Code a debugging artifact — and those artifacts are exactly where live credentials live. The user isn't being careless; the credential is incidental to the thing they're trying to show.
Concrete case from today. I was debugging an OAuth redirect that landed on the wrong origin, so I pasted the callback URL, because the URL was the bug:
http://localhost:5173/#access_token=eyJhbGciOi...&expires_at=...&provider_refresh_token=eyJhbGciOi...&refresh_token=<opaque>&token_type=bearer
That one paste put a live access token (1 h), a Supabase refresh token, and an IdP refresh token (~4 weeks) into the session transcript. There was no good alternative: truncating the URL by hand would have removed the evidence needed to diagnose it.
Worth noting how the blast radius outlives the moment: transcripts persist under ~/.claude/projects/**/*.jsonl, get replayed on --continue/--resume, are re-sent as context on later turns, and are read by /compact and summarisation. One paste becomes many copies.
Also relevant: revoking the session did not neutralise the pasted access token. Supabase access tokens are stateless JWTs, so deleting the session row only stopped refresh — the leaked token kept returning 200 with data until its own exp. Redaction at paste time is meaningfully cheaper than the cleanup, because for the leaked hour there is no cleanup short of rotating the project's signing key and logging out every user.
Proposed behaviour
On paste, if the content matches a credential pattern:
- Show an inline, non-blocking notice — e.g.
⚠ 2 possible secrets detected in pasted text. - Offer a one-key action to mask them (
[access_token redacted], preserving surrounding structure so the URL/command still reads as itself). - Leave it entirely to the user. Do not silently rewrite the buffer.
The opt-in part is essential and I'd argue against auto-redaction: sometimes the token is the subject — "decode this JWT and tell me why the aud claim is wrong" is a legitimate request that silent masking would break. The goal is to make the leak visible and one keystroke away from fixed, not to make it impossible.
Suggested starting patterns, biased toward precision over recall so the warning stays trustworthy:
- OAuth callback fragments/queries:
#access_token=,&refresh_token=,id_token=,provider_token= - JWTs:
eyJ[A-Za-z0-9_-]{10,}\.eyJ[A-Za-z0-9_-]{10,}\. - Provider key prefixes:
sb_secret_,sk-,ghp_/gho_/ghu_/ghs_,AKIA,xox[baprs]-,glpat-,AIza Authorization: Bearer <...>,-H "Authorization: ..."- Connection strings carrying a password:
postgres://user:pass@,Password=in keyword-form connection strings - PEM blocks:
-----BEGIN (RSA |EC |OPENSSH )?PRIVATE KEY----- .env-shaped lines:^[A-Z0-9_]*(SECRET|TOKEN|PASSWORD|KEY|CREDENTIAL)[A-Z0-9_]*=\S+
A useful hook point may already exist: large pastes are collapsed to a [Pasted text #N +M lines] placeholder, and the scan could ride that same interception.
Related
- #50014 — secret scrubbing for session logs (closed). Complementary but downstream: it cleans the record after the credential has already been transmitted. Catching it at the input boundary prevents the transmission.
- #82001 —
UserInputChangehook exposing the prompt input buffer. If that lands, this could plausibly ship as a bundled hook rather than core behaviour, which would also let teams extend the pattern list with their own key formats. - #29910 — built-in secrets management (adjacent; about supplying secrets deliberately rather than catching them accidentally).
Environment
Claude Code on Windows 11, but the request is platform-independent.