The Cursor control model, explained
Cursor’s control surface went through more churn than any other major harness’s in 2026: yolo mode became Auto-Run became Run Modes; two modes were deleted in May; an LLM classifier became the recommended default a week later. The current model is coherent once you see its shape — an allowlist, a sandbox, and a classifier, stacked — but a lot of published advice describes versions that no longer exist. This page is the current reference, 3.x era.
This page covers Cursor’s own controls. For wiring ACP into Cursor, see the install guide.
The defaults, before any configuration
Worth stating plainly because it surprises people: out of the box, terminal commands need approval — but workspace file edits don’t. Edits save to disk immediately; your undo is version control. Reads and searches are free; MCP tool calls prompt individually unless allowlisted; the agent can’t make arbitrary network requests (a few providers are permitted). Three protections always prompt regardless of mode: browser actions, file deletion, and writes outside the workspace. A short list of paths is deny-write under the sandbox — .git/config, .git/hooks, .vscode, .cursorignore — the harness protecting its own steering files.
Run Modes: the three-layer stack
Since 3.6 (May 2026), the permission posture is a Run Mode:
- Auto-review (recommended default). A pipeline: allowlisted calls run; other shell commands run sandboxed where possible; whatever’s left goes to an LLM classifier (a small Cursor-managed model) that returns allow, try-something-different, or ask-the-human. It covers shell, MCP, and fetch.
- Allowlist: listed actions run, everything else prompts. No classifier; sandbox optional.
- Run Everything: everything auto-runs — no sandbox, no classifier, no prompts. The escape hatch as a mode.
Two modes were removed in 3.5 — “Ask Every Time” (now: Allowlist with an empty list) and “Run in Sandbox” (folded into Allowlist). Stale advice frequently references both.
The configuration is genuinely novel, for better and worse: permissions.json (~/.cursor/ and per-project, with a team dashboard layer above both) takes allow_instructions and block_instructions as natural-language sentences, not command patterns. Flexible, readable — and probabilistic at the exact point most systems are deterministic. Cursor’s docs say the quiet part out loud, verbatim: “The allowlist is best-effort, not a security boundary. Determined agents or prompt injection might bypass it.”
That candor is earned by history — three published CVEs of allowlist bypass: command substitution (CVE-2025-54131), environment-variable poisoning and shell built-ins (CVE-2026-22708), and web-content prompt injection chained into auto-execution (CVE-2026-31854). All patched; the class is the lesson, and it’s the same lesson every string-matching permission system teaches.
One more line to internalize: .cursor/rules files steer the model’s behavior — they are not enforcement. Rules are prompts; Run Modes, permissions.json, sandbox.json, and hooks are the controls.
The sandbox
Real OS-level enforcement: Seatbelt on macOS, Landlock + seccomp on Linux (kernel 6.2+), and on Windows, the Linux sandbox inside WSL2 — no native Windows boundary. Bounds: workspace read/write, network blocked by default, /tmp writable, the steering-file deny-writes above. sandbox.json (user and project, project wins, admin layer above) extends readable/writable paths and network domains; the default network mode adds the common package registries. Unsandboxable commands — outside-workspace writes, privileged operations, network needs — escalate to the classifier or the human rather than silently running unconfined.
The hooks
A rich event set, current era: preToolUse/postToolUse, beforeShellExecution/afterShellExecution, beforeMCPExecution, beforeReadFile/afterFileEdit, subagent and session lifecycle, prompt-submit. Four config layers (enterprise MDM → team cloud → project → user); all matching hooks run, higher layer wins conflicts. Output can return permission: allow | deny | ask — with a documented asymmetry: ask is fully honored only for beforeShellExecution and beforeMCPExecution; on preToolUse it’s accepted by the schema but not enforced. Exit code 2 blocks, Claude Code-style — the hook-contract convergence again.
The default worth designing around: failClosed defaults to false. A crashed or slow hook fails open unless you say otherwise, per hook. If a hook is your enforcement, set it — and know what you’ve chosen when the hook’s backend is unreachable.
The record
Stale-fact flag, because we’ve repeated the older version ourselves: it isn’t that Cursor’s audit log is enterprise-gated — no native per-tool-call audit log exists on any tier. The enterprise dashboard is usage and spend analytics. The docs’ own suggested path to an audit trail is building one with hooks. For a harness whose default mode includes a classifier making allow decisions, that means the decisions with the least explanation also have the least record.
Cloud agents and the empty chair
Cursor’s cloud agents (formerly background agents) are the purest empty-chair case in the ecosystem: they don’t use Run Modes — they auto-run everything, inside an isolated VM with egress allowlists, delivering a branch and draft PR. That’s a defensible design — blast radius contained by the VM, output contained by review-before-merge — but be clear about what it is: per-action policy and per-action record are both absent; the control is the box and the PR review. The CLI carries the same posture into scripts: -p (print mode) gets “access to all tools, including write and shell,” and --force (alias: --yolo) auto-allows anything not explicitly denied. The empty-chair test is the checklist to run before either.
Where the native model ends
Cursor’s stack — allowlist, then sandbox, then classifier — is a reasonable answer to approval fatigue, and the sandbox work is solid. The structural edges:
- Probabilistic at the boundary. Natural-language permission instructions and a classifier verdict are judgment, not rules; Cursor says so itself. Anything that must hold needs the sandbox, a deny, or a fail-closed hook.
- No record at all. Uniquely among the big three, there is no native per-action audit log — the classifier’s allows vanish into the session.
- Config sprawl. permissions.json, sandbox.json, hooks.json, rules — four files with different semantics (two enforcing, one advisory, one mixed), per user and per project, per machine.
- The unattended tier skips policy entirely. Cloud agents trade per-action control for VM walls.
The composition: keep the sandbox and the classifier for what they’re good at — fatigue and containment — and put deterministic policy plus the missing record on the hook seam, failClosed: true, with our integration carrying the same workspace rules here as on every other harness, and writing the per-action ledger Cursor doesn’t have.