How do AI coding agents expose secrets? A coding agent reads files, runs shell commands and keeps a transcript, so it can reach your .env file, inherit API keys from your shell's environment, write what it read into plaintext logs on disk, send pasted keys to the model provider, and commit a key it was shown. The fixes are mostly configuration you can apply today: deny reads of secret files in the agent's own settings, run its commands in an OS-level sandbox that hides credential files and variables, shorten transcript retention, keep production credentials out of developer environments, and give agents a low-limit gateway key instead of a raw provider key.
This guide covers where secrets leak in agent workflows and what Claude Code and Cursor document to prevent it. The tool-access side of agents is in MCP server credential leaks; routing agent traffic through a gateway is in Claude Code and Codex through a gateway.
Four ways an agent reaches a secret
1. It reads the file
An agent explores the repository to understand it, and .env, config/secrets.yml and credentials.json are files like any other. In Claude Code, reading files inside the working directory needs no approval in the default mode, and a built-in set of read-only shell commands — including cat, head, grep and find — runs without a prompt in every mode1. Nothing malicious is required: an agent debugging a connection error will reasonably open the file that holds the connection string.
2. It inherits the environment
Shell commands an agent runs get the environment of the process that started them. Claude Code's sandbox documentation spells out the default: commands can read "most of the machine, including credential files such as ~/.ssh and ~/.aws/credentials", and environment variables are "inherited from Claude Code, including any secrets in its environment"2. If you export OPENAI_API_KEY=… in your shell profile, every command the agent runs — and every package install script those commands trigger — can read it.
That last part matters. The OSV advisory for the May 2026 "Mini Shai-Hulud" npm campaign describes packages, including two MCP server packages, whose install hook harvested cloud keys, GitHub and npm tokens and SSH keys, and persisted through, among other things, "AI agent session hooks"3. An agent that runs npm install runs that hook with your environment. AI supply chain attacks in 2026 covers the wider pattern.
3. It writes what it saw to disk
Claude Code stores each session as a transcript at ~/.claude/projects/<project>/<session>.jsonl, holding "every message, tool call, and tool result", in plaintext with only OS file permissions as protection; the documentation states that if a tool reads a .env file or a command prints a credential, the value is written to that transcript4. Transcripts are swept after cleanupPeriodDays, 30 days by default, while ~/.claude/history.jsonl keeps every prompt you typed until you delete it4. A key pasted into a prompt therefore lives in your history, in the transcript, and in the request sent to the model provider.
4. It puts the key into code
An agent asked to "make the integration work" may inline the key it found, write it into a test fixture, or add it to a config file that is not ignored. GitGuardian's 2026 report says commits co-authored by Claude Code leak secrets at about twice the baseline rate, and it counts 24,008 unique secrets exposed in MCP configuration files5. Those numbers describe a correlation in public commits, not a defect in one tool, but the direction is clear: more code written faster means more chances to commit a key. Secret scanning and push protection is the backstop for that.
What Claude Code documents
Deny reads of secret files
Claude Code's own settings example denies .env reads6:
{
"permissions": {
"deny": [
"Read(./.env)",
"Read(./.env.*)",
"Read(./secrets/**)"
]
}
}Know exactly what these rules cover. Read and Edit deny rules apply to Claude's built-in file tools, to file commands Claude Code recognizes in Bash such as cat, head, tail, sed and tee, and to redirection targets; they do not apply to a command that reads files without naming them, such as grep -r pattern ., or to a Python or Node script that opens files itself1. A .claudeignore file has no effect; move its entries into deny rules1. Bare file names match at any depth, so Read(.env) covers nested .env files under the working directory1.
Enforce it at the OS level with the sandbox
For enforcement that covers every process, Claude Code points to its sandbox1. The sandbox applies OS-enforced limits to Bash, PowerShell and Monitor commands and the processes they start; it runs on macOS, Linux and WSL2, and on native Windows commands run unsandboxed2. Its sandbox.credentials setting denies reads of listed credential files and unsets listed environment variables before each sandboxed command, and there is no built-in credential deny list, so only what you list is protected2:
{
"sandbox": {
"enabled": true,
"credentials": {
"files": [
{ "path": "~/.aws/credentials", "mode": "deny" },
{ "path": "~/.ssh", "mode": "deny" }
],
"envVars": [
{ "name": "OPENAI_API_KEY", "mode": "deny" },
{ "name": "ANTHROPIC_API_KEY", "mode": "deny" }
]
}
}
}The sandbox covers shell commands only: Claude's file tools, MCP servers and hooks run outside it, so you need both the permission rules and the sandbox2. Claude Code applies none of the credentials entries from project or local settings files, so put them in user or managed settings2. To strip credentials from all subprocesses regardless of sandboxing, Claude Code documents CLAUDE_CODE_SUBPROCESS_ENV_SCRUB2.
Make it organization policy
Settings in .claude/settings.json apply to everyone who clones the repository, and deny rules from it apply immediately, without the workspace-trust step that allow rules wait for6. Managed settings, delivered by MDM, a managed-settings.json file or the claude.ai admin console, sit above every other level and cannot be overridden by a developer's own files6. Put the .env deny rules and the sandbox credential list there.
Shorten what transcripts keep
Lower cleanupPeriodDays, set CLAUDE_CODE_SKIP_PROMPT_HISTORY where transcripts are not needed, and use claude purge to delete a project's local state after an incident4.
Keep MCP secrets out of shared files
A team .mcp.json should reference keys as ${API_KEY} rather than contain them; Claude Code expands environment variables in that file, and in a remote server's URL and headers it reads some credential variables as empty so a project's file cannot send your credentials to a server it names7. Personal MCP servers live in ~/.claude.json7, which is one more plaintext file worth knowing about.
What Cursor documents
Cursor's .cursorignore keeps files from Agent, Tab, Inline Edit and @ mention references, and Cursor ignores .env* files, .gitignore entries and a default list automatically8. Two caveats from the same page: "The terminal and MCP server tools used by Agent cannot block access to code governed by .cursorignore", and complete protection "isn't guaranteed"8. Treat it as a convenience that keeps secrets out of context, not as a boundary.
For MCP, Cursor reads .cursor/mcp.json in a project and ~/.cursor/mcp.json globally, supports ${env:NAME} interpolation, and its own recommendations are to use restricted keys with minimal permissions and environment variables for secrets, never hardcoded values9.
Controls that do not depend on the tool
Agent settings can be misconfigured, bypassed by a new tool or simply not deployed on a contractor's laptop. These hold regardless:
- Keep production credentials off developer machines. An agent can only leak what its environment holds. Production keys belong in the production secret manager, injected at runtime.
- Give each developer their own key with a low ceiling. A per-developer key with a spend limit and an expiry turns a leak into a bounded, attributable event. Bounding agent loops covers limits for runaway sessions as well.
- Prefer short-lived credentials. Where the provider supports federation or short-lived tokens, the key in the environment expires in minutes; see short-lived credentials for AI APIs.
- Detect keys in prompts. A key pasted into a prompt, or read from a file into a tool result, can be caught by a pattern check before the request leaves for the provider.
- Scan before push. A pre-commit hook catches the key an agent wrote into code.
How INFRO handles coding agent traffic
INFRO lets Claude Code, Codex and the agent SDKs call models through POST /v1/messages and POST /v1/responses with an INFRO gateway key in place of a provider key; setup is in Claude Code & Codex. What that changes for secrets:
- The developer holds a gateway key, not your provider key. Issue one per developer or per agent, limited to the providers and environment it needs, with a spend limit checked before the provider is called (
402when it cannot cover the request), an expiry, and an optional IP allowlist. Revoking it does not touch your provider account or anyone else's key. - Guardrails inspect agent traffic (Business plan). On
/v1/messagesand/v1/responsesthey check every text block, tool input and tool result; thesecretdetector matches OpenAI, Anthropic, Google, AWS access key id, GitHub, Slack, Stripe and INFRO key formats, JWTs and PEM private keys, and can block, redact or flag. It is pattern-based: it misses formats it does not know, such as AWS secret access keys and passwords. - The audit log records key changes, and the request log shows spend per key, so a leaked developer key shows up as one key's traffic.
The honest limit: a gateway key in a developer's environment can be read by the agent like any other variable. Its value is that it is bounded — by spend limit, expiry, IP range and environment — and revocable without touching the provider. And INFRO governs only traffic that goes through it; a provider key left in the same shell can be used directly against the provider, so remove raw provider keys from developer environments and keep provider-side limits as the backstop.
Summary
Coding agents reach secrets by reading files, inheriting the environment, recording what they saw and writing keys into code. In Claude Code, deny .env reads, enable the sandbox with credential rules, push both through managed settings and shorten transcript retention; in Cursor, rely on .cursorignore for context but not as a boundary. Then make the keys an agent can see cheap to lose: per-developer gateway keys with limits and expiry, short-lived credentials where providers offer them, guardrails on prompts and a scanner before every push.
