Skip to content

Permissions, sandboxes and approval modes across Claude Code, Codex and Cursor

Permissions and sandboxes in Claude Code, Codex and Cursor are three separate controls: an approval layer that decides whether an action runs, an OS sandbox that limits what a shell command can read and write, and a network allowlist that limits where it connects. An unattended run is safe only when all three are set, not the approval layer alone.

Your agent asked for approval forty times in one refactor, so someone added the bypass flag “just for today”. Two weeks later that flag sits in the CI job that triages public issues, the runner holds an npm publish token, and nobody can say which boundary would stop an injected curl. This page is for the developer configuring a session, the tech lead who owns shared settings, and the CTO who signs off on what unattended agents may reach. It is step 4 of the developer track, after repository context, because every later step, from worktrees to the issue-to-PR pipeline, assumes these boundaries exist.

  • A matrix of approval, sandbox, network and credential controls in Claude Code v2.1.283, Codex CLI 0.157.1 and Cursor.
  • A posture table per risk class and per run type (interactive, background, CI).
  • Configuration you can commit for all three tools, including a Codex GitHub Actions job.
  • A canary test that proves the boundary holds, instead of trusting a settings file.
  • Three copy-paste prompts and failure modes with recovery steps.

What are the three layers every agent run needs?

Section titled “What are the three layers every agent run needs?”

The vendors use different words for the same three questions. A strong answer to one does not cover the others.

LayerThe question it answersClaude CodeCodexCursor
ApprovalDoes this action run, and who says yes?Permission modes and allow/ask/deny rulesApproval policy and auto-reviewRun modes and Auto-review
OS sandboxWhat can a running command read and write?Sandboxed Bash toolSandbox or permission profileSandbox for shell commands
Network egressWhere can a running command connect?Sandbox proxy with a domain allowlistProfile network settingNot verified (see below)

The approval layer judges the command string before it runs. The sandbox is enforced by the operating system on the running process. Anthropic’s sandboxing guide puts the difference plainly: the sandbox boundary “holds regardless of what the model chose to run and even if an allowed command does more than its name suggests”. A deny rule on Bash(curl *) stops curl, not python -c opening a socket; the sandbox stops both.

Outside all three sits the outer boundary: the laptop, container, VM or cloud environment the agent runs in, and its credentials. Choosing it is covered in ephemeral environments and the tool catalogue in agent sandboxes compared.

How do Claude Code, Codex and Cursor compare on permissions and sandboxing?

Section titled “How do Claude Code, Codex and Cursor compare on permissions and sandboxing?”

Checked on 2026-09-26 against claude --help 2.1.283 and Anthropic’s permission, sandboxing and cloud-environment docs; codex --help 0.157.1, the rust-v0.157.1 source and the openai/codex-action README; and the @cursor/sdk 1.0.32 package. Cursor’s documentation site was unreachable from our environment on that date, so the Cursor column holds only what the SDK package or the 2026-08-28 docs check confirms.

