Claude Code permissions: how deny, ask and allow rules work, and when they don't

Claude Code checks deny rules first, then ask, then allow. That order is simple; what a rule actually covers is not. Here is the model, the public bug reports, and a test routine.

The short answer

Claude Code evaluates permission rules in the order deny, then ask, then allow, across every settings file, and the first match wins.1 So a deny rule always beats an allow rule, wherever each is written. Ask and deny rules also apply when any part of a compound command matches, so git status && rm -rf build meets a rule written for rm.1

What a rule covers is narrower than many people assume. A Read deny rule covers Claude’s file tools and recognised file commands such as cat and head, not a program that opens the file itself.1 And there are public reports of deny rules not applying at all in some versions and setups. Treat rules as one layer, and test them on your own machine.

Every MCP tool call goes through your rules first. Here a delete that doesn’t say which rows is stopped before it reaches the database. These are the rules deedglass init starts with; the call is made up.

How a rule is chosen

Rules live under permissions in .claude/settings.json (shared with the project), .claude/settings.local.json (yours only) and ~/.claude/settings.json (every project). They are merged and then checked in one order:

Step If a rule matches Example
1. Deny The call is refused Bash(rm -rf *)
2. Ask You get a permission prompt Bash(npx prisma migrate *)
3. Allow The call runs without a prompt Bash(npm test)
No match The mode decides Usually a prompt

In a Bash rule, a * can stand anywhere and matches any text, and a trailing :* equals a trailing *.1 That makes it easy to write a rule broader than you meant. Bash(npm run *) allows every script in package.json, including the one that resets your database.

What a rule covers

Three details trip people up.

Path forms. A Read deny rule written as a bare name such as Read(.env) matches at any depth. The docs read Read(./.env) as only the .env in the current directory.1 If you want every .env in the tree, use the bare form.

Programs that open files themselves. A deny on Read(.env) stops Claude’s Read tool and cat .env. It doesn’t stop node -e "console.log(require('fs').readFileSync('.env','utf8'))", or a test runner that loads .env and prints a connection error with the URL in it.1 If a secret must not reach the model, don’t leave it in a file the agent’s processes can open.

Commands hidden inside other commands. In claude-code#80868, a user in auto mode had ask rules for npx prisma db:* and npx prisma migrate:*. A compound command with a command substitution passed the production DATABASE_URL from .env as --shadow-database-url to prisma migrate diff; no prompt appeared, and the production database was wiped, then restored from a nightly backup.3 The rule was written for one spelling. The command used another.

Modes change what allow means

The permission mode decides what happens when nothing matches, and some modes change the rules themselves:

  • Deny rules block in every mode. Explicit ask rules still prompt in auto and bypass modes, and dontAsk denies whatever would have prompted.2
  • Auto mode drops broad allow rules when you enter it: a blanket Bash(*), wildcarded interpreters and package-manager run commands.2
  • A project file can’t switch on the risky modes. defaultMode set to auto or bypassPermissions in .claude/settings.json or .claude/settings.local.json doesn’t take effect.2
  • You can turn modes off. disableBypassPermissionsMode or disableAutoMode set to "disable" in any settings file turns that mode off.2

For a shared repository, the last two are worth using. They stop one teammate’s local file from widening what everyone’s agent may do.

When deny rules didn’t apply

Rules are software, and software has bugs. Two public cases:

  1. Symbolic links. CVE-2026-25724: before version 2.1.7, Claude Code didn’t strictly enforce deny rules when it reached a file through a symbolic link, so it could read a restricted file through the link. It was fixed in 2.1.7.5 Check your version with claude --version.
  2. Rules read late. In claude-code#46672, a Read(*/.env*) deny rule in the user settings file didn’t stop the Read tool returning a project .env at session start. It began blocking only after the settings file was edited mid-session. The issue was closed as a duplicate of a report about permissions not updating in active sessions.6

