Artificial Intelligence 3 min read 585 words

Claude Code and the "don't ask again" that ends up putting passwords on GitHub

ES
Claude Code and the "don't ask again" that ends up putting passwords on GitHub

Researchers at Apiiro have found over 100,000 .claude/settings.local.json files pushed to public GitHub repositories. Of those, around 1,100, from 63 different organisations, had secrets inside: database passwords, API tokens, private keys and even root logins. Ars Technica has covered it, and it’s one of those things that, when you read it, makes you think “well, of course”.

Where it comes from

When Claude Code is about to run a command and asks you first, one of the options is “Yes, and don’t ask again”. Pick it, and the rule is saved in .claude/settings.local.json so you won’t be asked again. Sometimes it’s saved as a pattern, but often the command is saved as is, with everything it contained.

So if one day you let it run something like this:

psql "postgresql://admin:MyRealPassword@db.internal:5432/production" -c "select count(*) from orders"

that command, password included, stays written in the file, more or less like this:

{
  "permissions": {
    "allow": [
      "Bash(psql \"postgresql://admin:MyRealPassword@db.internal:5432/production\" -c \"select count(*) from orders\")"
    ]
  }
}

As long as the file stays on your machine, it’s not a big deal. The problem is that Claude Code adds it to the user’s global .gitignore, not the repository’s. If you clone the repo on another machine, if a folder becomes a repo later or if someone runs a hurried git add ., the file goes into the commit. And according to Apiiro, in many cases it’s Claude Code itself that makes the commit without anyone noticing.

The most striking part is that the problem had been reported since August 2025, and that issue and other similar ones were closed automatically after 60 days of inactivity. Boris Cherny, from Claude Code, has said they’re looking into it, and that they’re considering building a secret scanner into the tool itself.

What I’d do today

It takes five minutes and there’s no need to wait for any patch:

  1. Add it to the repo’s .gitignore, don’t rely on the global one. The repo for this blog already has it:

    echo ".claude/settings.local.json" >> .gitignore
    
  2. Check whether it’s already pushed, in the current state and in the history:

    git ls-files | grep settings.local.json
    git log --all --oneline -- .claude/settings.local.json
    
  3. Look at what’s inside your local file, even if it isn’t pushed. It may well hold more than you remember:

    grep -nEi "password|passwd|token|secret|api[_-]?key|://[^:]+:[^@]+@" .claude/settings.local.json
    
  4. If there was a secret in a public repo, rotate it. Deleting the file or rewriting history isn’t enough: existing forks and clones still have the copy.

  5. Keep secrets out of commands. If the command uses psql "$DATABASE_URL", what gets saved is the variable name, not its value. Environment variables, a .env outside git or a secrets manager do that job much better.

Secret scanners like gitleaks or TruffleHog help, but they’re not infallible: of the 1,100 files with secrets Apiiro found, 215 weren’t detected by either of them. Worth having, but not as the only line of defence.

My takeaway

A week ago I wrote about Postman installing plugins into Claude Code, Codex and Copilot on its own, and the same thing comes up again here: agent configuration files have become one more part of the project, and we don’t watch them as carefully as the code.

“Don’t ask again” is really convenient, but every time you click it you’re writing something to a file. It’s worth knowing what gets written and where that file ends up, because a plain-text password doesn’t stop being one just because it sits inside a JSON file nobody looks at.

Source: Apiiro, via Ars Technica.