Back

Keeping secrets away from AI coding agents

I looked into how AI coding agents leak credentials and how to stop it. The basic rule is that a credential the agent can read is a credential it can leak. It does not matter how careful the agent's instructions are. The secret can come out in a response, in code the agent commits, or through prompt injection that makes the agent send out data it legitimately had access to. The only reliable fix is to keep the raw secret out of the agent's process. Deny rules and ignore files help but do not guarantee it.

The incidents worth knowing

These are documented, not hypothetical.

  • Copilot Chat "CamoLeak" (rated 9.6, 2025). An attacker put hidden instructions in invisible markdown comments inside a pull request. A human reviewer does not see them, but Copilot Chat reads them when asked to review the code. The instructions made Copilot encode secrets it found (AWS keys, private source) as a series of 1x1-pixel image requests through GitHub's image proxy, one pixel per character, and the attacker read the secret back from the image requests hitting their server. GitHub fixed it by turning off image rendering in Copilot Chat.
  • Claude Code loading .env automatically. Reported to load .env files into context without being asked, so any key in them becomes readable by default unless you set deny rules.
  • Claude Code committing an API key into a public repo inside a test file, unprompted.
  • Cursor uploading a local file with a key to cloud storage without the user knowing. It was caught by outside tooling, not the agent.

Containment, weakest to strongest

Ignore files are not enough. .gitignore, .cursorignore, .copilotignore. These are not reliable as the only control. .gitignore governs git, not filesystem reads, so it was never an access control.

Deny rules in the harness config, verified. In Claude Code you can set project deny rules in .claude/settings.json:

{ "permissions": { "deny": [
  "Read(./.env)", "Read(./.env.*)", "Read(./secrets/**)"
]}}

Treat this as a guardrail. Test it by asking the agent to show the .env contents and confirming it refuses while the rule is on. Better, keep secrets outside the project directory so there is nothing to deny in the first place.

Sandbox the agent. Run it in a container or microVM with no secret-bearing volumes mounted, so even if it tries to read a secret file there is nothing there.

Use a broker so the agent never holds the secret. A broker sits between the agent and the real API. The agent sends a placeholder like __api_key__, and the broker swaps in the real value before forwarding the request. Infisical Agent Vault does this over HTTPS_PROXY with no code changes and is compatible with Claude Code. This is the strongest option because there is no real credential in the agent's process to leak.

What I would actually do

  1. Keep .env and secret files out of any directory the agent can read.
  2. Use injection tools like op run so the agent's context never contains the raw values.
  3. For anything that needs a live key, put a broker in front instead of giving the agent the key, especially for agents that browse the web or read pull requests, since that is the injection path behind CamoLeak.
  4. Check MCP configs and agent dotfiles for plaintext credentials the same way you check .env.
  5. Keep secret scanning (gitleaks, TruffleHog) in pre-commit as a backstop for keys that end up in agent-written code anyway.

References

  • CamoLeak: reported by Omer Mayraz (Legit Security) via HackerOne; CSO Online and The Register coverage.
  • Claude Code / Cursor .env leakage write-up: Knostic (knostic.ai).
  • Infisical Agent Vault: github.com/Infisical/infisical
  • GitGuardian, State of Secrets Sprawl 2026.