MemoryLake
Back to all articles
NewsNovember 11, 2026·13 min read

Claude Code settings.local.json Is Leaking Secrets to Public Repos — Why Don't Ask Again Saves the Whole Command, and How to Clean Up a Repo That Committed It (2026)

You asked Claude Code to check why a staging login was failing. It proposed a database command, the command needed a password, and you picked the option that stops it from asking again. The command ran, the problem got fixed, and you moved on.

That approval didn't disappear. Claude Code wrote it to a file in your project, as a permission rule, and the rule is the command itself, password included. On November 5, 2026, the security firm Apiiro published research on what happens next: "Apiiro research found 2,539 real secrets in 1,994 public GitHub repositories: database passwords, cloud keys, API tokens, SSH root logins."

This guide explains what the file is, why the secret ends up in it, why Claude Code's own protection doesn't always hold, what Anthropic has said so far, and how to check and clean up your repos. It also covers the part of Apiiro's research that reaches beyond one file: agents record what they ran in other places too, including memory files.

If you want the broader picture of what Claude Code keeps on your machine between sessions, our guide to what Claude Code auto memory is covers the memory folder. This article is about the settings file and the approvals inside it.

What settings.local.json is

Claude Code reads settings from several files. Anthropic's settings documentation describes the project local one as .claude/settings.local.json, for "You, in this one project only." Its intended use is personal overrides for one project and testing before you share.

You don't have to create it. The documentation says Claude Code writes .claude/settings.local.json "the first time you give a standing approval on a permission prompt". The permissions documentation spells out what that means for shell commands. When you pick the option that stops Claude Code from asking again and the approval saves permanently, "such as for a Bash command or a WebFetch domain, Claude Code saves the rule to .claude/settings.local.json at the root of the git repository".

Two details from the same page matter here. Bash approvals last "Permanently per repository and command". And the rule applies "to future sessions anywhere in that repository, including sessions started in subdirectories and in worktrees." An approval you gave once keeps working, and keeps sitting in the file, until someone removes it.

Why the secret ends up in the file

Apiiro's report walks through the sequence. "Ask Claude Code to check a database or call an API, and the command it proposes carries the secret from your config file. It asks before running it." The secret has to be in the command, because the command can't work without it.

Then comes the approval. When you choose the don't-ask-again option, Apiiro writes, "it runs the command and adds a permission rule to .claude/settings.local.json in the project folder, so it won't ask again. The rule is the command string itself".

A permission rule is meant to describe what Claude Code may run. When the approved command has a password, token, or connection string typed into it, the rule stores that value in plain text.

Apiiro says this is the tool's behavior, not a quirk of one model: "This is Claude Code's own behavior, not the model's: it happens with Anthropic's latest models too, and we reproduced it with Opus 5.5 and Fable 5.1."

Why Claude Code's own protection doesn't always hold

Claude Code does try to keep the file out of git. The settings documentation says that "The first time Claude Code writes the file in a git repository that doesn't already ignore it, it adds **/.claude/settings.local.json to your global git excludes file, so the file stays out of your commits in every repository."

The key word is global. That exclude lives in your git configuration on your machine, not in the repository. Apiiro describes the gap: "That covers the common case. 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."

There's also a documented exception for files you made yourself. The settings table says Claude Code "keeps it out of git when it creates the file; if you create it by hand, add it to .gitignore yourself".

Once the file is tracked, Claude Code changes how it treats it, but keeps writing to it. The permissions documentation says: "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." Apiiro adds that the user isn't told this, "and the next approval is appended to the tracked file."

Apiiro also traced who committed the file: "In 539 of the 1,105 repos with a real secret that we traced, a commit co-authored by Claude changed the file." Its explanation is that Claude Code's commit instructions name files like .env and credentials.json, but "Its own settings file is not on that list."

What Apiiro found

The numbers come from read-only GitHub code and commit search on default branches, run November 1 to 3, 2026. Apiiro says it didn't test any credential: "No credential was used or tested, and no host was contacted."

The main counts:

  • The file itself. "Out of 104,185 public copies of this file on GitHub, 1,122 hold a real secret."
  • Distinct secrets in current copies. "Together they hold 1,379 distinct secrets: 636 provider keys and 743 passwords or tokens for public hosts."
  • Database access. Apiiro counted 379 database passwords and 122 Supabase service-role keys, plus 103 Supabase account tokens.
  • Server logins. "43 SSH credentials for public servers, some of them a root password in an sshpass command."
  • Organizations. "63 organizations hold a real secret in a public allowlist." Apiiro says most secrets belong to individual developers.

