Dev.to Security πŸ” Cybersecurity πŸ‘ 0 πŸ“– 6 min read

Your coding agent already saved the API key you thought was only in .env

Coding agents keep a local history of each session. That history is useful: you can resume yesterday's work, see which tools ran, and debug a failure. It is also a second home for secrets. I wrote a small local CLI, age

Coding agents keep a local history of each session. That history is useful: you can resume yesterday's work, see which tools ran, and debug a failure. It is also a second home for secrets.

I wrote a small local CLI, agent-secrets, that scans those files, prints a masked report, and can redact what it finds. This article walks through where the files live, what a scan looks like, and how to put a check in front of a commit. It uses only facts I could check, including a run of the tool against its own synthetic fixtures.

Two public reports, two surfaces

On 28 October 2026, Backslash Security published research on session history in Claude Code, Cursor and Codex. They looked at how secrets first entered a session. About three in four came from the agent's own work, mostly tool output such as a file read or a command. About one in ten was typed or pasted by the user. In a lab test, an agent given a normal debugging task read a project's .env on its own. Nobody pasted the key. The session kept it.

They also showed why a guard on .env is not enough. A skill that asked to read .env was blocked. A skill that asked only to write a weekly report from recent session transcripts was allowed through, and the keys came along inside those files.

On 9 November 2026, iTnews reported Apiiro's findings on public GitHub. Claude Code asks before it runs most shell commands. If you pick "Yes, and don't ask again", that choice can be stored as a rule in .claude/settings.local.json. When the rule records the exact command, any secret typed into the command is stored with it.

Apiiro's search found 104,185 copies of that settings file in 103,617 public repositories. Of those, 1,122 held at least one real secret, 1,379 distinct secrets in all. Adding older versions of the file, a shared settings file that teams commit deliberately, and saved agent chat transcripts, the tally reached 2,539 secrets across 1,994 repositories. Apiiro did not test whether the credentials still worked. One organisation they notified confirmed its key had been live.

Anthropic's Boris Cherny, quoted in that article, said Claude Code has historically not done secret scrubbing, and that the team was looking at auto mode and basic secret scanning. He also noted that Claude Code gitignores the settings file on creation, so the public copies had been checked in. Of the 1,122 files with a secret, gitleaks flagged 564 and TruffleHog 643, while 215 slipped past both. The file also sits on the developer machine as plain JSON.

Where the files are

Paths can change with the agent and the version. These are the defaults the CLI uses, and they match what the vendors or the reports describe.

Claude Code stores conversation transcripts as JSONL under $CLAUDE_CONFIG_DIR/projects/ (default ~/.claude). Official documentation says those files are plaintext, and that anything which passes through a tool, including file contents and command output, is written to the transcript: Explore the .claude directory. The tool also reads settings*.json there and ./.claude/settings*.json. It does not read ~/.claude.json.

Codex CLI transcripts are rollout-*.jsonl files under $CODEX_HOME/sessions/YYYY/MM/DD/, directly under $CODEX_HOME/sessions/, and under $CODEX_HOME/archived_sessions/. $CODEX_HOME defaults to ~/.codex.

Cursor support is experimental. The tool reads JSONL files under $CURSOR_CONFIG_DIR/projects/ (default ~/.cursor) when the path contains agent-transcripts. It does not read state.vscdb or ~/.cursor/chats. Backslash described a SQLite store under ~/.cursor/chats/. Those are different layouts, and Cursor changes them, so a clean result in one place is not proof about the other.

If you pass a path, the tool walks it for .jsonl, .json, .txt, .log, .md, .yaml, and .yml. .bak files are skipped unless you name the backup itself. Files larger than 32 MiB are skipped.

Install and a first scan

The package on PyPI is agent-transcript-secrets and the command it installs is agent-secrets:

pipx install agent-transcript-secrets

From a specific GitHub commit, use pipx install "git+https://github.com/kamilkaczmareksolutions/agent-transcript-secrets.git".

With no path, scan uses the default locations that exist on the machine:

agent-secrets scan

The repo ships synthetic fixtures in examples/. Every token there contains FAKE or EXAMPLE, and the shapes are wrong for GitHub secret scanning. I ran the tool on that tree. It exited with status 1 (findings exist) and printed:

