AI 的边界在哪里:权限决策体系与 14 步授权逻辑
AI 的边界在哪里:权限决策体系与 14 步授权逻辑
《Claude Code 架构解密》读书笔记 · 第06篇
对应章节:第5章前半(5.1-5.4)— 权限模型:在自动化与安全之间走钢丝(p.112-118)
导语
给 AI Agent 一把剪刀——它能帮你裁布,也可能剪断电线。真正的问题不是"要不要给",而是"怎么给,给多少,什么时候收回"。Claude Code 的权限系统(约 20 个模块、5000+ 行代码)用四级信任、八层规则源、14 步决策树,在"每次都弹窗确认"和"全部自动放行"之间找到了可行的中间路。本篇深入权限系统的核心数据结构与决策逻辑。
一、架构图解:权限系统的四层体系
┌──────────────────────────────────────────────────────┐
│ UI 层 │
│ useCanUseTool.tsx / PermissionPrompt.tsx │
│ → 展示权限请求、收集用户决策 │
├──────────────────────────────────────────────────────┤
│ 决策层 │
│ permissions.ts (hasPermissionsToUseTool) │
│ → 权限检查的主逻辑,14 步决策树的入口 │
├──────────────────────────────────────────────────────┤
│ 中间层(四大子系统) │
│ 规则匹配 │ 模式管理 │ AI分类器 │ 路径安全 │
│ shellRuleMatching │ PermMode │ yoloclassifier │ filesystem │
├──────────────────────────────────────────────────────┤
│ 基础设施层 │
│ 设置加载 / 规则解析 / 拒绝追踪 / 沙箱适配 │
└──────────────────────────────────────────────────────┘
自底向上读这张图:基础设施提供配置与状态,四大子系统处理各自专精的判断逻辑,决策层综合所有子系统给出最终结论,UI 层将结论可视化并收集用户输入。
二、核心点拆解
2.1 为什么 AI Agent 的权限比传统应用难得多
Android、Web 同源策略、Unix rwx 权限位——传统应用的权限模型已经很成熟。但 AI Agent 面临三重不确定性,让这些模型失效:
| 不确定性 | 传统应用 | AI Agent |
|---|---|---|
| 操作意图 | 开发者明确知道每步在做什么 | LLM 动态决定,可能执行 rm -rf /,可能受 Prompt 注入攻击 |
| 操作空间 | 有限 API 集合(相机/定位/通讯录) | Bash 工具的操作空间几乎无限,无法为每种命令预定义权限 |
| 用户判断力 | 权限请求对应直观的功能 | python -c 'import base64;exec(base64.b64decode("..."))'——多数用户无法快速判断安全性 |
结论:Agent 权限系统不能只做"允许/拒绝"的二选一,它需要多层次、多策略、能学习用户偏好的智能决策体系。
2.2 数据结构:权限判决三元组
权限检查的返回值不是简单的布尔值,而是 TypeScript 判别联合类型:
type PermissionDecision =
| { behavior: 'allow'; updatedInput?; decisionReason? }
| { behavior: 'ask'; message; suggestions?; decisionReason? }
| { behavior: 'deny'; message; decisionReason } // decisionReason 必填
为什么不用简单枚举? 因为每种决策携带的附加信息不同:
- allow 可以携带
updatedInput——“允许执行,但我帮你修正了输入”(如路径规范化)。允许不总是原样放行,权限层可以在放行的同时变换参数。 - ask 携带
message(解释为什么需要确认)和可选的suggestions(建议的规则),让 UI 知道展示什么信息。 - deny 的
decisionReason是必填的,ask 和 allow 是可选的。每一次拒绝都必须解释原因——拒绝比允许需要更多的解释。
决策溯源类型:每个判决都有完整的"决策原因链",记录来源是规则、模式、分类器、安全检查、Hook 还是工作目录限制。这不仅用于 UI 展示,也用于调试(Ctrl+D 可查看完整决策路径)。
特别值得关注的是 safetyCheck 中的 classifierApprovable 布尔字段——它标记了某些安全检查是否可以被 AI 分类器自动处理(true),还是必须由人工确认(false)。这个字段定义了 AI 自主决策的边界。
2.3 四级信任 × 八层规则源:正交的两个维度
四级信任机制:
| 行为 | 信任级别 | 语义 | 典型场景 |
|---|---|---|---|
allow |
静默通过 | “我信任这个操作” | 只读工具、用户白名单命令 |
ask |
人工审批 | “我不确定,让人来决定” | 文件写入、未知命令 |
deny |
静默拒绝 | “绝对不允许” | 企业策略禁止的命令 |
auto |
AI 分类器决策 | “让 AI 帮忙判断” | Auto 模式下的动态分类 |
前三级是确定性的,规则匹配就立即决策。第四级 auto 是 Claude Code 的独特创新——当没有规则明确覆盖时,调用专门的 AI 分类器(YOLO Classifier)做出判断。用 LLM 的语义理解弥补静态规则的覆盖盲区。
八层优先级规则源(高 → 低):
| 层级 | 来源 | 生命周期 | 谁设置 | 示例 |
|---|---|---|---|---|
| 1 | cliArg |
单次执行 | 启动者 | --allowed-tools "Bash(git:*)" |
| 2 | command |
命令执行期 | 命令作者 | 斜杠命令 frontmatter 预授权 |
| 3 | session |
当前会话 | 用户实时 | 点击"Yes, don’t ask again" |
| 4 | flagSettings |
动态 | 系统 | Feature flag 关联的权限覆盖 |
| 5 | policySettings |
持久 | 企业管理员 | 组织托管策略,禁止执行 curl |
| 6 | localSettings |
持久 | 个人 | .claude/settings.local.json(gitignored) |
| 7 | projectSettings |
持久 | 团队 | .claude/settings.json(提交到 Git) |
| 8 | userSettings |
持久 | 用户 | 全局设置,允许 git:* |
关键区分:规则的来源优先级 VS 行为的检查顺序 是正交的两个维度:
- 8 层优先级:同类规则(都是 allow)之间谁优先——
cliArg>userSettings deny > ask > allow的检查顺序:不同类别规则之间谁优先
这意味着:即使 session(高优先级)添加了某条 curl 的临时 allow,如果 policySettings 有 curl 的 deny 规则——deny 在 allow 之前被检查,临时允许不会覆盖企业策略的硬底线。
2.4 14 步决策树:权限检查的完整链路
hasPermissionsToUseTool 函数是整个权限系统的主入口,实现了精心排序的 14 步决策树:
hasPermissionsToUseTool(tool, input, context)
│
├─[1a] 工具级 deny 规则?─────────────→ DENY(不可覆盖)
├─[1b] 工具级 ask 规则?──────────────→ ASK(沙箱自动允许例外)
├─[1c] tool.checkPermissions() ───────→ 工具自身细粒度检查
├─[1d] 工具自身拒绝?─────────────────→ DENY
├─[1e] 需要用户交互?─────────────────→ ASK(如 AskUserQuestion)
├─[1f] 内容特定 ask 规则?────────────→ ASK(优先于 bypass 模式)
├─[1g] 安全检查(敏感路径)?─────────→ ASK(免疫 bypass)
│
├─[2a] bypassPermissions 模式?────────→ ALLOW
├─[2b] 工具级 allow 规则?────────────→ ALLOW
│
└─[3] 默认 → ASK
├─ dontAsk 模式 → 转为 DENY
└─ auto 模式 → AI 分类器流程
三个关键设计决策,每一个都有深刻的安全理由:
决策一:deny 永远在 allow 之前。 步骤 1a-1g(否定性检查)全部排在 2a-2b(肯定性检查)之前。即使传入 --allowed-tools "Bash(*)" 最高优先级全量允许规则,只要有任何 deny 规则匹配,操作仍然被拒绝。这是安全设计的"默认拒绝"原则(Default Deny)——先检查所有不应该做的事,确认没有红线后,才检查是否有放行的理由。
决策二:安全检查免疫 bypass 模式。 步骤 1g 的安全检查(对 .git/、.claude/ 等敏感路径的保护)在步骤 2a 的 bypassPermissions 检查之前。即使用户选择了"完全信任"模式,对这些关键配置文件的修改仍然需要确认。为什么? 因为 .git/config 和 .claude/settings.json 是权限系统自身的"根基"——如果 Agent 能不经确认地修改这些文件,它就能关闭自己的安全限制,形成"自我提权"攻击。免疫设计切断了这条攻击路径。
决策三:内容特定规则优先于 bypass。 步骤 1f 允许用户配置"某些具体命令总是需要确认"(如 Bash(rm:*) 配置为 ask),这些规则同样免疫 bypass 模式。让用户在"大部分自动化"的同时,对特定高危操作保留人工确认。
2.5 工具自检契约:checkPermissions 的分权设计
步骤 1c 调用了 tool.checkPermissions(input, context)——这是权限系统与工具系统之间的核心契约:
- BashTool:在这里执行命令注入检测和危险命令模式识别
- FileEditTool:在这里检查目标路径是否在工作目录内
- WebFetchTool:在这里验证 URL 是否在允许的域名列表中
分权的好处:通用权限逻辑(deny/ask/allow 规则匹配、模式检查)在 permissions.ts 中集中处理;工具特有的安全判断(这个 Bash 命令是否包含 rm -rf?这个文件路径是否包含符号链接逃逸?)由各工具自行负责。新增工具时,只需实现 checkPermissions 方法即可接入权限系统,不需要修改中心化的权限检查逻辑。这是开闭原则在安全系统中的完美体现。
三、横向对比
| 维度 | Android 权限 | Unix rwx | Claude Code |
|---|---|---|---|
| 权限粒度 | API 级别(相机/定位) | 文件级别(r/w/x) | 操作语义级别(命令/路径/内容) |
| 决策主体 | 用户(安装时或运行时) | 系统内核 | 用户 + AI 分类器 + 规则引擎 |
| 覆盖盲区 | 新权限类别无法预定义 | 无法描述操作意图 | AI 分类器兜底,但仍有限制 |
| 规则继承 | App 间隔离,无继承 | 文件所有者继承 | 8 层优先级,可被覆盖但有硬底线 |
| 可解释性 | 权限名称(ACCESS_CAMERA) | 权限位(rwxr-xr-x) | 完整决策原因链 + 规则溯源 |
| 绕过防御 | Root 可绕过 | Root 可绕过 | 安全检查免疫 bypass,硬编码敏感路径 |
核心差异:传统权限模型的权限主体是"身份"(这个进程/用户是谁),Claude Code 的权限主体是"操作语义"(这个操作在做什么、风险是什么)。这种从身份到语义的转变,是 AI 系统权限设计的根本创新。
四、实战启示
启示一:判别联合比枚举更能承载语义
allow/ask/deny 不用枚举而用判别联合类型,让每种决策可以携带不同的附加信息。这种设计在任何需要"带上下文的状态"的场景都适用——不要用 status: string,用带 payload 的联合类型。
启示二:把"为什么拒绝"做成必填字段
deny 的 decisionReason 是必填项,allow 是可选项。这个不对称设计强制开发者在拒绝时思考理由,让系统天然可解释。在自己的权限/审计系统中,拒绝事件必须附带原因。
启示三:规则来源优先级 vs 行为优先级——正交设计
这两个维度的正交设计值得反复品味。在任何层级化的规则系统中,“谁的规则优先"和"哪类规则优先"应该独立配置,不要耦合在一起。规则来源决定"同类规则中谁优先”,规则类型决定"不同类规则中谁优先"。
启示四:自我提权攻击——必须在架构层面切断
.git/config、.claude/settings.json 这类"元配置"文件必须有独立的保护路径,不能被任何模式(哪怕是 bypass)所覆盖。如果 Agent 能修改自己的权限配置,就能实现"自我提权"。这条攻击路径必须在架构设计时就切断,不能留给运行时检查。
启示五:checkPermissions 契约——开闭原则的安全版
“通用逻辑集中 + 工具特有逻辑分散"的分权设计让权限系统对扩展开放、对修改关闭。每个工具自负其安全责任,中心化的决策树只关注"综合所有意见后该如何决策”。在设计插件/工具系统时,这种安全责任的分权契约应该是标准模式。
五、下期预告
14 步决策树的第 [3] 步是整个权限系统最有趣的部分——当没有任何规则覆盖时,Auto 模式会调用 YOLO Classifier。下一篇将深入:AI 分类器的两阶段设计(64 token 快路径 + 4096 token 推理)、防 Prompt 注入的转录构建策略、断路器模式(连续拒绝 3 次 → 回退人工确认)、六种权限模式的状态机切换、以及渐进式信任 UX 的精妙设计。
思考题:如果企业部署时需要为不同项目配置不同的安全策略,现有的 8 层规则源架构是否足够?假设需要增加一个"项目组级别"的规则层,应该插入在哪一层?会带来哪些权衡?
更多推荐



所有评论(0)