ControlClaude Code 2.1.283Codex CLI 0.157.1Cursor
Approval modesdefault (shown as Manual), acceptEdits, plan, auto, dontAsk, bypassPermissions; Shift+Tab cyclesapproval_policy: untrusted, on-request (default), granular, never. The -a flag accepts only on-request and neverA Run modes page under Agent security; reported as Auto-review (default), Allowlist and Run Everything (unverified, 2026-09-26)
A model approves instead of youAuto mode: a classifier (Claude Sonnet 5 by default) reviews actions. It is the starting mode for interactive sessions from v2.1.283 (latest channel)Auto-review: approvals_reviewer = "auto_review" in config, or --approve-for-meAuto-review (SDK option local.autoReview)
Command rulespermissions.allow, ask, deny, evaluated deny, then ask, then allow.rules files (experimental) decide which commands may run outside the sandboxHooks: JSON over stdio that “can observe, block, or modify behavior”
OS sandboxBash, PowerShell and Monitor commands. macOS Seatbelt, Linux and WSL2 bubblewrap; native Windows not supportedEvery model-generated shell command. macOS Seatbelt, Linux and WSL2 bubblewrap; WSL1 rejectedSDK local.sandboxOptions; /sandbox in the CLI command list
Default write boundaryWorking directory, a per-user temp directory, and directories added with --add-dir:workspace profile writes the workspace; :read-only writes nothingNot verified
Default networkNo domains pre-allowed; first use of a host prompts, or goes to the classifier in auto mode:workspace “does not grant network access” (codex-action README; confirmed locally, see the canary below)Not verified
Egress allowlistsandbox.network.allowedDomains, deniedDomains, strictAllowlist, managed allowManagedDomainsOnlyNamed [permissions] profiles carry per-domain allow and deny (beta); legacy network_access is all or nothingNot verified
Secrets in the environmentsandbox.credentials: deny or mask for files and variables; nothing is protected by defaultshell_environment_policy: ignore_default_excludes defaults to true, so *KEY*, *SECRET*, *TOKEN* variables reach commands unless you set it to falseNot verified
Full bypass--dangerously-skip-permissions; refused under root or sudo outside a recognised sandbox--dangerously-bypass-approvals-and-sandboxNot verified
Admin lockManaged settings: disableBypassPermissionsMode, disableAutoMode, sandbox.failIfUnavailable, allowManagedDomainsOnlyrequirements.toml: allowed_approval_policies, allowed_approvals_reviewers, allowed_permission_profiles, experimental_network, rules (checked against config_requirements.rs at rust-v0.157.1)An mdm settings layer (SDK settingSources)

Notes: the Claude Code sandbox covers shell commands only; the Read, Edit and Write tools obey permission rules, not the sandbox. Codex permission profiles (beta, from 0.138.0) and legacy --sandbox values “do not compose”, so pick one per configuration.

Which posture fits each risk class and run type?

Section titled “Which posture fits each risk class and run type?”

A risk class belongs to a change, not a team. A run gets the posture of the highest class it can touch: a “docs only” task in a repository that holds billing code runs with the billing posture unless the sandbox denies it those paths. The tech lead owns this table per repository; the CTO or security owner signs off on the critical row and every exception.

Risk classApproval layerFilesystemNetworkCredentials in the environment
Low (docs, internal tooling)Reviewer model (auto mode, auto-review)Workspace writePackage registries onlyNone beyond a read-only repository token
Medium (feature logic behind a flag)Reviewer model, plus ask rules on git push and dependency changesWorkspace write, secret paths read-deniedRegistries plus named internal hosts, strict allowlistMasked or none
High (public API, tests, CI config)Reviewer model for code; ask rules on .github/, test and CI config pathsWorkspace write; CI config edits promptStrict allowlist, no wildcards on hosts that accept uploadsNone
Critical (auth, money, migrations, secrets, the harness itself)Manual or plan mode; never bypassWorkspace write inside a disposable container or VMNo egress beyond the registry mirrorNo production credentials, ever

A background or CI run has nobody to answer a prompt, so the run type limits which approval layer works:

Run typeApproval layer that worksWhat replaces the humanOuter boundary
InteractiveAny mode; auto mode or auto-review for long tasksYou, for the prompts that remainYour laptop, so the sandbox carries the weight
Background or cloudReviewer model; bypass only inside a disposable VM or containerThe classifier or reviewer, plus the network level of the cloud environmentA vendor VM or your own runner
CIAn exact allowlist (dontAsk) or never with a profileThe allowlist itself: anything unlisted is deniedA disposable runner with scoped tokens

Commit the team baseline to the repository and put the non-negotiable parts in managed settings, which users cannot override. Managed policy covers distributing one policy across tools.

Project baseline in .claude/settings.json (medium class): sandbox on, no unsandboxed retry, limited egress, two tokens stripped from sandboxed commands, and a prompt for pushes and CI edits:

