The command your agent approved is not the command that ran
Tomás Vega
Every command your coding agent runs is a stranger you agreed to execute with your credentials. (Sit with that for a second.) Docker walked through a fresh horror story on that theme today, and its target is the small comfort blanket most teams have wrapped around their agents: a list of commands the model can run without asking. The list works exactly as designed. That is the problem.
The post, by Ajeet Singh Raina on the Docker blog, is the fifth installment in the company's series about AI coding agents going sideways. This one sits on top of research from Pillar Security, disclosed as CVE-2026-22708 against Cursor. Cursor patched the specific bypass in version 2.3 and its documentation now describes the allowlist as best-effort. The trick underneath the CVE, though, is older than the CVE and older than the current wave of agents. Docker's argument is that the allowlist as a category deserves retirement.
The whole attack, in two commands
An agent reads a file it was asked to read. A README, a dependency, an issue comment, anything that gets text in front of the model. Buried in that text is an instruction meant for the agent instead of for you. The agent obliges and runs a shell built-in you never see. Pillar names three specifically: export, typeset, declare. Built-ins do not live on disk as programs, and an allowlist that scans for programs on disk waves them through without surfacing them at all.
The built-in sets an environment variable. PAGER, PYTHONWARNINGS, BROWSER, PERL5OPT, pick whichever your next binary is going to read at startup.
Then the agent runs a command you actually approve, or one that was already on the allowlist because obviously it was. git branch. python3 script.py. The binary starts up, reads its environment, finds the attacker's payload sitting inside it, and executes that instead.
No memory bug. No privilege escalation. Nothing in any log looks the slightest bit out of place. The developer sees an accurate approval prompt for a harmless command, says yes, and gets arbitrary code execution because a minute earlier something they were never shown quietly rearranged the furniture.
Is the primitive new? No. Pillar's write-up cites 2020 research from Elttam that spelled out how environment variables turn into code execution. It sat on the shelf for six years because pulling it off used to require sitting at somebody's keyboard, running several steps in the right order, and anyone with that much access had faster ways to cause damage. Coding agents removed every one of those obstacles at the same time.
What Docker's containment actually buys you
The pitch is Docker Sandboxes: run the agent inside a microVM with its own kernel, its own filesystem, and a deny-by-default network. Inside the box the agent can run whatever it wants, sudo included, which is exactly what Pillar recommended when they argued the industry should stop trying to allowlist individual commands. There is no list to slip past, because there is no list.
Run the attack again inside a sandbox and watch the containment show its edges. The injection still lands. The environment still flips. git branch still runs the payload. The payload then goes hunting for ~/.ssh/id_rsa and does not find one, because your home directory sits on the other side of the boundary. The persistence trick that appends to ~/.zshrc fails the same way: a poisoned copy written inside the box disappears with the box.
Good. That is real containment.
Now the honest part, from Docker's own write-up. Sandboxes forwards an SSH agent socket into the box so that ordinary work like git push keeps functioning, which means code inside the box can ask that agent to authenticate for as long as the sandbox is alive. Egress leaves through an HTTP proxy on your host that checks every request against rules you wrote, which is only as tight as those rules. The blast radius shrinks. It does not become zero. Sandboxing the process is not the same as sandboxing the outcome.
The check reads the surface; the meaning moved
The real lesson has nothing to do with shell built-ins in particular. Any check that reads only the surface of a command is telling you a story about a command it can no longer describe, because something upstream rewrote the meaning of that command out of frame. Every allowlist has this shape. Every approval prompt does too, whenever the thing you are approving reads state that something else can write to.
Which is why the Cursor patch, useful as it is, closes one path. Cursor's fix requires approval for anything the parser cannot classify, and to the company's credit its docs now say so plainly. The general case is still open.
If you are running a coding agent in your pipeline today, three things worth writing down. The allowlist is a UX feature, not a security control. Your agent runs as you, with your keys and your git remotes and your session tokens, and every readable file within reach is an instruction it might follow. The trust boundary that matters is the one around the agent process, not the one around the individual command it is about to run.
Approve the command. The environment already changed.
Source: Docker (docker.com)