P.K. SHARMA

Cyber security intelligence, AI governance, practitioner analysis

Apiiro says Claude Code's "don't ask again" left real secrets in 1,122 public settings files on GitHub

Apiiro counts 1,122 public GitHub files where a Claude Code "don't ask again" approval kept a real secret, 1,379 in all, and says 215 files got past both scanners it ran. The docs keep the file out of git only on the machine that wrote it. Read 7 November 2026.

By · LinkedIn · · 20 min read

One in five of the leaked files got past both scanners

Apiiro's research post of 5 November 2026 counts 1,122 public files on GitHub that hold a real secret saved by one Claude Code button, and reports that gitleaks and TruffleHog, two widely used secret scanners, both missed 215 of them. That is 19.2% (derived: 215 of 1,122), close to the "one in five" the post itself gives. The reason is the shape of the secret: "The gap is passwords inside commands." A password after a database client's flag is not a key with a recognisable prefix, so a scanner tuned to key formats lets it pass.

The file is .claude/settings.local.json. When Claude Code asks to run a shell command and the user picks "Yes, and don't ask again", the tool's own settings documentation says it saves the approval there as an allow rule. Apiiro's finding is that the rule keeps the whole approved command, secret included, and that the file then reaches public repositories in numbers. Of the 1,379 distinct secrets Apiiro counts in current copies, 501 open a database directly (379 database passwords and 122 Supabase service role keys): 36.3% (derived), the post's "more than a third".

This briefing rests on Apiiro's post and the two Claude Code documentation pages, read in full from copies saved on 7 November, and on the four GitHub issues Apiiro names, read through GitHub's API and saved at about 16:35 UTC on Saturday 7 November 2026.

What that does not establish. "Real secret" is Apiiro's classification, not a test. The post defines it as "a credential that matches a provider format or points at a public host, not one known to be valid." Apiiro says it used and tested no credential, so the post cannot say how many of the 1,379 still work, and it reports only one confirmation: "One company confirmed its key had been live." It does not say that anyone found or used any of these secrets. It does not show the behaviour on every version of Claude Code or every operating system. And it cannot say how many such files sit on laptops and never reached GitHub, which the post itself argues is the larger number.

Apiiro sells application security products, including one its site lists as Secrets Security, so a finding about leaked secrets is also its market. That does not change the counts, but every count here is Apiiro's, from its own collection, and nobody outside Apiiro has repeated it yet.

What the documentation promises, in its own words

Claude Code's settings page describes the file as personal to one project, and its table of settings scopes says: "Claude Code keeps it out of git when it creates the file". The longer passage is specific. The first time Claude Code writes the file in a git repository that does not already ignore it, it adds **/.claude/settings.local.json to the global git excludes file, "so the file stays out of your commits in every repository". It names where that excludes file lives: the path in core.excludesFile, otherwise $XDG_CONFIG_HOME/git/ignore or ~/.config/git/ignore. And it adds one caveat: "If you created the file by hand and Claude Code hasn't written to it yet, add it to .gitignore yourself."

The permissions page gives the lifetime. For Bash commands, the "don't ask again" behaviour is "Permanently per repository and command". So an approval does not expire when the session ends, and nothing in either page says the rule is ever removed on its own.

Read against Apiiro's numbers, the promise is narrower than its wording. A global excludes file is a setting on one machine. It is true, as the docs say, that it keeps the file out of commits in every repository on that machine. Apiiro's point is everything else: "It doesn't cover a folder that becomes a repo later, a clone on another machine, or a file someone already committed." And: "The rule never goes into the repo's own .gitignore, so nothing travels with the project to protect it."

This is where a friendly name does the work of a control. The word local in settings.local.json reads like a property of the file. It is not. The file sits inside the project folder like any other, and its privacy depends on a line in a different file, in the user's home directory, on whichever machine first wrote it. Copy the folder, clone it on a new laptop, run git init in a folder that was not a repo yet, or commit it before the exclude existed, and the name still says local while the file is in the commit. Apiiro puts it in one line: "Nothing in the repo marks that file as private".

The docs do describe one guard, aimed the other way. They say: "When the file is tracked in git, or .claude is a symlink, Claude Code treats it as repository-supplied instead and holds its rules until you trust the folder." That protects a user from rules someone else committed. It does nothing for a secret the user's own rule has already published. In neither documentation page did this site find a passage warning that a saved rule can contain a secret typed into the approved command.

