
CVE-2026-22708: The Cursor Allowlist Bypass
Day 17 made the case that coding agents are the sharpest target in the series. Today we prove it with a real one. CVE-2026-22708 is a disclosed vulnerability against Cursor, the AI coding tool, and it is the cleanest public example of a theme we have hit since Day 2: the control meant to constrain an agent becoming the mechanism that compromises it. We walk the class from the inside so you understand why it worked, not so you can rebuild it.
A boundary first, same as the other case studies. This is a real flaw in a real, widely used product. This post explains the mechanism and the lesson at the conceptual level, drawn from public disclosure, so you can recognize the pattern and defend against it. It is not a step by step weaponization guide, and the issue has been addressed in the product. Treat the understanding as the artifact. If you need exact technical specifics for defensive work, go to the vendor advisory and the primary disclosure.
The setup: why allowlists exist
A coding agent can run commands. That is dangerous, so products add a guardrail: an allowlist of commands the agent may run without asking, and everything else requires explicit human approval. The intent is reasonable. Let the safe, routine commands flow so the developer is not clicking approve forty times an hour, and gate the dangerous ones behind a human.
Common entries on such an allowlist are the everyday git and build commands, the ones a developer sees constantly and trusts without thinking. git status. git branch. The reasoning is that these are read mostly, low risk, and high frequency, so auto approving them is a good speed and safety trade.
Hold onto that reasoning, because the attack turns it inside out.
The flaw in one sentence
The allowlist trusted the name of the command, while the attacker controlled what the command actually did when it ran.
That is the whole thing. The guardrail checked is this command on the safe list and answered yes for an allowlisted name. But the behavior of a command is not fixed by its name. It depends on the environment it runs in, the arguments it receives, and what the things it invokes resolve to. If an attacker can influence any of that, an allowlisted name can carry an un allowlisted action.
Per the disclosure framing we cited on Day 11: an attacker could poison the agent’s execution environment so that allowlisted commands like git branch deliver arbitrary payloads, and the allowlist made the attack easier by auto approving the very commands the attacker needed. The control did not just fail to stop the attack. It accelerated it, because the auto approval removed the human from exactly the step the attacker used.
Why this is the perfect illustration of the series thesis
Every week we have returned to one idea: you cannot win at the detection layer, you win by constraining consequence. CVE-2026-22708 is that idea as a single CVE.
Think about what the allowlist was. It was a consequence side control, an attempt to limit what the agent could do. Good instinct. But it was implemented at the wrong granularity. It constrained which command name runs, not what that command is able to do. Granularity is everything in a consequence control. Constrain too coarsely and you have constrained nothing, because the attacker operates inside the unit you allowed.
This is the lesson that generalizes far past Cursor. A consequence control is only as good as the precision of what it actually restricts. An allowlist of command names restricts names. An allowlist of destinations restricts destinations. If the harmful action can be expressed within an allowed name, or routed to an allowed destination, the control is theatre. The Day 11 fix worked because it bound authority to a specific value the attacker did not control, the ticket owner. This CVE failed because it bound trust to a value the attacker could influence, the command name’s resolved behavior.
The chain, conceptually
Place it on the loop from Day 2, without operational detail.
- Delivery, ingest stage. Attacker text reaches the agent through one of the Day 15 vectors, in a coding agent most naturally through repository content, a dependency, or a PR or issue the agent reads.
- Influence the environment, assemble and execute stages. The injected instruction gets the agent to arrange the execution environment such that a later allowlisted command behaves differently than its name implies.
- Trigger via an allowed name, execute stage. The agent runs a command that is on the allowlist. It is auto approved precisely because it is trusted. The human is never consulted.
- Arbitrary execution, the payoff. The allowlisted command, in the poisoned environment, performs the attacker’s action. Because this is a coding agent, the payoff is code execution on the developer machine, the execution multiplier from Day 17.
A related disclosed Cursor issue sharpens the same point: an indirect prompt injection caused the agent to write a malicious configuration file without user approval, achieving remote code execution through that config. Same shape, different allowed action. The agent used a permitted capability, writing a project file, to establish an un permitted outcome.
What made it reliable
Two properties, both instructive for defenders.
The allowed commands were high frequency. Because git commands run constantly, using one as the trigger did not look anomalous. The malicious execution hid inside normal, expected activity, the same camouflage principle as postmark-mcp hiding exfiltration in an email tool’s normal traffic.
The human was trained out of the loop on exactly these. The allowlist existed to stop the developer approving routine commands. So for the precise commands the attacker chose, there was, by design, no human check. The control created the blind spot the attacker aimed at.
That second property is the uncomfortable one. The guardrail did not merely have a gap. The guardrail defined the gap, by declaring a set of actions as not needing oversight, and the attacker simply operated there.
Reproduce the principle safely in your lab
Do not reproduce the CVE. Reproduce the lesson, which is what matters, in your Day 17 toy coding agent.
- Give your sandboxed agent an allowlist: a set of command names it can run without asking, everything else prompts you.
- Put a normally safe command on the allowlist, something read like.
- Now, in your sandbox, demonstrate the granularity gap conceptually: show that the behavior of that allowed name depends on the