Founder pricing: Pro at $9.99/mo forever for the first 50 members. 18 of 50 left Claim yours →
All posts

Secrets management for AI coding agents: make the leak impossible, not unlikely

The SanuDesk team 6 min read
security secrets ai agents

The pull request looked like a formatting cleanup: three files, a title about tab width. In CI, Anthropic's own Claude Code Security Review action picked it up, read that title as an instruction the way it reads everything else, and did what it said. It opened /proc/self/environ, found ANTHROPIC_API_KEY, trimmed off the first few characters so neither Claude's own filters nor GitHub's secret scanning would recognize the shape, and posted the rest back as a PR comment.

Nobody pasted a key into a chat window. Nobody committed a .env file by mistake. An agent read a file it was told to read, then wrote to a comment it was told to write to, and a credential crossed the fence in between. The agent did not misbehave. It did exactly what an agent does with an instruction and a file, and neither one was marked as a secret.

Why AI coding agents leak secrets twice as often

GitGuardian's 2026 secrets sprawl report put a number on the feeling. Across 28.65 million secrets added to public GitHub in 2025, commits assisted by Claude Code leaked at a 3.2% rate, against a 1.5% baseline for public GitHub as a whole: more than double.

The report doesn't read this as the tool being broken. Part of it is size: agent-assisted commits run roughly twice the lines of a human one, twice the surface for a stray key to hide in. The rest is judgment. A developer pauses before pasting a production key into a debug script; an agent doesn't, because nothing marks that string as different from any other line of config. To a model, an API key and a port number are the same kind of token: text that configures something.

MCP configs carry the same risk at a wider scale. A Model Context Protocol config file often holds the credentials a session needs for every tool it's wired to. GitGuardian found 24,008 of those files exposed on public GitHub, 2,117 still holding a valid credential: one file, and the leak is every service it pointed at.

The three ways a secret gets out

Almost every incident traces back to one of three paths, and only one of them involves anything resembling an attack.

Committed straight into the repo. A stray .env swept in by a broad git add -A, or a fixture that kept a real key because a fake one didn't reproduce the bug. .gitignore stops a file being added by accident; a sweep command adds it on purpose, credentials included.

Echoed while debugging. An agent stuck on a failing request reaches for echo $STRIPE_KEY or cat .env to sanity check a value, exactly like you would, and now the key sits in a terminal transcript, a CI log, or a screenshot pasted into a review comment. Nothing was committed. The secret still left the machine.

Exfiltrated through an instruction the agent trusted. The opening story is the sharpest version: a prompt injection hidden in content the agent reads, paired with a tool reaching an environment variable and a channel to carry the answer out. Anthropic's fix was concrete: that action's Read tool now rejects files under /proc/ outright, shipped in Claude Code 2.1.128. The pattern itself, untrusted input, a live credential, an outbound channel, isn't specific to that one action.

Design the leak out instead of scanning for it

A scanner that catches a secret after the commit is a seatbelt. The fix that matters changes what's true before the agent ever opens the file.

Scan before the commit lands, not after. A pre-commit hook running gitleaks, trufflehog or git-secrets blocks a matching pattern before it enters history at all. This catches the accident, not a targeted exfiltration, but most leaks are the accident.

Scope every credential to the one job it does. A token that can only read a staging bucket is a bounded incident if it leaks. A master key that can do anything is a full compromise. Generate the narrow one, even when the broad one is one click faster to set up.

Keep the secret out of any file the agent can open. Inject it at runtime from a secrets manager or your shell instead of a .env on disk. A sandboxed process that scrubs its environment before it starts beats careful prompting every time, for the same reason a guardrail beats an instruction everywhere else: a file the agent cannot open is a control, a file it's asked not to open is a preference.

Never combine all three in one workflow. Untrusted input, a live credential, and an outbound channel together are what turned the opening story into an incident. Remove any one and the path closes.

Where secrets management goes wrong, and what it really costs

A secrets manager becomes the new single point of failure. Centralizing every credential behind one service is correct right up until that service has an outage or a botched rotation, and every agent session depending on it stalls mid-task with a generic auth error.

Rotation breaks things quietly. Rotate a leaked key and every unattended loop still holding the old value fails on its next run, often overnight, with nobody watching. The fix has a blast radius too, smaller than the leak, but real.

Scanners only find the shape they know. Gitleaks and its peers match known formats: AWS keys, GitHub tokens, Stripe keys. An internal token or a signed JWT for your own service often matches nothing, and a clean scan gets read as proof instead of as a boundary.

A clean scan is not a fix, the same way a green test suite is not proof of one. Treat it as a layer, not the whole answer.

A workspace where the blast radius is a folder, not a laptop

SanuDesk is a desktop app built around exactly that boundary. Your Claude Code, Codex and Gemini sessions tile into one grid, each scoped to a single project folder, so a credential a session can reach is whatever that folder holds, not your whole laptop. It's bring-your-own-model by design: your provider keys stay with you instead of routing through a relay that becomes one more thing to trust. A git pane beside every session shows the live diff as it forms, so a stray key in a tracked file is something you see before it's committed, not reconstructed from a transcript afterward. Work arrives through a Kanban board: deploy a card and it opens a fresh, scoped session, and the result parks in a review lane instead of merging itself. A nightly scan over the repo becomes a Loop, journaled per run instead of forgotten in a terminal tab. The grid and board are in the free plan (see pricing).

Run the scan you have been assuming would catch this

Pick one repo tonight and scan its full history, not just the working tree: gitleaks detect --source . --log-opts="--all", or the trufflehog equivalent. Read every hit, including the ones your instinct says are fine.

Zero hits doesn't mean clean. It means nothing matched a known shape. Any hit, even an old key you're sure was rotated, tells you the sweep already happened once, and the only real question left is whether the value was still live when it did.

Download SanuDesk free to keep every agent's reach scoped to a project folder, or see how the grid, the board and Loops fit together on the features page.

Run your own fleet of agents

SanuDesk is a free desktop app that tiles your Claude Code and Codex sessions and gives every project a Kanban board your agents work from.