The settings page also says something that bears on Apiiro's second file. It says "In the VS Code extension the approval card lets you pick the destination file, including the project's shared file, which changes the rule for everyone"; in the command line tool, it says, Claude Code writes only to the local file. One of the four issues Apiiro cites, #91270, reports VS Code approvals landing in the tracked shared file.

How the secret gets into the file

Apiiro's account starts with secrets that are already on a developer's machine: a database connection string in a config file, cloud keys, an API token in .env. Ask Claude Code to check why a login fails on staging or to run a migration, the post says, and the command it proposes carries that secret because it cannot work without it. Claude Code asks before it runs the command. Pick "Yes, and don't ask again" and the approval is written as a permission rule. "The rule is the command string itself", Apiiro says.

The documentation supports the shape of that. Its example rule Bash(npm run build) matches only that exact command, and the permissions page says that for a compound command Claude Code saves a separate rule for each subcommand that requires approval. It also says the dialog can offer a prefix rule instead, in which case it writes the wildcard form. The post's own example of a saved rule did not survive in the saved copy, so this briefing does not show one, and it does not show which option the prompt offered for each command Apiiro counted.

Apiiro is firm that this is the tool and not the model: "This is Claude Code's own behavior, not the model's", it says, adding: "we reproduced it with Opus 5.5 and Fable 5.1". It compared how 16 coding agents store approvals, from their documentation only, and says Claude Code is the only one confirmed to write the full approved command to a project file by default.

Drawn by this site from the Claude Code settings and permissions docs and Apiiro's post of 5 November 2026, all read 7 November 2026. Each card names its source.

The third route in the diagram is the one that should worry teams most. Apiiro traced 1,105 repositories with a real secret and found that in 539 of them a commit coauthored by Claude changed the file, which is 48.8% (derived). The post says those commits name every recent model, from Sonnet 4.5 to Opus 5.5. Apiiro does not say how the exclude was bypassed in those cases, or whether the file was already tracked when the agent committed it, so the count shows that the agent touched the file in a commit, not why git accepted it.

GitHub is only where Apiiro could count. The post says the same plain JSON file sits on every laptop where someone clicked the button, and that it can leave without git: in a published npm package, a Docker image built with a copy command, a zip archive or a synced folder. This site's briefing on Truffle Security's revocation programme reported that Truffle had found the same file name inside container images.

What Apiiro counted, and what each count means

The headline numbers come from read only GitHub code and commit search on default branches between 1 and 3 November 2026. Apiiro says it split searches by file size to stay under GitHub's cap of 1,000 results per query, listed every copy, and downloaded them all.

Drawn by this site from the counts printed in Apiiro's post of 5 November 2026. Percentages and the 217 and 661 totals are this site's arithmetic.

Apiiro's counts, from its post of 5 November 2026, with what each one does not show

Count, as Apiiro prints itWhat it measuresWhat it does not establish
104,185 copies in 103,617 reposPublic copies of the local file on default branches, 1 to 3 NovemberThat any copy holds a secret; 568 more files than repos (derived) means some repos hold several
1,122 files (1.1%)Files with a key in a known format, or a password or token next to a public hostThat any of them is valid
1,379 distinct secrets636 provider keys, 743 passwords or tokens for public hostsHow many were found or used by anyone else
501 that open a database (derived sum)379 database passwords, 122 Supabase service role keysWhether the databases accept connections from the internet
103 Supabase account tokensTokens Apiiro says control every project in the accountHow many accounts are distinct
43 SSH, 42 GitHub GitLab npm, 23 AWS, 6 private keysNamed classes inside the 1,379The remaining 661 (derived) beyond "mostly" API tokens and model keys
Median 217 days; 118 repos over a yearHow long the file had been in the repo before 2 NovemberHow long each secret was valid; median first commit is about 30 March 2026 (derived)
211 forks of 74 reposForks that carry the same secretsClones, which nobody can count
734 more in history, 509 reposSecrets in earlier versions that appear in no current copyA full total: Apiiro calls the history count "a floor"

Two comparisons are worth making, both derived. The history count is 53.2% of the current count (734 against 1,379): for every two secrets still in a current file, Apiiro found about one more in a version someone had already deleted. That is why its advice puts rotation before cleanup. And the total across every source, 2,539 distinct secrets in 1,994 repos, is 1,160 more than the current local files alone (derived: 2,539 less 1,379), which matches the "1,160 more" the post gives.

