Claude Code permissions: what to allow and what to never allow

The permission system is the difference between an assistant and an unsupervised process holding your credentials. It is worth twenty minutes of configuration, and it does less than most people assume, which is why the sandbox exists next to it.

By the Continuum team. We build a workbench that runs Claude Code, Codex, and their peers, so the model rates quoted here are the ones our own cost analytics ship with.

The short version

Permissions combine a mode, which sets default behaviour, with allow, ask, and deny arrays of Tool(specifier) rules. Rules are evaluated deny, then ask, then allow, and the first match wins regardless of how specific a later rule is. There are six modes as of August 2026: default, acceptEdits, plan, auto, dontAsk, and bypassPermissions. The system governs what the agent does directly and does not extend to MCP servers or to the code the agent runs.

What you need to know
  • Six modes now, not four. auto and dontAsk are the ones people miss.
  • Deny beats ask beats allow, first match. Specificity never changes the order.
  • Allow-list your test and lint commands. It removes most of the prompting.
  • MCP servers are outside this system. Scope the credential instead.
  • The real boundary for unattended work is the sandbox or a container, not a rule list.

The six modes

Permission modes as of August 2026. Set the starting one with permissions.defaultMode.

ModeBehaviourRight for
defaultPrompts on first use of each tool. Labelled Manual in the CLI, and manual is an accepted aliasEveryday work
planReads and runs read-only shell commands; edits nothingUnfamiliar or large tasks
acceptEditsAuto-accepts file edits plus common filesystem commands (mkdir, touch, mv, cp) inside the working directoryWell-scoped tasks in a clean tree
autoAuto-approves with a background classifier checking each action against your requestLong tasks where prompting has stopped being read
dontAskAuto-denies anything not pre-approved by an allow rule or the read-only setLocked-down CI
bypassPermissionsSkips prompts entirely, apart from explicit ask rules and root or home deletionsContainers and worktrees only

Shift+Tab cycles modes mid-session, which is more useful than it sounds: dropping into plan mode the moment a task turns out to be bigger than expected is the cheapest correction available.

How a call is actually decided

A tool call is matched against your rules in one fixed order.

  1. Deny rules. First match refuses the call outright.
  2. Ask rules. First match forces a prompt, even if a narrower allow rule also matches.
  3. Allow rules. First match runs the call without a prompt.
  4. No match: the mode decides.

Path syntax, which is where the mistakes are

How a path specifier is resolved in Read, Edit, and friends.

Written asResolves againstExample
//pathThe filesystem rootRead(//Users/alice/secrets/**)
~/pathYour home directoryRead(~/.ssh/**)
/pathThe settings file that declares the ruleEdit(/src/**) in project settings means <repo>/src/**
path or ./pathThe current directoryRead(*.env)

Bash matching, and the wrappers

  • A trailing * with a space enforces a word boundary: Bash(ls *) matches ls -la but not lsof. Without the space, Bash(ls*) matches both.
  • The :* suffix is equivalent to a trailing *, so Bash(npm run test:*) and Bash(npm run test *) are the same rule.
  • Compound commands are parsed. A rule must match each subcommand independently; the recognised separators are &&, ||, ;, |, |&, &, and newlines.
  • A fixed wrapper list is stripped before matching: timeout, time, nice, nohup, stdbuf, command, builtin, zsh noglob, and bare xargs.
  • Environment runners are not stripped. Bash(devbox run *), Bash(docker exec *), or Bash(npx *) allow whatever follows, including devbox run rm -rf .. Write one rule per inner command instead.
One tool call falling through three permission gates in fixed order: a deny rule matches first and refuses it, so the ask and allow gates are never evaluated THE CALL Bash(aws s3 ls) one fixed order, first match wins 1 deny refuses it outright Bash(aws *) MATCH the call is refused no match falls through 2 ask forces a prompt Bash(git push *) not evaluated 3 allow runs it with no prompt Bash(aws s3 ls) not evaluated no rule matched, so the mode decides default · plan · acceptEdits · auto · dontAsk · bypassPermissions A deny rule cannot carry an exception. If you need one, write a narrower deny.

The baseline every repository should have

Copy into .claude/settings.json and adjust.
{
  "permissions": {
    "deny": [
      "Read(.env)",
      "Read(.env.*)",
      "Read(//**/*.pem)",
      "Read(//**/id_rsa*)",
      "Read(//**/credentials*)",
      "Read(~/.ssh/**)",
      "Read(~/.aws/**)",
      "Read(~/.config/gh/**)",

      "Bash(git reset --hard *)",
      "Bash(rm -rf *)",
      "Bash(sudo *)",
      "Bash(curl * | sh)",
      "Bash(curl * | bash)"
    ],
    "ask": [
      "Bash(git push *)",
      "Bash(gh pr merge *)"
    ],
    "allow": [
      "Bash(npm run test *)",
      "Bash(npm run lint *)",
      "Bash(npm run build)",
      "Bash(git status)",
      "Bash(git diff *)",
      "Bash(git log *)",
      "Bash(git add *)",
      "Bash(git commit *)",
      "Read(**)",
      "Edit(src/**)",
      "Edit(test/**)"
    ]
  }
}

