Max Laktsionau, Forward Deployed Engineer at AdvantageWorks Max Laktsionau 14 min read AI-assisted

How to Stop Coding Agents From Leaking Secrets Into Your Repos

Over-the-shoulder view of a terminal where a coding agent asks to run a psql command with an inline password

PGPASSWORD=Prod_2026! psql -h db.internal -U admin. A developer types that into a coding agent's terminal prompt to check one row in production, the agent asks for permission, and the developer clicks "Yes, and don't ask again." The click is sensible. It saves the next twenty prompts. It also writes the whole command, password included, into a JSON file inside the project folder, and that file can ride along with the next commit.

On November 5, 2026, the application security firm Apiiro published research on exactly this path. It found real secrets in 1,122 of 104,185 public copies of .claude/settings.local.json on GitHub, the file where Claude Code saves those "don't ask again" approvals. Counting earlier versions in git history, shared settings files and saved transcripts, Apiiro reports 2,539 secrets across 1,994 repositories, tied to 63 organizations. In 539 of the 1,105 repos it traced, a commit co-authored by Claude itself had changed the file.

The list reads like a production inventory. Apiiro's breakdown includes database passwords, Supabase service-role keys, SSH logins, GitHub and npm tokens, AWS keys and private keys.

The approval that saves your team time is the leak.

Banning coding agents is off the table for most teams, so the useful decision for an engineering leader is narrower: where agent approvals are allowed to live, and how long a leaked string stays valid. Below: where each major agent keeps approvals and session state, which of those files reach git, five controls that close the gap, an audit you can run in one afternoon, and the lines to add to your AI-use policy.

How an approved command becomes a committed secret

Coding agents ask before running shell commands, and every one of them offers a way to stop asking. The mechanics differ, but the leak follows the same five steps:

  1. The agent proposes a command, or a developer asks it to run one, with a credential written inline: a bearer token in a curl header, a password in a connection string, an export AWS_SECRET_ACCESS_KEY=... line.
  2. The developer approves it and picks the persistent option so the agent stops interrupting.
  3. The agent saves an allow rule. In Claude Code's case, the rule is the literal command string, stored in the project's .claude/settings.local.json.
  4. Someone runs git add ., or the agent stages and commits its own changes, and the settings file goes in with them.
  5. The repo is pushed. If it is public, or ever becomes public, or is forked, the credential is readable by anyone who looks.

Step 3 is the one most teams miss, and it carries the whole problem. An allow rule is matched as a string or a prefix, so the safest rule is a pattern such as Bash(npm run test:*). The rule an approval click creates is whatever the command looked like at that moment. If the password was in the command, it is in the rule.

Step 4 has two authors. The human git add . is the familiar one. The newer one is the agent, which often stages everything it touched and writes the commit message. Apiiro's finding that Claude co-authored the commit in roughly half of the traced repos shows how routine that has become.

The secret shapes that turn up most are predictable:

  • Database connection strings and PGPASSWORD= or MYSQL_PWD= prefixes
  • curl -H "Authorization: Bearer ..." calls against internal or SaaS APIs
  • Cloud CLI calls with keys passed as flags or environment variables
  • sshpass -p logins to servers
  • API keys pasted into MCP server configuration under an env block

Which of these ends up in a repo depends on where the agent writes them, and that differs a lot between tools.

Where each coding agent keeps approvals and session state

The question to ask of every agent is simple: does it write approvals and session data inside the repo's working tree, or in the developer's home directory? Anything inside the working tree is one git add away from history. Anything in the home directory is safer from git but still sits on laptops, in backups and in dotfiles repos people sync to GitHub.

Apiiro compared the documentation of 16 coding agents and reports that Claude Code is the only one confirmed to save the full approved command in a project file by default. The other agents are not risk-free, though. They put the risk in different places. The details below reflect vendor documentation as of November 2026 and change often, so verify them against the version your team runs.

Claude Code

Claude Code reads settings from several layers. .claude/settings.json in the project is meant to be shared and committed, so the team gets the same allow and deny rules. .claude/settings.local.json in the project is personal, and it is where "don't ask again" approvals land. User-level settings live in ~/.claude/settings.json, and an enterprise managed-settings.json sits in a system path (/etc/claude-code/ on Linux, /Library/Application Support/ClaudeCode/ on macOS) and overrides the rest.

Anthropic's documentation says Claude Code configures git to ignore settings.local.json when it creates it. Per Apiiro's analysis, it does that through the developer's global git ignore list, not the project's own .gitignore. That protection does not travel. It misses folders that become repositories later, clones on other machines and CI runners, and any copy that was committed before. Session transcripts are written under ~/.claude/projects/, outside the repo, but they hold full command history and get pasted into repos and tickets when people share a session.

Cursor

