Claude Code Permissions Rule Builder
Build allow, ask, and deny rules, then test a tool call against them. Deny wins over ask, ask wins over allow.
Add a rule
Presets
Rules (checked in this order)
Test a call
The tester is a simplified model of the real matcher. It does not split compound commands or strip wrappers.
settings.json
Merge the "permissions" key into your settings file. Do not replace the file.
Deny, then ask, then allow
Claude Code checks deny rules first, then ask, then allow. The first bucket with a match decides, and specificity does not change that. If Bash(aws *) is in deny, then Bash(aws s3 ls) in allow never runs. The same holds for ask over allow.
A bare tool name in deny, such as Bash, removes the tool from Claude's context. A scoped rule like Bash(rm *) keeps the tool and blocks matching calls.
Rule syntax
| Rule | Matches |
|---|---|
Bash or Bash(*) | Every Bash command |
Bash(npm run build) | That exact command |
Bash(npm run *) | Commands starting with npm run, and bare npm run |
Bash(ls*) | ls, ls -la, and also lsof (no space before the star) |
Read(./.env) | The .env file in the current directory |
Read(~/Documents/*.pdf) | PDFs in the home Documents folder |
Read(//Users/alice/secrets/**) | Absolute path (double slash) |
Edit(docs/**) | File edits under docs. Use Edit, not Write, for path rules. |
WebFetch(domain:example.com) | Fetches to example.com only |
WebFetch(domain:*.example.com) | Any subdomain, not example.com itself |
Put the star after the subcommand: Bash(git log *) allows only git log. A star before the subcommand, like Bash(git * main), matches every git subcommand and triggers a startup warning for allow rules. The :* suffix is an older equivalent of a trailing *.
Limits of Bash rules
- Compound commands are split on
&&,||,;,|and newlines. Each part must match on its own. - A deny rule for
rm *does not match/bin/rmorbash -c 'rm ...'. - Wrappers such as
timeout,time,niceandnohupare stripped before matching.npxanddocker execare not. - For a hard block on file reads, use
Readdeny rules and the sandbox, not only Bash patterns.
Where the rules live
Put them under permissions in .claude/settings.json (shared), .claude/settings.local.json (personal), or ~/.claude/settings.json. Paths in user settings that start with / are relative to that settings location, so use // or ~/ there.
Checked against code.claude.com/docs/en/permissions on 2026-10-20. unverified: exact defaultMode value list for your installed version; run /permissions in Claude Code to see what yours supports.
FAQ
What is the order of Claude Code permission rules?
Deny is checked first, then ask, then allow. The first bucket with a matching rule decides. A narrow allow rule cannot override a broad deny rule.
How do I stop Claude Code from reading my .env file?
Add a Read deny rule such as Read(./.env) to permissions.deny in settings.json. A .claudeignore file has no effect according to the permissions docs.
What does Bash(npm run *) match?
Any command that starts with npm run, including npm run build and npm run test --watch. It also matches bare npm run. It does not match npm install.
Is a Bash deny rule a security boundary?
Not by itself. Bash(rm *) does not match /bin/rm or bash -c 'rm ...'. For a hard boundary, combine deny rules with the sandbox. Argument-constraining patterns like Bash(curl http://github.com/ *) are fragile.
Which file do permission rules go in?
Under the permissions key in a settings file: .claude/settings.json (shared), .claude/settings.local.json (yours), or ~/.claude/settings.json (all projects).