file                                                             line  type                     preview
---------------------------------------------------------------  ----  -----------------------  -----------------
examples/.claude/projects/-tmp-demo/session.jsonl                1     openai-api-key           sk-Ò€¦ENAI
examples/.claude/projects/-tmp-demo/session.jsonl                2     github-token             gho_Ò€¦OKEN
examples/.claude/projects/-tmp-demo/session.jsonl                2     github-token             ghp_Ò€¦OKEN
examples/.claude/projects/-tmp-demo/session.jsonl                2     github-token             github_pat_Ò€¦OKEN
examples/.claude/projects/-tmp-demo/session.jsonl                3     url-password             Ò€¦
examples/.claude/settings.local.json                             4     bearer-token             FAKÒ€¦KEN
examples/.claude/settings.local.json                             5     anthropic-api-key        sk-ant-Ò€¦EKEY
examples/.claude/settings.local.json                             6     aws-access-key           AKIAÒ€¦EY12
examples/codex/sessions/2026/11/09/rollout-demo.jsonl            2     google-api-key           AIzaÒ€¦MPLE
examples/codex/sessions/2026/11/09/rollout-demo.jsonl            2     slack-token              xoxb-Ò€¦OKEN
examples/codex/sessions/2026/11/09/rollout-demo.jsonl            2     stripe-live-key          sk_live_Ò€¦MPLE
examples/codex/sessions/2026/11/09/rollout-demo.jsonl            2     xai-api-key              xai-Ò€¦EKEY
examples/cursor/projects/demo/agent-transcripts/sess/sess.jsonl  1     high-entropy-assignment  notÒ€¦E99
examples/cursor/projects/demo/agent-transcripts/sess/sess.jsonl  1     private-key              -----BEGIN Ò€¦-----
14 secrets found.

The tool never prints the full secret. --json emits the same findings as structured data, with a source field (claude, claude-settings, codex, and so on) and an experimental flag.

The fixtures also include examples/clean/placeholders.json with allowlisted values such as sk-xxx. A scan reports nothing in that file.

Redact, then look at the backup

redact --dry-run shows the replacement and writes nothing. On the same fixtures it printed 14 lines of the form path:line kind preview -> [REDACTED:kind], then 14 secrets would be redacted.

Without --dry-run, redact copies each file to a .bak beside it, then replaces each finding with a [REDACTED:<kind>] token. The .bak file still holds the secret. Delete that copy after you check the redacted file, and do not commit it.

Exit status is 1 when findings exist, 0 when the tree is clean, and 2 for a missing path, a bad flag, or a hook installed outside a git repository.

Put a check in front of the commit

Two hooks exist.

The repo ships .pre-commit-hooks.yaml for the pre-commit framework. Pin rev to a tag:

repos:
  - repo: https://github.com/kamilkaczmareksolutions/agent-transcript-secrets
    rev: v0.1.0
    hooks:
      - id: agent-secrets

agent-secrets install-hook writes a git pre-commit hook that runs agent-secrets scan --staged on staged transcript and settings files. A secret blocks the commit. Running it again replaces that block and leaves the rest of an existing hook in place.

Neither hook rewrites git history. If a settings file or a transcript was already pushed, rotate the key. Removing the file from the working tree does not remove it from history, forks, or clones.

What it looks for, and what it misses

Detectors are regular expressions for common key shapes, plus an entropy check on assignments whose names end in key, token, password, or secret. The shapes covered today include OpenAI (sk-), Anthropic (sk-ant-), GitHub (ghp_, gho_, github_pat_), AWS (AKIA), Slack (xox), Stripe (sk_live_), Google (AIza), PEM private-key banners, Bearer tokens, and URLs that embed a password. An xAI rule for the xai- prefix is marked experimental.

A vendor format the patterns do not name can pass unseen. A long random-looking placeholder can be flagged. The tool does not rotate a key, scan other people's repositories, or talk to the network. A clean scan does not prove a secret never left the machine. If a key reached a remote repository, rotate it first, then clean the local copies.

The project is MIT, has no runtime dependencies, and lives at github.com/kamilkaczmareksolutions/agent-transcript-secrets.

πŸ“° Read the original article on Dev.to Security

Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes β€” full credit and traffic to the original publisher.