Claude Code’s settings.local.json file is leaking passwords and API keys to public GitHub repos, according to Apiiro. The security firm found 104,185 public copies of .claude/settings.local.json on GitHub. Of those, 1,122 held at least one real secret. The cause is one click on "Yes, and don’t ask again" in a permission prompt.
That choice saves the approved shell command as a rule in the file. If the command had a password or token typed into it, the secret is saved too. This post covers Apiiro’s findings, what Anthropic’s docs say, and how to clean up. Facts are as of the evening of November 8, 2026. If you are new to the tool, start with our explainer on what Claude Code is.
The short version: search your GitHub account and organization for the file. If one holds a secret, rotate the secret first. Then remove the file and purge it from git history. Add .claude/settings.local.json to each repo’s own .gitignore. Stop typing secrets into commands an agent runs; reference environment variables instead.
What Apiiro found in public settings.local.json files
Every number here comes from Apiiro’s post, Coding Agents Are Leaking Your Secrets to GitHub. The Hacker News reported it on November 7.
Apiiro says public GitHub holds 104,185 copies of .claude/settings.local.json in 103,617 repositories. Of those, 1,122 files (1.1%) hold a real secret, with 1,379 distinct secrets between them. Apiiro’s breakdown:
| Secret type | Count, per Apiiro | What it opens |
|---|---|---|
| Database passwords | 379 | A database directly |
| Supabase service-role keys | 122 | A Supabase database with full rights |
| Supabase account tokens | 103 | Every project in the Supabase account |
| SSH logins for public servers | 43 | A server, some as root |
| GitHub, GitLab and npm tokens | 42 | Code and package publishing |
| AWS keys | 23 | Cloud accounts |
The rest are mostly API tokens for third-party services and AI model keys, Apiiro says. It counted 63 organizations among the owners, from student groups to startups, software agencies and established vendors.
Apiiro says older versions in git history hold 734 more secrets in 509 repos. No current copy contains them, usually because someone deleted the file. Forks copy it too: 211 forks of 74 repos carry the same secrets. Counting history, the shared .claude/settings.json and saved agent chat transcripts, Apiiro’s total is 2,539 distinct secrets in 1,994 repos.
On whether the keys work: Apiiro did not test them. Its post says, "No credential was used or tested, and no host was contacted." A "real secret" in its count matches a known key format, or is a password or token next to a public host. It is not known to be valid. Apiiro says one organization it notified confirmed its key had been live.
How a secret ends up in the settings.local.json file
Anthropic’s Claude Code settings documentation describes the file as personal settings for one project. It says that when you choose "Yes, and don’t ask again" on a Bash command, Claude Code saves the approval there as an allow rule. The permissions documentation adds that the rule is saved at the root of the git repository and applies to future sessions anywhere in that repo.
Apiiro explains the rest. Ask Claude Code to check a database or call an API, and its proposed command often carries a secret from your config. The saved allow rule is the command string itself. So a command like PGPASSWORD=... psql -h db.example.com becomes a permanent line in the file, password included.
Apiiro reproduced it with Opus 5.5 and Fable 5.1 and calls it Claude Code’s behavior, not the model’s. It also traced 1,105 repos with a real secret. In 539, a commit co-authored by Claude changed the file.
What Claude Code does to keep settings.local.json out of git
Anthropic’s docs say Claude Code does try to keep the file private. The first time it writes the file in a git repo, it adds **/.claude/settings.local.json to your global git excludes file. That file is set by core.excludesFile, or defaults to ~/.config/git/ignore. If you create the file by hand, the docs say to add it to .gitignore yourself.
Apiiro points out the gaps. The global exclude lives on one developer’s machine. It misses a folder that becomes a repo later, a clone on another machine, and a file already committed. The rule never goes into the repo’s own .gitignore, so nothing travels with the project.
Anthropic’s permissions docs describe one more behavior. When the local file is tracked in git, Claude Code treats it as repository-supplied and holds its rules until you trust the folder. Apiiro says the user is not told the file is tracked, and the next approval is still added to it.
How to check your repos for a leaked settings.local.json
Work through these checks for your own account and any organization you manage. Our post on Claude Code permission modes and project settings covers how the shared and local files differ.
Search GitHub. In GitHub code search, run path:.claude/settings.local.json org:YOUR-ORG, then path:.claude/settings.json org:YOUR-ORG. GitHub’s code search syntax guide says path: matches anywhere in a file path. Type the org: name in full. Use user: for a personal account. Each public hit is a file anyone can read.
Check git history. A file deleted today can still sit in an old commit. In each local clone, run this.
git log --all --oneline -- .claude/settings.local.json
Any output means the file was committed at some point. git ls-files .claude/settings.local.json tells you if it is tracked right now.
Read the file on your machine. Apiiro says to read the allow list in every local copy, committed or not. On macOS or Linux, find ~ -path "*/.claude/settings.local.json" lists them. Look for passwords, tokens, keys and connection strings.
Confirm your global exclude. Run git config --global core.excludesFile and open that file. If it is empty, check ~/.config/git/ignore. Look for the **/.claude/settings.local.json line.
Clean up a leaked settings.local.json: rotate first
The order matters. GitHub’s guide to removing sensitive data from a repository says to revoke or rotate the secret first. Once it is rotated, the old value no longer grants access. Apiiro gives the same advice: "rotate first, clean up second."
1. Rotate every secret the file has ever held. Check old versions in history, not only the current file. Database passwords, cloud keys, SSH passwords and API tokens all count. For AI provider keys, see our post on OpenAI API key expiration controls. Our earlier coverage of the DreamHost breach walks through rotation for hosting and database passwords.
2. Untrack the file and ignore it in the repo. These are Apiiro’s steps.
git rm --cached .claude/settings.local.json
echo ".claude/settings.local.json" >> .gitignore
Commit both changes. The repo’s own .gitignore now protects every clone, not only your laptop.
3. Purge it from history. GitHub recommends git-filter-repo version 2.47 or later. Here is its example with this file’s path.
git-filter-repo --sensitive-data-removal --invert-paths --path .claude/settings.local.json
Then force-push. GitHub warns that copies can survive in forks, other users’ clones, cached views and pull requests. GitHub Support can remove cached views and references. Ask fork owners to delete or update their forks.
4. Delete secret-bearing rules from local copies. Remove every allow entry that holds a secret, even in repos you never pushed. Apiiro notes that malware on developer machines already scans for credentials in bulk.
Stop secrets from landing in settings.local.json again
Use environment variables or a secrets manager. Apiiro says to reference secrets as $VAR. The saved rule then records $DB_URL, not the password. Load those variables from your shell profile, a .env file kept out of git, or a secrets manager. Our env-var secrets migration checklist shows the pattern for WordPress sites.
Keep Claude Code out of your secrets files. Anthropic’s settings docs show deny rules like Read(./.env) and Read(./.env.*). These block Claude’s file tools from reading those files, so their values are less likely to show up in commands it proposes.
Prefer narrower pattern rules. Apiiro suggests approving patterns instead of literal commands. The permissions docs show the syntax: Bash(npm run *) allows any npm run script but not npm install. The docs warn to put the * after the subcommand, because Bash(git *) allows every git command. A pattern rule saves the shape of a command, not a secret typed into it.
Scan for this file in CI. GitHub’s guide recommends push protection to block pushes that contain hardcoded secrets. Apiiro found scanners alone miss many of these. Of the 1,122 files, gitleaks flagged 564 and TruffleHog 643, while 215 were flagged by neither. The gap was passwords typed inside commands, after PGPASSWORD=, sshpass -p or mysql -p. Apiiro also suggests adding the file to your organization’s .gitignore templates.
We covered the same habit in posts on credentials in AI tooling and the npm supply chain.
What Anthropic has said
A search of Anthropic’s and Claude’s sites on November 8 turned up no statement on the research. Apiiro’s post, updated November 8, says Boris Cherny, Anthropic’s head of Claude Code, replied on Hacker News on November 7. Apiiro quotes him saying the team is "looking into what we can do to prevent this." Options he named to prototype include "making sure auto mode catches this, building in basic secret scanning."
Per Apiiro, Cherny also said Claude Code gitignores the file when it creates it. In his view, the exposed copies were made by hand and checked in. That matches Anthropic’s docs. Apiiro’s data shows Claude itself committed the file in 539 repos.
Apiiro says users have reported this in GitHub issues since August 2025, with no reply from Anthropic. It also says it compared 16 coding agents’ documentation. Claude Code was the only one confirmed to write the full approved command to a project file by default. Apiiro found secrets in VS Code Copilot auto-approve settings in 9 repos too.
Frequently Asked Questions
What is .claude/settings.local.json?
It is Claude Code’s personal settings file for one project. According to Anthropic’s docs, Claude Code creates it the first time you choose “Yes, and don’t ask again” on a permission prompt, and saves the approved command there as an allow rule.
Why does the file contain secrets?
The saved rule is the full command string. If the command had a password or token typed into it, the secret is saved with it, according to Apiiro’s research.
Doesn’t Claude Code already gitignore settings.local.json?
Partly. Anthropic’s docs say Claude Code adds the file to your global git excludes the first time it writes it in a git repo. That does not cover files created by hand, other machines, or files already committed, and it never adds the file to the repo’s own .gitignore.
How many leaked copies did Apiiro find?
Apiiro found 1,122 public copies with at least one real secret, out of 104,185 public copies on GitHub. Across history, the shared settings file and agent transcripts, it counted 2,539 distinct secrets in 1,994 repos.
Did Apiiro confirm the keys still work?
No. Apiiro says no credential was used or tested. One organization it notified confirmed its key had been live.
I deleted the file. Am I safe?
No. The file remains in git history, forks and clones. Rotate every secret it ever held, then purge it from history with git-filter-repo.
How do I search my organization on GitHub?
Use GitHub code search with path:.claude/settings.local.json and org: followed by your full organization name. Repeat with path:.claude/settings.json.
Will secret scanners catch these leaks?
Not always. Apiiro says gitleaks and TruffleHog together missed 215 of the 1,122 files, mostly passwords typed inside commands. Add a check for this file path as well.