← Back to all posts
News

Apiiro Counts 2,539 Secrets Leaked by Claude Code's 'Don't Ask Again' Button

November 8, 2026 · 04:20 UTC · News
Apiiro Counts 2,539 Secrets Leaked by Claude Code's 'Don't Ask Again' Button

TL;DR

Security firm Apiiro downloaded every public copy of .claude/settings.local.json it could find on GitHub: 104,185 files across 103,617 repositories. 1,122 of them hold at least one real secret, and once git history, the shared settings file and saved agent transcripts count, the total is 2,539 distinct secrets across 1,994 repos. The mechanism is a button. Claude Code asks before running a shell command; pick Yes, and don't ask again on a command that carries a password, and the exact command line, secret included, lands in a plain JSON file inside the project. Apiiro notified the 63 affected organizations on November 2 and 3, and one confirmed its key had been live. An engineer on the Claude Code team showed up in the Hacker News thread on November 7 to say the team is looking into what it can do about this.


How one click becomes a leak

Ask Claude Code why the staging login fails and it proposes something like PGPASSWORD=hunter2 psql -h db.example.com -U app -c 'select 1'. Per its permissions model, it asks before running. Answer Yes and the command runs once. Answer Yes, and don't ask again and Claude Code writes an allow rule into .claude/settings.local.json in the project folder, and the rule is the command string itself, password and all. Apiiro reproduced the behavior on Opus 5.5 and Fable 5.1, so this is the harness, not a model quirk. Approving a command that contains a secret is like taping your garage code inside the mailbox so the babysitter stops texting you: convenient, permanent, and sitting in a box other people can open.

The settings docs say Claude Code adds the file to the developer's global git exclude the first time it writes it inside a git repository, so git on that one machine ignores it everywhere. The protection lives in one person's git config on one computer. Nothing goes into the repo's own .gitignore, so a clone on a second machine, a folder that becomes a repo later, or an already-committed file travels unprotected. Apiiro traced the paper trail: in 539 of the 1,105 repos with a real secret, a commit listing Claude as co-author changed the file, with model names from Sonnet 4.5 through Opus 5.5. The agent co-authored the evidence.

command witha secret yes, and don'task again settings filein the project committed topublic repo
The allow rule stores the command verbatim, so the secret rides into every commit, clone and fork.

What is actually exposed

The 1,122 current files hold 1,379 distinct secrets: 636 provider keys and 743 passwords or tokens sitting next to public hosts. More than a third open a database directly: 379 database passwords and 122 Supabase service-role keys. Another 103 are Supabase account tokens, which control every project in the account. Then come 43 SSH logins for public servers, some of them root passwords inside sshpass commands, 42 GitHub, GitLab and npm tokens, 23 AWS keys and 6 private keys. One university repo permanently approved a root SSH command to a public server.

The owners are 63 organizations, mostly software companies. Apiiro's strongest cases, each checked at a pinned commit: a database vendor's own admin-level cloud key, a startup's production Azure Postgres password, an agency's Square production access token, and the owner password of a medical-records app's cloud database. Apiiro did not test whether the secrets still work; the one key confirmed live came from the company itself, after disclosure.

Exposure scales with clicking. One in ten files over 8 KB holds a real secret; under 500 bytes, fewer than one in 500 do. Every file with a secret sat on its repo's default branch on November 2, and in the median repo it had been sitting there 217 days. In 118 repos, more than a year.

1,379 distinct secrets in current public files db passwords379 supabase roles122 supabase acct103 ssh logins43 git+npm tokens42 aws keys23 source: apiiro research, nov 2026
379 database passwords plus 122 Supabase service-role keys: 501 secrets open a database directly.

Deleting the file does not delete the secret

Older versions of the file hold 734 more secrets across 509 repos that appear in no current copy, usually because someone deleted the file and called it fixed. Git keeps it, and so do forks: 211 forks of 74 repos carry the same secrets as their originals.

The habit spreads beyond one file, because agents record what they ran in more places than a settings JSON. Of 76,011 shared .claude/settings.json files, which teams commit on purpose, 298 hold a real secret. Of 2,523 saved agent chat transcripts (.specstory, aider, Cursor exports), 120 files in 87 repos do. Of 31,053 Hugging Face Spaces, 126 contain the file, one with a database service key and a user password. Lakera found credentials in 33 such files across 30 npm packages back in April. Apiiro compared the documentation of 16 coding agents: Claude Code is the only one confirmed to save the full approved command into a project file by default.