Size matters too. Apiiro says one in ten files over 8 KB holds a real secret, against fewer than one in 500 under 500 bytes. A large allow list is a list of many approvals, so the longer a developer has used the button, the more the file can hold.

Not just one file

Apiiro then looked at other files agents write and developers commit. These counts come from a different collection: transcripts, memory files, handoff notes and Copilot settings were found by searching for key prefixes and connection strings, and the last three were classified by Apiiro's rules alone, without the second review it used for the main count.

Other agent files Apiiro checked, as its table prints them (post of 5 November 2026)

FileCheckedWith a real secret
Shared .claude/settings.jsonAll 76,011298 files
Agent chat transcripts (.specstory, aider, Cursor exports)2,523120 files in 87 repos
Agent memory files (MEMORY.md and similar)1,2009 repos
Handoff and deploy notes2,09533 files
VS Code Copilot auto approve settings1739 repos

The shared file matters because teams commit it on purpose, so the docs' exclude does not even apply; Apiiro says most of its secrets sit in allow rules as exact command lines. Apiiro also says a third of the handoff notes with a secret were written by the agent itself. Outside GitHub, it found the local file in 126 of 31,053 Hugging Face Spaces, one with allow rules holding a database service key and a user password, and on GitLab.com 2 of the 69 public copies it checked held a real secret. Its summary: "The habit that leaks is the same everywhere: typing a secret into a command that an agent then records."

What the scanners missed, and why it matters off GitHub

Apiiro ran gitleaks and TruffleHog, without verification, over all 104,185 files. Of the 1,122 with a real secret, gitleaks flagged 564 (50.3%, derived) and TruffleHog 643 (57.3%, derived). Since 215 were flagged by neither, 907 were flagged by at least one, and so 300 were flagged by both (derived: 564 plus 643 less 907). Each tool alone missed close to half.

Apiiro names the pattern. A password typed after PGPASSWORD=, sshpass -p or mysql -p, or a bearer header in a curl call, is an ordinary word in an ordinary command line, and both tools mostly let it through. The post says the same pattern appears wherever an agent records what it ran: transcripts, memory files and handoff notes.

The laptop is the other exposure. Apiiro recalls that the Shai-Hulud npm worm of September 2025 downloaded TruffleHog and ran it over victims' files, and that the malicious Nx packages, three weeks earlier, told a developer's own Claude Code and Gemini CLI to search the disk for secrets. On Apiiro's numbers, the scanner that worm ran flags 643 of the 1,122 files, as Apiiro ran it. This site has covered a later wave of that worm in its briefing on the Tensorlake npm release.

The four GitHub issues, checked

Apiiro says Claude Code users have reported the mechanism in GitHub issues since August 2025, and: "None got a reply from Anthropic, and three were closed automatically as inactive or duplicate." This site read all four through GitHub's API on 7 November and saved the record. The first was opened on 11 August 2025, 451 days before Apiiro's post (derived). Reporters are not named here.

The four issues Apiiro cites, as GitHub's record showed them on 7 November 2026

IssueWhat it reportsHow it was closed
#5544, opened 11 Aug 2025Approved commands with credentials stored as is in settings.local.jsonBy the github-actions bot on 3 Jan 2026, after 60 days of inactivity; not planned
#11982, opened 19 Nov 2025The local settings file no longer added to .gitignore when createdBy the bot on 5 Mar 2026 as inactive; not planned
#13106, opened 4 Dec 2025Tokens stored in settings.local.json and not ignored by git by defaultBy the bot on 8 Dec 2025 as a duplicate of #11982
#91270, opened 1 Sep 2026VS Code approvals written to the tracked shared settings.jsonBy the bot on 5 Nov 2026 at 10:14 UTC as inactive; not planned

So the record supports Apiiro's account and slightly overtakes it. All four issues are now closed, every one by the github-actions bot: three as inactive and one as a duplicate. The fourth, #91270, was closed by the bot on the morning Apiiro's post is dated; the post gives no time of publication, so "three" may simply have been true when it was written. Across the four threads, no comment comes from an account GitHub marks as an owner, member or collaborator of the repository; every comment is from the bot or from an account with no such role. Labels such as "has repro" and, on #5544, "area:security" were applied, but the record does not show by whom. None of this shows what Anthropic knows or plans. No Anthropic statement appears in any page read for this briefing, and this site did not contact Anthropic or Apiiro.

