<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Kamil Kaczmarek</title>
    <description>The latest articles on DEV Community by Kamil Kaczmarek (@kamilkaczmareksolutions).</description>
    <link>https://dev.to/kamilkaczmareksolutions</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4236109%2Ff088b9b6-dbbb-48a4-96d1-d87b3a7a6b06.png</url>
      <title>DEV Community: Kamil Kaczmarek</title>
      <link>https://dev.to/kamilkaczmareksolutions</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/kamilkaczmareksolutions"/>
    <language>en</language>
    <item>
      <title>Your coding agent already saved the API key you thought was only in .env</title>
      <dc:creator>Kamil Kaczmarek</dc:creator>
      <pubDate>Tue, 10 Nov 2026 01:20:35 +0000</pubDate>
      <link>https://dev.to/kamilkaczmareksolutions/your-coding-agent-already-saved-the-api-key-you-thought-was-only-in-env-3edf</link>
      <guid>https://dev.to/kamilkaczmareksolutions/your-coding-agent-already-saved-the-api-key-you-thought-was-only-in-env-3edf</guid>
      <description>&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;I wrote a small local CLI, &lt;code&gt;agent-secrets&lt;/code&gt;, 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.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two public reports, two surfaces
&lt;/h2&gt;

&lt;p&gt;On 28 October 2026, &lt;a href="https://www.backslash.security/blog/secrets-in-ai-agent-session-history" rel="noopener noreferrer"&gt;Backslash Security&lt;/a&gt; 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 &lt;code&gt;.env&lt;/code&gt; on its own. Nobody pasted the key. The session kept it.&lt;/p&gt;

&lt;p&gt;They also showed why a guard on &lt;code&gt;.env&lt;/code&gt; is not enough. A skill that asked to read &lt;code&gt;.env&lt;/code&gt; 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.&lt;/p&gt;

&lt;p&gt;On 9 November 2026, &lt;a href="https://www.itnews.com.au/news/dont-ask-again-in-anthropics-claude-code-leaks-developer-secrets-630203" rel="noopener noreferrer"&gt;iTnews&lt;/a&gt; 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 &lt;code&gt;.claude/settings.local.json&lt;/code&gt;. When the rule records the exact command, any secret typed into the command is stored with it.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the files are
&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;Claude Code stores conversation transcripts as JSONL under &lt;code&gt;$CLAUDE_CONFIG_DIR/projects/&lt;/code&gt; (default &lt;code&gt;~/.claude&lt;/code&gt;). 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: &lt;a href="https://code.claude.com/docs/en/claude-directory" rel="noopener noreferrer"&gt;Explore the .claude directory&lt;/a&gt;. The tool also reads &lt;code&gt;settings*.json&lt;/code&gt; there and &lt;code&gt;./.claude/settings*.json&lt;/code&gt;. It does not read &lt;code&gt;~/.claude.json&lt;/code&gt;.&lt;/p&gt;

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

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

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

&lt;h2&gt;
  
  
  Install and a first scan
&lt;/h2&gt;

&lt;p&gt;The package on PyPI is &lt;code&gt;agent-transcript-secrets&lt;/code&gt; and the command it installs is &lt;code&gt;agent-secrets&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pipx install agent-transcript-secrets
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From a specific GitHub commit, use &lt;code&gt;pipx install "git+https://github.com/kamilkaczmareksolutions/agent-transcript-secrets.git"&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;With no path, &lt;code&gt;scan&lt;/code&gt; uses the default locations that exist on the machine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;agent-secrets scan
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The repo ships synthetic fixtures in &lt;code&gt;examples/&lt;/code&gt;. Every token there contains &lt;code&gt;FAKE&lt;/code&gt; or &lt;code&gt;EXAMPLE&lt;/code&gt;, 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:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;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.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

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

&lt;h2&gt;
  
  
  Redact, then look at the backup
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;redact --dry-run&lt;/code&gt; shows the replacement and writes nothing. On the same fixtures it printed 14 lines of the form &lt;code&gt;path:line kind preview -&amp;gt; [REDACTED:kind]&lt;/code&gt;, then &lt;code&gt;14 secrets would be redacted.&lt;/code&gt;&lt;/p&gt;

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

&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  Put a check in front of the commit
&lt;/h2&gt;

&lt;p&gt;Two hooks exist.&lt;/p&gt;

&lt;p&gt;The repo ships &lt;code&gt;.pre-commit-hooks.yaml&lt;/code&gt; for the &lt;a href="https://pre-commit.com" rel="noopener noreferrer"&gt;pre-commit&lt;/a&gt; framework. Pin &lt;code&gt;rev&lt;/code&gt; to a tag:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;repos&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="na"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://github.com/kamilkaczmareksolutions/agent-transcript-secrets&lt;/span&gt;
    &lt;span class="na"&gt;rev&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v0.1.0&lt;/span&gt;
    &lt;span class="na"&gt;hooks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;agent-secrets&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;agent-secrets install-hook&lt;/code&gt; writes a git &lt;code&gt;pre-commit&lt;/code&gt; hook that runs &lt;code&gt;agent-secrets scan --staged&lt;/code&gt; 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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it looks for, and what it misses
&lt;/h2&gt;

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

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;The project is MIT, has no runtime dependencies, and lives at &lt;a href="https://github.com/kamilkaczmareksolutions/agent-transcript-secrets" rel="noopener noreferrer"&gt;github.com/kamilkaczmareksolutions/agent-transcript-secrets&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>python</category>
      <category>security</category>
      <category>claude</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
