Claude Code saved your password in settings.local.json? Check and fix it
When Claude Code asks to run a shell command and you pick “Yes, and don’t ask again”, it saves that exact command to .claude/settings.local.json in your project. If the command had a password or an API key in it, the file now holds it in plain text. In November 2026 Apiiro found real secrets in 1,122 public copies of this file on GitHub. Here’s how it happens, how to check your own repos in five minutes, and what to do if something leaked.
- How a password ends up in the file
- What Apiiro found
- “But Claude Code gitignores it”
- Check your repos in five minutes
- If a secret leaked
- Stop it from happening again
- What Anthropic says
- Questions
How a password ends up in the file
You ask Claude Code to check why a login fails on staging, or to run a query against the database. It reads the connection details from your config and proposes a command that contains them, because the command can’t work without them. Claude Code asks before it runs a shell command. You choose “Yes, and don’t ask again”, and it saves the command as an allow rule, word for word:
.claude/settings.local.json
{
"permissions": {
"allow": [
"Bash(PGPASSWORD=hunter2 psql -h db.example.com -U app -c 'select 1')"
]
}
}
The password here is made up, but the shape is the real one. Claude Code keeps this file at the root of the git repository, even when you started it in a subfolder. The rule stays there until someone deletes it, long after the command ran. And the longer you’ve been clicking “don’t ask again” in a project, the more the file holds.
It’s easy to miss. Claude Code asks many times a session, and after the tenth prompt most of us stop reading the command. A password is one word in a long line.
What Apiiro found
Apiiro, an application security company, searched public GitHub on November 1–3, 2026:
- 104,185 public copies of
.claude/settings.local.json, in 103,617 repositories. - 1,122 of them (1.1%) hold a real secret, 1,379 distinct ones: 379 database passwords, 122 Supabase service-role keys, 43 SSH logins (some with a root password), plus cloud, GitHub, npm and AI model keys.
- 2,539 secrets in 1,994 repositories once you add the file’s git history, the shared
.claude/settings.jsonthat teams commit on purpose, and agent transcripts saved in repos. 63 organizations are affected, from student groups to software vendors. - Claude often commits the file itself. In 539 of the 1,105 repositories Apiiro traced, a commit co-authored by Claude changed it.
- Secret scanners miss a lot of it. Gitleaks flagged 564 of the 1,122 files, TruffleHog 643, and 215 were flagged by neither. A password after
PGPASSWORD=,sshpass -pormysql -pdoesn’t look like a key format, so both tools mostly let it through.
Apiiro compared how 16 coding agents store approvals. Claude Code was the only one confirmed to write the full approved command to a project file by default.
“But Claude Code gitignores it”
It does, and that’s why most people think they’re safe. They aren’t always. The first time Claude Code writes the file in a git repository that doesn’t already ignore it, it adds **/.claude/settings.local.json to your global git excludes file on that machine (core.excludesFile, or ~/.config/git/ignore by default). Nothing goes into the repository’s own .gitignore, so the protection doesn’t travel with the project. It doesn’t cover:
- A file you created by hand before Claude Code wrote to it. The documentation says to gitignore that one yourself.
- A folder that became a repository later, or a copy of the project on a machine where that global rule isn’t set.
- A file that’s already committed. Git ignore rules don’t apply to tracked files, so every new approval is added to the tracked file and goes out with your next commit.
- Copies that leave without git: an npm package, a Docker image built with a copy command, a zip you send someone.
Even a file that never leaves your Mac is plain JSON in the project folder, readable by any malware that scans developer machines for credentials.
Check your repos in five minutes
In each repository, from its root. The * also catches copies in subfolders, which Claude Code versions before 2.1.211 wrote to:
# Is the file tracked right now?
git ls-files '*settings.local.json'
# Was it ever committed, on any branch?
git log --all --oneline -- '*settings.local.json'
# Is it ignored, and by which rule?
git check-ignore -v .claude/settings.local.json
Then look inside every copy on your Mac for the patterns scanners miss:
# Every copy in your home folder
find ~ -path '*/.claude/settings.local.json' -not -path '*/Library/*' 2>/dev/null
# Rules that carry a password, a token or a key
grep -nE 'PGPASSWORD=|sshpass -p|mysql -p|Bearer |://[^ ]+:[^ ]+@|sk-|ghp_|AKIA' .claude/settings*.json
The grep is a filter, not a verdict: read each hit. You can also see and delete your allow rules inside Claude Code with /permissions. On GitHub, search your account or organization for path:.claude/settings.local.json, and do the same for .claude/settings.json. If you commit agent notes, memory files or chat transcripts, check those too: Apiiro found secrets in all three.
We ran this on the Mac we build Meowdoro on: four settings files, 54 allow rules, none that the grep flagged. Our repository also ignores the whole .claude/ folder in its own .gitignore, so even a bad rule wouldn’t get committed. That one line is the cheapest fix on this page.
If a secret leaked
- Rotate first. Revoke the key or change the password at the provider, today. Deleting the file doesn’t help: the secret stays in git history, and in every fork and clone. Apiiro found 734 secrets that were only in old versions of the file, and 211 forks carrying the same secrets.
- Untrack the file and ignore it in the repository itself:
git rm --cached .claude/settings.local.json echo '.claude/settings.local.json' >> .gitignore git commit -m "Stop tracking Claude Code local settings" - Delete the rule from your local copy too, or in
/permissions. Otherwise the old password sits on your disk and gets used again. - Clean the history if you need to, for example with
git filter-repo --invert-paths --path .claude/settings.local.jsonand a force push. It’s worth doing on a public repo, but it doesn’t reach forks, so it never replaces rotation. - Turn on secret scanning and push protection in your repository’s security settings, so the host warns you next time.
Stop it from happening again
- Keep secrets out of commands. Put them in environment variables or your secrets manager. Then Claude proposes
psql "$DATABASE_URL", and the rule records the variable name, not the password. - Write patterns instead of saving exact commands. In
/permissionsor a settings file, allow a family of commands with*for the part that varies, likeBash(npm run *). A pattern is shorter, covers more, and has no password in it. - Read the command before “don’t ask again”. One look at the line saves a rotation later. If it has a secret in it, say yes once.
- Add the file to every repository’s
.gitignore, or to your organization’s template, so the rule travels with the project and covers every machine. - Consider auto mode. In auto mode a classifier reviews actions instead of you, so there are fewer prompts to click through and fewer rules saved. It isn’t a complete fix: it still asks about some commands, and you can still answer “don’t ask again”.
What Anthropic says
Apiiro shared the research with Anthropic before publishing. On November 7 Boris Cherny, who leads Claude Code, answered on Hacker News that the team is “looking into what we can do to prevent this.” The ideas he named: making auto mode catch it and building in basic secret scanning, to be prototyped before anything ships. He also said Claude Code gitignores the file when it creates it, so in his view the exposed copies were made by hand and checked in, and he recommended auto mode and the secret scanning that GitHub and other hosts offer. Until a fix ships, the checks above are on you.
Where Meowdoro fits
We built Meowdoro, a focus timer around the MacBook notch that shows your Claude Code and Codex sessions. It never approves anything and never reads your commands. It shows which session is waiting for a permission, so you can answer it when you’re back at the desk and able to read the line, instead of clicking “don’t ask again” in between two other things. Try it and tell us what’s missing.
Questions
What is .claude/settings.local.json?
Your personal Claude Code settings for one project. Claude Code applies it over the shared .claude/settings.json and writes your standing approvals into it, such as “Yes, and don’t ask again” for a Bash command. It’s meant to stay on your machine.
Does Claude Code add settings.local.json to .gitignore?
It adds it to your global git excludes file the first time it writes the file in a repository that doesn’t already ignore it. It doesn’t touch the repository’s own .gitignore, and it doesn’t cover a file you made by hand or one that’s already committed. Add the line to the repository’s .gitignore yourself.
Is the file safe if I never committed it?
Safer, not safe. It’s plain text in your project folder, so malware or a copied folder can take it. Delete any rule with a secret in it, even if the file never left your Mac.
Does deleting the file fix a leak?
No. The secret stays in git history, forks and clones. Rotate the credential first, then untrack and ignore the file.
Does this happen in Codex?
Apiiro compared 16 coding agents’ documentation and found only Claude Code confirmed to write the full approved command to a project file by default. Any agent can still leak a secret you type into a command into its transcripts or notes, so the same habits help everywhere.
Related guides
Numbers: Apiiro, “Coding Agents Are Leaking Your Secrets to GitHub”, with its November 8 update quoting Boris Cherny, read on November 9, 2026. How the file works: the Claude Code documentation on settings and permissions, read on November 9, 2026. The example password is made up.