Book a Free Strategy Call
Skip the read: talk to Walid in 30 min.
Free strategy call. We map your AI engineering team, you keep the notes.
If you ever picked "Yes, and don't ask again" on a Claude Code prompt for a command that had a password, token or connection string in it, that secret is now sitting in plain text in .claude/settings.local.json. If that file was ever committed, it's in your git history too. In November 2026, the security firm Apiiro found at least one secret in 1,122 of 104,185 public copies of that file on GitHub.
You can check a repo in about five minutes. The order matters: find the files, rotate whatever they hold, and only then clean up git. This guide gives you the commands for each step (we ran them on a test repo with Claude Code 2.1.322 on 2026-11-12), explains why the protection Claude Code adds by default didn't stop these leaks, and covers the permission setup that keeps secrets out of the file in the first place.
What happened: how "don't ask again" writes secrets to a file
When Claude asks to run a Bash command and you choose "Yes, and don't ask again", Claude Code saves that approval as an allow rule in .claude/settings.local.json. Anthropic's settings docs describe this: the file is created "the first time you give a standing approval on a permission prompt".
The rule is the command itself. Approve this once:
PGPASSWORD=hunter2 psql -h db.example.com -U app -c "select 1"
and the file now holds:
{
"permissions": {
"allow": [
"Bash(PGPASSWORD=hunter2 psql -h db.example.com -U app -c \"select 1\")"
]
}
}
The same goes for sshpass -p, mysql -p<password>, a curl with an Authorization: Bearer header, or a database URL with the password inline. Nothing in Anthropic's documentation says stored rules get redacted, and the Cloud Security Alliance research note on the incident says the same.
This isn't new. A GitHub issue titled "approved bash commands containing credentials are stored as is with plain text credentials in settings.local.json" (#5544) was opened on 2025-08-11 and closed as "not planned" on 2026-01-03. A second report, #13106, was closed as a duplicate in December 2025.
In short: nobody broke into anything. A settings file stored the exact command you approved, as designed, and then the file ended up somewhere public.
What Apiiro found, by the numbers
Apiiro searched public GitHub for the file and checked each copy for credentials. Its report is dated November 6, 2026. The figures below are the report's own, as laid out in the CSA note (Apiiro's site blocked our automated fetch, so we checked them there and against news coverage, and they match).
What the 1,379 secrets were, and where else they showed up:
| Finding | Number | Scope |
|---|---|---|
| Database passwords | 379 | Among the 1,379 secrets in current files |
| SSH logins for public servers | 43 | Same |
| GitHub, GitLab and npm tokens | 42 | Same |
| AWS keys | 23 | Same |
| Secrets found only in git history | 734 | Across 509 repositories |
| Forks carrying the same secrets | 211 | Forks of 74 repositories |
Shared .claude/settings.json copies with secrets | 298 of 76,011 | The team file, not the local one |
| Total across all sources | 2,539 secrets in 1,994 repositories | Current files, history, shared settings and agent chat logs combined |
Three more points from the report shape how urgent this is for you:
- In half of the affected repositories, the file had been on the default branch for at least 217 days. Leaks like this tend to sit there for months.
- Apiiro did not test whether the secrets still worked. One company it notified confirmed its exposed key was live.
- Of 16 coding agents whose documentation Apiiro compared, Claude Code was the only one confirmed to save the full approved command in a project file by default.
Different outlets quote 1,122, "about 1,100", 1,379 or 2,539. They measure different things: 1,122 is files, 1,379 is distinct secrets in current files, and 2,539 adds git history, shared settings and chat logs.
Why the global gitignore didn't stop it
Claude Code does try to keep this file out of git. Per the docs, the first time it writes the file in a repo that doesn't already ignore it, it adds **/.claude/settings.local.json to your global git excludes file (core.excludesFile, or ~/.config/git/ignore by default). On the machine we tested on, that line was already there.
That protects one laptop, in some cases. It misses these:
- A file you created by hand. The docs say that if you created it yourself before Claude Code wrote to it, you add it to
.gitignoreyourself. - A file git already tracks. Git's own docs are clear that ignore rules don't affect files git already tracks. One
git add -f, or one commit made before the exclude existed, and every later change gets committed as normal. - Any other machine. A global exclude doesn't travel with the repo. A teammate's laptop, a dev container, a CI runner or a Docker build context has no such line.
- Packages and images. The exclude only affects git. An npm tarball or Docker image built from the folder can include
.claude/anyway. - The shared settings file. In the VS Code extension, the approval card lets you pick where the rule goes, including the committed
.claude/settings.json. Apiiro found secrets in 298 shared files; promoting approved rules this way is one likely route.
Here is item 2 in our test repo, with the global exclude in place. After a forced add, git tracks the file and shows edits to it, and the plain check-ignore check says nothing:
$ git ls-files
.claude/settings.local.json
$ git status --short
M .claude/settings.local.json
$ git check-ignore -v .claude/settings.local.json
(no output, exit code 1)
$ git check-ignore -v --no-index .claude/settings.local.json
/home/you/.config/git/ignore:3:**/.claude/settings.local.json .claude/settings.local.json
Watch out:
git check-ignoreskips tracked files unless you pass--no-index. A tracked settings file can look "not ignored" even with the pattern in place, so checkgit ls-filesinstead.
Free weekly brief
Steal our production automations
The exact n8n flows, Claude Code setups, and prompts we ship for clients, broken down step by step. No spam, unsubscribe anytime.
Check your repo in 5 minutes
Run these from the repo root. They only read, so they're safe on any clone.
- Is either settings file tracked right now?
git ls-files | grep -E '(^|/)\.claude/settings(\.local)?\.json$'
No output means neither file is tracked in this checkout. A hit on settings.local.json means it's committed.
- Was it ever committed, on any branch?
git log --all --oneline -- '*.claude/settings.local.json'
A file that was deleted later still shows here, and so does its content.
- Does the history hold anything that looks like a credential? Apiiro's 215 misses were mostly passwords in command syntax, so grep for those patterns as well as token prefixes:
git log --all -p -- '*.claude/settings*.json' | grep -nE \
'PGPASSWORD=|sshpass -p|mysql[^"]* -p[^ "]|Bearer |://[^/ :]+:[^@ ]+@|AKIA[0-9A-Z]{16}|ghp_|github_pat_|glpat-|npm_'
On our test repo this printed the planted PGPASSWORD=hunter2 line from a commit that had already been "fixed". Treat any hit as exposed if the repo was ever public, forked or cloned outside your company.
-
Across the whole organization: on GitHub, run a code search for
path:.claude/settings.local.json org:<your-org>, then the same forpath:.claude/settings.json. Include archived repos and forks. Code search only sees the current default branch, so repeat step 3 on every repo it finds. -
In packages you publish: run
npm pack --dry-runand look for.claude/in the file list. Check Point found 428 of about 46,500 npm packages shipping.claude/settings.local.json, and 33 of those files held credentials (SecurityBrief).
Found a secret? Rotate first, then clean up
The order matters. Deleting the file or rewriting history doesn't make a leaked key safe: forks, clones, caches and anyone who scraped the repo still have it.
- Rotate every credential the file has ever held. Database passwords, SSH logins, cloud keys and package-publishing tokens first, since those reach real systems. Check access logs for use you can't explain.
- Untrack the file and ignore it at repo level, so the rule travels with every clone:
git rm --cached .claude/settings.local.json
printf '\n**/.claude/settings.local.json\n' >> .gitignore
git add .gitignore && git commit -m "Stop tracking Claude Code local settings"
- Delete the secret-bearing rules from your local copy: open
.claude/settings.local.jsonand remove anyallowentry with a literal password, token or URL credential in it. Claude Code reloads settings files on save. - Rewrite history with
git filter-repoonly if you need to, and only after rotation. On a public repo, contact GitHub support about cached views and forks. A rewrite tidies the repo; the rotation is what protects you. - Add the file to
.npmignoreand.dockerignoreif the repo builds packages or images.
Stop it happening again
The real fix is to keep secrets out of commands, so there's nothing to save.
Reference credentials, don't type them. Use an environment variable your shell already has (psql "$DATABASE_URL"), a ~/.pgpass file, an SSH key in the agent, or a secrets manager CLI. An approved rule then stores $DATABASE_URL, not the password.
Approve patterns, not one-off commands. A rule like Bash(npm run test *) covers a family of safe commands. Anthropic's permissions docs explain the syntax. Avoid wide patterns on tools that carry credentials, such as curl, ssh or database clients: for those, approve once without "don't ask again".
Deny reads of secret files. The settings docs show the shape:
{
"permissions": {
"deny": ["Read(./.env)", "Read(./.env.*)"]
}
}
If your team manages Claude Code centrally, put deny rules like these in managed settings so they apply on every machine and developers can't remove them from their own files.
Scan for command-syntax passwords. Default rule sets missed 215 files. In gitleaks, extend the default config with a rule of your own:
[extend]
useDefault = true
[[rules]]
id = "inline-command-password"
description = "Password passed inline to a CLI"
regex = '''(PGPASSWORD=\S+|sshpass -p\s*\S+|mysql(\s[^"\n]*)?\s-p[^\s")]+)'''
Run it on full history in CI and as a pre-commit hook. Turn on secret scanning and push protection on your git host as well.
Add **/.claude/settings.local.json to your repo templates. One line in the .gitignore your team starts new repos from covers every clone, every CI runner and every new hire. If you write Claude Code hooks, a PreToolUse hook that blocks git add on the file is another backstop.
Rotate now, untrack, repo .gitignore, then decide on a history rewrite
Still rotate. Deleted files stay readable in every clone and fork
Clean literal secrets out of local rules and add the repo-level ignore anyway
Org-wide search, managed deny rules, CI secret scanning on full history
Is Claude Code safe to use after this?
Yes, with the same care you'd give any tool that runs shell commands with your credentials. The settings file is one of several places an AI coding agent can expose secrets. The others are what it can read, which MCP servers it talks to, and what ends up in transcripts and logs. We rank them in our post on Claude Code security risks, and AI agent security best practices covers the wider agent picture.
For a team, one developer cleaning their laptop doesn't settle it. You need the setup itself to stop a repeat. A Claude Code security audit checks what your AI coding agents can read, run and send: files and secrets, shell permissions, MCP servers and prompt-injection paths, with a written report and a hardened configuration package at the end. If you'd rather set up the permissions and hooks with help, our Claude consulting team does that too.
FAQ
What is .claude/settings.local.json?
It's Claude Code's per-project personal settings file. Claude Code creates it the first time you give a standing approval, such as "Yes, and don't ask again" on a Bash command, and saves that approval as an allow rule. It overrides the shared .claude/settings.json for you only and is meant to stay out of git.
Does Claude Code gitignore settings.local.json automatically?
Partly. The first time Claude Code writes the file, it adds **/.claude/settings.local.json to your global git excludes file, so it works on that machine only. A file you created by hand, a file git already tracks, and every other machine or CI runner aren't covered. Add the line to the repo's own .gitignore.
How do I know if my secrets leaked?
Run git log --all --oneline on the settings file path to see whether it was ever committed, then search the patch history for passwords and token prefixes. If any secret shows up in a repo that was public, forked or cloned outside your company, treat it as exposed. Apiiro found secrets in 1,122 of 104,185 public copies of the file.
Is deleting the file enough?
No. Deleting it or untracking it stops new leaks, but earlier commits, forks and clones still hold the secret. Rotate every credential the file ever held first, then untrack it, add it to the repo .gitignore, and rewrite history only if you need to.
Why didn't gitleaks or TruffleHog catch these?
Many of the secrets were passwords inside command syntax, such as PGPASSWORD=, sshpass -p or mysql -p, which don't look like known token formats. Apiiro reported that gitleaks flagged 564 of the 1,122 files, TruffleHog 643, and neither flagged 215. Add custom rules for inline command passwords and scan full history.
Has Anthropic fixed it?
We found no official Anthropic statement or redaction fix as of 2026-11-12. Two GitHub issues on stored credentials were closed without a code change: #5544 as "not planned" and #13106 as a duplicate. Until that changes, keep secrets out of approved commands and make sure the file is ignored in every repo.
Can you check our repos and Claude Code setup for us?
Yes. Our Claude Code security audit covers settings files and secrets exposure, shell permissions, MCP servers and prompt-injection paths for teams using Claude Code, Copilot or Cursor. It ends with a written report and a hardened configuration package.
Sources: Apiiro: coding agents leaking secrets via "don't ask again" (Nov 6, 2026), Cloud Security Alliance research note (Nov 8, 2026), Claude Code settings docs, Claude Code permissions docs, GitHub issue #5544, GitHub issue #13106, Git gitignore docs, SecurityBrief on npm packages, gitleaks
Book a Free Strategy Call
Building this in production?
Walid runs a 30-min call to map your AI engineering team. Free, no slides.
Free 30-minute call
Want us to look at your setup?
Leave your name and email. You get the calendar right after.

Ex-IBM AI engineer and enterprise architect. Adel owns the technical architecture behind every automation and AI agent system AY Automate ships.
