Last updated: November 11, 2026
TL;DR
Claude Code permissions are allow, ask and deny rules in a settings file. Claude Code checks every tool call against them in a fixed order, deny first, then ask, then allow, and the first match decides, however specific the other rules are. A rule names a tool and, optionally, what it applies to: Bash(npm run *), Read(./.env), WebFetch(domain:example.com).
- โ
Allow what you run all day.
Bash(npm run *)stops the prompts for every npm script. - ๐ซ Deny the files Claude must never read.
Read(./.env)also blocks edits to that file. - โ Ask before anything you can't undo. An ask rule prompts even when an allow rule matches.
- ๐ Watch the leading slash.
/pathanchors at the settings file's location, and//pathis the filesystem root. - ๐งฑ Know where rules stop. A Bash rule matches command text, not the program, so it isn't a security boundary on its own.
Anthropic's permissions page is the source for every rule here, read on November 11, 2026, with Claude Code at 2.1.322 on npm. In its words, permission rules "are enforced by Claude Code, not by the model," so a line in CLAUDE.md can't grant or revoke anything.
Before you start
Three things shape how the steps go:
- Pick the file first. Rules live in the same settings files as every other key:
~/.claude/settings.jsonfor you,.claude/settings.jsonfor the team,.claude/settings.local.jsonfor you in one project. Our settings.json guide covers which file wins. - Committed allow rules wait for trust. Allow rules in a project's
.claude/settings.jsonapply only after each person accepts the workspace trust dialog for the folder. Deny and ask rules apply right away, since they only restrict. - "Yes, and don't ask again" writes a rule for you. For a Bash command or a web fetch domain, Claude Code saves an allow rule to
.claude/settings.local.jsonat the repository root. A file-edit approval lasts only until the session ends.
Step 1: Allow the commands you run all day
Add allow rules for the commands you'd approve every time anyway. This lets Claude Code run npm scripts and git commits without asking, and refuses commands that begin with git push:
{
"permissions": {
"allow": [
"Bash(npm run *)",
"Bash(git commit *)"
],
"deny": [
"Bash(git push *)"
]
}
}
Where you put the * decides how much a rule allows. Claude Code matches everything before the first * exactly as written, so put it after the subcommand: Bash(git log *) allows only git log commands, while Bash(git *) allows every git command. Claude Code warns at startup about an allow rule with a * before the subcommand.
| You write | Matches | Doesn't match |
|---|---|---|
Bash(npm run build) | npm run build only | anything else, since a rule with no * is one exact command |
Bash(npm run *) | npm run build, npm run test, bare npm run | npm install |
Bash(ls *) | ls -la, bare ls | lsof |
Bash(ls*) | ls -la, lsof | |
Bash(git * main) | git merge main, git push origin main | git log |
The space before a trailing * is part of the rule, which is why Bash(ls *) skips lsof and Bash(ls*) doesn't. An older form, Bash(ls:*), means the same as Bash(ls *), but only at the end of a pattern.
Compound commands don't slip through. A rule like Bash(safe-cmd *) doesn't approve safe-cmd && other-cmd: Claude Code splits commands on &&, ||, ;, | and the other shell separators, and every part has to be allowed on its own.
Step 2: Deny the files Claude must never read
Block secrets with Read deny rules:
{
"permissions": {
"deny": [
"Read(./.env)",
"Read(./.env.*)",
"Read(./secrets/**)"
]
}
}
A Read deny rule does more than block reads. It also blocks the Edit and Write tools on the same path, including creating a new file there. Claude Code makes a best-effort attempt to apply it to Grep, Glob, @file mentions and pasted image paths, and it applies to file commands it recognizes in Bash, such as cat, head, tail and sed.
Two things trip people up here:
.claudeignoredoes nothing. Anthropic says to move its entries intoReaddeny rules.Grep(path),Glob(path)andWrite(path)rules never match. Claude Code accepts them and then ignores the path. WriteRead(docs/**)instead ofGrep(docs/**), andEdit(docs/**)instead ofWrite(docs/**).
Step 3: Ask before anything you can't undo
An ask rule prompts every time, even when a more specific allow rule also matches. Use it for the commands a human should see first:
{
"permissions": {
"ask": [
"Bash(git push *)",
"Bash(npm publish *)"
]
}
}
Ask and deny rules reach further into a command than allow rules do. They match any part of a compound command, including a command inside a subshell, a command substitution or a for loop, so Bash(git clean *) still prompts for cd /tmp && git clean -f. Anthropic says that holds even in auto mode.
Ask rules also catch commands that start with an environment variable. An allow rule won't match past an assignment of most variables, but a deny or ask rule matches past any of them, so a Bash(rm *) deny still matches FOO=bar rm -rf tmp/.
Step 4: Allow or deny MCP tools
MCP tools use their full names. mcp__github__* matches every tool from a server named github, and mcp__github__get_issue matches one tool. To deny every MCP tool from every server, use mcp__* as a deny rule.
Allow rules are stricter. They accept a wildcard only after a literal mcp__<server>__ prefix, so mcp__github__* works as an allow rule and mcp__* doesn't: an unanchored allow glob is skipped and approves nothing. Allow rules for MCP tools also take no specifier in parentheses. Add one, and the rule matches no call at all.
Step 5: Check what's in force
Run /permissions. The dialog lists every rule and the settings file it came from, and you can add or remove rules there. A change applies from Claude's next tool call, even mid-turn.
If a rule seems to do nothing, check the startup messages. Claude Code warns about a deny or ask rule whose tool name matches no known tool, which catches typos, and claude doctor lists the rules it skipped. Tool names have to be the canonical ones from Anthropic's tools reference, not the labels shown in the transcript.
Reference: the order rules run in
| Order | Rule type | What happens when it matches |
|---|---|---|
| 1 | Deny | The call is blocked. A bare tool name, such as Bash, removes the tool from Claude's context entirely |
| 2 | Ask | Claude Code prompts, even if an allow rule also matches |
| 3 | Allow | The call runs without a prompt |
| 4 | No match | The permission mode decides |
An allow rule can't carve an exception out of a deny rule. A broad Bash(aws *) deny blocks aws s3 ls even if Bash(aws s3 ls) is in your allow list.
The same order holds across files. If your user settings allow something and the project denies it, the deny wins, and a user-level deny also beats a project-level allow. No other level can override a managed deny.
Reference: how path rules anchor
Read and Edit rules use gitignore syntax, and the leading characters decide where a path starts:
| Pattern | Starts at | Example |
|---|---|---|
//path | The filesystem root | Read(//Users/alice/secrets/**) |
~/path | Your home directory | Read(~/.zshrc) |
/path | The settings file's location | Edit(/src/**/*.ts) in a project file means the project's src |
path or ./path | The current directory | Read(*.env) |
The single slash is the trap. /Users/alice/file isn't an absolute path, and in ~/.claude/settings.json a rule like Read(/secrets/**) blocks ~/.claude/secrets/**, not a secrets folder in your project. For a user-level rule that applies in every project, use // or ~/.
Depth has one more rule. A bare filename matches at any depth, so Read(.env) covers every .env under the current directory. A single-folder pattern like secrets/** matches at any depth as a deny or ask rule, but only directly under the current directory as an allow rule.
Where Claude Code permissions stop protecting you
A Bash rule matches the command text Claude writes. Anthropic is explicit that a deny or ask rule "isn't a security boundary around the program":
| Rule | Stops | Doesn't stop |
|---|---|---|
Bash(curl *) | curl https://example.com | /usr/bin/curl https://example.com, sh -c 'curl https://example.com' |
Bash(rm *) | rm -rf build/ | /bin/rm -rf build/, bash -c 'rm -rf build/' |
File rules have the same shape. They don't cover a script that opens files itself, such as a Python or Node program, or a command that reads files without naming them. For limits that don't depend on how a command is written, Anthropic points to the sandbox, which our Claude Code sandbox guide covers. To inspect the full command with your own logic, use a PreToolUse hook, as our hooks guide shows.
Hooks and rules combine in one direction. A hook that exits with code 2 blocks a call before any rule runs, even if an allow rule matches. A hook that returns "allow" can't get past a deny or ask rule.
Reference: team controls
Two keys matter most when a team shares a policy:
allowManagedPermissionRulesOnlymakes managed settings the only source of permission rules, so rules in personal and project files stop counting. Only managed settings can set it.disableBypassPermissionsModeturns off bypass mode. It usually lives in managed settings, but it works from any scope, so a developer can also set it to lock themselves out.
Permission modes, such as auto mode and bypass mode, decide what happens when no rule matches. Our guide to whether Claude Code is safe for a business compares them.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| An allow rule never stops the prompt | An ask or deny rule matches the same call, or part of a compound command isn't allowed | Check /permissions for a broader ask or deny rule |
| You still get prompted after "don't ask again" | That saved a local allow rule, and an ask rule in a project or managed file outranks it | Remove or narrow the ask rule at its source |
A Grep(path) deny doesn't stop searches | Grep path rules never match | Use Read(path) |
A user-level Read(/secrets/**) does nothing in projects | A single slash anchors at ~/.claude in user settings | Use // or ~/ |
| A teammate still gets prompted for a committed allow rule | Project allow rules wait for workspace trust | Have them accept the trust dialog |
| An MCP allow rule matches nothing | It has a specifier in parentheses, or an unanchored glob | Use mcp__<server>__* or the full tool name |
Frequently asked questions
How do permissions work in Claude Code?
Claude Code checks each tool call against allow, ask and deny rules in your settings files. Deny rules run first, then ask, then allow, and the first match decides. With no match, the permission mode decides. Rules are enforced by Claude Code itself, so instructions in a prompt or CLAUDE.md can't change them.
How do I stop Claude Code asking for permission every time?
Add allow rules for the commands you approve anyway, such as Bash(npm run *) for every npm script, or choose "Yes, and don't ask again" on a prompt, which saves the rule for that repository. Keep ask rules for anything you can't undo, since they still prompt.
Can an allow rule override a deny rule in Claude Code?
No. Deny rules are checked first, and the first match wins regardless of which rule is more specific. A broad deny like Bash(aws *) blocks aws s3 ls even when an allow rule names it exactly. A deny in any settings file also beats an allow in any other.
How do I stop Claude Code reading my .env file?
Add Read(./.env) and Read(./.env.*) to permissions.deny. That also blocks edits to those files. It doesn't stop a script that opens files itself, so for a hard guarantee turn on the sandbox. A .claudeignore file has no effect; move its entries into Read deny rules.
What does the star do in a Claude Code Bash rule?
It stands in for any text, spaces included. Put it after the subcommand: Bash(git log *) allows only git log commands, while Bash(git *) allows every git command. A trailing star with a space before it also matches the bare command, so Bash(ls *) matches ls but not lsof.
Why does my Claude Code permission rule not match?
Usually one of four reasons: a deny or ask rule matches first, the path anchors somewhere other than you expected, the rule uses Grep, Glob or Write with a path, which never match, or the tool name is a transcript label rather than the canonical name. Check /permissions and the startup warnings.
Are Claude Code permission rules a security boundary?
Not on their own. A Bash rule matches the command text Claude writes, so the same program called by its full path or through sh -c isn't matched. Anthropic recommends the sandbox for limits that don't depend on how a command is written, and PreToolUse hooks for your own checks.
Write down why each rule exists
A permission rule says what Claude Code may do. It doesn't say why, and without the reason Claude keeps trying the blocked command and working around the block. Our guide to compound engineering for Claude Code shows how to build those written rules up one fix at a time, and our Claude Code best practices for a team covers which rules belong in settings and which in CLAUDE.md.
Stop guessing. Score your first workflow.
Score one workflow in about five minutes. Nine questions, a grade, and the specific gap holding it back - no call required.
Related Posts

Claude Code on Amazon Bedrock: when a team should route through AWS
Route Claude Code through Amazon Bedrock when AWS has to process your prompts and code, or your compliance program needs it. What a team gives up on Bedrock, what it gains, and the AWS-billed middle option, Claude Platform on AWS.

Claude Code settings.json: every scope, and which file wins
Claude Code reads settings from four JSON files plus managed settings, and the highest level that sets a key wins. Where each file lives, who it reaches, how lists merge, and why a key you commit can still miss a teammate.

Claude Code monitoring for a team: which tool answers which question
Claude Code monitoring for a team takes three tools: the analytics dashboard for who uses it and what it ships, the spend report or Console for cost, and OpenTelemetry for live per-person tokens and cost. What each shows, who can open it, and the limits to know.