Cursor (the editor most teams pilot after Copilot) stores its terminal command allowlist in its own internal state rather than in a project file, and supports permissions.json at ~/.cursor/permissions.json and at <workspace>/.cursor/permissions.json, with the two concatenated. The bigger in-repo risk is .cursor/mcp.json, the project-level MCP server config, where API keys often end up in plain text under env. Teams commit .cursor/ freely because it also holds shared rules. Note that .cursorignore controls what the agent reads, not what git commits.

OpenAI Codex

Codex keeps its configuration in ~/.codex/config.toml, including approval_policy and the sandbox mode. Session approvals last only for the current thread. When a developer accepts a persistent rule, Codex writes a prefix rule to ~/.codex/rules/default.rules in the home directory. That design keeps approvals out of the repo by default. The exposure comes from teams that commit a project-level config, paste session logs into issues, or sync their home dotfiles to a public repo.

GitHub Copilot in VS Code

Copilot's agent mode in VS Code can auto-approve terminal commands through the chat.tools.terminal.autoApprove setting. That setting can live in user settings or in the workspace's .vscode/settings.json, and MCP servers can be configured in .vscode/mcp.json. Many teams commit .vscode/ by convention to share editor setup, so a workspace-level auto-approve list or MCP config with a pasted key goes straight to the remote. Copilot's cloud coding agent runs on GitHub with environment secrets managed in repository settings, which is the safer pattern.

Exposure map at a glance

Agent

File or location

Inside the repo?

What it stores

Secret risk

Commit it?

Claude Code

.claude/settings.local.json

Yes

"Don't ask again" approvals as literal commands

High

Never

Claude Code

.claude/settings.json

Yes

Shared allow and deny rules

Medium, if someone copies a local rule in

Yes, after review

Claude Code

~/.claude/projects/

No

Session transcripts

Medium, when shared

Never

Cursor

.cursor/mcp.json

Yes

MCP servers, often with keys in env

High

Only with ${env:VAR} references

Cursor

.cursor/permissions.json

Yes

Workspace allowlist entries

Medium

After review

OpenAI Codex

~/.codex/rules/default.rules, ~/.codex/config.toml

No

Persistent prefix rules, approval policy

Low in repo, medium in synced dotfiles

Never from home

Copilot in VS Code

.vscode/settings.json, .vscode/mcp.json

Yes

Terminal auto-approve list, MCP config

Medium to high

Only after review

Why your secret scanner misses part of this

Here is the uncomfortable part for teams that feel covered. Most engineering orgs already run secret scanning, usually GitHub secret scanning with push protection. It helps, and it is not enough on its own for this leak.

GitHub's scanner matches supported provider patterns: AWS keys, Stripe keys, GitHub tokens and a few hundred more. Push protection blocks a push only when one of those patterns matches. A Postgres password, an internal service token or an SSH password is a free-form string with no prefix to detect. GitHub's AI-based detection of generic passwords, which uses Copilot, widens coverage, but it does not catch everything, and custom formats need custom patterns your security team has to write.

Apiiro's numbers show the gap from the other side. Of the files with real secrets, gitleaks flagged 564 and TruffleHog flagged 643, and 215 were flagged by neither tool. A pattern-based scanner cannot recognise a password it has no pattern for.

Two more blind spots matter. Server-side scanning only sees what gets pushed, so transcripts and local settings files on laptops are invisible to it. And once a secret is in public history, detection is incident response, not prevention. Forks keep it, clones keep it, and Microsoft Threat Intelligence reported on November 5, 2026 that attackers are already running agentic tooling to search public GitHub for exposed secrets.

Five controls that close the gap

No single control covers every agent and every path. Layer them, cheapest first, and notice that only one of the five removes the leak instead of catching it later.

1. Repo .gitignore rules

Add the agent paths to every repo's own .gitignore: .claude/settings.local.json, any *.local.json agent files, transcript exports, and .cursor/mcp.json unless it only references environment variables. Put the same lines in your repo templates and in a global gitignore pushed to developer machines. This is cheap and immediate. It fails open on brand-new repos, on git add -f, and on files already tracked, which need git rm --cached to stop being tracked.

2. Managed settings pushed by IT

This control stops the problem at the source. Claude Code's enterprise managed-settings.json overrides user and project settings, and with allowManagedPermissionRulesOnly set to true it ignores permission rules from user, project and local files entirely. Your platform team defines the allow and deny list once, and an approval click can no longer create a new rule. VS Code supports policy-managed settings for Copilot's auto-approve behaviour, and Codex and Cursor configs can be distributed through device management where your tooling supports it. This takes more work to roll out, and it is the only control that removes the leak instead of catching it later.

3. Pre-commit and push-time secret scanning

