# AI coding agents and secrets: how keys leak and how to stop it

URL: https://infro.io/blog/ai-coding-agents-secrets-exposure
Author: INFRO Editorial (https://infro.io/blog/editorial)
Publisher: INFRO
Published: 2026-10-21
Last modified: 2026-10-21
Last reviewed: 2026-10-21 (next review due 2026-11-20)
Topic: Agents and multimodal systems
Format: Guide

> Coding agents can read .env files, inherit secrets from the shell environment, store what they read in plaintext transcripts, and commit keys they were shown. Claude Code lets you deny reads of .env with permission rules and block credential files and variables with its OS-level sandbox; Cursor's .cursorignore blocks files from Agent and Tab but not from its terminal or MCP tools. Add environment separation, short-lived or low-limit keys, prompt guardrails that detect API keys, and give agents a gateway key instead of a raw provider key.

Key takeaways:
- Agents reach secrets four ways: reading files such as .env, inheriting the shell's environment, storing what they saw in plaintext transcripts, and writing keys into code or commits.
- Claude Code's Read deny rules cover its file tools and the shell commands it recognizes, such as cat; they do not cover a script that opens the file itself, which is what the OS-level sandbox is for.
- Claude Code writes every tool result to a plaintext transcript; anything an agent reads from .env is on disk until the retention sweep removes it, 30 days by default.
- Cursor's .cursorignore keeps files from Agent, Tab and @ mentions, ignores .env files by default, and cannot block the terminal or MCP tools.
- Give agents a gateway key with a spend limit and expiry instead of a raw provider key, and detect API keys in prompts before they reach the provider.

**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](https://infro.io/blog/mcp-server-credential-leaks); routing agent traffic through a gateway is in [Claude Code and Codex through a gateway](https://infro.io/blog/claude-code-codex-through-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 mode[1]. 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](https://infro.io/blog/ai-supply-chain-attacks-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 transcript[4]. Transcripts are swept after `cleanupPeriodDays`, 30 days by default, while `~/.claude/history.jsonl` keeps every prompt you typed until you delete it[4]. 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 files[5]. 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](https://infro.io/blog/ai-api-keys-github-secret-scanning) is the backstop for that.

## What Claude Code documents

### Deny reads of secret files

Claude Code's own settings example denies `.env` reads[6]:

```bash
{
  "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 itself[1]. A `.claudeignore` file has no effect; move its entries into deny rules[1]. Bare file names match at any depth, so `Read(.env)` covers nested `.env` files under the working directory[1].

### Enforce it at the OS level with the sandbox

For enforcement that covers every process, Claude Code points to its sandbox[1]. 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 unsandboxed[2]. 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 protected[2]:

```bash
{
  "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 sandbox[2]. Claude Code applies none of the `credentials` entries from project or local settings files, so put them in user or managed settings[2]. To strip credentials from all subprocesses regardless of sandboxing, Claude Code documents `CLAUDE_CODE_SUBPROCESS_ENV_SCRUB`[2].

### 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 for[6]. 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 files[6]. 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 incident[4].

### 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 names[7]. Personal MCP servers live in `~/.claude.json`[7], 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 automatically[8]. 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 values[9].

## 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](https://infro.io/blog/bound-ai-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](https://infro.io/blog/short-lived-credentials-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.

> **Note: A five-minute baseline for a team**
> Commit a `.claude/settings.json` that denies `Read(./.env)`, `Read(./.env.*)` and `Read(./secrets/**)`; add `.env*` to `.gitignore` and confirm Cursor's default ignore covers it; move every key in `.mcp.json` to `${VAR}` references; install a pre-commit secret scanner; and replace raw provider keys in developer shells with per-developer gateway keys that have a spend limit and an expiry.

## 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](https://infro.io/docs/coding-agents). 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 (`402` when 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](https://infro.io/docs/features/guardrails) inspect agent traffic** (Business plan). On `/v1/messages` and `/v1/responses` they check every text block, tool input and tool result; the `secret` detector 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](https://infro.io/docs/org/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.

## Questions

### Can Claude Code read my .env file?

Yes, by default: reading files inside the working directory needs no approval. Add Read deny rules such as Read(./.env) and Read(./.env.*) to settings. They block Claude's file tools and shell commands Claude Code recognizes, such as cat; for scripts that open files themselves, turn on the sandbox and deny the path there too.

### Does .cursorignore stop Cursor's agent from reading secrets?

It keeps ignored files from Agent, Tab, Inline Edit and @ mentions, and .env files are on Cursor's default ignore list. Cursor's documentation says the terminal and MCP tools used by Agent cannot be blocked by .cursorignore, and that complete protection is not guaranteed.

### Where does Claude Code store what the agent saw?

In plaintext session transcripts under ~/.claude/projects/, which record every message, tool call and tool result. If a tool reads a .env file, the value is in that file. Transcripts are removed after cleanupPeriodDays, 30 by default; prompt history in ~/.claude/history.jsonl is kept until you delete it.

### Should a coding agent use my provider API key?

Prefer not. Give the agent a key you can afford to lose: a gateway key per developer with a spend limit, an expiry and an IP allowlist, or a short-lived provider credential. A raw provider key in a developer's environment can be read by the agent, any package it installs and anything else on the machine.

## Sources

1. Configure permissions — Claude Code Docs. https://code.claude.com/docs/en/permissions (accessed 2026-10-21)
2. Configure the sandboxed Bash tool — Claude Code Docs. https://code.claude.com/docs/en/sandboxing (accessed 2026-10-21)
3. MAL-2026-4068: Malicious code in @antv/mcp-server-antv (npm) — OSV. https://osv.dev/vulnerability/MAL-2026-4068 (accessed 2026-10-21)
4. Explore the .claude directory — Claude Code Docs. https://code.claude.com/docs/en/claude-directory (accessed 2026-10-21)
5. The State of Secrets Sprawl 2026 — GitGuardian. https://gitguardian.com/state-of-secrets-sprawl-report-2026 (accessed 2026-10-21)
6. Settings files and precedence — Claude Code Docs. https://code.claude.com/docs/en/settings (accessed 2026-10-21)
7. Connect Claude Code to tools via MCP — Claude Code Docs. https://code.claude.com/docs/en/mcp (accessed 2026-10-21)
8. Ignore files — Cursor Docs. https://cursor.com/docs/context/ignore-files (accessed 2026-10-21)
9. Model Context Protocol (MCP) — Cursor Docs. https://cursor.com/docs/context/mcp (accessed 2026-10-21)
