
On 5 November 2026, Apiiro researchers Shmulik Cohen and Matan Giladi published a Claude Code secrets leak: 1,379 distinct credentials in public copies of .claude/settings.local.json . Apiiro sells application and agent security products. The file stores allow rules in plaintext, and a machine-local Git exclude does not keep it off GitHub.
Key takeaways
- Apiiro's 5 November 2026 research found 1,379 credentials in 1,122 public .claude/settings.local.json files, about 1.1% of 104,185 copies.
- "Yes, and don't ask again" stores the approved shell command verbatim, so inline passwords and tokens land in plaintext.
- The Git protection is a machine-local global exclude, not a project .gitignore. A fresh clone or force-add can commit the file silently.
- History adds 734 secrets; with shared settings and transcripts the published total is 2,539 secrets across 1,994 repositories. Scanners missed 215 current files.
What did Apiiro find in the Claude Code secrets leak?
Apiiro measured 104,185 public copies of the file across 103,617 GitHub repositories. Real secrets appeared in 1,122 of them, about 1.1%.
Those files held 1,379 distinct credentials. More than a third granted direct database access: 379 database passwords and 122 Supabase service-role keys. SSH logins, AWS-style cloud keys, and repository tokens made up the rest.
The coverage assigns no CVE, no CVSS score, and no fixed version, and it does not quote Anthropic. An August 2025 bug report already described approved commands with API keys stored verbatim.
How does "Yes, and don't ask again" store the command?
Claude Code asks before most shell commands. “Yes, and don’t ask again” saves the exact command as a plaintext allow rule in .claude/settings.local.json inside the project. A database connection string, API token, or password typed into that command is stored in the same string. The auto-approve risk model for unattended coding-agent runs is the decision side; the settings file is the copy Git can commit.
Apiiro also found secrets in saved transcripts, memory files, and deployment notes, and it flags the shared file .claude/settings.json for the same audit.
Why doesn't the Git exclude stop a public push?
On the first write inside a repository, Claude Code adds the path to the developer’s global Git excludes. The entry is not in the project’s .gitignore and does not travel with the clone.
Those rules skip untracked files only. They do not drop a path already in the index, and git add -f overrides them. A force-add, another machine, or a fresh clone can commit the file with no warning.
What else was in Git history and agent artifacts?
History review found 734 more secrets in 509 repositories, usually after the file was deleted and the blob stayed. The current default branch will not show them.
Apiiro’s full tally is 2,539 secrets across 1,994 repositories once history, shared settings files, and saved transcripts are counted together. Search every ref, not the tip of one branch:
git log --all --full-history -- .claude/settings.local.json .claude/settings.json
Other leak paths never touch this file. The six paths coding agents use to leak secrets maps those channels.
Why did Gitleaks and TruffleHog miss 215 files?
Gitleaks and TruffleHog together missed 215 of the 1,122 affected files. A clean scan is not proof the allow-rule file is safe. Apiiro did not publish a cause; the credentials sit inside approved command strings, which is a plausible reason pattern matchers fail, but that is an inference. Read the command text after the scanners run. That check is why the file still belongs in a secure code review.
What should security leads do this week?
Rotate every password, key, and token that may have appeared in an approved command before you rewrite Git history. Treat a credential that reached a public remote as compromised; rewrites do not erase forks or clones that already exist.
- Preserve repository URLs, commit hashes, and credential identifiers before anyone deletes files.
- Revoke or rotate at the provider and confirm the old credential fails.
- Run
git rm --cached .claude/settings.local.json, then add that path or the.claude/directory to.gitignoreand to organization repo templates. - Review transcripts, memory files, deployment notes, and release archives. Redact scanner output before it enters a ticket.
- Reference a secrets-manager variable, such as
$DB_URL, so the allow rule stores a variable reference, not a password.
Prove it in a disposable repository with a dummy value, from a fresh clone and a second machine. A passing test on the laptop that already has the global exclude does not count.
Persisted approvals belong in the test scope. LLM application penetration testing should exercise real tool approvals and the files they write.
FAQ
Does Claude Code store passwords in settings.local.json?
Yes, when you approve a shell command with "Yes, and don't ask again." The exact command is written as a plaintext allow rule, so an inline password, connection string, or API token is stored with it.
Does Claude Code add settings.local.json to gitignore?
No. It adds the path to the developer's global Git excludes. That rule stays on one machine, does not travel with the repository, and does not untrack a file that is already committed.
Why did Gitleaks and TruffleHog miss Claude Code secrets?
Together they missed 215 of 1,122 affected files. Apiiro did not publish a cause; secrets inside approved command strings are a plausible reason. A clean scan is not clearance.
Should I rotate database passwords and Supabase service-role keys if that file was ever pushed?
Yes. Rotate first, then clean the repository. Apiiro counted 379 database passwords and 122 Supabase service-role keys in current files, and deleting the file does not revoke a credential that was already cloned.
If coding agents approve shell commands in your repos, the allow-rule file is part of the attack surface. agent::offense tests persisted approvals and secret bleed into Git as part of LLM application penetration testing.