The File Claude Code Writes When You Choose "Don't Ask Again"

Claude Code asks before it runs a shell command. One of the answers is "Yes, and don't ask again". It is the answer most of us pick for npm test, and it is the one this post is about.

What the answer does

When you choose it, Claude Code writes a rule to .claude/settings.local.json at the root of the repository, so the command runs without a prompt in every later session there. The rule is the command itself. For npm test it looks like this:

{
  "permissions": {
    "allow": ["Bash(npm test *)"]
  }
}

Now picture the command being psql postgres://app:S3cretPass42@db.example.com/app -c "select 1". The rule saved is that whole string, password included. It sits in a JSON file inside your project, and nothing marks it as a secret. It just looks like a permission.

How it reaches GitHub

Claude Code tries to keep the file out of git. The first time it writes the file in a repository that does not already ignore it, it adds **/.claude/settings.local.json to your global git ignore file. That is a sensible default, and it has three holes:

  • It lives on your machine, not in the project. A teammate, a CI runner or your next laptop does not have it.
  • It does nothing for a file that git already tracks, which is what happens when someone committed .claude/ before the ignore existed.
  • It does not cover a folder you turn into a repository later.

Apiiro looked at the result. They scanned 104,185 public copies of .claude/settings.local.json on GitHub and found real-looking secrets in 1,122 of them. Counting git history and related settings files, that was 2,539 secrets across 1,994 repositories: 379 database passwords, 43 SSH logins, 42 GitHub, GitLab and npm tokens, 23 AWS keys. In 539 repositories, the commit that changed the file was co-authored by Claude. The researchers did not try any of the credentials. One company confirmed its exposed key was live.

Check your own repositories

The file is JSON, so you can read what it saved:

jq -r '.permissions.allow[]?' .claude/settings.local.json

Read the list. A line with a password, a token or a key in it is a secret stored in plain text in your project. To check whether git tracks the file:

git ls-files --error-unmatch .claude/settings.local.json

If that prints the path, the file is in your history.

Fix it

  1. Stop tracking the file and ignore it in the project itself, so the rule travels with the repository:

    git rm --cached .claude/settings.local.json
    echo ".claude/settings.local.json" >> .gitignore
    
  2. If a secret was ever pushed, rotate it. Removing it from the current commit does not remove it from history, and history is what scanners read.

  3. Stop putting secrets in the command text in the first place. Set them in your shell before you start the agent, or keep them in a credentials file the tool reads itself. Writing AWS_SECRET_ACCESS_KEY=... aws s3 ls does not count: the secret is still part of the command, and the command is what gets saved. A command without a secret in it saves a harmless rule.

The step that matters

The leak happens at the end, on git push, but the mistake happens at the start: a secret typed into a command that an agent is about to run. That is the step where a check can still see it. We tested node9 with a fresh default configuration on three such commands. A database connection string was blocked before it ran. A curl with a bearer token was held for review, and so was an AWS secret key assigned inline in front of aws s3 ls. In each case the secret was visible at the moment of the command, and that is where it has to be caught, before any tool saves it anywhere.

We keep a record of incidents like this one, each with its sources, at node9.ai/incidents.

Sources

Share
XRedditHacker NewsLinkedIn

Related posts

Security · AI

We Read 760 Repositories That Run AI Agents in CI

760 public repositories run an AI agent from GitHub Actions. We read every one of them by hand. In 3 of 10, a stranger can reach the agent, and in most of those the agent holds a secret or a token that writes to the repository.

Security · AI

Structure or Text: Two Ways to Read a Command, and Why We Needed Both

We wrote a post called Why Regex Is Not Enough. It was right about what a parser buys you and incomplete about what it costs. Here is the distinction we should have drawn the first time.

Security · AI

What Can a Hijacked AI Agent Actually Reach on Your Machine?

A compromised coding agent doesn't need a zero-day - it inherits everything your shell can reach, and its malicious reads look exactly like its helpful ones. Runnable checks to see your own blast radius, why exfil hides in traffic you approved, and how to read your exposure score in ten seconds.