Neither case means rules are useless. Both mean a rule you never tested is a rule you don’t know works. Current versions reload settings files when they change, and /status lists the files that loaded.8 Check it after every change, and keep a short test you can run.

A starting settings.json

A conservative project file for a repository with a database:

Code Data in JSON, the format software uses to pass records and settings.

{
  "permissions": {
    "deny": [
      "Read(.env)",
      "Read(.env.*)",
      "Bash(rm -rf *)",
      "Bash(npx prisma migrate reset*)",
      "Bash(npx prisma db push --force-reset*)"
    ],
    "ask": [
      "Bash(npx prisma *)",
      "Bash(psql *)",
      "Bash(git push *)"
    ],
    "allow": [
      "Bash(npm test)",
      "Bash(npm run lint)",
      "Bash(git status)",
      "Bash(git diff *)"
    ],
    "disableBypassPermissionsMode": "disable"
  }
}

One timing detail: allow rules in a committed project file wait until each teammate trusts the folder, while deny and ask rules apply right away.8 Notice what is missing from allow: npm run *. Allow the scripts you have read, one by one. For anything a pattern can’t express, such as SQL inside a psql command, add a hook. A PreToolUse hook can answer deny or ask after reading the full command, and deny and ask rules are still evaluated whatever it returns.4 Our hook post has a tested one.

Test your rules

Run this routine after any change to settings, and after upgrading Claude Code:

  1. Run /status and check that the Setting sources line lists the file you changed.8
  2. Ask Claude to read .env. It should be refused. Then ask it to print the file with a one-line node or python command. If that works, your secret is reachable; move it out of the tree.
  3. Ask for a denied command inside a compound one, such as git status && rm -rf tmp. It should be refused.1
  4. Ask for a destructive command through a script, such as npm run db:reset. If it runs without a prompt, your allow list is too broad.
  5. Try a symlink to .env from another directory, and confirm you’re on 2.1.7 or later.5
  6. Write down what you tested and the version. Next upgrade, run it again.

You can also check a repository without running anything. deedglass scan reads .claude/settings.json, .claude/settings.local.json and, with --user-configs, ~/.claude/settings.json, and says which spellings of destructive database commands would run without a prompt.7 It reads only the names of database URL variables in .env files, never their values, and flags the ones no Read deny rule covers.7 For tool calls beyond the shell, see approvals for MCP tool calls; for the database role underneath it all, the Postgres write access guide. To try the checks without installing anything, open the playground; to grade what an agent can reach on your own machine, run npx deedglass scan (the grade, explained).

Written with AI assistance and checked against our claims register.

The takeaway

Claude Code deny rules beat ask and allow but cover only its own tools and known commands; a symlink CVE and a stale-settings bug say: test them.

Read nextA Claude Code hook that asks before DROP TABLE, migrate reset and rm -rf Try it: install in 7 s

Related on this site

More posts

Sources

  1. 1Claude Code docs: Permissions · code.claude.com · checked 26 October 2026
  2. 2Claude Code docs: Permission modes · code.claude.com · checked 26 October 2026
  3. 3anthropics/claude-code #80868 · github.com · checked 26 October 2026
  4. 4Claude Code docs: Hooks reference · code.claude.com · checked 26 October 2026
  5. 5GHSA-4q92-rfm6-2cqx / CVE-2026-25724: deny bypass via symbolic links · github.com · checked 26 October 2026
  6. 6anthropics/claude-code #46672 · github.com · checked 26 October 2026
  7. 7Deedglass docs: the scan · docs.deedglass.com · checked 26 October 2026
  8. 8Claude Code docs: Settings files and precedence · code.claude.com · checked 26 October 2026

Published 26 October 2026, sources last checked 26 October 2026. If something here is out of date, write to hello@deedglass.com.

Try it on your own setup

npx deedglass scan

Grades only your own setup. Nothing is uploaded.

Pricing