Ai Coding 4 min read

Apiiro finds real secrets in 1,122 public Claude Code settings files, and 215 of them evaded two scanners

Choosing "Yes, and don't ask again" can save a command's password verbatim in .claude/settings.local.json. Apiiro counted 2,539 distinct secrets across 1,994 public repositories.

On November 5, security firm Apiiro published research showing how an ordinary permission prompt in Claude Code puts credentials on public GitHub. When a developer approves a shell command with “Yes, and don’t ask again,” Claude Code saves an allow rule to .claude/settings.local.json. If the command contained a password, the password goes into the rule. Apiiro found real secrets in 1,122 of 104,185 public copies of that file. Apiiro’s site blocked our fetch, so the figures below come from iTWire, Cyber Security News and Let’s Data Science, which summarize the report.

Two numbers that often get merged

Headlines combine two different counts. The narrower one covers the current local settings files: 1,122 files, about 1.1% of those scanned, holding 1,379 distinct secrets. The broader figure, 2,539 distinct secrets across 1,994 public repositories and 63 organizations, also includes git history, the shared .claude/settings.json and saved agent transcripts. Older versions of the file in git history alone contributed 734 more secrets in 509 repositories.

Apiiro collected the data between November 1 and 3 and notified affected organizations on November 2 and 3. Reported types include 379 database passwords, 122 Supabase service-role keys, 43 SSH logins, 42 GitHub, GitLab or npm tokens, and 23 AWS keys. Apiiro did not test whether credentials still worked, though one notified company confirmed its key was live. Cyber Security News cites examples including a production Azure PostgreSQL password and an owner password for a medical-records database.

Why the default protection didn’t hold

Claude Code does try to keep this file out of git. Its settings documentation says that the first time it writes the file in a repository that doesn’t already ignore it, it adds **/.claude/settings.local.json to the user’s global git excludes file.

That is a per-machine safeguard, and nothing is written to the repository’s own .gitignore. Git ignore rules also have no effect on a file that is already tracked, so a file committed once keeps being committed. Coding agents can do that committing themselves: of 1,105 repositories Apiiro traced, 539 had the file changed in a commit co-authored by Claude.

The persistence is what makes this different from a secret pasted once into a terminal. A permission rule is meant to be kept, so the credential stays in a file nobody reads, in a format that looks like configuration.

Scanners missed a fifth of them

Of the 1,122 files, Gitleaks flagged 564 and TruffleHog flagged 643, and neither flagged 215, about 19%. The misses were mostly passwords following PGPASSWORD=, sshpass -p or mysql -p, or sitting in request headers. These secrets have no recognizable prefix such as those on AWS or GitHub keys, so format-based detection has little to match on. Apiiro’s own count relied on key-format matching plus an AI reviewer working on masked candidates, so its method was not the same as running those two tools.

This was reported before

The behavior isn’t new. GitHub issue #5544, opened August 11, 2025, reported that approved commands containing credentials were stored verbatim in the permissions.allow array. It drew no maintainer comment, and a bot closed it as “not planned” on January 3, 2026 after 60 days of inactivity. Let’s Data Science, citing The Hacker News, reports that the researchers received no reply from Anthropic. We found no public statement or fix from the company in the coverage we reviewed.

The underlying design problem isn’t unique to one tool. Any agent that turns approvals into stored rules has to decide what to do with secrets inside approved strings, and Claude Code’s approach of persisting the literal command is the simplest answer and the riskiest. It sits alongside other recent cases of agent convenience features widening the attack surface.

What to do now

Rotate first. A credential in a public commit has to be treated as compromised, and deleting the file or rewriting history doesn’t undo that. Then:

  • Check whether the file is tracked with git ls-files | grep settings.local.json, and if so run git rm --cached .claude/settings.local.json and add the path to the repository’s own .gitignore.
  • Search history as well as the current tree, including .claude/ transcripts and memory files.
  • Add explicit scanning for PGPASSWORD=, sshpass -p and mysql -p, because default rules can miss them.
  • Reference secrets through variables populated by a secret manager, so that the approved command contains $DB_PASSWORD instead of the value. An inline assignment with a literal value still writes it to the rule.
  • Review saved rules with /permissions, and prefer narrow patterns over approving one-off commands that contain credentials.

The thing to watch is whether Anthropic changes the default, for example by redacting secret-shaped values before saving a rule or refusing to persist commands with inline credentials.

Get Insanely Good at AI

Get Insanely Good at AI

The book for developers who want to understand how AI actually works. LLMs, prompt engineering, RAG, AI agents, and production systems.

Keep Reading