Claude Code permissions guide

Claude Code Permissions Guide

Understand allow, ask, and deny rules, scope merging, wildcard patterns, and how to verify active rules.

How Claude Code permissions work

Before an applicable tool call runs, Claude Code compares it with the active permission rules and the current permission mode.

Allow
Approves a matching action without manual approval. Use it for safe, frequent commands and files. Example: Bash(npm run lint), Read(./src/**).
Ask
Prompts before a matching action. Use it to preserve an explicit approval point for consequential operations such as Bash(git push *).
Deny
Blocks a matching action. Use it for sensitive paths such as Read(./.env). A matching deny rule wins even if another loaded scope allows the same action.

Claude Code checks matching rule categories in the order deny → ask → allow. The first matching category decides, and a more-specific rule does not change this order. If no rule matches, behavior falls back to the active permission mode: default or manual generally prompts, while other modes may approve or deny automatically.

Safe permissions starter

{
  "permissions": {
    "allow": [
      "Bash(npm run test *)"
    ],
    "deny": [
      "Read(./.env)",
      "Read(./.env.*)",
      "Read(./secrets/**)"
    ]
  }
}

Start small, then add one behavior at a time

The permission example approves one narrow test command and denies common secret locations. Keep your initial set minimal and expand only when you can name the exact command or path you need.

Write permission rules that stay narrow

An allow rule removes a prompt for a matching action. It does not turn the full tool list into an allowlist. Review every pattern against a real command before sharing it.

Prefer an exact command first
Bash(npm run lint) is easier to review than a rule that approves every npm command. Expand a rule only after you can name the extra commands it needs to match.
Use trailing wildcards deliberately
Bash(npm run test *) matches the command prefix plus following arguments. The space before * creates a word boundary. Without that space, a pattern can match a longer command name you did not intend.
Anchor file paths intentionally
Read and Edit rules use gitignore-style patterns. //path starts at the filesystem root, ~/path starts at the home directory, /path is relative to the settings source, and path or ./path is relative to the current directory.
Use Read and Edit for path rules
Read(path) covers built-in reads, while Edit(path) covers built-in file-editing tools. A Read deny also blocks Edit on the same path, but use an Edit deny when no built-in file tool should modify that path.
Keep sensitive paths in deny
A matching deny rule blocks an action even when another loaded scope contains a broader allow rule. Cover each real secret location your repository uses, then verify the result with /permissions.
Use ask for consequential actions
An ask rule keeps an explicit approval point for a matching action. Rule categories are checked in the order deny, ask, allow, and the first matching category decides regardless of rule specificity.

Permission deny rules are enforced by Claude Code, not by the operating system. They do not stop an arbitrary Python, Node, or other subprocess from reading a file directly; enable sandboxing when you need OS-level enforcement.

How permissions merge across scopes

Array-valued permission rules concatenate and deduplicate across loaded scopes instead of being replaced by a single winner. General source precedence is Managed → command-line → Local → Project → User.

A matching deny wins across loaded scopes

A deny rule in any loaded scope blocks a matching action even if another scope has an allow rule. This follows permission category order rather than choosing the most-specific rule.

Evaluation order: deny → ask → allow

If your User scope has allow: ["Bash(rm *)"] and your Project scope has ask: ["Bash(rm *)"], the matching ask rule prompts because ask is checked before allow. Specificity does not change that order.

Verify with /permissions

/permissions shows the resolved rules and the settings file that contributed each rule. Run it after changing any scope to confirm that the loaded result matches your intention.

Verify permission rules with /permissions

Inspect the resolved rules after every change, then test the specific action you intended to control.

  1. Run /permissions

    Review the resolved allow, ask, and deny rules and the settings file that contributed each rule.

  2. Check the evaluation order

    Rules are evaluated deny → ask → allow, and the first matching category decides. If you see an unexpected result, look for a match in an earlier category.

  3. Test one action at a time

    Run a single Bash command or Read action and observe whether Claude Code prompts, approves, or blocks. Compare the result with your intended rule and active permission mode.

  4. Check scope sources

    /permissions shows the source settings file for each rule. This reveals whether a rule from an unexpected scope is affecting the result.

  5. Reproduce across surfaces

    Test in terminal, VS Code, and JetBrains if you use multiple surfaces. The same settings may be available, but the working directory and environment context can differ.

Local diagnostic

Validate your permissions now

The browser-only checker can flag JSON syntax errors, exact allow/deny conflicts, and likely path, scope, or precedence causes. It does not replace Claude Code's /permissions view or the official settings schema.

Run settings checker

Claude Code permissions — FAQ

What is the difference between allow, ask, and deny in Claude Code permissions?

Allow removes the approval prompt. Ask keeps an explicit approval point. Deny blocks the action. Claude Code checks matching rule categories in deny, ask, allow order, so the first matching category decides. More-specific rules do not change that order. If no rule matches, behavior falls back to the active permission mode: default or manual generally prompts, while other modes may approve or deny automatically.

How do permission rules merge across scopes?

Array-valued permission rules are concatenated and deduplicated across loaded scopes. General source precedence is Managed, command-line, Local, Project, User, but a deny rule in any loaded scope still wins over ask or allow for a matching action. Use /permissions to see the resolved rules and their source settings files.

How do I verify which permission rules are actually in effect?

Run /permissions inside Claude Code. It shows the resolved allow, ask, and deny rules along with the settings file that contributed each rule.

How do I write a narrow permission rule that does not accidentally approve too much?

Start with an exact command or path, such as Bash(npm run lint) or Read(./src/**). Use trailing wildcards deliberately: Bash(npm run test *) matches that command prefix plus arguments, and the space before * creates a word boundary. Keep sensitive paths in deny and verify the result with /permissions.

Official sources

Sources last reviewed August 11, 2026.