Claude Code permissions: a starter allow and deny list
Permission rules decide what Claude Code may do without asking. They are short, they live in a JSON file, and they are enforced by the program, not by the model.
The three lists
- allow: runs without a prompt.
- ask: always prompts, even if an allow rule also matches.
- deny: blocked.
They are checked in the order deny, then ask, then allow, and the first match wins. Two consequences: a broad deny cannot be undone by a narrower allow, and a deny in any settings file beats an allow in another one.
Where the rules go
~/.claude/settings.json: yours, for every project..claude/settings.json: the project's, safe to commit so the team shares it..claude/settings.local.json: yours, for this project only. Not for committing.
Type /permissions in a session to see every rule and which file it came from.
A starter list
{
"permissions": {
"allow": [
"Bash(npm run build)",
"Bash(npm run test *)",
"Bash(git commit *)"
],
"ask": [
"Bash(git push *)"
],
"deny": [
"Read(./.env)",
"Read(./secrets/**)"
]
}
}
Read it line by line. Build and test run freely. Commits run freely. A push always stops and asks. The .env file and the secrets/ folder are off limits to Claude's file tools.
How the patterns work
- A bare name such as
Bashmatches every use of that tool. With parentheses it matches one kind of use. - For Bash,
*matches any text, spaces included. Put it after the subcommand:Bash(git log *)allows onlygit log, whileBash(git *)allows every git command. - A rule with no
*matches that exact command only. - File rules take paths:
Read(./.env),Read(./secrets/**). Web fetches take a domain:WebFetch(domain:example.com).
Modes are a separate switch
Rules say what is allowed. The mode sets how often it asks. The names are default (asks on first use of each tool), acceptEdits (accepts file edits in the working directory), plan (explores and reads, does not edit your files), auto, dontAsk (refuses anything that would prompt) and bypassPermissions. Set a starting mode with defaultMode. Keep bypassPermissions for containers and throwaway virtual machines, where a mistake cannot reach anything you care about.
What rules do not do
- A line in CLAUDE.md is not a rule. It shapes what Claude tries. Only a rule in settings changes what is allowed.
- A Bash deny is not airtight. It does not match the same program called by another route, for example
git -C . pushagainst a rule forgit push, or a command wrapped insh -c. Treat rules as a seatbelt. For anything that must not happen, add a sandbox or keep the credential out of reach. - A
.claudeignorefile does nothing. Move its entries intoReaddeny rules. - Syntax changes between versions. A rule Claude Code cannot use is skipped with a warning at startup, and
claude doctorshows your resolved settings, so check after editing.
Check it works
- Add the rules to
.claude/settings.local.jsonin a scratch project first. - Run
/permissionsand confirm each rule is listed. - Ask for something each rule should stop, and something it should allow. Both should behave.
This page follows Anthropic's permissions documentation as we read it in October 2026. That page is the authority, and we are not affiliated with Anthropic. We wrote this from the documentation and have not security-tested the starter list.
If you want a second pair of eyes on your CLAUDE.md and settings, the Tune-Up Kit is a checklist for exactly that.
See also: Claude Code in GitHub Actions.