Two findings explain why deleting the file today isn't enough. First, the files tend to sit for a long time: "In the median repo with a real secret, the file was first committed 217 days before that." Second, history keeps them: "Earlier versions of the file hold 734 real secrets, in 509 repos, that appear in no current copy, usually because the file was deleted."

Bigger files hold more. Apiiro writes: "One in ten files over 8 KB holds a real secret, against fewer than one in 500 under 500 bytes."

Why secret scanners miss many of them

You might expect a secret scanner to catch this. Apiiro ran gitleaks and TruffleHog over every file. "Of the 1,122 files with a real secret, gitleaks flagged 564 and TruffleHog 643; 215 were flagged by neither."

The reason is the shape of the secret. Scanners look for known key formats. A password typed after PGPASSWORD=, sshpass -p, or mysql -p looks like an ordinary word in an ordinary command line. Apiiro's summary: "The gap is passwords inside commands."

It isn't only one file

This is the part of the research that matters beyond permissions. Apiiro writes: "The same password-in-a-command pattern appears wherever an agent records what it ran: transcripts, memory files, handoff notes, and a scanner tuned to key formats misses it in all of them."

Its table of other files includes:

  • Shared .claude/settings.json. All 76,011 public copies checked, 298 files with a real secret. Teams commit this file on purpose.
  • Agent chat transcripts saved in repos. 2,523 checked, 120 files in 87 repos.
  • Agent memory files (MEMORY.md and similar). 1,200 checked, 9 repos.
  • Handoff and deploy notes. 2,095 checked, 33 files. Apiiro adds: "A third of the handoff notes with a secret were written by the agent itself."

The memory-file count is small, but the pattern is the same. Anything an agent writes down about what it did can carry whatever was in the command. If you keep project context in files an agent maintains, those files deserve the same care as the settings file.

By default, Claude Code stores session transcripts in your home directory, under ~/.claude/projects. Our guide to finding Claude Code session history covers where they live and how long they're kept. Apiiro's transcript count covers exports from several tools that people saved into repositories.

On your machine, too

Most copies never reach GitHub, and Apiiro argues that doesn't make them safe. "The allowlist is plain JSON at the same path in every project, and most of the secrets in it sit next to the host they open." It also notes the file can leave without git, "in a published npm package, a Docker image built with a copy command, a zip archive or a synced folder."

If you work across several checkouts of one repository, the file sits at one place. The documentation says that in a worktree, Claude Code "uses the file at the main checkout's root." Our guide to whether Claude Code worktrees share memory explains what else is shared across worktrees.

What Anthropic has said

Apiiro says it shared the research with Anthropic before publication. In an update dated November 8, Apiiro reports that Anthropic's head of Claude Code responded on Hacker News on November 7.

According to Apiiro's update, he wrote that the team is "looking into what we can do to prevent this," and named approaches it plans to prototype, "making sure auto mode catches this, building in basic secret scanning". Apiiro also reports that, in his view, the exposed copies were created manually and checked in, and that he recommended auto mode over permission rules and the secret scanning that GitHub and other hosts offer.

As of the Claude Code releases published through November 10, 2026, the release notes don't describe a change to how approvals are saved. Treat this as an open issue and check the changelog before relying on any fix.

What people try first

Deleting the file and committing the deletion. It removes the current copy. It doesn't remove history, forks, or clones, which is where Apiiro found 734 secrets that appear in no current copy.

Assuming the global exclude protects every machine. It protects the machine where Claude Code wrote it. A new clone, a CI runner, or a folder that became a repo later doesn't have it.

Relying on a secret scanner alone. Both scanners Apiiro tested missed a large share of files, because passwords inside commands don't look like keys.

Approving the same long command again and again. Each standing approval adds a literal rule, and Apiiro found larger files far more likely to hold a secret.

The Fix: Check, rotate, then clean up

Step 1: Find every copy

Search your GitHub organization for .claude/settings.local.json and .claude/settings.json. Then check each repo's git history for the file, even if it's gone today. On your own machine, find every copy and read its allow list. Inside Claude Code, /permissions lists your rules and the settings file each one comes from.

Step 2: Rotate before you delete

Apiiro's order is "rotate first, clean up second." Any credential that has ever been in the file, in any commit, should be treated as exposed. Removing the file doesn't clear history, forks, or clones.

Step 3: Untrack the file and protect the repo

If the file is tracked, Apiiro recommends untracking it with git rm --cached .claude/settings.local.json and adding it to the repo's own .gitignore. That way the protection travels with the project, not just with your machine.

Step 4: Change how you approve

Delete every rule that contains a secret, even in files that were never committed. Then stop typing secrets into commands. Reference them by variable name, so the rule records the name and not the value. Approve patterns rather than literal commands: the documentation's example Bash(npm run *) matches npm run build and npm run test --watch. Be careful with how broad a pattern is, because the documentation notes that everything before the first * is what limits the rule.