{
"permissions": {
"deny": ["Read(./.env)", "Read(./.env.*)", "Bash(terraform apply *)"],
"ask": ["Bash(git push *)", "Edit(/.github/**)"]
},
"sandbox": {
"enabled": true,
"allowUnsandboxedCommands": false,
"network": {
"allowedDomains": ["registry.npmjs.org", "github.com", "api.github.com", "codeload.github.com"]
},
"credentials": {
"files": [
{ "path": "~/.aws/credentials", "mode": "deny" },
{ "path": "~/.ssh", "mode": "deny" }
],
"envVars": [
{ "name": "NPM_TOKEN", "mode": "deny" },
{ "name": "GITHUB_TOKEN", "mode": "deny" }
]
}
}
}

Managed settings for the organization, delivered by MDM or server-managed settings, hold the keys a checked-out repository cannot set or relax:

{
"permissions": { "disableBypassPermissionsMode": "disable" },
"sandbox": {
"enabled": true,
"failIfUnavailable": true,
"allowUnsandboxedCommands": false,
"network": { "strictAllowlist": true, "allowManagedDomainsOnly": true }
}
}
  • The allowlist names exact GitHub hosts instead of *.github.com, because the wildcard would also match gist.github.com and uploads.github.com, both places an injected command could send data.
  • Project settings cannot start a session in auto or bypassPermissions; those values are ignored there. Set the starting mode in user or managed settings.
  • strictAllowlist denies unlisted hosts instead of prompting, and has no effect in a repository’s settings files. It needs v2.1.219 or later.
  • On Linux and WSL2, install bubblewrap and socat first. Run /sandbox in a session: if it shows only a Dependencies tab, the sandbox is not running.
  • /permissions lists every active rule and the file it comes from, and shows recent auto-mode denials.

A background run cannot answer prompts, so the approval layer decides alone and the environment’s network level becomes the main egress control.

  • Cloud sessions (claude.ai/code, routines) have a Network access level: None, Trusted (package registries, GitHub and cloud SDKs; the default), Full or Custom. GitHub traffic, enabled MCP connectors and the Anthropic API bypass it, so disable connectors a task does not need.
  • Cloud sessions ignore defaultMode: "bypassPermissions" and "dontAsk" from settings files, so a repository cannot start one in bypass mode.
  • Local background sessions started with --bg are refused in bypass mode until you have accepted the bypass warning once interactively. In background sessions, strict sandbox mode also covers commands you type at the ! prompt.

In CI an agent meets untrusted text: issue titles, pull request bodies and diffs from forks. Give the job an exact allowlist, a workspace-only sandbox and a token scoped to its one task. The GitHub Actions hardening (pull_request_target, token permissions, OIDC) lives in agent identity, credentials and secrets.

