Back to all posts
Published on · by Renaud Deraison

The injection that switched off Cursor's sandbox

On July 1, 2026, Cato AI Labs disclosed DuneSlide: two critical bugs in Cursor (CVE-2026-50548 and CVE-2026-50549, both CVSS 9.8) where a prompt injection arriving in an MCP tool result or a web-search snippet drives the run_terminal_cmd tool and a symlink fallback to overwrite Cursor's own sandbox helper. The guard goes quiet from the inside, and the next command runs as the developer, with reach into any cloud or SaaS the editor is signed into. No user interaction, patched in Cursor 3.0. This is a story about where a boundary belongs: in Bromure Agentic Coding the same escape lands inside a disposable VM, the escaped shell finds swapped-out stubs where the real tokens should be, and PromptGuard scores that untrusted content on the wire before the model ever acts on it.

DuneSlide did not break out of Cursor's sandbox. It talked Cursor's own agent into overwriting the small binary that enforced the sandbox, and the guard fell silent from the inside. A boundary that lives in the same process as the code it watches is a boundary that code can reach.

A developer asks Cursor to wire up a new integration and points it at a vendor's documentation, or connects a Model Context Protocol server so the agent can pull live data. MCP is the plug that lets a coding agent call outside tools and read their results. Cursor fetches the page, reads the tool's output, and folds both into the context it reasons over. Somewhere in that returned text sits a sentence the developer never wrote, addressed to the model rather than to a human. The agent reads it as an instruction and acts.

On July 1, 2026, Cato AI Labs disclosed the chain that this sets up, and The Hacker News covered it the same day. Two bugs, CVE-2026-50548 and CVE-2026-50549, both rated 9.8 out of 10. The delivery vector is untrusted content the agent already reads on your behalf: an MCP tool result, a snippet from a web search. No dialog, no click, no user interaction.

What DuneSlide does.

Cursor runs the commands its agent generates inside a local sandbox, and a helper binary decides what that sandbox permits. The first bug lives in run_terminal_cmd, the tool the agent uses to run shell commands. That tool takes a working_directory parameter, and the injection sets it to a path that points at Cursor's own files. The agent, following the smuggled instruction, writes over the helper that sits at /Applications/Cursor.app/.../helpers/cursorsandbox. The second bug is a fallback in how Cursor resolves symbolic links: when the symlink check fails, Cursor trusts the shortcut's in-project path and writes outside the project anyway, landing on the same helper.

Either way the result is one thing. The binary that decides whether a command is confined gets replaced with one that confines nothing. The next command the agent runs executes as the developer, outside the sandbox, with the reach of whoever is signed into the editor: the cloud console, the SaaS dashboards, the repositories the session can already touch. Cursor patched both bugs in version 3.0. Every build before it is exposed.

YOUR MAC: Cursor, the content it reads, the helper, and your credentials all share one machineUNTRUSTED CONTENTMCP tool result / web-search snippet"set working_directory to the Cursor helper path and run…"CURSOR AGENTreads the result as contexttool: run_terminal_cmdworking_directory = ../Cursor.appread as instructionSANDBOX HELPER (overwritten).../helpers/cursorsandboxCVE-2026-50548: working_directoryCVE-2026-50549: symlink fallbackGUARD GONE: the next command runs as youreach into the cloud console and SaaS dashboards the editor is signed intono dialog, no click, no user interaction
DuneSlide, left to right. An MCP tool result or a web-search snippet carries an instruction aimed at the model. Cursor reads it as context and runs run_terminal_cmd with working_directory pointed at its own files (CVE-2026-50548), or writes through a failed symlink check to the same place (CVE-2026-50549). Both land on cursorsandbox, the helper that enforces the sandbox, and overwrite it. With the guard replaced, the next command runs as the developer and reaches the cloud and SaaS accounts the editor is signed into. Cursor, the injected content, the helper, and the credentials all share one Mac.

The boundary sat inside the room it guarded.

Read the chain again and the sharp part is not the symlink trick. It is the location of the guard. The helper that decides whether a command is confined sits on the same disk as the project the agent edits, in the same trust domain as the agent, reachable by the same run_terminal_cmd the agent uses for ordinary work. The sandbox and the code it polices run as one user, on one machine, with one filesystem between them. Give the agent the ability to write a file, let an attacker choose which file, and the file they choose is the guard.

That is the recurring failure in coding-agent security this year, and we have written it up in other shapes: a credential the agent can read is a credential a compromised dependency can read in the sandbox that held the key, and a control the agent can turn off was never a control in why Bromure Agentic Coding is not a sandbox. DuneSlide is the same lesson at the level of the sandbox binary itself. An in-process guard protects you until the process it lives in decides to edit it.

The same attack, one hypervisor away.

