Permission & Sandboxing Design for Coding Agents
How allow/ask/deny permission gating and execution sandboxing are supposed to stop a coding agent from doing damage — and exactly how CVE-2026-21852 (Claude Code) and CVE-2026-22708 (Cursor) got past both.
The short answer
Two Layers, Not One Control
It's tempting to treat "permissions" as a single feature — a list of commands an agent is or isn't allowed to run. In every harness that documents this properly, it's actually two separate mechanisms doing different jobs:
- Permission gating — a decision made before a tool call executes. Every call is evaluated against allow/ask/deny rules in a fixed precedence order, with deny always winning. Read tools (search, list, fetch) typically default to auto-approved; write and side-effecting tools (edit, run a command, call an external API) default to requiring approval.
- Sandboxing — isolation of the environment a tool call actually runs in, independent of whether it was approved. A container or VM boundary means that even a call that should have been denied, or one that does something the allowlist didn't anticipate, still can't reach the host filesystem, network, or credentials it wasn't scoped to.
The reason both exist is that neither is sufficient alone. A permission list without sandboxing is one bug away from full host access. A sandbox without permission gating means every tool call — including ones that should never run unattended — executes silently inside the boundary, which just relocates the blast radius instead of controlling it.
How The Major Harnesses Split The Two Layers
Every harness reviewed for this guide treats isolated execution as a named, first-class component rather than an implementation detail — the split below is drawn from each vendor's own documentation.
| Harness | Permission model | Isolation approach |
|---|---|---|
| Claude Agent SDK | allowed_tools / disallowed_tools plus a permission_mode (default, acceptEdits, plan, dontAsk, auto, bypassPermissions), deny always winning | Caller-supplied sandbox around Bash/file tools |
| Codex (OpenAI) | Config/auth-managed approval per surface (CLI, web, IDE, macOS) | Sandboxed tool execution built into the shared Rust core, stateless requests for zero data retention |
| Cursor Agent | Per-model-tuned instructions and a command allowlist for Auto-Run | Terminal execution tuned per underlying model; no fixed tool-call cap |
| Microsoft Agent Framework | Built-in providers gating file access, todo, and skill scope | Foundry Hosted Agents: dedicated VM-isolated sandbox per session |
| OpenHands | Explicit security/confirmation component as a first-class architectural piece | Isolated Docker workspace with resource and filesystem limits per agent |
Case Study: CVE-2026-21852 — A Trust Prompt Bypassed Before It Rendered
Claude Code, patched in 2.0.65
ANTHROPIC_BASE_URL). The harness loaded that setting and issued requests to it — leaking the user's API key — before the trust confirmation prompt was shown. The trust prompt exists precisely to stop an untrusted repo from taking action on first open; the vulnerable ordering let a network call fire ahead of the one checkpoint that was supposed to gate everything else.The failure here isn't a missing permission rule — it's a sequencing bug. The permission system correctly required trust confirmation before most repo-supplied behavior could run, but a specific class of config (network-endpoint settings) was evaluated during settings load, which happened earlier in the startup sequence than the trust prompt itself. The gate existed; it just wasn't positioned early enough to cover everything that happened before it.
Case Study: CVE-2026-22708 — An Allowlist That Didn't Cover Shell Built-Ins
Cursor, fixed in 2.3
export and typeset aren't external commands, so they weren't evaluated the same way. A prompt-injected instruction (delivered through untrusted content the agent had ingested) used those built-ins to poison the shell environment and reach remote code execution, without ever triggering an approval prompt.The same root pattern as the Claude Code case, from a different angle: the allowlist was complete against the surface it was designed to check — named external commands — but shell built-ins are a category the allowlist logic never modeled as write-class actions at all. An attacker didn't need to defeat the allowlist; they needed to act somewhere the allowlist wasn't looking.
Prompt Injection Is The Delivery Mechanism For Both Classes
Neither CVE required the user to type a malicious command. Both were reachable through content the agent ingested as part of its normal job — a repository's own settings file, or text containing an injected instruction. Indirect prompt injection (untrusted content in a PR description, a repo file, or a web page reaching the model with tool-execution ability still attached) is rated high-likelihood, high-impact in aggregated 2026 CVE tracking, with Claude Code, AutoGPT, Dify, and Roo-Code all ranking among the repos with the most security advisories in this class. The permission and sandboxing layers exist specifically to contain what an agent does after it has ingested content you didn't fully control — which is most of what a coding agent reads by design.
What An Adversarial Audit Actually Checks
The common failure mode across both CVEs is the same: a permission boundary tested against its documented surface, not against everything adjacent to it. An audit needs to go looking for the adjacent surface deliberately:
- Attempt to trigger a side-effecting action (network call, file write, command execution) without ever tripping the approval or trust-confirmation path — not just verify the happy-path prompt appears.
- Check whether any config or settings value is read and acted on before the trust/approval checkpoint that is supposed to gate the rest of the session.
- Treat environment-variable-setting commands and shell built-ins as write-class actions in any custom allowlist, not as inert no-ops exempt from review.
- Confirm isolation is actually backing the allowlist — container or VM boundaries around anything that runs arbitrary commands — so a gap in the list doesn't translate directly into host access.
- Verify the harness version in use is patched against known CVEs in this class before assuming the design is sound; both fixes here were version-gated, not architecture-gated.
For the full harness architecture this permission/sandbox layer sits inside — the execution loop, context compaction, and verification steps around it — see Agent Harness Design for Production. The core term is defined in the glossary as Agent Harness.
Not sure your allowlist covers what it thinks it covers?
Both CVEs in this guide passed as complete against their own documented surface. An adversarial audit is the only way to find the gap before it's found for you.