Stated, and not stated

What the saved pages state, and what they leave open (read 7 November 2026)

QuestionStated, and by whomNot stated
Where an approval goesIn .claude/settings.local.json, as an allow rule (Claude Code docs)That the rule may hold a secret typed in the command
Whether the secret is kept"The rule is the command string itself" (Apiiro)An example rule, which the saved copy lost
How it is kept out of gitA line in the global git excludes file, first write only (docs)Anything in the repo's own .gitignore
How many leaked1,122 files, 1,379 secrets in current copies (Apiiro)How many are valid; one live key confirmed
Who is affected63 organisations, mostly software companies (Apiiro)Their names, which Apiiro withheld
Who was toldEach organisation case, on 2 and 3 November (Apiiro)Whether Anthropic was told before publication
Vendor responseFour issues closed by a bot (GitHub record)Any Anthropic statement or planned change
Other agentsClaude Code is the only one of 16 confirmed to do this by default, from documentation (Apiiro)Tests of the other 15

What to do, in order

Apiiro's advice comes first, in its own order and close to its words. Its summary line is the right first instruction: "Found something: rotate first, clean up second." Items marked as this site's suggestion are not Apiiro's.

Take this with you

Ranked checklist

  • Search your GitHub organisation for .claude/settings.local.json and .claude/settings.json. Apiiro: each hit is a file anyone can read right now.
  • Check each repository's git history for the file, even if it is gone today. Apiiro: if history has it, so does every fork and clone.
  • Rotate every credential the file has ever held, before any cleanup. Removing the file does not clear history, forks or clones.
  • If the file is tracked, untrack it with git rm --cached .claude/settings.local.json and add the path to the repository's own .gitignore.
  • Find every copy on your own machines and read the allow list. Delete every rule that contains a password, token, key or connection string, even if the file was never committed.
  • Stop typing secrets into commands. Reference them as variables from a secrets manager, so the rule records $DB_URL and not the secret.
  • Approve patterns such as Bash(npm run:*) instead of literal commands.
  • Security teams: treat .claude/, .cursor/, .gemini/, .specstory/ and agent memory files as secret bearing, like .env, on laptops and in repositories.
  • Security teams: add those folders to pre commit and CI secret scanning, including history, and make sure the scanner catches passwords inside commands, not only key formats.
  • Security teams: add .claude/settings.local.json to the organisation's .gitignore templates, and triage credentials paired with public hosts first.
  • This site's suggestion: for CI and packaging, check that Docker build contexts, npm package file lists and archive jobs exclude the .claude folder, since Apiiro says the file can leave without git.
  • This site's suggestion: tell developers plainly that the global exclude exists only on the machine that first wrote the file, so a new laptop or a fresh git init starts unprotected.

Technical details

These are the artifacts the primary sources print, copied as printed. No secret, sample credential or search query for other people's files appears here. First, the two project files Apiiro tells teams to look for. The first is personal and normally excluded on the machine that wrote it; the second is the shared file the docs say teams commit.

Text
.claude/settings.local.json
.claude/settings.json
File paths as printed in Apiiro's post of 5 November 2026 and the Claude Code settings docs, read 7 November 2026.

Next, the single line the settings docs say Claude Code writes to the user's global git excludes file the first time it writes the local file in a repository. A defender can check whether it is present on a given machine; its absence on a new laptop, build host or container is the gap Apiiro describes.

Text
**/.claude/settings.local.json
Global git exclude line, as printed in the Claude Code settings docs, read 7 November 2026.

Where that excludes file lives, as the docs list the locations in order. Note that none of these is inside the repository.

Text
core.excludesFile
$XDG_CONFIG_HOME/git/ignore
~/.config/git/ignore
Excludes file locations, as printed in the Claude Code settings docs, read 7 November 2026.

The untracking command Apiiro gives, to be run in a repository where the file is tracked, followed by the path it says to add to the repository's own .gitignore. Untracking does not remove the file from history, so rotation comes first.

Text
git rm --cached .claude/settings.local.json
Untrack command, as printed in Apiiro's post of 5 November 2026, read 7 November 2026.
Text
.claude/settings.local.json
Path Apiiro says to add to the repository's own .gitignore and to organisation templates; the line is the path as printed in its post of 5 November 2026.

