If you've used Claude Code's "Yes, and don't ask again" on a shell command with a password or token in it, that secret is now saved in plain text in .claude/settings.local.json. If that file was ever committed, treat the secret as leaked. Research published on November 5, 2026 found more than a thousand such files on public GitHub. Check whether the file is tracked, rotate anything it ever held, then make the repo ignore it for good.
This guide covers what happened, why the default protection fails, and the commands to check, clean up and prevent it, for solo developers and for teams.
What Apiiro found
Apiiro researchers Shmulik Cohen and Matan Giladi published the original report on November 5, 2026. The figures below come from coverage of that report by DEV Community and Cyber Press, which agree on the main numbers.
| Metric | Figure |
|---|---|
Public settings.local.json copies scanned |
104,185 |
| Copies holding at least one secret | 1,122 |
| Distinct secrets in those files | 1,379 |
| Total secrets across all agent-related files, history and chat logs | 2,539 in 1,994 repos |
| Organizations the files belong to | 63 |
| Repos where a Claude co-authored commit changed the file | 539 of 1,105 traced |
| Files missed by both gitleaks and TruffleHog | 215 |
The secrets were mostly live infrastructure credentials, not demo keys. Within the local settings files, Cyber Press lists 379 database passwords, 122 Supabase service-role keys, 103 Supabase account tokens and 43 SSH credentials. The DEV write-up adds 42 GitHub, GitLab or npm tokens and 23 AWS keys. Examples include a production Azure PostgreSQL password and a production Square token.
There are two caveats on the numbers:
- Apiiro counted a value as a secret if it matched a known key format, or if a password sat next to a public server address and an AI reviewer agreed. It did not test whether the credentials still worked. One company it notified confirmed its key had been live.
- The shared
.claude/settings.jsonwas also affected: 298 of its 76,011 public copies held a secret, according to the DEV summary.
Apiiro compared the documentation of 16 coding agents. Claude Code was the only one confirmed to save the full approved command into a project file by default.
Why it happens, and why the gitignore doesn't save you
The approval is saved word for word
Claude Code's permissions docs say a "Yes, and don't ask again" approval for a Bash command lasts "permanently per repository and command." The settings docs say Claude Code saves it as an allow rule in .claude/settings.local.json. Sometimes the prompt offers a wildcard pattern, such as npm test *. Otherwise the rule is the literal command. So approving
PGPASSWORD=hunter2 psql -h db.example.com -U app
saves hunter2 into a JSON file in your project, and it stays there.
This isn't new. GitHub issue #5544, opened on August 11, 2025, reported that approved commands with API keys and tokens were "stored as is with plain text credentials." A bot closed it as "not planned" on January 3, 2026, after 60 days of inactivity. No maintainer resolved it.
The default ignore rule only covers your machine
According to the settings docs, the first time Claude Code writes the file in a repo that doesn't already ignore it, it adds **/.claude/settings.local.json to your global git excludes file. That's core.excludesFile, or ~/.config/git/ignore by default. That protects you on that machine, but it leaves several gaps:
- It isn't committed. Teammates, CI runners and your next laptop don't get the rule.
- Files you create by hand aren't covered. The docs say: "if you create it by hand, add it to
.gitignoreyourself." - Ignore rules don't untrack files. If the file was committed before the rule existed, git keeps tracking it.
- Agents and scripts commit too. In 539 of the 1,105 traced repos, a commit co-authored by Claude changed the file.
npm packages can leak it too
This isn't only a git problem. In April 2026, Check Point found .claude/settings.local.json inside 428 of about 46,500 npm packages it monitored. Thirty-three of those files, across 30 packages, contained credentials, IT Brief reported. If you publish packages, check your tarballs as well as your repos.
How to check your repositories
Run these from the root of each repo you've used Claude Code in. Also check private repos: they get forked, mirrored, shared with contractors and connected to third-party apps.
1. Is the file tracked now, or was it ever?
git ls-files | grep -E '(^|/)\.claude/settings(\.local)?\.json$'
git log --all --oneline -- '**/.claude/settings.local.json' .claude/settings.local.json
git check-ignore -v .claude/settings.local.json
The first command shows tracked copies, including ones in subdirectories. The second shows whether any commit ever touched the file. The third shows which ignore rule applies, if any. If the third command prints a path to your global excludes file and nothing from the repo's .gitignore, you're protected only on that machine.
2. Look at what's inside
Open the file, or run /permissions in Claude Code to see every rule and the file it came from. Look under permissions.allow for anything that looks like a credential: PGPASSWORD=, a password attached directly to -p (as in mysql -phunter2), Authorization: Bearer, sshpass -p, --token, connection strings like postgres://user:pass@, and provider prefixes such as ghp_, sk-, AKIA or eyJ (JWTs, including Supabase keys).
3. Scan history with a scanner and with grep
Use both, because the scanners missed 215 files in Apiiro's dataset. Most misses were passwords after PGPASSWORD=, sshpass -p or mysql -p, which have no provider prefix to match.
gitleaks git -v # gitleaks 8.19+
trufflehog git file://.
git log --all -p -- .claude/ | grep -nE 'PGPASSWORD=|sshpass -p|mysql .*-p|://[^:/ ]+:[^@ ]+@|Bearer '
On Windows PowerShell, pipe git log --all -p -- .claude/ into Select-String -Pattern 'PGPASSWORD=|sshpass -p|Bearer ' instead.
Also check other agent folders. The DEV write-up recommends scanning .claude/, .cursor/, .gemini/ and .specstory/, because saved chat transcripts made up part of Apiiro's 2,539 total.
4. Check published packages
npm pack --dry-run
If .claude/ shows up in the file list, add it to .npmignore, or tighten the files field in package.json. Then review versions you've already published.
Cleanup: rotate first, then untrack
Do these in this order. Deleting the file first feels productive, but it doesn't help: the secret is still in history, in forks (Apiiro found 211 forks of 74 repos carrying the same secrets), and in every clone.
-
Rotate every credential the file has ever held. That includes database passwords, SSH passwords, API tokens and cloud keys. For Supabase service-role keys, rotate in the project dashboard, then update your deploy environment.
-
Check access logs for each rotated credential, going back to the first commit that contained it.
-
Stop tracking the file and ignore it in the repo:
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" -
Clean the rules on your machine. Use
/permissionsor edit the file to delete any allow rule that contains a secret. Otherwise the plaintext is still on disk, and gets committed again the next time something stages it. -
Optionally rewrite history with a tool like
git filter-repo. This is cleanup only. It doesn't undo exposure that has already happened, and it forces every collaborator to re-clone.
Prevent it: change the habit and add a guardrail
Keep secrets out of approved commands
Put credentials in environment variables, loaded from a secrets manager or an ignored .env, so the approved command contains only the variable name:
PGPASSWORD="$DB_PASSWORD" psql -h "$DB_HOST" -U app
If you approve that, the saved rule contains $DB_PASSWORD, not the password. When the prompt offers a pattern (such as npm run test *), take it instead of approving one exact command. You get fewer rules and less chance of a literal value being saved.
Then stop Claude reading the files that hold the real values. The settings docs show deny rules for this:
{
"permissions": {
"deny": ["Read(./.env)", "Read(./.env.*)"]
}
}
Put this in ~/.claude/settings.json to cover every project, or in the shared .claude/settings.json to cover the team. The docs say deny rules in the shared file apply immediately, without waiting for workspace trust. Note that this blocks Claude's file tools; it doesn't stop a shell command from running cat .env. Treat it as one layer, not a sandbox.
Add a guardrail that runs everywhere
A pre-commit hook that refuses the file outright is simpler and more reliable than hoping a scanner recognizes a password:
#!/bin/sh
# .git/hooks/pre-commit (or a shared pre-commit framework hook)
if git diff --cached --name-only | grep -qE '(^|/)\.claude/settings\.local\.json$'; then
echo "Refusing to commit .claude/settings.local.json (may contain approved commands with secrets)."
exit 1
fi
Run the same check in CI with git ls-files, because local hooks can be skipped. Failing the build on a tracked local settings file costs nothing and catches commits made by agents.
For teams and platform owners
- Add
**/.claude/settings.local.jsonto your organization's.gitignoretemplates and repo scaffolding. - Add rules to your secret scanner for passwords inside commands (
PGPASSWORD=,sshpass -p,mysql -p, credentials in URLs), not just provider-prefixed keys. - Review the shared
.claude/settings.jsonin code review like any other config. The settings docs note that the VS Code extension's approval card lets you choose the shared file as the destination, which commits a rule for everyone. - If you're comparing coding agents, ask each vendor where approvals are saved and whether they're redacted. Apiiro's 16-agent comparison suggests the answer varies. The AI coding assistants directory is a starting point for a shortlist.
What to watch and what to do this week
The fix that matters is on Anthropic's side: redacting or refusing secrets in saved rules, and writing the ignore rule into the repo's own .gitignore instead of a global file. As of November 8, 2026, none of the coverage includes a response from Anthropic or a changelog entry addressing it. Watch the Claude Code settings docs for a change to the ignore behavior.
Until then, the checklist is short:
- Run the three
gitchecks above in every repo where you've used Claude Code. - Rotate anything you find, starting with database and cloud credentials on publicly reachable hosts.
- Commit a repo-level ignore rule and a pre-commit or CI check.
- Move credentials out of command lines and into environment variables.
If you ship tools for developers, the same lesson applies to your product: a convenience feature that saves user input to disk needs a redaction step and a safe ignore default. More security write-ups for builders are on the LaunchBoosts blog.
Frequently asked questions
Does Claude Code store passwords in settings.local.json?
Yes, if the password appeared in a Bash command you approved with "Yes, and don't ask again". Claude Code saves the approved command as an allow rule in .claude/settings.local.json, and unless the prompt offered a wildcard pattern, the full command is saved word for word, password included. Claude Code does not redact it.
Is .claude/settings.local.json gitignored by default?
Only partly. The first time Claude Code writes the file in a repository that doesn't already ignore it, it adds the path to your global git excludes file on your own machine. That rule isn't committed, so it doesn't protect teammates, CI runners, fresh clones, or files that were already tracked. Add the path to the repository's own .gitignore.
Will gitleaks or TruffleHog catch secrets in Claude Code settings files?
Often, but not always. In Apiiro's scan of 1,122 affected public files, gitleaks flagged 564 and TruffleHog flagged 643, and 215 files were missed by both, with the scanners' verification step turned off. Most of the misses were plain passwords after PGPASSWORD=, sshpass -p or mysql -p, which have no recognizable key format, so search for those patterns too.
I deleted settings.local.json from my repo. Am I safe?
No. The secret is still in git history, in any forks, and on any machine that cloned the repo. Rotate every credential the file ever held first, then stop tracking the file and add it to .gitignore. Rewriting history is optional cleanup and doesn't replace rotation.
Has Anthropic fixed the Don't Ask Again secrets issue?
No fix had been reported as of November 8, 2026, and none of the coverage includes a statement from Anthropic. A related GitHub issue, #5544, opened in August 2025, was closed automatically as "not planned" on January 3, 2026, after 60 days of inactivity. Until a fix ships, keep secrets out of commands you approve and gitignore the file in every repo.
Sources
- Apiiro: Coding agents leaking secrets on GitHub via "Don't Ask Again" (original research)— apiiro.com
- DEV Community: Claude Code's 'Don't Ask Again' Saved Secrets in 1,122 Public Files. 215 Beat Both Scanners— dev.to
- Cyber Press: Claude Code's "Yes, and Don't Ask Again" Feature Exposes Passwords and API Keys on GitHub— cyberpress.org
- Claude Code docs: Settings files and precedence— code.claude.com
- Claude Code docs: Configure permissions— code.claude.com
- GitHub issue #5544: approved bash commands containing credentials stored in plain text in settings.local.json— github.com
- IT Brief UK: Claude Code can leak secrets in public npm packages (Check Point research)— itbrief.co.uk
