Mask secrets before they reach the store - #53
Merged
Merged
Conversation
A session records whatever crossed it: a key pasted into a prompt, a printenv, a .env read back by a tool. All of it landed in agentlens.db in clear text, a file with no authentication in front of it. The pattern table is agento11y's tier-1 set (Apache-2.0, patterns from Gitleaks; see NOTICE) - 22 high-confidence formats. Their mask format is kept verbatim so their own 28 light-mode fixtures serve as the tests, which is the only real proof a transcribed regex is faithful. Their tier 2 is left out on purpose: it guesses at key=value shapes, and DB_PASSWORD=x in a transcript is often the thing you opened the transcript to find. Masking sits in the two prepared statements every adapter writes through, so prompts, model output, tool arguments and tool results are covered in one pass. Cost is 2 microseconds per event with no secret, 4 with one. The fixtures are stored base64: GitHub's secret scanning blocked the first push because they carry realistic example keys, which is fair, so the test decodes them rather than the repo allowlisting a secret. Of 44,934 existing events, 12 across 8 sessions contain something this would now remove. Old rows are not rewritten; AGENTLENS_REDACT=0 disables. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NZDHk4yVYP4jMZkTUMQ1Lw
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.
A session records whatever crossed it — a key pasted into a prompt, a
printenv, a.envread back by a tool — and all of it landed inagentlens.dbin clear text, a file with no authentication in front of it. A token in a transcript is a token at rest.The pattern table is the tier-1 set from Grafana's agento11y (Apache-2.0, patterns hand-curated from Gitleaks; attribution in NOTICE). Twenty-two high-confidence formats: cloud and provider API keys, GitHub and Slack tokens, private-key blocks, connection strings carrying credentials, bearer tokens.
Their mask format is kept verbatim so their own 28 light-mode fixtures are the tests here — the only real proof that a transcribed regex is faithful. All pass unchanged.
Their tier 2 is deliberately left out. It guesses at
key=valueshapes, which is right for a product that must not leak and wrong here:DB_PASSWORD=hunter2in a transcript is often the thing you opened the transcript to find.Where it hooks in: the two prepared statements every adapter writes through, not per field and not per adapter, so prompts, model output, tool arguments and tool results are covered in one pass. The event is already serialized JSON there and every mask is plain ASCII, so masking the serialized form cannot break it. Cost measured at 2 µs per event with no secret, 4 µs with one.
Scale of the exposure: of 44,934 existing events, 12 across 8 sessions contain something this would now remove. Some of those are certainly the example tokens from this feature's own development, and I did not look to find out which. Old rows are not rewritten, and
AGENTLENS_REDACT=0turns masking off.One footnote: the first push was rejected by GitHub's secret scanning, because the borrowed fixtures carry realistic example keys. That is the correct behaviour from it, so the corpus is stored base64 and decoded by the test rather than allowlisted. 82 tests pass.
https://claude.ai/code/session_01NZDHk4yVYP4jMZkTUMQ1Lw