Claude Code permissions: allow, ask and deny rules
Claude Code allowed tools settings explained: the rule syntax, where allow, ask and deny rules live, a safe starter set, and how deny rules protect files.
Contents
Claude Code's allowed tools settings are permission rules in a settings.json file: allow runs a tool without asking, ask always prompts, deny blocks it. A rule names a tool, optionally with a pattern, like Bash(npm run test *) or Read(./.env). Deny is checked first, then ask, then allow, and the first match decides.
That's the whole model. The rest of this guide is the syntax, where the rules live, a starter set we'd paste into a new project, and the places rules don't reach.
The rule syntax#
A rule is Tool or Tool(specifier), per Anthropic's permissions docs:
| Rule | What it matches |
|---|---|
Bash |
Every shell command |
Bash(npm run build) |
Exactly npm run build |
Bash(npm run *) |
npm run build, npm run test --watch, npm run |
Bash(git log *) |
Every git log command, and nothing else from git |
Read(./.env) |
Reading .env in the current directory |
Read(.env) |
Any .env at or below the current directory |
Edit(/src/**) |
Edits under src/ at the project root |
Read(~/.ssh/**) |
Anything under ~/.ssh in your home folder |
Read(//etc/**) |
The absolute path /etc/... (note the double slash) |
WebFetch(domain:docs.python.org) |
Fetches from that host |
mcp__github__* |
Every tool of the MCP server named github |
Four details that trip people up:
- Put the
*after the subcommand.Bash(git log *)allows onlygit log;Bash(git *)allows every git command,git pushincluded. - The space before a trailing
*counts.Bash(ls *)doesn't matchlsof;Bash(ls*)does. - Compound commands are split.
Bash(npm test *)doesn't approvenpm test && rm -rf build: each part must match on its own. /pathisn't absolute. A single leading slash anchors at the settings file's project (or~/.claudein user settings). Use//for a real absolute path and~/for your home folder.
Path rules use Read and Edit only. A Write(...) or Glob(...) path rule is accepted but never matches; write Edit(...) and Read(...) instead.
Where Claude Code's allowed tools settings live#
| File | Who it affects | Use it for |
|---|---|---|
~/.claude/settings.json |
You, in every project on this machine | Your personal allow list, global deny rules for secrets |
.claude/settings.json |
Everyone who works in the repo (commit it) | The team's shared rules |
.claude/settings.local.json |
You, in this project only (kept out of git) | Personal exceptions; where "Yes, and don't ask again" saves |
| Managed settings | Everyone your organisation deploys it to | Policy nobody can override |
When the same list appears in several files, Claude Code merges them. And a deny anywhere beats an allow anywhere: if your user file allows something and the project file denies it, it's denied, and the reverse holds too.
One safety detail: allow rules in a repo's committed .claude/settings.json only take effect after you accept the workspace trust dialog for that folder. deny and ask rules apply straight away, because they only restrict.
/permissions opens the rules in force as a list you can edit mid-session, each one tagged with the file it came from.
A safe starter set#
The file below is close to what we'd put in .claude/settings.json for a Node project whose package.json has lint, test and build scripts. Rename the commands to match yours.
{
"$schema": "https://json.schemastore.org/claude-code-settings.json",
"permissions": {
"allow": [
"Bash(npm run lint)",
"Bash(npm run test *)",
"Bash(npm run build)",
"Bash(git add *)",
"Bash(git commit *)"
],
"ask": [
"Bash(git push *)",
"Bash(npm install *)"
],
"deny": [
"Read(.env)",
"Read(.env.*)",
"Read(./secrets/**)",
"Read(~/.ssh/**)",
"Read(~/.aws/**)",
"Bash(curl *)",
"Bash(wget *)"
]
}
}
Why these choices:
- Allow the boring loop. Lint, test, build and commit are what Claude runs most. Common reading commands (
ls,cat,grep,find,diffand read-onlygit) already run without a prompt, so they need no rule. - Ask for what leaves the machine or changes dependencies. A push or a new package is worth a glance.
- Deny secrets and raw network tools. Claude still has the WebFetch tool for documentation, and you can allow specific hosts with
WebFetch(domain:...).
Note Read(~/.ssh/**) and Read(~/.aws/**) sit better in ~/.claude/settings.json, so they cover every project. A repo file can carry them too.
How deny rules protect files#
A Read deny rule blocks Claude's file tools from reading the path, and also blocks Edit and Write on it. It also applies to shell commands Claude Code recognises as file commands, such as cat, head, tail, sed and tee, and to redirects like < file.
Anthropic's docs are clear about where that stops: deny rules "don't apply to a command that reads files without naming them, such as grep -r pattern .", or to "a Python or Node script that opens files itself". The same goes for Bash rules: Bash(curl *) stops curl https://example.com but not /usr/bin/curl or sh -c 'curl ...'. In the docs' words, a deny rule "isn't a security boundary around the program".
So rules are the first layer, not the only one. When a limit must hold:
- Turn on the sandbox (
/sandbox, macOS, Linux and WSL2), which makes the operating system enforce file and network limits on every shell command and the processes it starts. - Add a
PreToolUsehook that reads the whole command and blocks what you don't want. Our hooks examples include one that blocksrm -rfand one that protects.env, lockfiles and.git. - Keep secrets out of reach in the first place. If the file isn't on the machine, no rule has to hold.
Rules and modes work together#
A tool call that matches no rule goes to the permission mode. Shift+Tab switches the mode in the CLI, and --permission-mode sets it at launch.
| Mode | A tool call with no rule |
|---|---|
default |
Prompts once |
acceptEdits |
File edits in the project go through |
plan |
Nothing is edited until you approve the plan (when plan mode is worth it) |
bypassPermissions |
Runs |
--dangerously-skip-permissions starts a session in bypassPermissions, and there allow rules stop mattering, since everything runs anyway. Deny rules don't. Anthropic's permission modes page has it in one line, "Deny rules block in every mode, including bypassPermissions", and explicit ask rules still prompt there too. Before an overnight run with that flag, write your deny rules and hooks first.
How Shuriken fits in#
Shuriken runs Claude Code on your own computers, and each chat has a mode picker. For Claude Code it lists Plan ("Reads and plans; changes nothing"), Always ask ("Asks before each new tool"), Accept edits ("Edits files without asking") and Auto-approve ("A model approves the safe requests"), plus Full access. Approvals show up in the chat, on whichever device you're using, and you get a notification on your computer when one is waiting.
Every chat except the Main chat works in its own git worktree on its own branch. Its edits reach your branch through Finish, which runs the checks listed in your shuriken.json before it merges. That stops two chats breaking each other's builds. It isn't a security boundary: the agent still runs as you, so the rules above matter as much in Shuriken as in a terminal. If you'd like every chat's approvals in one place, download Shuriken.
Sources
- Configure permissions (Claude Code docs) (accessed 22 October 2026)
- Settings files and precedence (Claude Code docs) (accessed 22 October 2026)
- Permission modes (Claude Code docs) (accessed 22 October 2026)
Frequently asked questions
How do I stop Claude Code asking for permission every time?
Pick Yes, and don't ask again on the prompt, which saves the rule to .claude/settings.local.json, or write allow rules such as Bash(npm run test *) into a settings file yourself. Keep deny rules for what must never run.
Which wins, an allow rule or a deny rule?
Deny. Claude Code checks deny, then ask, then allow, and the first match decides. A specific allow rule can't carve an exception out of a broader deny rule.
Where is Claude Code's settings.json?
Your own rules for every project go in ~/.claude/settings.json. Team rules go in .claude/settings.json in the repo, and your personal rules for one project in .claude/settings.local.json.
Do deny rules stop Claude reading a file with cat?
For commands Claude Code recognises, such as cat, head and tail, yes. Not for a script that opens the file itself; use the sandbox for that.