Rules cut prompt fatigue for safe commands while keeping dangerous ones blocked in every mode.
Modes set the baseline; permission rules fine-tune it. Rules live in a settings file under permissions, in three lists:
allow: run without asking (for example your test command),ask: always ask, even in modes that would auto-approve,deny: never allowed, in any mode.
They are checked in that safety order: deny first, then ask, then allow. A broad deny always wins over a narrower allow.
A rule names a tool and optionally a specifier in parentheses:
Bash(npm run test *): any command starting withnpm run test. The*is a wildcard; the space before it matters (Bash(ls *)matchesls -labut notlsof).Read(./.env)andRead(./secrets/**): file paths, written like.gitignorepatterns.Edit(...)works the same for writes.WebFetch(domain:docs.python.org): web fetches to one domain.mcp__github__*: all tools from one MCP server namedgithub.- A bare tool name such as
Bashmatches every use.
Bash rules understand shell operators: allowing Bash(npm test *) does not allow npm test && rm -rf build. But matching is by command text, so a deny on Bash(git push *) does not catch git -C . push. Treat Bash denies as guard rails, not a security boundary.
Manage rules with /permissions, which lists each rule and the file it came from, or edit settings files directly. Choosing "yes, don't ask again" at a prompt writes an allow rule for you. /fewer-permission-prompts can suggest an allowlist from your history.
Rules are enforced by Claude Code, not by the model, so they hold even if a prompt or file tries to talk Claude into something.
Example
{
"permissions": {
"allow": [
"Bash(npm run lint)",
"Bash(npm run test *)",
"Bash(git diff *)",
"WebFetch(domain:developer.mozilla.org)"
],
"ask": [
"Bash(git push *)"
],
"deny": [
"Read(./.env)",
"Read(./.env.*)",
"Read(./secrets/**)",
"Bash(curl *)"
]
}
}Remember
- allow, ask and deny lists are checked deny, then ask, then allow
- Specifiers: Bash(cmd ), Read/Edit(path patterns), WebFetch(domain:...), mcp__server__
- Rules are enforced by Claude Code itself; manage them with /permissions
Watch out for
- Writing an allow rule like Bash(git *) to stop prompts, which also pre-approves git push, reset and clean.