Step 5: Treat agent files as sensitive

Add .claude/, saved transcripts, handoff notes, and agent memory files to your pre-commit and CI secret scanning, including history. Make sure the scanner checks for passwords inside commands, not just key formats.

Setting this up in MemoryLake

Apiiro's finding comes down to one habit: an agent writes down what it did, and whatever you typed into the command goes with it. Settings files, transcripts, and memory files all record work. In each of them, whatever was in the command can end up recorded along with the work.

MemoryLake is a memory layer for the project context you want an assistant to have, such as which database a service uses, where staging lives, and which commands the team runs, available to assistants you connect, such as ChatGPT, Claude Code, and Codex.

You write the entries yourself, in your own words. Nothing is read out of, written to, or deleted from your .claude folder, your settings files, your session transcripts, your auto memory, your repositories, or any vendor's store. Claude Code's permission rules work the way Anthropic describes them, and you still need to clean up the settings file as described above.

Step 1: Create an API key

Sign in and generate a key from the dashboard. Store it the way you'd store any other credential, outside your repositories.

The MemoryLake console showing the API keys screen, where a new key is created and copied for use in an agent
The MemoryLake console showing the API keys screen, where a new key is created and copied for use in an agent

Step 2: Upload your first memories

Write the context, not the credentials: the name of the environment variable a script expects, the host a service runs on, the steps your team follows to rotate a key. One fact per entry, and never a password or token.

The MemoryLake workspace with the first documents uploaded, listing each file as it becomes searchable memory
The MemoryLake workspace with the first documents uploaded, listing each file as it becomes searchable memory

Step 3: Connect your AI & agents

Connect Claude Code and any other assistants you use. They can draw on the same written context, while the secrets stay in your secrets manager.

The MemoryLake integrations screen listing the AI clients and agent frameworks that can be connected to the memory layer
The MemoryLake integrations screen listing the AI clients and agent frameworks that can be connected to the memory layer

What this changes in practice

The first difference is a clear line between context and credentials. Project knowledge lives in entries you wrote, and the values that open systems live somewhere built for them.

The second is fewer reasons to paste secrets into a session. When the assistant already knows which variable holds the connection string, the command can reference the variable.

Best practices for Claude Code permissions

Read the command before approving it. Especially when it connects to a database or a server.

Approve patterns, not literal commands. A rule like Bash(npm run *) doesn't carry arguments you typed once.

Keep secrets in variables. The rule then records the variable name.

Add the file to the repo's own .gitignore. Don't rely on a global exclude that only your machine has.

Review the allow list regularly. /permissions shows every rule and where it comes from.

Scan agent files and history. Settings, transcripts, handoff notes, and memory files can all carry commands.

Rotate on any doubt. History and forks keep what you delete.

Conclusion

When you pick the option to stop Claude Code from asking about a shell command, Claude Code saves that command as an allow rule in .claude/settings.local.json. If the command contained a password or token, the rule contains it too. Claude Code adds the file to your global git excludes when it creates it, but that protection doesn't travel with the repository, and Apiiro found 1,122 public copies of the file holding a real secret, with 2,539 distinct secrets across all the agent files it checked.

Check your repos and history for the file, rotate anything it ever held, untrack it and add it to the repo's .gitignore, and change how you approve commands. Then treat every file an agent writes, including memory files, as something that can carry a secret.

Frequently asked questions

What is .claude/settings.local.json in Claude Code?

It's the project local settings file, meant for your own overrides in one project. Claude Code also writes your standing permission approvals there as allow rules.

Why does settings.local.json contain my password?

When you approve a shell command with the option to stop asking, Claude Code saves the command as a rule. If you typed a password or token into that command, it's saved as part of the rule.

Isn't settings.local.json gitignored automatically?

Claude Code adds it to your global git excludes the first time it writes the file in a repository that doesn't already ignore it. That protects your machine, not other clones, and it doesn't apply to a file you created by hand or one that's already committed.

I deleted the file. Am I safe?

Not necessarily. Apiiro found 734 secrets in earlier versions of the file that appear in no current copy. Rotate any credential the file has ever held.

Has Anthropic fixed this?

Apiiro reports that Anthropic's head of Claude Code said the team is looking into prevention, including secret scanning and auto mode. Check the Claude Code changelog for any change to how approvals are saved.

Do secret scanners catch these files?

Many, but not all. Apiiro reports that 215 of the 1,122 files with a real secret were flagged by neither gitleaks nor TruffleHog, because passwords inside commands don't match key formats.