When we use Claude Code to connect to databases, log into servers, or
call external APIs, a confirmation prompt regularly appears in the
terminal. To avoid hitting Enter every single time, many people default
to option 2: allow and don’t ask again. Inside the project folder sits a
.claude/settings.local.json configuration file dedicated to
recording local allow rules. Once you select that option, the tool
remembers that you approved the database client, while also copying the
entire command line you entered, complete with plaintext passwords and
public IPs, verbatim into that file.
Security firm Apiiro recently published a scan report uncovering 2,539 valid secrets exposed through these configurations across 1,994 public repositories on GitHub. Many developers assume they have never manually pushed that configuration folder to a repository. Yet after tracing the commit histories, Apiiro found that nearly half of the leaked files were packaged and pushed to GitHub by Claude itself when developers asked it to commit their code.
It all starts with small shortcuts taken for convenience. When you
ask the AI to check data in an online test database or log into a remote
machine to inspect logs, it often assembles a complete one-liner
terminal command. To bypass interactive prompts, it stuffs the password
directly into the command line in formats like
PGPASSWORD=<plaintext_password> psql -h <public_host>
or
sshpass -p <plaintext_password> ssh root@<public_host>.
The moment you click allow and don’t ask again, that entire string is
written verbatim into the allowlist array inside
.claude/settings.local.json. A file meant to store
permission toggles quietly turns into a plaintext password book holding
both the front-door key and the street address.
That password book has already spread across public repositories. The
1,994 repositories and 2,539 secrets mentioned earlier span commit
histories, shared configurations, and several other surfaces; even
looking strictly at the 104,185 .claude/settings.local.json
files currently sitting on default branches on GitHub, 1,122 of them
store plaintext credentials directly.
Those live files directly hold 1,379 valid credentials in total. They have been sitting publicly searchable on default branches for a median of 217 days.
Database passwords paired with public host addresses alone account for 379 of them, alongside 43 login passwords for public servers. The haul also includes 122 privileged Supabase service-role keys capable of bypassing all row-level security policies, 103 Supabase account tokens with control over every project under an account, and an assortment of cloud platform and third-party API keys.
Why couldn’t secret scanners stop these leaks, even when so many
teams already have them installed? Researchers ran gitleaks
and TruffleHog, the two most widely used open-source
detection tools, against the files already confirmed to contain
sensitive credentials. Surprisingly, both tools missed the same 215
files, a miss rate of 19.2%. Traditional scanners rely heavily on
spotting garbled token strings with recognizable prefixes like
sk- or ghp_. Once plaintext database passwords
sit tucked inside ordinary command-line arguments, they look no more
conspicuous to a scanner than everyday English words.
After the report hit Hacker News on November 7, it sparked a wave of questions from developers. Claude Code team lead Boris Cherny stepped in that same day to explain that this local configuration file was designed strictly to stay on a single machine. He claimed that Claude Code automatically adds the file to gitignore when creating it, meaning the leaked files must have been manually created and checked into repositories by users.
Inspecting the package code of the Claude Code v2.1.299
client, however, reveals that the actual implementation does not match
his account. Earlier versions did write the ignore rule into the
repository’s own .gitignore file, which travels with the
codebase. After users complained about the tool modifying project files,
the team changed this policy in late 2025, a shift cited across multiple
community
bug reports for causing secret leaks. Today, when the client creates
.claude/settings.local.json, it leaves the project’s
.gitignore untouched and instead writes an entry to
~/.config/git/ignore, the global Git ignore list that
exists only on the current machine.
This single-machine mechanism breaks down outright in two common
scenarios. First, if you start using Claude inside a brand-new empty
folder, click don’t ask again, and only run git init
afterward to initialize the repository, the client detects no repository
at the time and skips writing the ignore rule altogether. Second, even
when the client does write the global ignore rule on your current
computer, that rule stays on that machine alone; the moment you copy the
project to another computer or move it into a container, the protection
vanishes.
Similarly, when packaging software for release, packaging tools do not consult Git’s global ignore list. A package scan report published by security firm Lakera this April also found 428 publicly published npm packages that bundled this local configuration file directly inside, with 30 of those packages carrying valid secrets.
Even more damaging is how Claude Code handles commits. The client’s
package code shows that even after
.claude/settings.local.json enters Git’s tracked files
list, the client detects that state, quietly logs an internal telemetry
event without showing any warning to the user, and keeps appending new
passwords to the file.
When the user finally asks Claude to commit code, the defense
collapses completely. The official Git commit system prompt for Claude
explicitly warns the model not to commit secret files such as
.env or credentials.json, yet leaves out its
own .claude/settings.local.json. As a result, among the
1,105 leaked repositories with traceable commit histories, 539 had their
files pushed to GitHub by commits carrying the
Co-Authored-By: Claude trailer when users asked Claude to
commit code, accounting for 48.8%.
At this point you might wonder: how hard could it really be to check a command for passwords before writing it to a file? In the Hacker News discussion, Boris Cherny explained that Claude Code has historically never performed secret scrubbing because plenty of off-the-shelf tools already exist on the market, adding that the team is exploring prototype solutions to prevent such leaks.
In reality, the community uncovered this exact pitfall 15 months ago.
On August 11, 2025, a developer submitted a security
vulnerability report with reproduction steps to the official Claude
Code repository, pointing out that approved commands save plaintext
passwords verbatim inside settings.local.json. Official
engineers never replied; after sitting unanswered for nearly five
months, an official bot marked the issue as not planned and closed it
automatically at the start of 2026.
More surprising still, opening the cloud sync module inside the
client package reveals a ready-made piece of password-filtering code
sitting right there. It scans a user’s permissions.allow
rules one by one, strips out any rule that looks like it contains a
credential on the spot, and even has the user-facing notice text already
written.
If that ready-made filtering code already ships inside the package, why do plaintext passwords still show up in local configuration files? Because this official filter only runs when a user enables an official cloud remote session and uploads local settings to Anthropic’s cloud servers. When a user clicks don’t ask again and writes the command into a local project folder, the program never calls that existing filter. Protecting official cloud servers while leaving users’ local repositories exposed puts a double standard inside the very same package.
In his response, Boris also advised community members to stop using allow rules and switch to auto mode, where a background AI classifier judges whether a command is dangerous. Yet a reader in the comments immediately pointed out that whenever auto mode’s background classifier encounters a sensitive command carrying passwords or connecting to an external server, it is designed not to decide on its own and falls back to a manual prompt asking you to confirm. Click allow permanently, and the password still gets written into the exact same file. Confronted with that loop, Boris could only concede on the spot that the reader made a good catch and that the team still needed to figure out another approach. Moreover, auto mode’s own blocking capability is far less reliable than advertised: an independent evaluation just updated in late October by the Hong Kong University of Science and Technology tested 128 everyday development tasks and found that when instructions leave operational scope ambiguous, auto mode failed to block 81.0% of dangerous actions.
Since we can rely neither on the tool’s built-in single-machine ignore feature nor on auto mode, we have to close the hole ourselves. Taking 30 seconds right now to do two things will make your repositories completely immune.
First, stop trusting single-machine global ignore rules and add
.claude/ to every project’s own .gitignore
file; if the project publishes packages, add it to
.npmignore as well. You can also search your computer for
existing .claude/settings.local.json files to check whether
they hold any plaintext passwords you casually approved in the past.
Second, completely separate approving which command to run from what
the password actually is. When having Claude Code connect to databases
or log into servers, do not write plaintext passwords directly as
literals in the command; use environment variable names instead, or use
the 1Password CLI op run --env-file .env -- claude to
inject secrets into memory at startup.
That way, even if you cut corners and click allow and don’t ask again
100 times, Claude Code will only write variable names like
PGPASSWORD=$DB_PASSWORD psql -h <host_address> into
.claude/settings.local.json. Even if that file ever gets
pushed to GitHub, anyone who grabs it walks away with nothing more than
an empty shell.