Run gitleaks or TruffleHog as a pre-commit hook, enable push protection, and write custom patterns for your internal token formats and for strings like PGPASSWORD= and sshpass -p. Check one detail that often goes wrong: some scanner configs exclude dotfolders, which drops .claude/, .cursor/ and .vscode/ from scope. Add them back explicitly. Agent-authored commits go through the same hooks as human ones, so this catches the agent too.

4. Short-lived credentials

Assume some strings will leak and make them expire. Use OIDC federation for CI instead of stored cloud keys, cloud SSO sessions measured in hours for engineers, and scoped tokens with expiry dates for SaaS APIs. A leaked token that died an hour after it was minted is a log entry, not an incident. This is also the control that limits damage from everything already sitting in public history.

5. Sandboxed agent runs

An agent cannot leak a secret it never sees. Run agents inside devcontainers or the agent's own sandbox mode (Claude Code and Codex both ship one), give the sandbox development credentials only, and restrict network egress to the hosts the task needs. Keep production database access behind a bastion or a broker that the agent's environment cannot reach.

Controls compared

Control

Stops

Misses

Effort to roll out

Owner

Repo .gitignore

Accidental commits of known agent files

New repos, git add -f, files already tracked

Hours

Each team, via templates

Managed settings

New literal-command rules being created

Agents without central policy support

Days to weeks

Platform or IT

Pre-commit and push scanning

Known patterns and custom patterns at commit time

Free-form passwords without a pattern, local-only files

Days

AppSec

Short-lived credentials

Long-term damage from any leak

The leak itself

Weeks to months

Platform and cloud

Sandboxed runs

Agents touching production secrets at all

Secrets developers paste in manually

Weeks

Platform

If your team does only two things this week, push managed settings that block new literal-command rules, and rotate or shorten every credential already exposed.

If you want a second set of eyes on your agent settings before the audit, our free 30-minute AI Readiness Snapshot call covers exactly this kind of rollout gap.

The one-afternoon audit

This runbook fits in a single afternoon for most mid-size orgs. The order matters more than the tools: find, scan, rotate, then clean up and prevent. Teams that reverse the middle two steps end up with a tidy history and live credentials.

Security lead writing a credential rotation order on a whiteboard while engineers work at laptops in a meeting room
  1. Org-wide search (about 30 minutes). Use GitHub code search across your organization for the agent files:

`` org:your-org path:.claude/settings.local.json org:your-org path:.claude/settings.json org:your-org path:.cursor/mcp.json org:your-org path:.vscode/settings.json "autoApprove" org:your-org path:.vscode/mcp.json ``

Also search for transcript-shaped files committed at repo roots, such as *.jsonl session exports, and list every repo where a match exists, public or private.

  1. History scan (about 60 minutes). For each matching repo, scan the full history, including deleted files. A file removed last month is still in every clone:

`` git log --all --full-history -- .claude/settings.local.json gitleaks detect --source . --log-opts="--all" trufflehog git file://. --results=verified,unknown ``

Run both scanners. Apiiro's data shows each one catches secrets the other misses, and neither catches all of them, so read the matched files yourself as well.

  1. Rotate first (60 to 90 minutes). Rotate every credential you found before you touch history. Work in order of blast radius: cloud admin keys and CI deploy tokens first, then production database passwords and payment or core API keys, then third-party SaaS tokens, then internal and development tokens. For each one, check the provider's access logs for use since the date of the first commit that contained it.
  2. Then clean up. Run git rm --cached on the agent files, add the ignore rules, delete any allow rule that holds a secret, and rewrite history with git filter-repo or BFG where it is worth the disruption. Accept that public clones and forks keep the old copy, which is why rotation came first.
  3. Then prevent. Push the managed settings, enable push protection with your custom patterns, and add the pre-commit hook to your repo templates so new repos start safe.

What done looks like:

  • [ ] Every match from the org-wide search is reviewed
  • [ ] Full-history scans ran on every matching repo
  • [ ] Every exposed credential is rotated and its access logs checked
  • [ ] Agent files are untracked and ignored in each repo's own .gitignore
  • [ ] Managed settings are deployed to engineering machines
  • [ ] Push protection and custom patterns are on for the whole org

What to put in your AI-use policy

Most AI-use policies cover data sent to models. Few cover what the agent writes back to disk. Add a short section for coding agents. These clauses are written so you can paste them in and adjust the names:

Printed AI acceptable use policy on a desk with a handwritten red-pen clause about agent credentials in the margin
  • Credentials are never typed inline in commands an agent runs. Reference them through environment variables or a secrets manager.
  • Agent permission rules are managed centrally through managed settings. Developers do not create persistent approvals for commands that contain credentials.
  • Project-level agent configuration (.claude/, .cursor/, .vscode/) is reviewed in pull requests like code.
  • Local agent settings files and session transcripts are never committed, attached to tickets or shared in chat.
  • Coding agents run without production credentials by default. Production access goes through an approved broker.
  • Any exposed credential is rotated before history is cleaned, and the incident is logged.
  • Agent-authored commits are subject to the same pre-commit hooks and push protection as human commits.

