三类规则按 deny → ask → allow 的顺序求值,第一条命中的规则直接决定结果,且与规则的”具体程度”无关——一条宽泛的 deny 永远挡住一条精确的 allow,ask 也会盖过同样命中的 allow。这是 Claude Code 默认拒绝策略在工程上的直接体现,也是写权限配置时最容易踩的坑。
一、求值规则的一句话总结
官方文档原话:”Rules are evaluated in order: deny, then ask, then allow. The first match in that order determines the outcome, and rule specificity doesn’t change the order.” 这意味着:
- 先扫所有 deny,命中即拒绝;
- 未命中 deny 再扫 ask,命中即弹确认;
- 未命中 deny 和 ask 最后扫 allow,命中即放行;
- 三类都没命中时,默认升级为 ask(让用户决定)。
二、为什么具体程度不改变顺序
如果按”更具体的规则覆盖更宽泛的规则”这种直觉设计权限系统,配置会变得难维护且易误用。Claude Code 选择了”安全优先”的简单顺序:deny 永远是最先被检查的类别,ask 次之,allow 最后。这样写规则时只需关心”我要不要拦”,不用纠结”哪条更具体”。
配置示例:deny 不会被 allow 覆盖
permissions:
allow:
- "Bash(aws s3 ls)"
deny:
- "Bash(aws *)"
上面这个配置里,”aws s3 ls” 仍然会被 Bash(aws *) 拦下,不会因为它更具体就被放行。
三、deny 规则的两类行为
deny 规则根据写法有两种效果,必须分清:
| 写法 | 效果 | 适用场景 |
|---|---|---|
Bash(裸工具名) |
把整个工具从模型上下文里删除 | 完全禁止某个工具类 |
Bash(rm *)(带括号限定) |
工具仍可见,匹配的动作在调用时被拦 | 允许用 Bash 但禁止特定命令 |
裸 deny 适用”完全不该出现”的高风险工具(如 EndConversation 之外任意工具都可裸 deny);带 specifier 的 deny 适用”工具可用,但这条要拦”的精细控制。两者混用容易导致”以为挡住了,结果模型根本看不到工具”或反之。
四、ask 规则的边界
ask 规则是”总会弹确认”的兜底。三类容易混的点:
- ask 优先于 allow。即使用户已经配了
Bash(git commit *)这种精确 allow,仍然可以为Bash(git push *)单独配一条 ask,每次推送都让人确认; - ask 不会”自动记住”。和允许规则在弹窗里点”是,不要再问”的会话级持久化不同,ask 的目的是”任何时候都要人看一眼”;
- 未识别的工具默认按 ask 处理。模型提出一个没有匹配的调用,权限系统不会自作主张放行,而是弹给用户。
五、规则合并 vs 顺序的常见误解
误解一:”更具体的 allow 应该胜过更宽泛的 deny。”——不会,三类按顺序求值,deny 先吃。
误解二:”用 allow 写明细节就能逃开 deny。”——同样不会,因为顺序问题,deny 命中时根本没有 allow 机会。
误解三:”ask 配过就不会再弹。”——除非在弹窗里点”是,不要再问”且模式是按命令持久化的 allow,否则 ask 每次都弹。
误解四:”deny 写错只是放错一次。”——deny 写错可能让模型根本看不到工具(裸 deny)或被绕开(带 specifier 的 deny 没覆盖到位),错误形态更隐蔽。
六、可执行的规则排错步骤
- 跑
/permissions命令,列出所有当前生效的规则及其来源(用户级 / 项目级 / 托管级); - 找被错误拦截的调用,先看 deny 列表里有没有命中(最常见原因);
- 命中后判断是宽泛 deny 还是精确 deny:宽泛 deny 想要放行只能去掉这条;
- 没命中 deny 再看 ask 列表:ask 命中是无声的,弹窗容易让人误以为是 allow;
- 仍然没命中才会落到 allow;allow 不匹配时默认 ask,确认逻辑闭环。
七、规则来源与覆盖关系
| 优先级 | 来源 | 用途 | 典型设置 |
|---|---|---|---|
| 最高 | 托管设置(managed policy) | 企业/组织统一下发 | 禁止删 ~/.ssh/、禁访问某些主机 |
| 中 | 项目级 .claude/settings.json |
仓库内共享规则 | 禁止提交 .env、限制 Bash 命令 |
| 中 | 用户级 ~/.claude/settings.json |
个人默认规则 | 允许常用 git 子命令 |
| 最低 | 会话内临时规则 | 临时调整 | 临时 deny 某危险命令 |
高优先级的设置会覆盖低优先级的同名规则。例如托管设置里 deny 了 Bash(rm -rf /),无论用户在项目或个人设置里怎么写 allow,都拦不下来。
八、配置实战
# .claude/settings.json
permissions:
allow:
- "Bash(npm run *)"
- "Bash(git commit *)"
- "Bash(* --version)"
ask:
- "Bash(git push *)"
- "Bash(npm publish *)"
deny:
- "Bash(rm -rf /)"
- "Bash(rm -rf ~)"
- "Bash(* --force *)"
- "Read(./.env)"
- "WebFetch(domain:paste.example)"
按”先 deny 兜底、再 ask 显式确认、最后 allow 提速”三段式写权限,匹配顺序就完全可控,团队成员加新规则时也不容易撞到先前的硬约束。
常见问题(FAQ)
Q1:更具体的 allow 能不能盖过宽泛的 deny?
不能。三类规则按 deny→ask→allow 顺序求值,deny 命中就拦截,与具体程度无关。
Q2:ask 和 allow 都命中时听谁的?
听 ask 的。ask 的优先级高于 allow,会强制弹确认。
Q3:deny 写错会让工具消失吗?
会的,裸 deny(如 Bash)会把整个工具从模型上下文里抹掉,模型甚至看不到它。