Security firm Apiiro found that Claude Code’s “Yes, and don’t ask again” button can save a full command, password included, into a project file. That file then ends up on public GitHub more often than you would think.
The research counted 2,539 secrets across 1,994 public repositories. This guide shows you how to check your own repos, clean up, rotate keys and set safer permission rules.
- The file: approvals are saved as allow rules in
.claude/settings.local.json, word for word. - The leak: if an approved command had a password or token in it, that secret now sits in a JSON file in your project.
- The numbers: 1,122 public copies held at least one secret; 2,539 secrets in total across 1,994 repos.
- The gap: Claude Code adds the file to your global git excludes, not the project’s
.gitignore, so the protection does not travel. - Fix order: rotate first, then untrack the file, add it to
.gitignore, and switch to pattern rules with variables.
What Apiiro Found
Apiiro researchers Shmulik Cohen and Matan Giladi published the report on November 5, 2026. The Hacker News, iTWire and others covered it on November 7.
They searched public GitHub for Claude Code’s local settings file. Then they checked how many copies held real credentials, such as database passwords, SSH logins and cloud keys.
The short version: a convenience button meant to cut down on prompts quietly turned into a secret store. And that store often got committed.
.claude/settings.local.json right now. Open the file and look.How the Leak Happens
When Claude wants to run a Bash command, Claude Code asks you first. One option is “Yes, and don’t ask again”. Picking it saves an allow rule so the same command runs without a prompt next time.
Where the rule is saved
Anthropic’s docs say the rule goes into .claude/settings.local.json at the root of the git repository. It applies to every future session in that repo, including subfolders and worktrees.
Rules come in two shapes. A pattern like Bash(npm test *) is harmless. An exact command is saved as typed, and that is where secrets slip in.
What a leaky rule looks like
Here is a made-up example of the kind of rule Apiiro describes. The password sits in plain text:
{
"permissions": {
"allow": [
"Bash(PGPASSWORD=SuperSecret123 psql -h db.example.com -U admin)",
"Bash(npm run *)"
]
}
}
Why gitignore did not save people
Claude Code tries to keep this file private. The first time it writes the file in a repo, it adds **/.claude/settings.local.json to your global git excludes file.
That protection has three holes, per Apiiro and Anthropic’s own docs:
- It is per machine: a teammate, a CI runner or a new laptop does not have your global excludes.
- It does not cover tracked files: if the file was committed before, git keeps tracking it.
- Hand-made files are skipped: if you created the file yourself, the docs say you must add it to
.gitignoreyourself.
Note: Apiiro traced 1,105 repos and found that in 539 of them, a commit co-authored by Claude changed this file. That does not prove Claude added every secret, but it shows agents commit this file too.
The Findings by the Numbers
These figures come from Apiiro’s research as reported by The Hacker News and iTWire. Apiiro did not test whether the secrets still work, though one company confirmed its key was live.
| Metric | Number |
|---|---|
| Public copies of settings.local.json found | 104,185 (in 103,617 repos) |
| Copies holding at least one secret | 1,122 (about 1.1%) |
| Distinct secrets in current files | 1,379 |
| Extra secrets in older git versions | 734 across 509 repos |
| Shared settings.json copies with secrets | 298 of 76,011 |
| Total secrets (all sources) | 2,539 across 1,994 repos |
| Organizations owning affected files | 63 |
| Time on default branch (half of repos) | 217 days or more |
What kinds of secrets leaked
- Database passwords: 379, and over a third of all secrets open a database.
- SSH logins: 43 for public servers.
- Code host tokens: 42 for GitHub, GitLab and npm.
- Cloud keys: 23 AWS keys.
The reports that were auto-closed
This was not a new discovery. GitHub issue #5544 on anthropics/claude-code, filed August 11, 2025, described the same behaviour. A bot closed it as “not planned” after 60 days of no activity.
Apiiro says four related reports were closed the same way, with no reply from Anthropic. As of November 7 coverage, Anthropic had not commented on the research. Apiiro also says Claude Code was the only one of 16 coding agents it compared that saves the full command in a project file by default.
How to Check Your Repos (Step by Step)
Do this for every repo where you have used Claude Code. It takes a few minutes per repo.
- Open the local file. In the repo root, open
.claude/settings.local.json. Look inpermissions.allowfor anything that looks like a password, token, key or URL with credentials. - Check if git tracks it. Run
git ls-files .claude/. Ifsettings.local.jsonis listed, it has been committed. - Check the history. Run
git log --all --oneline -- .claude/settings.local.json. Any output means old versions exist, even if you deleted the file later. - Check the shared file too. Repeat steps 2 and 3 for
.claude/settings.json. Apiiro found secrets there as well, mostly in allow rules. - Search your GitHub org. In GitHub code search, try
org:YOUR-ORG path:.claude/settings.local.json. This finds copies in repos you forgot about. - Check forks. If a public repo held the file, its forks may still have the secret. Apiiro found the same secrets copied into 211 forks of 74 repos.
- Open /permissions. Inside Claude Code, run
/permissions. It lists every rule and which settings file it came from, so you can spot and delete bad ones.
Tip: Check the file on your laptop even if it was never committed. A secret in a local file can still leak through backups, synced folders or a future careless commit.
Rotate Secrets and Clean Up
The order matters here. Cleaning git history first does not help if someone already copied the secret.
Step 1: Rotate first
Change every password, token and key the file has ever held. That includes values only in old commits. Treat public exposure as already stolen.
Step 2: Stop tracking the file
Remove the file from git but keep it on disk, then ignore it at the project level:
git rm --cached .claude/settings.local.json
echo ".claude/settings.local.json" >> .gitignore
git add .gitignore
git commit -m "Stop tracking Claude Code local settings"
Step 3: Edit the local file
Open the file and delete any allow rule that holds a secret. You can also do this from /permissions. Claude Code reloads settings files while it runs, so no restart is needed.
Step 4: Decide on history rewriting
You can strip the file from old commits with a tool like git filter-repo. This needs a force push and breaks teammates’ clones, and it cannot reach forks or copies already made.
Warning: Rewriting history is not a fix on its own. Once a secret has been public, only rotation makes it safe. Rewrite history for hygiene, never instead of rotating.
Safer Permission Rules
You do not have to give up “don’t ask again”. You just need rules that never contain the secret itself.
Use patterns, not exact commands
Per Claude Code’s permissions docs, a * wildcard matches any text. Put it after the subcommand so the rule stays narrow:
{
"permissions": {
"allow": [
"Bash(npm run *)",
"Bash(git log *)",
"Bash(psql *)"
],
"deny": [
"Read(./.env)",
"Read(./.env.*)",
"Read(./secrets/**)"
]
}
}
The older :* suffix, as in Bash(npm run:*), still works and means the same as a space plus a trailing *. The dialog now writes the space form.
Keep secrets in variables
Load credentials from a secrets manager or a shell variable, and have commands reference the name, like $DB_URL. Then a saved rule only stores the variable name, not the value.
Rule shapes compared
| Rule | Stores a secret? | Verdict |
|---|---|---|
Bash(mysql -u root -pHunter2 app) |
Yes, the password | Delete and rotate |
Bash(curl -H "Authorization: Bearer sk-...") |
Yes, the token | Delete and rotate |
Bash(psql "$DB_URL") |
No, only the name | Fine |
Bash(npm run *) |
No | Fine and broad |
Admins can go further with managed settings. These sit above every user and project file, so a company can set deny rules no developer can override. See our Claude Enterprise security controls guide for the admin side.
Scanning with gitleaks and TruffleHog
Secret scanners help, but Apiiro’s numbers show their limits. Of the 1,122 leaky files, gitleaks flagged 564 and TruffleHog flagged 643. Neither caught 215.
Most misses were passwords typed inside commands, such as after PGPASSWORD=, sshpass -p or mysql -p. Those do not look like a standard API key.
gitleaks commands
# Scan the whole git history of the current repo
gitleaks git -v .
# Scan just the folder on disk (catches untracked files)
gitleaks dir -v .claude/
TruffleHog commands
# Scan a local repo's history (run from the parent folder)
trufflehog git file://my-repo
# Scan a whole GitHub org, verified hits only
trufflehog github --org=YOUR-ORG --results=verified
# Scan local files, including settings.local.json
trufflehog filesystem .claude/
Add a pre-commit hook
The gitleaks project ships a pre-commit hook. Add it to .pre-commit-config.yaml so a secret is blocked before it is ever committed:
repos:
- repo: https://github.com/gitleaks/gitleaks
rev: v8.24.2
hooks:
- id: gitleaks
Use the latest release tag from the gitleaks repo instead of the example version. Then add a custom rule for passwords inside commands, since the defaults miss many of them.
Checklist for Security Teams
If you run security for a team that uses coding agents, Apiiro’s advice boils down to this list:
- Treat agent folders as secret stores:
.claude/,.cursor/,.gemini/and.specstory/, on laptops and in repos. - Scan history, not just HEAD: old versions held 734 extra secrets in Apiiro’s data.
- Update gitignore templates: add
.claude/settings.local.jsonto your org’s default.gitignore. - Tune scanners: add rules for passwords inside commands and connection strings.
- Prioritize public endpoints: a credential paired with a public host gives direct access, so rotate those first.
Our Claude Code hooks guide shows how a PreToolUse hook can block risky commands. Our auto mode guide covers a mode with fewer prompts and fewer saved rules.
My Take
This one is less a hack and more a design trap. The button does exactly what it says, but “remember this command” quietly becomes “remember this password” when the password is part of the command.
The fix on our side is cheap: one .gitignore line, variables instead of literals, and a scanner in CI. What worries me more is the auto-closed reports. A bot closing a security report as “not planned” is not the same as a fix.
Until Anthropic changes the default, treat settings.local.json like a .env file. If you are new to the tool, our Claude Code commands cheat sheet lists every config file it reads.
Troubleshooting: Common Problems and Fixes
git rm says the file did not match
The file was never tracked, which is good. Still add it to .gitignore so a teammate cannot commit it later.
The file keeps coming back in git status
You ran git rm --cached but did not add the ignore line. Add .claude/settings.local.json to the project .gitignore and commit it.
I deleted a rule but still get no prompt
The same rule may live in another file. Run /permissions to see each rule’s source, and check ~/.claude/settings.json and .claude/settings.json too.
I now get prompted for everything
You removed exact rules without adding patterns. Add narrow pattern rules such as Bash(npm run *) for the commands you trust.
The scanner found nothing, but I know a password was there
Default rules often miss passwords inside commands. Search the file by hand for -p, PASSWORD=, token and ://user: patterns.
Frequently Asked Questions
Is Claude Code’s “Don’t Ask Again” a security vulnerability?
It is a risky default rather than a classic bug. The feature saves the approved command as written, so any secret inside it gets stored in plain text in a project file.
Where does Claude Code save “don’t ask again” approvals?
In .claude/settings.local.json at the root of the git repository, as entries under permissions.allow. The file applies to every session in that repo.
Is settings.local.json gitignored by default?
Claude Code adds it to your global git excludes the first time it writes the file. It does not add it to the project’s .gitignore, so other machines and already-tracked files are not protected.
How many secrets did Apiiro find?
2,539 secrets across 1,994 public GitHub repositories, counting current files, old versions and other agent files. Current local settings files alone held 1,379 distinct secrets.
Do I need to rotate a secret that was only in my local file?
If it never left your machine, deleting the rule is usually enough. If the file was ever committed, pushed, synced or backed up somewhere shared, rotate it.
Will gitleaks or TruffleHog catch these leaks?
Partly. In Apiiro’s test, gitleaks flagged 564 and TruffleHog 643 of the 1,122 leaky files, and 215 were missed by both, mostly passwords typed inside commands.
Has Anthropic fixed this?
As of November 7, 2026 coverage, Anthropic had not commented and earlier GitHub reports had been auto-closed. Check the Claude Code changelog for any later change.