The safer rule shape Apiiro recommends: a pattern rule instead of a literal command, and a variable instead of a typed secret, so the saved rule records the name of the variable. The permissions docs confirm the colon form: "The :* suffix is an equivalent way to write a trailing wildcard".

Text
Bash(npm run:*)
$DB_URL
Pattern rule and variable reference, as printed in Apiiro's post of 5 November 2026, read 7 November 2026.

The command prefixes Apiiro says scanners tuned to key formats mostly let through. A defender adds these to secret scanning rules for agent folders, transcripts and notes; Apiiro also names bearer headers in curl calls but prints no exact pattern for them, so none is given here.

Text
PGPASSWORD=
sshpass -p
mysql -p
Scanner miss prefixes, as printed in Apiiro's post of 5 November 2026, read 7 November 2026.

The agent folders Apiiro tells security teams to treat as secret bearing, like .env.

Text
.claude/
.cursor/
.gemini/
.specstory/
Agent folders, as printed in Apiiro's post of 5 November 2026, read 7 November 2026.

Apiiro's method and its limits

Apiiro uses the two detector classes secret scanners use. Provider patterns match known key formats. Generic secrets are passwords and tokens with no known format, and Apiiro counts one only when a public host sits on the same line, as in a connection string. Rules then remove placeholders, common default passwords, local and private hosts, keys that are public by design, expired tokens, and GitHub and npm tokens whose checksum fails.

Then a model was used. Every remaining distinct value, 2,774 in all, was reviewed in its masked context by an independent LLM reviewer working from a written rubric, Apiiro says. A value counts as a real secret only when the rules and the review agree: 2,539 across all sources. So 235 values (derived) passed the rules but were not counted. A language model therefore helped decide which values count, alongside the rules, for the main files; memory files, handoff notes and Copilot settings were classified by the rules alone. File history covers 43,120 repos, and Apiiro calls the history count a floor.

Apiiro says it checked every organisation case at a commit pinned link and reported each one to its owner on 2 and 3 November 2026. It cites earlier work by ironPeak and Lakera on the same class of file; this briefing did not read those reports.

The question

The documentation is accurate about what it does: one line in one excludes file, on the machine where the file was first written. Apiiro's count is about every case outside that sentence, and about secrets that were only ever in the file because a person clicked a button meant to save time. The issues asking for a different design were closed by a bot. So the question for any team using Claude Code is not whether the exclude works. It is this: if "don't ask again" keeps the exact command, who in your organisation has read every allow list on every machine, and in every commit, since the first approval?

Key facts

Sources

  1. PrimaryCoding Agents Copy Your Secrets to a Hidden File. Thousands Are on GitHub, 5 November 2026: the mechanism, all counts, scanner results, other agent files, advice, disclosure and method. Read in full from a saved copyApiiroaccessed 2026-11-07
  2. PrimarySettings files and precedence: what .claude/settings.local.json is, the global git exclude line and where it is written, and the VS Code destination note. Read from a saved copyAnthropic (Claude Code Docs)accessed 2026-11-07
  3. PrimaryConfigure permissions: don't ask again behaviour per tool, exact and wildcard Bash rules, compound commands, and the trust rule for a tracked local file. Read from a saved copyAnthropic (Claude Code Docs)accessed 2026-11-07
  4. PrimaryIssue 5544: approved commands with credentials stored in settings.local.json; state, closing reason and comments read through the APIGitHub (anthropics/claude-code)accessed 2026-11-07
  5. PrimaryIssue 11982: local settings file not added to .gitignore; state, closing reason and comments read through the APIGitHub (anthropics/claude-code)accessed 2026-11-07
  6. PrimaryIssue 13106: tokens in settings.local.json not ignored by default; closed as a duplicate of 11982GitHub (anthropics/claude-code)accessed 2026-11-07
  7. PrimaryIssue 91270: VS Code approvals written to the tracked shared settings.json; closed by the bot on 5 November 2026GitHub (anthropics/claude-code)accessed 2026-11-07

Share this briefing

Know someone who owns this problem? Send it to them.

Related briefings

The briefing, in your inbox

Practitioner analysis of cyber and AI security news. No vendor noise.

How often

Every new briefing in one email, at 7am, or at 7am, 12:30pm and 6pm. Nothing is sent when nothing is new. Unsubscribe any time.