Permissions, Sandboxing and Security
All six permission modes and which one your sessions actually start in, the rule syntax that quietly opens more than you meant, the OS-enforced Bash sandbox, and what no mode ever auto-approves.
Chapter 11 | Permissions, Sandboxing and Security Written for: everyone who lets Claude Code run commands on their own machine
Three layers get muddled together constantly. Separate them first:
| Layer | Answers |
|---|---|
| Permission mode | Does Claude ask you before acting? |
Permission rules (allow / ask / deny) | Is this tool or command allowed at all? |
| Sandbox | Once a command is running, what can it reach? |
The official documentation states the relationship plainly: “Permission modes decide whether Claude asks before an action, and the Bash sandbox and outer isolation boundaries decide what an action can reach once it runs.”
11.1 The six permission modes
| Config value | Label on screen | Runs without asking | Suits |
|---|---|---|---|
default | Manual | Reads only | Sensitive work, unfamiliar code |
acceptEdits | Edit automatically | Reads, file edits, and common filesystem commands (mkdir, touch, mv, cp) | Iterating on code you are reviewing |
plan | Plan | Reads, plus classifier-approved commands when auto mode is available | Exploring a codebase before changing it |
auto | Auto | Everything, with background safety checks | Long tasks, less prompt fatigue |
dontAsk | Don’t ask | Only pre-approved tools | Locked-down CI and scripts |
bypassPermissions | Bypass permissions | Everything | Isolated containers and VMs only |
The most confusing part: the mode that reviews everything is called Manual on screen, but its config value is
default. The CLI acceptsmanualas an alias (v2.1.200 or later).
Which mode your session starts in
This is where the previous edition of this guide was out of date. On the Pro, Max and Team plans the built-in starting mode is auto mode, not Manual.
The order of precedence is:
- The
--permission-modeflag, or--dangerously-skip-permissions permissions.defaultModein a settings file- The built-in default
And the built-in default depends on how you are running. The first row that matches applies:
| Case | Starting mode |
|---|---|
Any settings file sets disableAutoMode to "disable" | default |
| Feature-flag fetching is off | default |
| Your first session after an install or upgrade | default |
claude -p or the Agent SDK | default |
| Amazon Bedrock, Google Cloud’s Agent Platform, Microsoft Foundry, Claude Platform on AWS | default |
| A Pro, Max or Team plan, in a terminal or the VS Code extension | auto |
| An Enterprise plan or a Claude Console API key | default |
There is one hidden trap: setting "auto" or "bypassPermissions" in .claude/settings.json or .claude/settings.local.json has no effect. In the bypassPermissions case the session starts in Manual instead. Other values work from any settings file.
Switching mid-session
In the CLI, press Shift+Tab to cycle. From auto, the first press goes to default, then the cycle runs default → acceptEdits → plan → back to default. The status bar shows the active mode, such as ⏸ manual mode on or ⏵⏵ auto mode on.
In VS Code click the mode indicator beneath the prompt box; in the desktop app use the selector next to the send button. The JetBrains plugin runs the CLI in the IDE terminal, so Shift+Tab works there too.
What no mode ever auto-approves
Including bypassPermissions. This list is worth memorising:
- Tools matched by an explicit
askrule you wrote - Connector tools your organisation set to
ask - Tools requiring a person: the built-in
AskUserQuestion, and MCP tools markedrequiresUserInteraction rmandrmdiraimed at a critical path, which no allow rule and no hook"allow"can approve- The cross-session messaging safeguards
And deny rules block in every mode, bypassPermissions included. Allow rules, by contrast, have no effect in bypassPermissions.
When to use --dangerously-skip-permissions
The short answer: only inside a container or VM that is already isolated, and on Linux and macOS, running as a non-root user.
claude -p "<prompt>" --dangerously-skip-permissions
In such a -p run, the few calls that would still prompt are denied rather than allowed through.
11.2 Permission rules — the syntax that bites
{
"permissions": {
"defaultMode": "default",
"allow": ["Bash(npm run *)", "Read(src/**)", "WebFetch(domain:github.com)"],
"ask": ["Bash(git push *)"],
"deny": ["Read(./.env)"],
"additionalDirectories": ["/tmp/work"]
}
}
Evaluation runs deny, then ask, then allow, and the first match in that order decides. Rule specificity does not change the order, so a broad deny like Bash(aws *) blocks everything matching it, including calls that also match a narrow allow like Bash(aws s3 ls).
The space before the asterisk
The most common mistake there is:
| Written as | What you get |
|---|---|
Bash(git diff *) | Commands beginning with git diff |
Bash(git diff*) | Also catches git diff-index, unintentionally |
Bash(git *) | Every git command, which is probably wider than you meant |
An asterisk placed before the subcommand, as in Bash(git * main), is a risky form, because it also covers things like git -c core.fsmonitor=<script> diff main. Claude Code warns at startup when it finds an allow rule shaped like that.
Path depth means different things in allow and deny
| Written | In a deny rule | In an allow rule |
|---|---|---|
Edit(src/**) | Blocks both src/ and vendor/pkg/src/ | Allows only src/ beneath the working directory |
Edit(**/src/**) | — | Allows any src/ at any depth |
Put simply: deny rules reach wide by default, allow rules reach narrow. That is the safe default, but it surprises people when an allow rule does not behave as expected.
Other forms in common use
"WebFetch(domain:example.com)"
"WebFetch(domain:*.example.com)"
"mcp__puppeteer"
"mcp__puppeteer__puppeteer_navigate"
"Agent(Explore)"
11.3 The Bash sandbox
This is entirely absent from the previous edition of this guide, and it is a different kind of layer from permission rules, because the operating system enforces it, not Claude Code.
It supports macOS, Linux and WSL2. Native Windows is not supported — run Claude Code inside WSL2 there. On macOS there is nothing to install; it uses the built-in Seatbelt framework. Linux and WSL2 need two extra packages.
Turn it on by typing /sandbox in a session — the panel tells you what is missing — or set sandbox.enabled to true in a settings file.
Two modes
Auto-allow: commands that can be sandboxed run and are approved automatically. Commands that cannot — those needing a host outside the allowlist, for instance — fall back to the regular permission flow.
Regular permissions: every Bash command goes through the normal flow, sandboxed or not.
The subtlety few people know: auto-allow works independently of your permission mode. Bash commands that modify files inside the sandbox boundary run without prompting even in Manual mode, where the file-editing tools would have asked. The exception is plan mode, where auto-allow does not widen approvals.
Even in auto-allow, deny rules still hold, ask rules naming a command such as Bash(git push *) still force a prompt, and rm/rmdir aimed at a critical path still goes through the normal flow.
Files — what can be written where
| Scope | |
|---|---|
| Writable | The working directory and below, anything added via --add-dir or permissions.additionalDirectories, and the session temp directory $TMPDIR points at |
| Readable | The entire computer, except specifically denied directories |
| Not writable | Everything else, including ~/.bashrc and system binaries in /bin/ |
Read that twice. By default the sandbox can still read credential files such as
~/.aws/credentialsand~/.ssh/. To stop it, configuresandbox.credentialsor add those paths todenyReadyourself. The sandbox is primarily about blocking writes; it does not close off reads for you.
Paths protected even inside the writable zone
The reasoning is direct: a command able to edit these files could grant itself permissions, or add a hook or MCP server that Claude Code then runs outside the sandbox.
The protected set covers the .claude settings files and the skills, agents, commands and hooks directories beneath them, .mcp.json, the files Claude Code runs on its own, shell startup files such as .bashrc and .zshrc, .gitconfig, the .vscode and .idea directories, hooks and config inside .git, and most of ~/.claude.
There is no way to exempt one of these individually. An allowWrite entry or an Edit allow rule covering the path does not lift the protection. The only way to turn it off is filesystem.disabled, which switches off filesystem isolation for every path at once.
The network
By default no domain is allowed. The first time a command needs a new one, Claude Code prompts (or sends it to the classifier in auto mode). Answering Yes allows that host for the session; “Yes, and don’t ask again” saves a WebFetch(domain:...) rule to your local settings.
Pre-allow them to avoid the prompt:
{
"sandbox": {
"enabled": true,
"network": {
"allowedDomains": ["github.com", "*.npmjs.org"],
"deniedDomains": ["evil.example.com"],
"strictAllowlist": true
}
}
}
strictAllowlist denies instead of prompting — but setting it in a repository’s .claude/settings.json or .claude/settings.local.json has no effect. It must come from user settings, managed settings, or --settings.
Domain wildcards support two forms only: a leading *. and a bare *. A wildcard anywhere else, such as domain:example.*, still matches WebFetch but has no effect on sandboxed commands.
If your organisation uses a proxy, set HTTPS_PROXY, HTTP_PROXY and NO_PROXY in the env block of your settings, not just in your shell, so background agents inherit them.
The escape hatch when a command cannot be sandboxed
Claude Code reports which path or host the sandbox denied, and Claude may retry with the dangerouslyDisableSandbox parameter, which runs outside the sandbox and goes through the regular permission flow.
To be prompted every time, even in auto mode, add an ask rule:
"ask": ["Bash(dangerouslyDisableSandbox:true)"]
To close the hatch entirely, set "allowUnsandboxedCommands": false. The /sandbox panel calls this Strict sandbox mode.
11.4 Enforcement across an organisation
The managed settings file lives at:
| OS | Path |
|---|---|
| macOS | /Library/Application Support/ClaudeCode/managed-settings.json |
| Linux and WSL | /etc/claude-code/managed-settings.json |
| Windows | C:\Program Files\ClaudeCode\managed-settings.json |
Claude Code no longer reads the legacy Windows path
C:\ProgramData\ClaudeCode\managed-settings.json.
When several teams own different parts of a policy, split them into files under managed-settings.d/ beside managed-settings.json in the same system directory, rather than editing one shared file.
Settings an administrator can genuinely enforce include permissions.defaultMode, permissions.disableAutoMode set to "disable" to remove auto mode from the cycle so nobody can pick it, deny rules, and sandbox.network.allowManagedDomainsOnly, which blocks non-listed domains outright rather than prompting and honours only the entries from managed settings.
11.5 Recommended setups by situation
| You want | Start with | Isolation required |
|---|---|---|
| To review every action yourself | claude --permission-mode default | None |
| Fewer prompts without a classifier | Manual plus the sandbox in auto-allow mode | The built-in sandbox (macOS/Linux/WSL2) |
| To explore before changing anything | claude --permission-mode plan | None |
| Long hands-off work | claude --permission-mode auto | None, though a sandbox adds depth |
| CI with an exact allowlist | claude -p "..." --permission-mode dontAsk --allowedTools "Bash(npm test)" "Read" | Whatever your runner provides |
| Fully unattended | claude -p "..." --dangerously-skip-permissions | Required: a container or VM |
What this chapter settles
The mode answers whether Claude asks, the rules answer whether it is allowed, the sandbox answers what it can reach. The three work independently, and opening one does not close another. The two facts most worth remembering: deny rules block in every mode, bypass included, and the sandbox is about blocking writes — it does not close off reads of your credential files by itself.
Glossary for this chapter
| Term | Meaning |
|---|---|
| Manual mode | The mode that asks before nearly everything; its config value is default |
| Classifier | The second model that reviews actions on your behalf in auto mode |
| Protected paths | Paths the sandbox refuses to write, even inside the writable zone |
| Auto-allow | The sandbox mode that approves sandboxable commands itself |
| Strict sandbox mode | Closing the dangerouslyDisableSandbox escape hatch |
| Managed settings | The organisation’s policy file, overriding every other level |