返回《Claude Code 架构设计指南》

权限系统,规则、模式与分类器

IT周瑜

模型想执行一个工具,最后一道闸门在 src/utils/permissions/permissions.ts,入口函数 hasPermissionsToUseTool。它分两层,内层 hasPermissionsToUseToolInner 跑一条有序决策链,外层对内层结论做模式转换。函数签名上的返回值有三种行为,allow 放行,deny 拒绝,ask 转交用户裁决。整个系统的复杂度都在这条链和这几个转换上。

决策链的顺序有讲究,源码里用编号注释标得清清楚楚。第一步查规则,用户在配置里写的 deny 规则命中整工具,直接拒绝,没有商量。ask 规则命中则要询问,唯一的例外是 Bash 配了沙箱自动放行时往下放行,交给 Bash 自己的检查。第二步让工具自查,调用上一章说的 checkPermissions,内容级的判断在这里,某个路径能不能改,某条命令能不能跑。自查的结论分几个出口,内容级 deny 直接拒绝。两个特殊出口越过后面一切模式,内容级 ask 规则和安全检查,改 .git、.claude、.vscode 这类敏感路径触发的询问连 bypass 模式都拦不住,源码注释称之为 bypass 免疫。第三步看模式,bypassPermissions 直接放行。第四步查 allow 规则,命中放行。全都走完还没结论,默认 ask。

hasPermissionsToUseTool 规则匹配 deny 命中直接拒绝 ask 命中转询问 deny deny 工具自查 tool.checkPermissions 内容级规则与安全检查 强制询问出口 safetyCheck 与内容级 ask bypass 模式也不能跳过 bypassPermissions 直接放行 allow 规则 命中放行 allow allow 都没命中 默认 ask 模式转换在外层 dontAsk 把 ask 转成 deny auto 把 ask 交给分类器
图 6-1 决策链自上而下,右侧是三个出口,模式转换在链外收尾

模式一共六种,default、plan、acceptEdits、bypassPermissions、dontAsk、auto,终端里按 shift 加 tab 循环切换,切换逻辑在 getNextPermissionMode。模式对链上结论做二次加工,外层函数在链返回后接手。dontAsk 把 ask 转成 deny,用户不想被问,那就一律拒绝。bypassPermissions 在链中段就放行了,但如前所述,安全检查和内容级 ask 规则拦在它前面,用户显式配置的约束优先级高于模式,这是整条链最重要的优先级安排。

auto 模式是这份源码里最值得细读的设计,界面上俗称 YOLO。它接手每一个 ask 决策,先走快路径,安全工具白名单直接放行,acceptEdits 范围内的编辑重放结论。快路径都 miss 才请出分类器,一个用 sideQuery 跑的旁路模型调用,不进对话历史,专门裁决这次工具调用危不危险,代码在 yoloClassifier.ts,提示词单独放在一个目录里维护。auto 和 bypass 的差别就在这里,bypass 是无条件信任,auto 是每一次询问都过一道机器审查,审错了还有熔断兜底。

熔断机制叫 denialTracking,统计连续拒绝次数。分类器连续拒绝达到阈值,就不再自动裁决,退回真人询问,避免模型在一条死路上无限空转。任何一次放行都会清零计数,源码特意注释了这个设计,让规则放行的成功调用也能打断连续拒绝的记录。计数状态对异步子代理有单独的本地副本,因为异步子代理改不动全局状态,注释里说明了不这样做阈值永远到不了。

规则本身的内容级匹配也值得一提。Bash 的规则可以写命令级别的通配,git 空格星号表示放行所有 git 子命令,匹配实现在 shellRuleMatching,依赖每个工具提供的匹配器准备函数把规则编译成断言。配置的来源和写法在这份仓库配套的权限配置笔记里有完整整理,六种配置方式按优先级叠在这个决策链的入口处。理解了链、模式、分类器三层,权限系统的任何行为都能对上号。