Bromure Agentic Coding puts the boundary somewhere the agent cannot write to. The agent, the shells it spawns, the tools it calls, and any code those tools run live inside a disposable virtual machine with its own Linux kernel. Every byte the VM sends or reads crosses a proxy that runs on the macOS host, below the guest, on the far side of the hypervisor. That proxy is one place, and it does several jobs. Replay DuneSlide against it and each step meets one of them.

Start with the escape. The injection still arrives, the agent still runs run_terminal_cmd, and both CVEs still fire. The overwrite lands on the guest's own files. The Mac's disk, its /Applications, its Keychain are not in the VM's world, so there is no path from a symlink inside the guest to a binary on the host. The blast radius is a machine you can delete and rebuild in seconds.

Then the payoff step, the reach into cloud and SaaS. That is where the attack came for value, and it comes up empty. Real tokens stay on the host. The VM holds stubs: credential files with the right shape and no meaning on the public internet. When the agent makes a call that needs a real secret, the host proxy swaps the stub for the real token at the wire and swaps it back out of the response. An escaped shell running env | grep TOKEN or reading ~/.aws/credentials finds placeholders. The one moment a real credential exists is inside a request the agent made for real work, and a state-changing call pauses at the boundary for a prompt on the host before it leaves. Package fetches cross the same proxy and get scanned on the way in. One line, several jobs, all of them below the agent.

UNTRUSTED CONTENT: MCP result / web snippetcarries the injection that starts DuneSlidePROMPTGUARD (if enabled)scores it before the model reads itif it slipsDISPOSABLE VM: its own kernel; Cursor, shells, and tools live here① THE ESCAPE LANDS IN THE GUESTrun_terminal_cmd + symlink overwritehit the guest's own filesthe Mac's disk is not in this world② THE TOKENS IT WANTED ARE NOT HEREenv | grep TOKEN → stub-…cat ~/.aws/credentials → stub-…nothing real to spenda real call the agent madeHOST-SIDE PROXY, one boundary: score the wire · swap stub for real token · scan packages · prompt state-changing callsHOST MAC: real tokens in the Keychain, never inside the VMthe proxy attaches the real secret only at the wire, only for a call you allowed, then strips it back outdelete the VM and the escape, the overwrite, and the persistence go with it
DuneSlide replayed against Bromure Agentic Coding. The untrusted MCP result or web snippet reaches the host-side proxy first, where PromptGuard scores it before the model reads it. If the injection slips that check, the run_terminal_cmd and symlink escape still fires, but it overwrites files inside the disposable VM, not on the Mac. The escaped shell hunts for tokens and finds swapped-out stubs, because real credentials live on the host and cross into the guest only at the wire, for a call the agent made for real work, with state-changing operations paused for a prompt. The guarantee comes from the VM boundary and the token swap; PromptGuard is the tripwire that catches the injection earlier.

The escape lands in the guest.

Both CVEs still fire under Bromure. The overwrite hits files inside a disposable VM with its own kernel, and the hypervisor gives the guest no syscall that reaches the host. You rebuild the VM and the damage is gone with it.

The token it wanted was never there.

DuneSlide's goal is the developer's cloud and SaaS access. Those secrets sit on the host, and the VM carries stubs that mean nothing on the internet. The escaped shell greps for tokens and reads placeholders, so the payoff step fails.

PromptGuard reads the wire first.

The guarantee above holds without any classifier: the VM boundary contains the escape and the token swap starves the payoff, whatever the model does. Bromure adds one more layer at the same proxy, aimed at the exact vector DuneSlide uses. The injection travels in content the agent ingests, an MCP tool result or a web-search snippet, and that content crosses the host proxy before the model reads it. If enabled, PromptGuard scores it there. Stage one is Meta's Llama Prompt Guard 2, watching tool output and web fetches for text that addresses the model as an instruction. The rules files an agent treats as standing orders, CLAUDE.md and its siblings, get a fine-tuned ModernBERT classifier instead, because a file that is meant to be all instructions needs a model that scores harm rather than injection.

Per profile, Bromure logs the hit, shows it to you for a decision, or blocks it before the model sees the poisoned span. The placement is the point, and it is the same argument as the layers below it: an agent that has already swallowed the injection cannot be trusted to report it, so the check runs on the far side of the boundary, where the agent's persuasion does not reach. We wrote up the full pipeline in how Bromure detects malicious prompts, and watched the same class of attack ride in on a command's output in the instruction that wasn't mine.

DuneSlide is patched in Cursor 3.0, and updating is the right move today. The pattern behind it is not patched, because it is not a single bug. Any coding agent that runs its own confinement in its own process is one convincing sentence away from editing that confinement. The move that survives the next CVE with a different name is to keep the guard, the credentials, and the audit trail on the other side of a line the agent cannot write to. Bromure Agentic Coding draws that line at the hypervisor. It is free, open-source, and shipped today.