Name an owner. Platform or security owns the managed settings and reviews them every quarter, or whenever an agent ships a new permission feature. Agent releases move weekly , so a policy nobody owns goes stale within one release cycle.

What the vendors are changing, and why not to wait

Anthropic has responded. Boris Cherny, who leads Claude Code, said in a Hacker News discussion on November 7, 2026 that the team is looking at checks in auto mode and basic secret scanning. Apiiro also notes that users had reported this mechanism in GitHub issues since August 2025. Elsewhere, GitHub has extended secret detection with AI-based generic password scanning, and tools such as GitGuardian now ship hooks that scan prompts and tool calls inside the agent loop.

These changes will reduce new leaks. They will not rotate a password that has sat in a public fork since spring, and they will not reach the agents your team installed without telling anyone . Vendor fixes help with what comes next. The audit deals with what has already been committed.

Common mistakes

  • Rewriting history before rotating, which leaves live credentials in every existing clone
  • Treating a private repo as safe, when forks, contractors, mirrors and future open-sourcing all change who can read it
  • Watching only settings.local.json and ignoring shared settings.json, which is where a copied local rule ends up being committed on purpose
  • Scanner configs that skip dotfolders
  • Giving the agent the same credentials as the engineer running it

Fix it where approvals are stored

The approval click is where the leak starts, so fix it there. Control which rules agents can create, keep their files out of git, and make every credential short-lived enough that a leaked string expires before anyone uses it.

If you want help running this audit or setting up managed agent settings across your team, book a free 30-minute AI Readiness Snapshot . We will go through your repos and agent configuration with you and leave you with a prioritised fix list.

Frequently asked questions

GitHub secret scanning searches a repository's entire git history, on every branch, for credentials that match known token formats such as AWS keys, GitHub tokens and Stripe keys, and raises an alert when it finds one. With push protection turned on, it can also block a push that contains a supported secret before it reaches the remote.

It runs free on public repositories and on private repositories with GitHub Advanced Security. Its limit is the pattern list: a free-form database password or an internal service token has no recognisable prefix, so it can pass unnoticed unless you add custom patterns or rely on GitHub's AI-based generic password detection.

No. .claude/settings.local.json is the personal Claude Code file where "Yes, and don't ask again" approvals are saved as the literal command, so it can hold passwords, tokens and connection strings. Commit the shared .claude/settings.json after review, and keep the local file out of git.

Claude Code adds the local file to your global git excludes when it first creates it, but that does not protect other machines, CI clones or files already committed. Add .claude/settings.local.json to each repo's own .gitignore and run git rm --cached on any copy that is already tracked.

In cybersecurity, a secret is any credential that grants access to a system without a human login: API keys, database passwords, access tokens, SSH keys, private certificates and cloud access keys. Anyone holding the string can act with the permissions it carries.

Secrets differ from ordinary sensitive data because they are executable. A leaked customer record exposes data, while a leaked cloud key lets an attacker create, read or delete resources until the key is rotated, which is why short-lived credentials limit the damage of any leak.

Yes. Rotate or revoke the credential first, then clean the history. Rewriting history does not invalidate the secret, and public commits are cloned, forked and scraped by automated tools soon after they are pushed.

After rotation, check the provider's access logs for use since the first commit that contained the secret. Then untrack the file, add ignore rules, and rewrite history with git filter-repo or BFG where it is worth the disruption, accepting that existing clones keep the old copy.

Use central managed settings rather than per-developer habits. Claude Code's enterprise managed-settings.json overrides user and project settings, and with allowManagedPermissionRulesOnly set to true an approval click can no longer create a new permission rule.

Pair that with credentials referenced through environment variables or a secrets manager instead of typed inline, pre-commit secret scanning that includes agent folders such as .claude/, .cursor/ and .vscode/, and short-lived tokens. A single developer can get most of the benefit from pattern-based allow rules like Bash(npm run test:*) and a repo-level .gitignore.

They cover different halves of the problem. A sandbox, such as a devcontainer or the agent's built-in sandbox mode, stops the leak at the source because the agent never sees production credentials. CI and pull-request scanning catch secrets after they are committed, which is detection rather than prevention.

Use both. Run agents with development-only credentials and restricted network egress, and keep pre-commit hooks plus push protection as the backstop for anything a developer pastes in by hand.

Editorial statement

This article was produced with AI assistance and has undergone human review and editorial control. Max Laktsionau holds editorial responsibility for its content within the meaning of Article 50(4) of Regulation (EU) 2024/1689.