Permission rules look like globs and behave like something narrower, which is where the trouble starts. A rule is matched against the command as written, and there is no implicit trailing wildcard — so a rule covering a test command does not cover the same command with a flag on the end. Deny beats ask, ask beats allow, and anything unmatched falls through to whatever the mode's default is. All of that is easy to state and hard to hold in your head while writing a dozen rules. This tests them: paste the rules, paste the commands you expect to run, and see the outcome for each one alongside the rule that decided it. It also finds rules that can never fire, including allow rules sitting under a broader deny.
Features
Every command shown with its outcome and the rule that decided it
Deny over ask over allow, applied the way the real resolver does
Finds rules that can never fire because a broader rule covers them
Catches an allow rule made unreachable by a deny
Reports rules that are not valid rules at all
Shows unmatched commands rather than guessing what happens to them
Wildcards handled as written — no invisible trailing match
Emits the settings.json permissions block
How to use the Permission Rule Tester
Paste your allow, ask and deny rules
Paste the commands you expect to run
Read the outcome for each, and which rule decided it
Copy the permissions block into settings.json
Example
Input
Bash(npm run test)
Output
npm run test:unit — no rule matched
There is no implicit wildcard: the rule covers that exact command only.
Common errors & troubleshooting
My rule does not cover the command I expected. — There is no trailing wildcard. Bash(npm run test) matches only that command; Bash(npm run test:*) covers the variants.
A command I allowed still asks. — Something with higher precedence matched it. Every matching rule is listed for each command, in the order they are resolved.
One of my rules is marked unreachable. — A broader rule of at least equal precedence already covers everything it matches. An allow under a broad deny is the usual case — deny always wins, so the allow does nothing.
A line is reported as not a rule. — Rules are Tool or Tool(pattern). A bare command with no tool prefix is not a rule and is ignored.
Frequently asked questions
Is the pattern a glob or a regular expression?
Neither exactly. It is a literal match with * standing for any run of characters, applied to the command as written. Regular-expression syntax is treated as literal text, which is why a rule containing a dot matches only a dot.
What happens to a command no rule matches?
It falls through to whatever the current permission mode does by default — which may be to ask, or to allow. The tester shows these as unmatched rather than guessing, because the answer depends on the mode rather than the rules.
Which wins when several rules match?
Deny, then ask, then allow. Every matching rule is shown for each command so you can see what was overridden rather than inferring it.
Why does it flag an allow rule under a deny?
Because the allow can never take effect — deny always wins. People write those expecting an exception to a broad deny, and there is no error to tell them it did nothing.
Should I write mostly allow rules or mostly deny rules?
Deny rules are the ones that hold. An allow list makes routine work quiet; a deny list is what stops the specific things you never want to happen, and it wins over everything else.
Related tools
Hooks Builder — Build settings.json hooks and catch the matchers that never fire.
Subagent Builder — Build a subagent definition file with its tools and model set.
Slash Command Builder — Build a custom slash command, with arguments and a tool allow-list.
MCP Server Config Builder — Generate an MCP server entry for stdio, HTTP or SSE — and the CLI one-liner.