What rules do not protect, and what does

Being clear about the boundary is more useful than a longer allow list.

Where the permission system stops.

RiskCovered by rules?Why
Agent reads a credential fileYesDeny patterns
Agent runs a destructive commandYesDeny patterns
Agent writes outside the projectYesDirectory scope
An MCP server writes to your databaseNoThe server runs with its own credential
npm install runs a malicious postinstallNoYou allowed the command; the package does the rest
A test suite you asked it to run does something badNoSame
Agent writes a subtle bugNoThat is what review is for

The sandbox is the layer that covers the gap

Claude Code ships an OS-level sandbox for the Bash tool on macOS, Linux, and WSL2. Native Windows is not supported; run inside WSL2 there. It constrains Bash commands and their child processes, which is precisely the postinstall case rules cannot reach.

Minimal sandbox configuration.
{
  "sandbox": {
    "enabled": true,
    "network": {
      "allowedDomains": ["registry.npmjs.org", "*.github.com"]
    },
    "filesystem": {
      "allowWrite": ["/tmp/build"],
      "denyRead": ["~/.aws", "~/.ssh"]
    }
  }
}

Two layers, two jobs.

Permission rulesSandbox
Applies toEvery toolThe Bash tool and its child processes
Enforced byClaude CodeThe operating system
Stops a prompt injectionOnly if Claude asks for the denied thingYes, the boundary holds regardless
Covers a malicious dependencyNoYes

Building the list without doing it up front

01

Start with the deny and ask lists above

That is the part that matters and it needs no tuning. Commit it before anyone works in the repo.

02

Work normally for a day in default mode

Do not try to predict what you will approve. You will be wrong in both directions.

03

Allow-list what you approved repeatedly

If you said yes to it ten times, it belongs in allow with a specific pattern. Approving a compound command with "Yes, don’t ask again" already saves a separate rule per subcommand, up to five.

04

Commit the result and review changes

Run /permissions to see every rule and the file it came from, then move the ones worth sharing from settings.local.json into settings.json.

05

Never blanket-allow Bash

A bare Bash allow rule is bypass mode with extra steps. If you want fewer prompts than that, turn on the sandbox instead, which is a boundary rather than an absence of one.

Questions people ask

A mode sets the default behaviour, and allow, ask, and deny arrays of Tool(specifier) rules grant, prompt for, or refuse specific tool calls. Rules are evaluated deny, then ask, then allow, and the first match decides.

Six as of August 2026: default (also labelled Manual), plan, acceptEdits, auto, dontAsk, and bypassPermissions. Set the starting one with permissions.defaultMode, and cycle mid-session with Shift+Tab.

Read credential files and SSH or cloud config, hard reset, recursive delete, pipe a download into a shell, or run sudo. Put all of those in deny explicitly, and put irreversible-but-legitimate operations such as git push in ask rather than deny.

Partly. You can allow or deny individual MCP tools with mcp__server__tool rules, but the server itself runs with its own credentials, so a read-only agent with a write-capable database server can still write. Scope the credential.

It is normally necessary, and it means allowing whatever your test suite and its dependencies do. Rules cannot reach inside a command they permitted. Turning on the Bash sandbox is what closes that gap, because the OS constrains child processes too.

In a container, a sandboxed session, or an isolated git worktree, where the boundary is enforced by the environment rather than by prompts. Pair it with dontAsk mode so anything you did not pre-approve fails cleanly instead of running.

Project allow rules and additionalDirectories grant capability, so Claude Code applies them only after you accept the workspace trust dialog for that folder. Deny and ask rules are unaffected. In a claude -p run there is no dialog and the rules stay ignored.

Add it to allow with a specific pattern such as Bash(npm run test *). Note the space before the asterisk: it forces a word boundary, so the rule matches npm run test unit but not a differently named command with the same prefix.

Sources

Every figure above was read from these pages on August 2026. Vendors reprice without notice; if you find a stale number, tell us.

  1. Claude Code permissions reference
  2. Claude Code sandboxed Bash tool
  3. Claude Code settings reference
Try it

A worktree is
a boundary.

Continuum runs each session in its own git worktree, so an unsupervised agent can only affect one branch.

free app · your subscriptions · local-first