What did Apiiro find in public Claude Code settings files?
A small settings file that Claude Code writes to remember approved commands has turned up in more than 100,000 public GitHub repositories. In about 1,100 of those copies, the file contains a password, token or key that anyone can read.
Software security firm Apiiro published the findings on 6 November. Its researchers scanned 104,185 public copies of .claude/settings.local.json across 103,617 repositories. A value counted as a secret when it matched a known key format, or when a password or token sat beside the address of a public server. An AI reviewer also had to agree.
They found 1,122 copies, about 1.1 percent of the total, holding at least one secret. Those files contained 1,379 distinct secrets between them. The gap between the two numbers matters, because one saved command can carry several credentials.
Apiiro says it did not test whether the credentials still work. One organisation the researchers notified confirmed that its key had been live. The affected files belonged to 63 organisations, mostly software companies of different sizes.
The strongest examples were an admin-level cloud key belonging to a database vendor and the owner password for a medical-records database. Those are not credentials that belong in a file the public can browse, and they are not unusual shapes for the leak.
The file itself is easy to overlook. It holds permission rules rather than application code, so it does not look like something that deserves a security review. It is also written automatically, which means many developers have one without ever deciding to create it.
The scan covered public repositories only. Private repositories, local machines and build artifacts sit outside this count, so 1,122 is a floor for public exposure rather than a total.
How does 'Yes, and don't ask again' save a password?
Claude Code asks for permission before running most shell commands. The prompt offers a persistent choice: "Yes, and don't ask again". Picking it writes an allow rule into .claude/settings.local.json, so matching commands run without a prompt in later sessions.
Anthropic's documentation describes the same behaviour. Claude Code creates the file the first time a user gives a standing approval on a permission prompt, such as that option for a Bash command. The file is listed as project-local settings that apply to one person in one project.
The saved rule is not always a broad pattern. It can be something like npm test *, or it can be one exact command. When the exact command is stored, everything typed into it is stored too.
That includes credentials passed inline. A password after PGPASSWORD=, an SSH password after sshpass -p or a MySQL password after mysql -p all end up as plain text in the JSON file. Nothing in the format marks those values as sensitive.
Apiiro reproduced the behaviour with current models, including Opus 5.5. The researchers also found saved commands in public files with the secret still inside them, which shows the rule survives as written rather than being redacted later.
The design is understandable. Repeated prompts interrupt work, and a persistent approval saves time. A bug report on Anthropic's tracker from August 2026 describes one session with about 20 prompts and about 20 rules, none of which matched again.
The cost is a durable record of a command that was meant to be used once. A developer who approves a database migration against production may be saving the production password along with the rule.
There is a second-order problem. Rules that are broader than the task can also widen what the agent may run later. The file records a decision made in a hurry, and later sessions treat it as standing policy.
Why doesn't Claude Code's git protection stop the leak?
Claude Code does try to keep the file out of version control. Anthropic's settings documentation says the tool adds it to the user's global git excludes the first time it writes the file. On its own terms, that is a sensible default.
That protection has limits. It lives in one user's git configuration on one machine. It does not add the file to the project's own .gitignore, so the protection does not travel with the repository or reach a teammate's clone.
It also does nothing for a copy that was already committed. Once the file is in history, a later exclusion on one laptop cannot remove it from the remote or from anyone's existing clone.
The numbers suggest commits happen anyway. The researchers traced 1,105 repositories and found that in 539 of them, a commit listing Claude as a co-author changed the file. Apiiro takes that as evidence that Claude often commits the file itself, though a co-author line alone does not prove who staged it.
Git history widens the exposure. Older versions of the file held another 734 secrets across 509 repositories, usually because the file was deleted later. Deleting a file does not delete its past.
Forks spread it further. The researchers found 211 forks of 74 repositories carrying the same secrets as the originals. A fork is a full copy, including the history that holds the removed file.
That is why Apiiro puts rotation first. Revoking a credential is the only step that works after the value has been public, regardless of what happens to the repository. Cleanup without rotation leaves a working key in someone else's clone.
The pattern is common to secrets that live in version control. The file is easy to remove from the working tree, the commit that introduced it is not, and the copy count grows with every clone and fork.
Which secrets leaked, and how long were they exposed?
The secrets are mostly the ordinary credentials of everyday development. The researchers counted 379 database passwords, 43 SSH logins for public servers, 42 tokens for GitHub, GitLab and npm, and 23 AWS keys.
Database passwords dominate because database commands are common and often take credentials inline. A single careless psql or mysqldump command can leave a working password inside the saved rule.
Teams also commit a shared version of the file, .claude/settings.json, on purpose so colleagues inherit project permissions. Secrets appeared in 298 of 76,011 public copies, mostly inside allow rules rather than in personal overrides.
Exposure is not brief. When the researchers checked, every file containing a secret was still on its repository's default branch. In half of those repositories, the file had been there for at least 217 days.
Adding current files, old versions, the shared project file and saved agent chat logs, the total reached 2,539 secrets across 1,994 repositories. The headline figure depends on where the boundary is drawn, which is why the researchers publish both counts.
Claude Code is not the only agent with local settings, but it stands out in the comparison. The researchers reviewed documentation for 16 coding agents. Only Claude Code was confirmed to save the full approved command in a project file by default.
The affected organisations are not only small teams. Apiiro describes the owners as 63 organisations, mostly software companies of all sizes, and cites a database vendor's own admin-level cloud key among them.
For a reader, the practical question is not whether the file is embarrassing but whether a credential in it can reach a live system. Database passwords and SSH logins are the entries most likely to answer yes.
Why do secret scanners miss these files?
Ordinary secret scanning does not reliably catch the problem. The researchers ran gitleaks and TruffleHog over all 1,122 files that contained a secret, without the tools' verification step.
Gitleaks flagged 564 of the files and TruffleHog flagged 643. Neither tool flagged 215 of them. Both tools are widely used, so the gap is not a niche failure.
The misses cluster around passwords typed inside commands. Scanners look for known key formats, and a password after mysql -p or PGPASSWORD= looks like an ordinary word in an ordinary command line. There is no prefix that announces it as a credential.
The verification step matters too. The researchers ran the scanners without it, which means the tools were not asked to confirm whether a candidate secret was live. Organisations that rely on verification alone may see even less.
That matters because the same file can leave a laptop by other routes. It can sit in an npm package, a Docker image, a zip archive or a synced folder, even if it never reaches GitHub.
An earlier campaign shows why attackers would care. In September 2025, the Shai-Hulud worm ran the TruffleHog scanner on infected developer machines, and the scanner flagged more than half of the files it examined, according to reporting on the incident.
For defenders, the lesson is that a clean repository scan is not proof of safety. The file can exist on disk, in build artifacts and in package registries that mirror code.
A scanner that parses shell commands for inline passwords would close part of the gap. None of the two tools tested here did that by default.
Has this Claude Code problem been reported before?
Yes. A bug report filed on 11 August 2025 said approved commands containing API keys and tokens were stored word for word in settings.local.json.
An automated bot closed that report as "not planned" on 3 January, after 60 days without activity. Apiiro says all four related reports it cites were closed by the same bot, and that none received a reply from Anthropic.
A separate report in April 2026 covered a different exit route. Check Point researchers found the file inside public npm packages. Out of about 46,500 packages monitored, 428 contained the file, and 33 files across 30 packages contained credentials.
The npm cases matter because package tarballs are effectively permanent. Even a deprecated version can remain cached or mirrored, so a credential in a published package should be treated as compromised.
The behaviour has also been documented in Anthropic's own issue tracker. A report from 12 August 2026 describes the prompt saving verbatim command strings, absolute paths included, instead of a general rule.
The common thread is a mismatch between convenience and secret hygiene. An approval designed to reduce prompts also creates a durable record of whatever was in the command.
That makes it a design question rather than a single bug. Fixing one file path will not help if the next agent version stores approvals somewhere else, or if another product copies the same pattern.
The public record also shows how reports get lost. A closed issue with no reply is easy to read as a decision, even when the underlying behaviour has not changed.
What should developers and security teams do now?
Apiiro's guidance puts rotation ahead of cleanup, because a leaked key stays valid after the file is removed. The order matters more than the tooling.
Security teams get a different list. Apiiro recommends treating agent folders such as .claude/, .cursor/, .gemini/ and .specstory/ like .env files. That means scanning them on laptops as well as in repositories, including git history.
It also recommends adding those paths to organisation .gitignore templates and checking credentials tied to public servers first, because those grant access from anywhere.
The open question is whether Anthropic changes the default. A redaction step, a warning when a command contains a credential, or an automatically written project ignore file would reduce the flow at the source.
Tooling could close part of the gap too. Scanners that parse shell commands for inline passwords, rather than matching only known key formats, would have caught many of the 215 files that both tools missed.
Until then, the decision sits with developers at the prompt. The safest habit is to keep secrets out of the command line entirely. Any command approved permanently should be treated as something that may one day be published.
Apiiro's dataset also gives security teams a benchmark. A quick search for the file across an organisation shows whether the problem is theoretical or already present in its own repositories.
Its checklist for developers follows the same order: find the files, rotate what they held, then remove the habit that put secrets there.
- •Search every repository, and its history, for .claude/settings.local.json and .claude/settings.json.
- •Rotate every password, token and key the files have ever held.
- •Untrack the local file with git rm --cached .claude/settings.local.json, then add it to the repository's .gitignore.
- •Open the file on your own machine and delete any allow rule that contains a secret.
- •Pass secrets as variables from a secrets manager, so the saved rule stores $DB_URL rather than the value.
- •Approve command patterns such as Bash(npm run:*) instead of single commands.
