Fenzi
blog, permissions

How do Claude Code permissions work?

Claude Code checks every tool call against your deny, ask and allow rules, in that order, and the permission mode decides what happens to the calls no rule matches. Claude Code enforces both, outside the model, but a rule only matches the command or path it can recognize. For limits that hold for every process, the docs point to a sandbox.

By The Fenzi team, , five-minute read

Level 1PermissionsAnalysisOctober 2026

Two settings decide whether Claude Code runs a tool call or asks you first: your permission rules and your permission mode. Anthropic's Configure permissions page is clear about who holds them: Claude Code enforces the rules itself, and the model has no say. A line in CLAUDE.md changes what Claude tries. It does not change what Claude Code allows.

Rules: deny, then ask, then allow

A rule names a tool, or a tool and a pattern: Bash(npm run build), Read(./.env), WebFetch(domain:example.com). Deny blocks the call. Ask prompts you. Allow runs it without a prompt.

Claude Code checks deny first, then ask, then allow, and the first match wins. A narrow allow cannot carve a hole in a broad deny, so Bash(aws *) in deny also blocks aws s3 ls. The same order holds across files: a deny in your user settings beats an allow in the project's, and nothing overrides a rule in managed settings, not even a command line flag.

Modes: what runs when no rule matches

The permission modes page lists six. You switch with Shift+Tab, --permission-mode, or defaultMode in settings.

Manual (config value default) runs reads without asking and prompts for edits, shell commands and network calls. acceptEdits adds file edits and common filesystem commands such as mkdir and mv. plan reads and explores but does not edit your source. auto runs without routine prompts while a second model, a classifier, reviews actions first. dontAsk runs reads and whatever you pre-approved, and denies anything that would prompt, for CI. bypassPermissions runs everything.

Two details matter more than the list. Since Claude Code v2.1.283, auto is the starting mode for interactive terminal and VS Code sessions, so you may be in it without having chosen it. And deny rules block in every mode, while allow rules have no effect in bypass, which is why our answer to whether dangerously skip permissions is safe starts with the boundary around the process. Anthropic's own warning on auto mode is short: it "reduces permission prompts but does not guarantee safety."

Where a rule stops matching

A Bash rule matches the command text Claude writes, after Claude Code splits && chains and strips wrappers such as timeout and nohup. It does not match the same program called another way. The docs give the examples: Bash(curl *) in deny stops curl https://example.com and does not stop /usr/bin/curl or sh -c 'curl ...'.

A deny or ask rule covers the invocation Claude usually produces and isn't a security boundary around the program.Claude Code docs, Configure permissions

File rules have the same edge. A Read deny covers Claude's file tools and shell commands Claude Code recognizes, such as cat, head and sed. It does not cover grep -r run from the folder that holds the file, or a Python or Node script that opens the file itself. None of this is a bug. The rules govern Claude's tools; the operating system governs the processes those tools start.

"A deny rule keeps Claude out of my .env."

Revision 1: it keeps Claude's tools out. A script Claude writes still needs a wall.

The repository can ship settings too

Rules live in several files: managed settings, your user settings in ~/.claude, and the project's .claude/settings.json, which arrives with every clone. Claude Code applies a project's allow rules and extra directories only after you accept the workspace trust dialog. Deny and ask rules apply at once, since they only restrict.

That trust step has failed before. Anthropic's advisory GHSA-mmgp-wc2j-qcv7 describes a repository that set defaultMode to bypassPermissions in its committed settings, so the trust dialog was skipped on first open. Version 2.1.53 fixed it. The lesson outlasts the fix: a file in the repository is text someone else wrote, the same as a README that says to curl a script.

Headless runs need extra care. claude -p never shows the trust dialog, and the project's hooks and env block still apply. For a repository you did not write, the docs suggest --setting-sources user so Claude Code reads neither its settings nor its .mcp.json.

A setup that holds

Deny secrets twice. Add Read deny rules for .env and key folders, and turn on the sandboxed Bash tool, which the operating system enforces for every shell command and its children. Its defaults still allow reads of most of the machine, including ~/.ssh and ~/.aws/credentials, until you add denyRead rules.

Pick the mode for the task. acceptEdits while you read the diff, plan before a large change, dontAsk with a short allow list in CI. Keep bypass for a container or virtual machine you would throw away.

Put team rules where users cannot edit them. Managed settings can turn off bypass with permissions.disableBypassPermissionsMode, turn off auto with permissions.disableAutoMode, and make themselves the only source of rules with allowManagedPermissionRulesOnly.

Ask what the process can reach, not what the model intends. The usual failures are ordinary: a file it had no business reading, a host it should not call. Our list of AI agent security risks walks through them, and what agentic AI security means to us comes down to directories, hosts, keys and a person who approves more.

A side note on how we do it

We build Fenzi, which starts Claude Code in its manual mode inside an OpenShell sandbox, so Claude's rules decide what it tries and the sandbox decides what any process can reach; the agents sheet shows the flags. It is one way to put a wall under the rules, and Claude Code's own sandbox or a container can do the same.

So, how do they work?

Rules pick out tool calls by name and pattern, deny first. The mode handles everything the rules do not name. Both run in Claude Code, outside the model, and both stop at the command text and paths Claude Code can recognize. Write the rules for the mistakes Claude makes, and put a sandbox under them for the ones it does not announce. The example settings files in Claude Code's repository, lax, strict and Bash sandbox, are a good place to start; its README says to test them locally before rolling them out.

The Fenzi team, October 2026.Read the sources

One line to install. One command to run.

curl -fsSL https://fenzi.dev/install.sh | sh

Free per machine. No account. The model does not get a vote on its own limits.