Claude Code's "Don't Ask Again" Button Can Write Your Passwords Into Your Repo
A one-click approval is saved as a line of text. Apiiro found that line holding live-looking credentials in 1,122 public GitHub files. Here is how it happens and how to clean it up.
The riskiest file a coding agent writes is not the code. It is the note it keeps about what you said yes to.
Coding agents ask permission before running anything consequential in your terminal, and every developer gets tired of answering. So the tools offer a shortcut. In Claude Code, Anthropic's terminal coding agent, it reads "Yes, and don't ask again." Click it and the prompt goes away for good.
What most people never check is where that "for good" lives. It lives in a small JSON file inside your project. If the command you approved had a password in it, the file has the password too. And the security firm Apiiro just counted how many of those files are sitting on public GitHub with secrets inside: 1,122.
This matters because it is not a bug in the usual sense. Each piece works the way the docs describe. The leak comes from how the pieces fit together, and that means the fix is a habit, not a patch you wait for.
What the button actually saves
Think of a nightclub guest list. The bouncer, Claude Code's permission system, checks every command against the list before it runs. "Don't ask again" adds a name to the list so the bouncer stops calling you.
The trouble is what counts as a "name." Claude Code's permissions docs say approved rules follow the format Tool(specifier), for example Bash(npm run build), and that Claude Code "saves the rule to .claude/settings.local.json at the root of the git repository." When the specifier is the exact command, the exact command is what gets saved.
So imagine Claude runs this to check a database:
psql "postgres://admin:Hunter2-prod@db.example.com:5432/app" -c "select count(*) from users"
You click "Yes, and don't ask again." Now the guest list holds the whole string, password and server address included. According to The Hacker News' write-up of Apiiro's research, that is exactly the problem: Claude Code can save the approved command "word-for-word, including the secret, in a project settings file." Pattern rules like npm test * were less of an issue. Exact commands were the problem.
What Apiiro found on GitHub
Apiiro's own post would not load from my workspace, so the figures below come from The Hacker News (November 7) and AICoder, and a separate check against Apiiro's post found the same numbers.
The researchers searched GitHub for the file and found 104,185 copies. About 1.1% of them, 1,122 files, held at least one secret, adding up to 1,379 distinct secrets. A value only counted if it matched a known key format, or if a password sat next to a public server address, and an AI reviewer also had to agree.
What was in them is the part that should change your Monday:
- 379 database passwords (over a third of the secrets opened a database directly)
- 43 SSH logins for public servers
- 42 tokens for GitHub, GitLab and npm
- 23 AWS keys
Count older versions of the file, shared settings.json files and saved agent chat logs, and the total reaches 2,539 secrets across 1,994 repositories, per the same coverage.
Two caveats keep this honest. Apiiro did not test whether the secrets still work, so "1,379 live credentials" would overstate it. But The Hacker News reports that one company Apiiro notified confirmed its key had been live.
Why .gitignore did not catch it
Here is the part that surprised me when I read Anthropic's settings docs. Claude Code does try to keep this file out of git. The docs say: "Claude Code keeps it out of git when it creates the file; if you create it by hand, add it to .gitignore yourself."
The way it does that matters. It adds **/.claude/settings.local.json to your global git excludes, a personal ignore list that lives on your machine, not in the project.
That is like keeping the umbrella in your own car. You stay dry. Anyone else who uses the project, a teammate, a CI runner (the server that builds and tests your code), a fresh laptop, a contractor, does not have your global excludes, and nothing in the repo tells their git to skip the file. The Hacker News puts it bluntly: "the protection does not travel with the project."
The data backs that up. In 539 of 1,105 repositories the researchers traced, a commit listing Claude as a co-author changed the file. And among repos with a leaked secret, half had left the file on the default branch (the main branch people clone) for at least 217 days.
There was also a warning shot. A bug report about this behavior, anthropics/claude-code#5544, filed in August 2025, was closed as "not planned" by a bot after 60 days of inactivity, according to The Hacker News.
Why your secret scanner may have missed it
Most teams run a secret scanner, a tool that searches commits for anything shaped like a key. Apiiro ran two popular ones over the 1,122 files. gitleaks flagged 564. TruffleHog flagged 643. And 215 files slipped past both.
That gap makes sense once you see what the secrets look like. A scanner is good at AKIA... (an AWS key has a recognizable shape). It is much worse at a password typed into the middle of a psql connection string, which looks like ordinary text to a pattern matcher. The format that leaks most here, database passwords inside commands, is the one scanners find hardest.
Put this into practice
Do these in order. Rotation comes before cleanup on purpose.
1. Find every copy, including history. In each repo:
git log --all --oneline -- .claude/settings.local.json .claude/settings.json
git log --all -p -- .claude/settings.local.json | grep -niE "password|passwd|token|secret|api[_-]?key|://[^/ ]+:[^@ ]+@"
The first line tells you whether the file was ever committed. The second prints any line in its history that looks like a credential, including user:password@host inside a URL. Expect noise; read the hits by eye.
For a whole GitHub org, the GitHub CLI can search filenames on default branches:
gh search code --filename settings.local.json --owner YOUR_ORG --limit 1000
Without --limit, the command stops at 30 results, and GitHub's search has its own caps, so split a very large org by repo or team. It also only covers what is on default branches today, so still run the history check on anything it finds.
2. Rotate first. Any secret that ever appeared in the file is burned. Deleting the file does not remove it from git history or from forks, so change the password or revoke the token before you touch the repo.
3. Stop tracking the file, in the project itself.
git rm --cached .claude/settings.local.json
echo ".claude/settings.local.json" >> .gitignore
git commit -m "Stop tracking Claude Code local permissions"
Then check that the ignore now comes from the project, not just your machine:
git check-ignore -v .claude/settings.local.json
If the output points at .gitignore, the rule travels with the repo. If it points at a file in your home directory, it does not.
4. Change how you approve. Two habits remove most of the risk:
- Approve patterns, not literal commands. A rule like
Bash(npm run test:*)covers a family of commands without recording any single one. You can add these yourself underpermissions.allowin.claude/settings.json, the shared project file, which is safe to commit as long as it holds no secrets:
{
"permissions": {
"allow": ["Bash(npm run test:*)", "Bash(npm run lint:*)"]
}
}
- Keep credentials in environment variables. If Claude runs
psql "$DATABASE_URL", the saved rule holds the variable's name, not the password.
5. Make it a team default. Add .claude/settings.local.json to your org's .gitignore templates, and tell your scanner to treat .claude/, .cursor/ and .gemini/ folders the way it treats .env. The Hacker News' recommendations for security teams say the same.
When you run all this, the likely outcome is boring: most repos come back clean. The point is that you now know, instead of assuming.
What this fix does not do
Pattern rules are not free. The docs warn that "a * in a Bash rule matches any text, including spaces," so Bash(git * main) approves far more than it looks like. Keep the * at the end, after a specific subcommand.
Deny rules are softer than they sound. Per the permissions docs, a deny rule like Bash(curl *) does not stop /usr/bin/curl or sh -c 'curl ...'. A permission list is a filter on strings, not a sandbox, and the docs themselves say these rules are not a security boundary around a program.
History is forever. Steps 3 and 4 protect future commits. Only rotation protects you from the past.
And the numbers have limits. They come from one firm's scan of public repos. Private repos were not part of it, and nobody outside Apiiro has re-run the count. Claude Code is also not alone in keeping state in project folders; of 16 coding agents whose docs the researchers compared, it was the only one confirmed to save the full approved command in a project file by default, which says something about Claude Code and nothing about whether the others are clean.
The bigger habit
The useful lesson here reaches past one button. Every agent tool turns your quick decisions into durable text: approvals, memory notes, generated configs, chat logs. You review the live session because you are watching it. You rarely review the file it leaves behind, and that file is the part that ships.
So pick one folder your agents write to, open it, and read it like a stranger would. Start with .claude/settings.local.json. It takes two minutes, and it is the cheapest security review you will do this month.
Sources: The Hacker News on Apiiro's research (Nov 7, 2026); AICoder summary (Nov 6, 2026); Apiiro's original post (not reachable from the author's workspace; figures confirmed against it by a Codex check); Claude Code settings docs and permissions docs, read November 8, 2026, with Claude Code 2.1.319 the latest npm release; anthropics/claude-code#5544.