权限管线:Agent 的护栏
本章摘要:
权限管线是 Agent 的护栏,核心是严格性短路——按
deny (拒绝) > ask (审批) > allow (允许)的优先级串联六步检查:格式(是否用对了)→ 规则(是否可以用)→ 工具(自定义检查)→ 安全路径(拒绝写入敏感路径)→ 模式(是否可以放行)→ 人工窗口(最后由用户兜底)。任意一步命中更严格的判断就提前返回,后面的 allow 规则(哪怕用户设置了完全访问权限)无法覆盖。

上一节讲了工具系统——Agent 在一次会话中可能调用几十次工具,每次调用都潜藏风险:一个 rm -rf,一次意外的 npm publish,都可能造成不可逆的后果。
本章的重点:权限检查是一个流水线——一旦某一步做出了决定,后续步骤就不再处理。
权限检查的六步管线
每次工具调用都按以下顺序进入管线,任意一步命中更严格判断(deny / ask / safetyCheck)的步骤提前返回,后续步骤不再处理:
第一步:格式验证
检查工具调用参数的格式问题——缺少必填参数、类型不匹配、传了工具定义中不存在的字段。这一层只做"语法层面"的校验,不管权限意图。
第二步:规则匹配(deny → ask → allow)
按严格顺序检查权限规则:
- deny(拒绝):在拒绝名单上 → 立即拒绝
- ask(审批):被标记为"总是询问" → 强制弹出确认
- allow(允许):在放行名单上 → 直接通过
重要规则:优先级为
deny>ask>allow。无论规则来自哪个层级,只要有一个地方配置了高优先级规则,就会覆盖其他地方的配置。换句话说:deny 永远不能被 allow 越过、ask 永远不能被 allow 跳过。
第三步:工具自定义权限检查
每个工具可以实现自己的权限检查逻辑,除了可以返回 allow、deny、ask外,还允许:passthrough:我做不了判断,让后续关卡做判断(可被 allow 规则覆盖)。
第四步:安全路径保护
对一些核心目录的写入操作强制需要人工确认——无论其他关卡如何判断,都必须由人来决定:
- 版本控制元数据目录(如
.git/) - Agent 自身配置目录(如
.claude/) - 编辑器配置目录(
.vscode/、.idea/、.husky/) - 用户 shell 配置文件(
.gitconfig、.bashrc、.zshrc)
这一关的设计理念是:连分类器也无权批准,必须由人来确认。因为这些路径一旦被破坏,影响的不仅是当前任务,还可能波及版本控制、用户环境等更深的系统状态。
第五步:模式特定处理
在没有匹配到明确规则时,不同的权限模式会有不同的默认行为(详见后文"权限模式"一节):
bypassPermissions:全部放行auto:进入 AI 分类器审查dontAsk:将 ask 转为 deny,非交互式default/acceptEdits:进入人工确认窗口plan:只读操作直接执行,写入操作进入人工确认
第六步:人工窗口
如果前五步都没做出终局决定,系统弹出交互提示,由人类兜底。这一步是"最后的保险"——任何自动检查的盲点,最终都由用户来兜。
但实际上,为了避免用户陷入无止尽的审批流程,在弹出审批弹窗时,会有以下三种角色同时进行审批,任何一个完成了审批就会自动"短路",流程继续:
- 自动化脚本——开发者或者用户都可以定义一些自动化脚本(被称为 Hooks,可以理解为外挂在系统外的执行流程,可以在 Agent 的某一环节被触发,Hooks:Agent 的生命线监控器章节会介绍) 可根据自定义规则自动批准或拒绝。
- AI 分类器——auto 模式下,会有另一个 AI 模型判断当前操作是否安全("AI 监督 AI",本文后续会详细介绍)。
- 用户——终端界面手动选择"允许/拒绝/本次允许",可将决策持久化到配置文件。
注意:安全路径保护(第四步)对 Hook 的 allow 决定免疫——即使 Hook 批准了,写入核心目录仍需人工确认。
权限模式:关卡的严格程度
权限模式控制"在没有匹配到明确规则时,系统的默认行为"。从严格到宽松排列:
default:逐次确认
每次写入或命令执行都需用户确认(被 allow 规则放行的除外),只读操作直接执行。最安全也最繁琐。
acceptEdits:自动批准编辑
自动接受工作目录内的文件编辑和常见文件系统命令。超出工作目录的路径、受保护路径的写入、所有其他命令仍然会提示。
plan:只读模式
Agent 读取文件、运行只读命令进行探索并编写计划,但不编辑源代码。计划准备好后用户可选择批准执行方式。适用于代码审查和架构分析。
auto:AI 分类器
用另一个 AI 模型代替人工审批——"AI 监督 AI"。因为调用 AI 有延迟和推理成本,所以 Agent 系统可以做几层处理以平衡效率和安全:
- 前置过滤:一些非常宽泛的允许规则先移除(比如使用行命令工具执行任意命令,启动任意 Subagent 等等)。auto 模式目的就是审核这些命令。
- 快速通道:被标记为"安全"的工具(只读、任务管理、规划等)直接跳过分类器
- 两阶段判断:
- 快速判断:少 token 预算,提示词会非常严格,倾向于拒绝工具调用,所以此时被允许的调用大概率非常安全,也就可以直接放行了。
- 深度复核:可疑命令使用多 token 预算进行复核,防止错杀一些安全的工具调用。
- 熔断兜底:连续拒绝或累计拒绝过多时自动回退到人工确认
分类器默认偏保守:破坏性操作、外部数据外发、不可逆操作都会拦截。工作目录内的本地文件操作、安装已声明的依赖、只读请求等被视为安全。
dontAsk:仅允许预先批准的工具
将 ask 转为 deny,完全非交互式。仅与用户预先批准的规则和只读命令匹配的操作可以执行。适合无交互环境使用(比如一些自动化流水线)。
bypassPermissions:绕过权限完全放行
跳过所有权限提示和安全检查,工具调用立即执行。受保护路径的写入也被允许。适用于受控环境(容器、虚拟机、无网络访问的开发容器)。
重要例外:bypassPermissions 并非全权放行——若用户配置了项目级的 ask 规则(如
Bash(npm publish:*))或写入对象是受保护路径(如.git/、.claude/),则在管线更早的步骤已经决定要 ask 了,根本到不了 bypassPermissions 进行权限模式处理。
规则的两层持久化机制
用户在权限提示中有两种选择:
- "仅允许一次":规则只写入内存,本次会话有效,关闭后消失
- "始终允许":规则同时写入内存(立即生效)和配置文件(持久化),重启后仍然有效
两者的共同点是内存即时生效,区别只在是否持久化到文件。持久化失败不影响当前会话。
Bash 工具的权限检查:为什么需要单独设计
进阶内容:这一节是权限管线中最复杂的一环,初读可以跳过。
Bash 工具的权限检查与一般工具不同:一行 Bash 命令本质是一段程序,可能包含管道、重定向、变量替换、条件执行、循环体等多种结构。如果只用简单的字符串匹配来判断权限,攻击者只需加一个 | cat /etc/passwd 就能绕过 Bash(git *) 的放行规则。
因此 Bash 工具的检查走完整的语法分析 → 语义检查 → 规则匹配流程:
- 语法解析:用解析器把命令字符串解析为抽象语法树(AST),把命令从文本变成结构
- 复合命令拆分:复合命令的每个子命令必须独立通过检查——比如
git status && npm test不能因为git status被放行就整体放行 - 路径约束验证:检查命令中涉及的所有文件路径是否在允许范围内
- 语义安全检查:识别命令注入、参数混淆、路径绕过等攻击模式
但需要明确:没有任何权限系统能防止所有攻击手段。Bash 防护本质上是启发式模式匹配——针对已知攻击模式设检查点,无法覆盖未知或变种攻击。如果命令结构过于复杂以至于无法可靠分析,系统也会拒绝自动放行——无法证明安全的命令,默认视为不安全。
更详细的设计可以参考:Bash 工具权限检查详解
本章要点
- 权限管线是六步独立检查的串联,一步否决全局否决
- deny > ask > allow 是不可绕过的铁律
- 纵深防御的价值在于"即使某一层失守,下一层仍能拦截"
- 权限模式控制关卡的严格程度,从逐次确认到完全绕过
- Bash 工具的权限检查最复杂,因为它本质是执行一段程序
- 自动检查不是万能的——人工窗口承认系统的不完备性