Your scanner misses about a fifth of it

Apiiro ran gitleaks and TruffleHog over all 104,185 files, without the tools' verification step. Of the 1,122 files with a secret, gitleaks flagged 564 and TruffleHog 643; 215 were flagged by neither. The gap is mundane. Scanners hunt key formats: a provider prefix followed by a long random string. A password typed after PGPASSWORD=, sshpass -p or mysql -p is an ordinary word in an ordinary command line, and both tools mostly let it through. The same blind spot covers transcripts, memory files and handoff notes, everywhere an agent records what it ran.

the 1,122 files with a secret, by who flags them all with secret1,122 trufflehog643 gitleaks564 missed by both215 source: apiiro research, nov 2026
One in five files with a real secret passes both scanners, mostly passwords typed inside commands.

The file does not need GitHub to travel, either. It is plain JSON in the project folder, and it rides inside npm packages, Docker images built with a copy command, zip archives and synced folders. Malware already looks for it: the September 2025 Shai-Hulud npm worm downloaded TruffleHog and ran it over victims' files, and a few weeks earlier malicious Nx packages instructed the victim's own Claude Code and Gemini CLI to search the disk for secrets.

Anthropic's answer, and a paper trail of closed issues

Boris, an engineer on the Claude Code team, posted in the Hacker News thread: historically Claude Code has not done secret scrubbing, but "we are looking into what we can do to prevent this." He argued settings.local.json is meant to stay local, that Claude Code gitignores it when it creates the file, and that in this case "the files were manually created and checked in." He recommended auto mode and host-side secret scanning. Commenters pushed back on auto mode as the safety answer, on the grounds that it approves everything and is only as safe as the sandbox around it.

The issue history is less flattering. Users have reported the mechanism since August 2025; issue 5544, filed August 11, 2025, said approved commands with API keys were being stored word for word. An automated bot closed it as not planned on January 3, after 60 days without activity. The four related reports Apiiro cites were all closed by the same bot, the most recent this week. None got a reply from Anthropic. The bot kept its schedule. The secrets, median 217 days on a default branch, kept theirs.

What to do this afternoon

  • Search your GitHub organization for .claude/settings.local.json and .claude/settings.json, and check each repo's git history for them, even where the files are gone today.
  • Rotate everything the file has ever held, before cleaning up. Deleting the file clears neither history nor forks.
  • Untrack it with git rm --cached .claude/settings.local.json, then add it to the repo's own .gitignore so the rule travels with the project.
  • Open the local copy on your machine even if it was never committed, and delete every rule that contains a secret.
  • Stop typing secrets into commands. Reference them as $DB_URL from a secrets manager, so the saved rule holds the variable name.
  • Approve patterns like Bash(npm run:*) instead of exact commands.
  • Security teams: treat .claude/, .cursor/, .gemini/ and .specstory/ plus agent memory files as secret-bearing, like .env, on laptops as well as in repos. Put them in pre-commit and CI scanning, history included, and make sure the scanner catches passwords inside commands, not only key formats.

Key Takeaways

  • Apiiro counted 104,185 public copies of .claude/settings.local.json on GitHub; 1,122 of them (1.1%) hold at least one real secret, 1,379 in all.
  • Counting git history, shared settings files and agent transcripts, the total is 2,539 distinct secrets across 1,994 repos, hitting 63 organizations; one company confirmed its key had been live.
  • The leak vector is the permission prompt: "Yes, and don't ask again" saves the exact command, secret included, into a project file protected only by the developer's global git ignore, never the repo's own .gitignore.
  • In 539 of 1,105 traced repos, a commit co-authored by Claude changed the file, with model names from Sonnet 4.5 through Opus 5.5.
  • gitleaks flagged 564 of the 1,122 files and TruffleHog 643; 215 were caught by neither, mostly passwords typed inside commands.
  • Rotate first, clean up second: history and forks still hold 734 secrets that exist in no current copy of the file.

Sources: Apiiro, Coding Agents Copy Your Secrets to a Hidden File, The Hacker News, Hacker News thread with the Claude Code team response, Claude Code settings docs, Claude Code permissions docs, claude-code issue 5544

AIClaude CodeAnthropicSecuritySecretsGitHubCoding agentsSecret scanning
CONSOLE
$