Software Engineeringunpacked

Claude Code permissions: allow, ask and deny rules, permission modes, and what wins

Claude Code permissions decide which tool calls run without asking. You write allow, ask and deny rules (for example Bash(npm run *) or Read(./.env)) in a settings file or with /permissions, and pick a permission mode as the baseline. Rules are checked deny first, then ask, then allow, and Claude Code enforces them itself: what you write in CLAUDE.md or a prompt never changes what is allowed.

How Claude Code permissions work

In Manual mode (the default permission mode), Claude Code asks before actions that can change your system:

Tool type Asks first?
Reads and searches No, inside the working directory and added directories
Bash commands Yes, except a built-in set of read-only commands
File edits and writes Yes
Web fetch Yes, except preapproved documentation domains
Web search Yes

Answering "Yes, and don't ask again" saves a rule. To manage rules directly, run /permissions (it shows each rule and the settings file it comes from) or edit a settings file. The docs are explicit that "permission rules are enforced by Claude Code, not by the model": CLAUDE.md shapes what Claude tries, it does not grant or revoke access.

Allow, ask and deny rules

json { "permissions": { "allow": ["Bash(npm run *)", "Bash(git commit *)", "Edit(src/**)"], "ask": ["Bash(git push *)"], "deny": ["Read(./.env)", "WebFetch"] } }

Rule syntax

Rule Matches
Bash Every shell command
Bash(npm run build) Exactly that command
Bash(npm run *) Every npm script: put the * after the subcommand
Read(./.env) Reading .env in the current directory
Edit(src/**) Edits anywhere under src/
WebFetch(domain:example.com) Fetches to that domain
mcp__github__* Every tool of the github MCP server
mcp__github__create_issue One MCP tool

Write the command you want to allow and replace the parts that vary with *. Matching is by the text before the first *, so Bash(git log*) and Bash(git log *) differ in what they accept.

What wins: evaluation order and settings levels

"Rules are evaluated in order: deny, then ask, then allow. The first match in that order determines the outcome." Specificity doesn't matter: a broad deny like Bash(aws *) blocks aws s3 ls even if an allow rule names it exactly.

Across settings files the same holds: if any level denies a tool, no other level can allow it. A user deny blocks a project allow, a project deny blocks a user allow, and managed settings sit above everything, including command line flags.

Hooks don't bypass rules either: a deny or ask rule still applies even when a PreToolUse hook returns allow, while a hook that exits with code 2 blocks the call before the rules are checked.

Permission modes: the baseline

Mode Runs without asking
default (Manual) Reads only
acceptEdits Reads, file edits, common file commands
plan Reads; Claude plans without editing (plan mode)
auto Everything, reviewed by a background classifier
dontAsk Pre-approved tools and reads; everything else denied
bypassPermissions Everything; for isolated containers and VMs only

Cycle modes with Shift+Tab, or start with --permission-mode. Rules sit on top of the mode, and deny rules hold in every mode.

Permissions vs sandboxing

Permissions decide which tools, files and domains Claude Code may use. Sandboxing adds OS-level limits on what shell commands can reach on disk and on the network. Use both: the sandbox still holds if a prompt injection steers Claude toward something your rules didn't anticipate.

Permissions in CI and the Agent SDK

Unattended runs can't answer prompts. In CI, give claude -p an exact --allowedTools list and --permission-mode dontAsk, so anything else is denied instead of hanging or silently widening; see Claude Code GitHub Actions and CI. In the Agent SDK, allowed_tools pre-approves tools (it does not restrict them) and disallowed_tools blocks them.

Permissions on the Claude Certified Architect exam

Expect permissions inside Domain 3 (Claude Code configuration) and Domain 1 (Agent SDK tool access) of the CCAR-F exam. The recurring trap: trying to enforce a hard limit through CLAUDE.md or a prompt, when only rules, hooks and modes are enforced by the tool.

More in all articles.

Sources: Configure permissions; Choose a permission mode; Run Claude Code programmatically; Agent SDK permissions.

FAQ

How do I allow a command in Claude Code without being asked?

Add an allow rule such as Bash(npm run *) to permissions.allow in .claude/settings.json, or approve it with Yes, and don't ask again, or use /permissions.

What is the order of Claude Code permission rules?

Deny, then ask, then allow. The first match in that order decides, and a more specific allow rule cannot carve an exception out of a broader deny rule.

Can CLAUDE.md change what Claude Code is allowed to do?

No. Permission rules are enforced by Claude Code, not by the model. Instructions in CLAUDE.md or a prompt shape what Claude tries, not what Claude Code allows.

Do project settings override my personal deny rules?

No. A deny at any level blocks the call, and managed settings can't be overridden by any other level, including command line flags.