Claude Code's "Don't ask again" saved passwords in 1,122 public files. Check your repo.

On November 5, security firm Apiiro published research on a file most Claude Code users never open: .claude/settings.local.json. Apiiro looked at a snapshot of public GitHub taken on November 2. It found 104,185 public copies of that file, in 103,617 repos. 1,122 of the copies held a real secret. That is about 1.1%, or roughly 1 in 93. In total those files held 1,379 distinct secrets.
It was a big story on Hacker News too, with 292 points and 199 comments when I read it. If you use Claude Code, this is worth ten minutes. Here is what happened, what the numbers don't prove, and what to check.
How a password ends up in that file
When Claude Code wants to run a command, it asks you. One of the choices is "Yes, and don't ask again". If you pick it, Claude Code saves an allow rule to .claude/settings.local.json so it never asks about that command again.
The problem is what the rule is made of. Apiiro's wording: "the rule is the command string itself, secret included." So if a command had a password in it, the password is now saved in plain text in the rule.
The commands Apiiro named are the usual ones where people type a password inline: anything after PGPASSWORD=, sshpass -p or mysql -p. Apiiro does not publish an example rule word for word, so here is one shape with a placeholder, not a real secret:
{
"permissions": {
"allow": [
"Bash(PGPASSWORD=<your-password> psql -h staging-db -U app -c \"SELECT * FROM orders WHERE id=42\")"
]
}
}
Apiiro compared 16 coding agents. Claude Code was the only one confirmed to write the full approved command to a project file by default. They reproduced it with Opus 5.5 and Fable 5.1, and Apiiro calls it Claude Code's behavior, not the model's.
"But it's gitignored"
Mostly, on your own machine, yes. Claude Code's settings docs say it adds **/.claude/settings.local.json to your global git excludes the first time it writes the file in a repo. It does not touch the project's .gitignore.
A global exclude lives on one machine. That means it misses at least these cases:
- A folder that becomes a git repo later.
- A clone on another machine.
- A file that was already committed.
The protection is local. The repo itself carries no rule, so anyone who clones it, or any folder you turn into a repo after the fact, starts without one.
The numbers
All of these come from Apiiro's report. I could not open Apiiro's page myself because it blocks bots, so I read the outlets that did: DEV, madrobot and GBHackers. The original is Apiiro's post, and the discussion is on Hacker News.
- 1,122 of 104,185 files held a real secret.
- By type: 379 database passwords, 43 SSH logins, 42 GitHub, GitLab or npm tokens, and 23 AWS keys.
- Gitleaks flagged 564 of the 1,122 files and TruffleHog flagged 643. Neither caught 215. Those 215 were usually passwords typed after
PGPASSWORD=,sshpass -pormysql -p. - In 539 of 1,105 leaking repos Apiiro could trace, a commit co-authored by Claude changed the file.
- 63 organisations were affected, and one confirmed its leaked key was still live.
- The median leaking file was first committed 217 days before the snapshot.
- Git history held 734 more secrets in 509 repos. Forks carry copies too. The wider total is 2,539 secrets in 1,994 repos.
- The shared
.claude/settings.jsonis a smaller problem: 298 of 76,011 public copies held a secret.
What this doesn't prove
Be fair to the research and to Anthropic here.
Apiiro did not test whether the credentials still work. "Real secret" here means the text matches a key format or sits next to a public host. Some of these are probably dead. One organisation said its key was live, and that is the only confirmation I have seen.
The leak also needs a second step. Claude Code saves the file locally. The file only becomes public when someone commits it and pushes. The feature wrote the secret, and people published it.
Users have reported this on GitHub since August 2025. One issue, #5544, was closed by a bot as "not planned" on January 3, 2026, after 60 days of inactivity. In the coverage I read, Anthropic had not commented on the research. I won't guess what happens next.
3 checks for your repo
Run these in each repo where you have used Claude Code.
1. Look. Was the file ever tracked, and did it ever hold a password?
git ls-files .claude/settings.local.json
git log --all --oneline -- .claude/settings.local.json
git log --all -p -- .claude/ | grep -nE 'PGPASSWORD=|sshpass -p|mysql .*-p'
The first command prints the path if the file is tracked right now. The second lists every commit that touched it. The third searches that history for the password patterns above.
Then run a scanner. Apiiro's numbers say to run both, since each caught files the other missed:
gitleaks git -v
trufflehog git file://.
Inside Claude Code, /permissions lists each rule and the file it came from, so you can spot a rule with a password in it.
2. Rotate. Change every secret the file ever held. Deleting the file does not clean git history, forks or other people's clones. Assume anything that was public has been copied.
3. Stop it. Untrack the file and put it in the project's own .gitignore:
git rm --cached .claude/settings.local.json
echo ".claude/settings.local.json" >> .gitignore
Then delete any allow rule that contains a secret.
A safer way to approve things
The root habit is approving a literal command with a password in it. Two changes help.
First, keep secrets in variables. Reference something like $DB_URL in the command and set it in your shell, so the rule Claude Code saves holds the variable name and not the password.
Second, approve patterns, not commands. A rule like Bash(npm run:*) is useful and holds nothing private. A rule that is a full psql call with a password in it is not.
You can also block reads of your env files with deny rules:
{
"permissions": {
"deny": ["Read(./.env)", "Read(./.env.*)"]
}
}
My opinion: I'd approve patterns, not commands. "Don't ask again" is a fine button for something like npm run test. I'd stop using it for anything where I typed a secret.
What to do this week
If you work solo:
- Run the 3 checks above in every repo you use Claude Code in.
- Rotate anything the history search finds.
- Add
.claude/settings.local.jsonto each project's.gitignore, not only your global excludes.
If you run a team:
- Put
.claude/settings.local.jsonin your organisation's.gitignoretemplate, so new repos start protected. - Scan the agent folders, not just source code:
.claude/,.cursor/,.gemini/and.specstory/. Put the scan in pre-commit and in CI. - Ask people to run the history search on old repos. The median leaking file was 217 days old, so the problem may already sit in your history and not in new code.
Sources: Apiiro's report (November 5, 2026), DEV, madrobot, GBHackers and the Hacker News thread.
Do you check what your coding agent saves to your repo, or do you trust the defaults?