Claude Certification Guides
CCAR-F/Domain 39 min read

Lesson 3.2

Permission Modes and Allow/Deny Rules

How Claude Code decides whether a tool call runs, asks or is blocked, and how to express that policy with modes and rules.

Every tool call in Claude Code passes through a permission check before it executes. The outcome is one of three: run it, ask the user, or block it. Two inputs decide that outcome: the current permission mode, which sets the default posture, and the rules in settings, which override the default for specific tools and patterns. The exam tests the modes, the rule syntax and the precedence between them.

Permission modes

ModeBehaviour
defaultRead-only tools run freely; edits and commands prompt unless a rule allows them
acceptEditsFile edits in the project are auto-approved; commands still prompt
planClaude may read and analyse but not edit or run commands; it produces a plan for review
dontAskAnything not explicitly allowed by a rule is denied rather than prompted
bypassPermissionsNothing prompts; everything runs. Only for isolated environments

Set the mode with --permission-mode <mode> on the command line, permissions.defaultMode in settings, or cycle through modes interactively with Shift+Tab. The --dangerously-skip-permissions flag is the command-line form of bypassPermissions and its name is a deliberate warning: it should only be used inside a sandbox or container with no credentials worth protecting. Enterprise administrators can set disableBypassPermissionsMode to make it unavailable.

dontAsk is the mode for unattended runs that must never hang on a prompt: combine it with a carefully built allow list, and anything unexpected fails fast instead of waiting for a human who is not there.

Rule syntax

Rules live under permissions in any settings file and come in three lists: allow, ask and deny. Each entry names a tool and, optionally, a pattern in parentheses.

{
  "permissions": {
    "allow": [
      "Read",
      "Bash(npm run lint)",
      "Bash(npm run test:*)",
      "Edit(src/**)",
      "WebFetch(domain:docs.anthropic.com)",
      "mcp__github__get_issue"
    ],
    "ask": ["Bash(git push:*)", "mcp__slack"],
    "deny": ["Read(./.env*)", "Bash(rm -rf:*)", "Edit(infra/**)"]
  }
}

Pattern conventions:

  • A bare tool name (Read) matches every use of that tool.
  • Bash(exact command) matches that command exactly; Bash(prefix:*) matches any command beginning with the prefix. Wildcards elsewhere in the string are also supported in current versions.
  • Edit, Write and Read take gitignore-style path patterns; patterns are relative to the project root unless they begin with / (absolute) or ~ (home).
  • WebFetch(domain:host) restricts web fetches to a domain.
  • mcp__server__tool names one MCP tool; mcp__server names every tool on that server.

Precedence

deny wins over ask, and ask wins over allow. A tool call that matches a deny rule anywhere in the merged settings is blocked even if an allow rule from another scope matches too. Rules from all scopes are combined, so a user's personal allow list cannot defeat a project's deny list, and neither can defeat an enterprise managed deny. Mode is consulted only when no rule matches: an allow rule runs the tool even in default mode, and an ask rule prompts even in acceptEdits mode. Do not rely on deny rules to protect a bypassPermissions run; treat that mode as switching the permission system off and keep it out of shared environments.

Deny rules on Read and Edit are the mechanism for protecting secrets: denying Read(./.env*) prevents the model from ever seeing the file's contents, which is stronger than asking it not to look.

Working directories

By default Claude Code may read and edit within the directory it was launched in. permissions.additionalDirectories (or --add-dir) extends that boundary to other paths. Anything outside the permitted directories prompts, regardless of mode other than bypass.

Hooks and permissions

A PreToolUse hook runs after the rule check has decided to allow or ask, and can override by returning permissionDecision of allow, deny or ask. This makes hooks the place for policies that rules cannot express, such as "allow git commit only when the test suite passed earlier in this session". A PermissionRequest hook fires when a prompt would be shown and can answer on the user's behalf, which is how automated approval services integrate.

Practical policy design

Start from the actions that must never happen (deny), then the actions that need a human (ask), then the routine actions that should never interrupt (allow), and let the mode cover the rest. Review the merged result with /permissions, which shows the effective rules and their sources. For unattended agents prefer dontAsk with an explicit allow list over bypassPermissions; the former fails closed, the latter fails open.

Exam focus

  • Modes: default prompts for edits and commands, acceptEdits auto-approves project edits, plan is read-only, dontAsk denies anything not allowed, bypassPermissions runs everything.
  • --dangerously-skip-permissions equals bypassPermissions and belongs only in isolated sandboxes; enterprises can disable it.
  • Rule precedence is deny > ask > allow; rules from all scopes are merged, so a project deny cannot be undone by a user allow.
  • Bash(prefix:*) is prefix matching; Edit(path/**) uses gitignore-style globs; mcp__server covers a whole MCP server.
  • Denying Read on secret files stops the model seeing them, which is stronger than an instruction.
  • For unattended runs, dontAsk plus an allow list fails closed; bypassPermissions fails open.

Check yourself

5 questions

  1. 1.A project's permissions contain an allow rule Bash(aws s3 ls) and a deny rule Bash(aws *). Claude attempts to run aws s3 ls. What happens?

  2. 2.Which permission mode lets Claude read files and run read-only commands to explore a codebase, but not edit source files?

  3. 3.A team adds the allow rule Bash(npm run test*) intending to approve the project's test scripts. During review an architect flags a problem. What is it?

  4. 4.What is the difference between a deny rule written as Bash and one written as Bash(rm *)?

  5. 5.To restrict network access, a team writes the allow rule Bash(curl https://github.com/ *) and expects curl to reach only GitHub. How should this be assessed?