dontAsk denies everything that would prompt, so the allowlist is the whole policy. --permission-prompts none makes sure nothing waits for an answer. --settings turns the sandbox on for this run and forbids the unsandboxed retry, and the --disallowedTools rule keeps the tests, which are the oracle, out of reach of Edit (adjust /tests/** to your layout, using the same path form as the other rules on this page):

Terminal window
# CI: run and fix tests, nothing else
claude -p "Run the test suite and fix failing tests in src/. Do not edit tests." \
--permission-mode dontAsk \
--permission-prompts none \
--settings '{"sandbox":{"enabled":true,"allowUnsandboxedCommands":false}}' \
--allowedTools "Read" "Edit" "Bash(npm test)" "Bash(npm run lint)" \
--disallowedTools "Edit(/tests/**)"

claude -p sessions start in Manual, so unlisted actions are denied even without --permission-mode. For a read-only triage job, --restricted removes the command-running tools and WebFetch unless --tools names them, and refuses bypassPermissions.

How do you prove the sandbox boundary holds?

Section titled “How do you prove the sandbox boundary holds?”

A settings file is a claim. A canary is evidence: a command that must fail under the policy, run the way the agent runs. Run it when you set a posture, on every harness settings change, and after each tool upgrade.

  1. Pick three probes for the posture: a write outside the workspace, a read of a secret path, and a connection to a host that is not on the allowlist.

  2. For Codex, run the probes through codex sandbox, which applies the same sandbox the agent gets. With codex-cli 0.157.1 on Linux, this run printed ok, then Read-only file system for /etc, then a refused connection for curl:

    Terminal window
    codex sandbox -c 'default_permissions=":workspace"' -- sh -c \
    'echo ok > probe.txt && cat probe.txt; echo x > /etc/probe; curl -sS -m 5 https://example.com'
  3. For Claude Code and Cursor, run the canary prompt below in a session with the posture you ship. Each probe must be blocked or denied; for background and CI postures, an approval prompt counts as a failure.

  4. Record the result in the evidence bundle of the pull request that changed the settings. The harness owner signs off on the canary output, not on the settings diff.

  5. Re-run the canary in CI whenever .claude/settings.json, config.toml or a hook changes. Harness changes are critical class.

Copy-paste prompts for permissions and sandboxing

Section titled “Copy-paste prompts for permissions and sandboxing”

What breaks in agent permissions and sandboxes, and how to recover

Section titled “What breaks in agent permissions and sandboxes, and how to recover”
  • The sandbox is not running, and nothing says so. By default, if bubblewrap or socat is missing or the platform is native Windows, Claude Code warns and runs commands unsandboxed. Recovery: set sandbox.failIfUnavailable: true in managed settings, and on Ubuntu 24.04 or later add the AppArmor profile for bwrap that Anthropic’s sandboxing guide gives.
  • The agent retries outside the sandbox. Claude Code may rerun a blocked command with dangerouslyDisableSandbox, which goes through normal approval, so in auto mode the classifier may approve it. Recovery: allowUnsandboxedCommands: false, or an ask rule on Bash(dangerouslyDisableSandbox:true).
  • A deny rule is bypassed by another spelling. Bash(curl *) does not match sh -c 'curl …'. Recovery: treat command rules as a first filter; the network allowlist is the egress control.
  • Project settings that do nothing. auto or bypassPermissions as defaultMode, strictAllowlist, credential mask entries and filesystem.disabled are ignored in a repository’s .claude/ settings. Recovery: move them to user or managed settings.
  • An allowlisted host doubles as an exit. A wildcard on a host that accepts uploads or gists lets an injected command send data out through an approved domain; Claude Code’s own advisories list exfiltration through a pre-approved HuggingFace domain in WebFetch (Moderate, 2026-06-13); the same pattern applies to any allowlisted upload host. Recovery: allow exact hosts, prefer a registry mirror, and put deniedDomains on upload endpoints.
  • Secrets ride along in the environment. Codex passes *TOKEN* variables to commands unless ignore_default_excludes = false; Claude Code protects only what you list. Recovery: strip or mask them, and keep production credentials out of agent environments.
  • Codex rejects the flags. codex -a untrusted and -a on-failure fail with “invalid value” in 0.157.1, codex exec -a never fails with “unexpected argument”, and a profile plus --sandbox does not compose. Recovery: untrusted and granular go in config.toml; put -a before exec; pick profiles or legacy flags, not both.
  • A -p run quietly skips blocked work. In headless Claude Code with auto mode, three consecutive classifier blocks or 20 in total pause auto mode; the action does not run and Claude keeps working. Recovery: never gate the job on the agent’s exit code; re-run tests and lint in a separate CI step and fail on those.
  • The agent itself has bugs. Claude Code’s security advisories include a sandbox escape via git worktree path confusion (High, 2026-06-25) and a workspace-trust bypass through a repository-controlled settings file (High, 2026-03-18). Recovery: stay on a supported channel, keep an outer container for critical class, and re-run the canary after each upgrade.

Frequently asked questions

Is a permission mode the same as a sandbox?

No. A permission mode or approval policy decides whether an action runs and who approves it. An OS sandbox limits what a shell command can read, write and reach once it runs. Unattended runs need both, plus a network allowlist.

Which setting lets an agent run unattended safely?

None on its own. Pair a reviewer or allowlist approval layer (Claude Code auto or dontAsk mode, Codex auto-review or never) with a workspace-only sandbox, a network allowlist and no production credentials in the environment.

When is it acceptable to bypass all permission checks?

Only inside an external boundary you control, such as a disposable container or VM with no production credentials and restricted egress. Both vendors' bypass flags say the same in their help text.

Edit page

Last updated: