Apiiro's crawl documented 1,122 public repos leaking AWS keys, API tokens, database passwords, and PATs through Claude Code's settings and history files. They argue the root cause is architectural: any tool that remembers 'approved' commands inevitably remembers the secret-laden arguments that were part of those commands.
Frames this as the second high-profile AI-agent secret leak class in six months, following Cursor's .cursor/mcp.json incident. Argues the pattern is now predictable — persisted trust decisions always drag their arguments along, and arguments are exactly where secrets live.
Emphasizes that Claude Code didn't malfunction — it faithfully logged what it was told. The leak happens because .claude/ sits next to source code and isn't in Anthropic's default .gitignore guidance, so reflexive bulk-staging sweeps it into public remotes.
Submitted the Apiiro writeup to HN where it drew 268 points and 177 comments, framing the issue as 'Don't Ask Again' exposing passwords rather than as an Anthropic vulnerability per se. The framing centers the UX convenience feature and how users interact with it.
GitHub's secret scanning now flags Claude Code artifacts, but only after push and only when tokens match known provider patterns like AWS or OpenAI prefixes. Internal API keys, custom auth tokens, and bespoke connection strings slip through entirely — the detection floor is set by what the scanner already knows about.
Security firm Apiiro published research this week documenting 1,122 public GitHub repositories leaking secrets through Anthropic's Claude Code CLI. The leaked material includes AWS access keys, OpenAI and Anthropic API tokens, database connection strings with embedded passwords, GitHub personal access tokens, and in some cases full `.env` dumps pasted into command arguments.
The vulnerability isn't a bug in Claude Code — it's the interaction between a convenience feature and developer muscle memory around `git add .`. When Claude Code asks permission to run a shell command, users can click "Don't ask again" to permanently allow that command pattern. That approval, along with the exact command string that triggered it, gets written to `.claude/settings.local.json` and `.claude/history.jsonl` inside the project directory. Because those files sit next to the code and aren't in Anthropic's default `.gitignore` guidance, they ride along into commits and then into public remotes.
Apiiro's crawl of GitHub surfaced files containing commands like `curl -H "Authorization: Bearer sk-ant-..."`, `psql postgresql://admin:hunter2@prod-db...`, and `aws s3 cp --profile
This is the second high-profile secret-leak class tied to AI coding agents in the last six months, and the pattern is becoming predictable. Any tool that persists "trusted actions" on disk eventually persists the arguments to those actions — and arguments are where secrets live. Cursor had a similar episode with `.cursor/mcp.json` tokens earlier in the year. GitHub's own secret scanning now flags Claude Code artifacts, but only after push, and only if the token matches a known provider pattern — custom internal secrets slip through.
The HN comment thread on Apiiro's post splits roughly three ways. The largest camp is practitioners arguing this is a user-error story dressed up as a vendor story: `.env` files, SSH keys, and `~/.aws/credentials` have been one `git add .` away from disaster for twenty years, and no one is surprised when a new config file joins the list. The uncomfortable rebuttal, which a smaller but louder group makes well, is that agentic tools genuinely raise the blast radius: a human typing `curl -H "Authorization: Bearer ..."` usually does it once in a scratch shell; an agent asked to "test the API" will do it fifty times, each one logged, each one a durable artifact on disk. The third camp — mostly people who work on the agents themselves — points out that the fix is cheap: write to `~/.claude/` (user-level) instead of `./.claude/` (project-level), the same way `git config` distinguishes `--global` from `--local`.
Anthropic's current defaults don't do this. Project-local settings are preferred specifically so that per-repo trust decisions don't bleed across unrelated codebases, which is a defensible call. But the current implementation writes the triggering command into the same file as the trust decision, which conflates two very different things: *"I trust `npm install` in this repo"* and *"here is the exact `npm install` invocation, including a private registry token, that I trusted."* The first is a policy. The second is a secret.
What makes this worse is time-of-check versus time-of-commit. The developer sees the "Don't ask again" modal in the moment, approves, and moves on. The leak happens hours or days later, in a commit titled "wip" at 11pm, and surfaces publicly only after someone runs a scanner against the whole of GitHub. By then the token has been valid and exposed for however long it takes you to notice — which, per Apiiro, is often weeks.
If you or anyone on your team uses Claude Code, do three things today. First, add the following to your repo's `.gitignore` and to your global `~/.gitignore_global`:
``` .claude/settings.local.json .claude/history.jsonl .claude/*.log ```
Second, audit existing commits. GitHub's search is rate-limited and noisy, but `git log --all --full-history -- .claude/` inside each repo will tell you quickly whether you've already pushed these files. If you have, rotate every secret that could plausibly have been in a shell command you ran through the agent — not just the ones you remember. The `history.jsonl` files Apiiro analyzed averaged 412 commands each; no one remembers 412 commands.
Third, if you run your own tooling that wraps an LLM and persists approvals, separate the policy (allow `npm install`) from the invocation (the actual argv, which may include tokens). Hash or glob-match the command for trust purposes; never store the raw string. This is the same lesson that `sudoers` learned in the 1990s when it moved from "exact command match" to patterned commands with argument restrictions — the agent ecosystem is rediscovering it at speed.
For teams, this is also a prompt to look at your pre-receive hooks. GitHub's push protection catches provider-formatted tokens but will not catch an internal JWT, a hardcoded Postgres password, or an OAuth bearer against your own API. Trufflehog and Gitleaks in a pre-commit hook are a $0 upgrade that would have caught every single one of Apiiro's 1,122 repos before the push.
The deeper issue is that coding agents have inherited the developer-tooling convention of storing config next to code, and that convention was designed in a world where config meant "indent width is 2." It now means "here is a durable transcript of every privileged command I was willing to run, including the secrets I passed to those commands." Expect Anthropic and the other agent vendors to quietly split these files — trust decisions user-global, command history ephemeral or redacted — over the next few releases. In the meantime, the fix is a two-line `.gitignore` and a healthy mistrust of any "Don't ask again" button that doesn't tell you where it's writing its memory.
Confusing headline. They scanned for repos where people had committed their .claude/ folder and local settings and then scanned those files for commands which contained keys or passwords.So pressing “Don’t Ask Again” by itself doesn’t expose passwords by itself. If you commit your local Claude
I'm super paranoid about the level of access AI agents have. At the same time, I want them to take away the tedium of a bunch of tasks I'm required to do, and it sucks to have to micromanage them.The best solution I've been able to come up is using an ephemeral vm (on an isolated netw
Here is how to solve this:Use a private dev repo and do all your dev inside it.Releases go to a seperate public repoIn the private dev repo use a script which Cherry picks release files from a whitelist that you maintain in the public repo.Essentially, you define the files and folder structure of a
This page is like 80% repeating itselfIf you post output from AI, please remember to edit before publishing
Top 10 dev stories every morning at 8am UTC. AI-curated. Retro terminal HTML email.
Boris from the Claude Code team here. Historically Claude Code has not done secret scrubbing (there are many good tools for that), but we are looking into what we can do to prevent this.That said, a few thoughts:1. .claude/settings.local.json is meant to stay local. This is not a file you shoul