Skip to content

Permissions, sandboxing & approval modes

Every CLI in scope has some way of deciding three things, on every action the agent attempts: do it without asking, ask me first, or refuse outright. The shapes are wildly different:

  • Claude Code: rule-based allow/ask/deny per tool, layered with managed policy.
  • Codex: OS-level sandbox tiers combined with approval policies.
  • opencode: per-tool allow/ask/deny, scoped per primary agent.
  • Cursor: auto-run modes (Always ask / Auto-run in sandbox / Always run everything) plus command allow/denylists; OS-level Agent Sandbox on macOS.
  • Copilot: per-call tool approval in VS Code Chat, with chat.permissions.default and per-category auto-approve knobs; optional shell sandbox; org-wide content exclusions.

Same job — decide what the agent can do unattended — different mental models, different vocabulary, different layers.

You’re two settings into the agent’s lifecycle and you’ve already hit the wall. Either it pops up a confirmation dialog on every single command — and you spend the session pressing y, y, y until you stop reading what you’re approving — or you’ve turned approvals off and now there’s nothing between the agent and rm -rf node_modules in the wrong directory.

There’s a real range of actions the agent does in a session: reading a file (boring, always fine), running tests (boring, always fine), editing your code (usually fine), running a one-off shell command (depends on the command), pushing to remote (you definitely want to know), chmod-ing system files (probably not). Treating all of those as one “do you approve?” question is what produces both fatigue and accidents.

That’s the gap permissions close. Decide once, declaratively: which tools are always fine, which always need you in the loop, which are flatly forbidden. Then the agent runs unattended through the boring 80% and only interrupts you for the genuinely risky 20% — and the truly dangerous things just can’t happen, even if the agent decides they’re a good idea.

Real-world examples of permission rules people actually set:

  • Always allow — reads (Read, Grep, Glob), test runs (pnpm test, pytest), formatters (prettier, ruff), git status/log/diff.
  • Ask first — any git push, any git rebase, deletions, anything touching node_modules or package-lock.json, npm install/pip install.
  • Always denyrm -rf, edits to .env or secrets, writes outside the project root, sudo, chmod, pushing to main directly.
  • Scoped allow patternsBash(npm run *) allowed, Bash(npm install *) asked. Same tool, different command shapes.
  • Per-agent profiles (opencode) — your plan agent is read-only no matter what; your build agent has full edit access; a custom security-review agent has read + a narrow shell allowlist.
  • Org-wide policy — managed config forbids any tool that can touch prod for everyone in the team, regardless of what they set locally.

The test: if you’ve ever caught yourself approving a command without actually reading it, your permission rules are too tight. If you’ve ever discovered the agent did something you wouldn’t have approved, they’re too loose.

You want to…Reach forNot
Decide broadly what the agent can do unattendedPermissionsHooks
Block one specific command pattern with logic (regex against args, conditional logic)Hook on PreToolUsePermissions alone
Take the agent fully read-only for one sessionPlan modeToggling every permission
Restrict a worker subagent’s toolsSubagent tools: fieldSession-wide permissions
Enforce a rule across the whole orgManaged/policy configPer-user settings

Permissions are coarse and declarative — “Bash is ask, Read is allow.” Hooks are fine and procedural — “if the bash command matches git push.*main, block it and log it.” Use permissions for the policy; reach for hooks when the policy needs logic.

Permission rules live in settings.json (project, user, or managed). Each rule targets a tool with allow / ask / deny:

{
"permissions": {
"allow": ["Bash(npm run test:*)", "Read(src/**)"],
"deny": ["Bash(rm:*)", "Edit(.env)"],
"ask": ["Bash(*)"]
}
}

Layering: managed policy beats user beats project. An org admin can deny tool patterns globally; project settings can broaden within those limits.

Plan mode (Shift-Tab) gates all mutations as a posture — see Plan mode.

Claude Code does not impose OS-level process sandboxing; permissions are enforced at the tool-dispatch layer.

AspectClaude CodeCodexopencodeCursorCopilot
Primary axisAllow/ask/deny per toolSandbox tier × approval policyPer-tool per-agentAuto-run mode + allow/denylistPer-call approval + auto-approve knobs
OS-level sandboxNoYes (read-only / workspace-write / danger-full-access)No (wrap in a container if you need it)Yes (Agent Sandbox, macOS; other OSes unverified)Optional (chat.agent.sandbox.enabled, macOS/Linux)
Approval modesask rulesuntrusted / on-request / neverallow / ask / denyAlways ask / Auto-run in sandbox / Always run everythingdefault / autoApprove / autopilot
Org/managed overrideYes (managed settings.json)Yes (managed config)Yes (managed config)Yes (Team / Enterprise)Yes (Business / Enterprise; content exclusions, MCP policy)
Evaluation orderDeny > Ask > AllowSandbox blocks first, then approval gateLast match winsSandbox first, then allow/denylist (with caveats)Per-call gate unless auto-approve set
Per-agent profileNo (single profile per session)No (single mode per session)Yes (per primary agent)
“YOLO mode”Allow Bash(*) + Edit(*) etc.danger-full-access + neverAll tools allow”Always run everything”chat.tools.global.autoApprove
ToolHow
Claude CodeAdd allow rules for the tool pattern, or move Bash(...) out of ask.
Codex--ask-for-approval never (and pick a sandbox you trust).
opencodeSet the relevant tool to allow on the active primary agent.
CursorSwitch auto-run mode to “Auto-run in sandbox” (or “Always run everything,” with the usual warning).
CopilotSet chat.permissions.default to autoApprove, or flip specific chat.tools.*.autoApprove keys.
  • “Sandbox” is a real OS-level isolation tier in Codex; in Claude Code and opencode the word is used loosely (or refers to Docker).
  • “Approval” in Codex is the frequency policy (ask/never); in Claude Code it’s the result of an ask rule. Same word, different layer.
  • Codex’s on-failure approval mode is deprecated — don’t recommend it.