Skip to content

权限管线:Agent 的护栏

本章摘要

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

permission-pipeline

上一节讲了工具系统——Agent 在一次会话中可能调用几十次工具,每次调用都潜藏风险:一个 rm -rf,一次意外的 npm publish,都可能造成不可逆的后果。

本章的重点:权限检查是一个流水线——一旦某一步做出了决定,后续步骤就不再处理。

权限检查的六步管线

每次工具调用都按以下顺序进入管线,任意一步命中更严格判断(deny / ask / safetyCheck)的步骤提前返回,后续步骤不再处理

第一步:格式验证

检查工具调用参数的格式问题——缺少必填参数、类型不匹配、传了工具定义中不存在的字段。这一层只做"语法层面"的校验,不管权限意图。

第二步:规则匹配(deny → ask → allow)

按严格顺序检查权限规则:

  • deny(拒绝):在拒绝名单上 → 立即拒绝
  • ask(审批):被标记为"总是询问" → 强制弹出确认
  • allow(允许):在放行名单上 → 直接通过

重要规则:优先级为 deny > ask > allow。无论规则来自哪个层级,只要有一个地方配置了高优先级规则,就会覆盖其他地方的配置。换句话说:deny 永远不能被 allow 越过、ask 永远不能被 allow 跳过。

第三步:工具自定义权限检查

每个工具可以实现自己的权限检查逻辑,除了可以返回 allowdenyask外,还允许: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 模式目的就是审核这些命令。
  • 快速通道:被标记为"安全"的工具(只读、任务管理、规划等)直接跳过分类器
  • 两阶段判断
    1. 快速判断:少 token 预算,提示词会非常严格,倾向于拒绝工具调用,所以此时被允许的调用大概率非常安全,也就可以直接放行了。
    2. 深度复核:可疑命令使用多 token 预算进行复核,防止错杀一些安全的工具调用。
  • 熔断兜底:连续拒绝或累计拒绝过多时自动回退到人工确认

分类器默认偏保守:破坏性操作、外部数据外发、不可逆操作都会拦截。工作目录内的本地文件操作、安装已声明的依赖、只读请求等被视为安全。

dontAsk:仅允许预先批准的工具

将 ask 转为 deny,完全非交互式。仅与用户预先批准的规则和只读命令匹配的操作可以执行。适合无交互环境使用(比如一些自动化流水线)。

bypassPermissions:绕过权限完全放行

跳过所有权限提示和安全检查,工具调用立即执行。受保护路径的写入也被允许。适用于受控环境(容器、虚拟机、无网络访问的开发容器)。

重要例外:bypassPermissions 并非全权放行——若用户配置了项目级的 ask 规则(如 Bash(npm publish:*))或写入对象是受保护路径(如 .git/.claude/),则在管线更早的步骤已经决定要 ask 了,根本到不了 bypassPermissions 进行权限模式处理。

规则的两层持久化机制

用户在权限提示中有两种选择:

  • "仅允许一次":规则只写入内存,本次会话有效,关闭后消失
  • "始终允许":规则同时写入内存(立即生效)和配置文件(持久化),重启后仍然有效

两者的共同点是内存即时生效,区别只在是否持久化到文件。持久化失败不影响当前会话。

Bash 工具的权限检查:为什么需要单独设计

进阶内容:这一节是权限管线中最复杂的一环,初读可以跳过。

Bash 工具的权限检查与一般工具不同:一行 Bash 命令本质是一段程序,可能包含管道、重定向、变量替换、条件执行、循环体等多种结构。如果只用简单的字符串匹配来判断权限,攻击者只需加一个 | cat /etc/passwd 就能绕过 Bash(git *) 的放行规则。

因此 Bash 工具的检查走完整的语法分析 → 语义检查 → 规则匹配流程:

  1. 语法解析:用解析器把命令字符串解析为抽象语法树(AST),把命令从文本变成结构
  2. 复合命令拆分:复合命令的每个子命令必须独立通过检查——比如 git status && npm test 不能因为 git status 被放行就整体放行
  3. 路径约束验证:检查命令中涉及的所有文件路径是否在允许范围内
  4. 语义安全检查:识别命令注入、参数混淆、路径绕过等攻击模式

但需要明确:没有任何权限系统能防止所有攻击手段。Bash 防护本质上是启发式模式匹配——针对已知攻击模式设检查点,无法覆盖未知或变种攻击。如果命令结构过于复杂以至于无法可靠分析,系统也会拒绝自动放行——无法证明安全的命令,默认视为不安全

更详细的设计可以参考:Bash 工具权限检查详解

本章要点

  • 权限管线是六步独立检查的串联,一步否决全局否决
  • deny > ask > allow 是不可绕过的铁律
  • 纵深防御的价值在于"即使某一层失守,下一层仍能拦截"
  • 权限模式控制关卡的严格程度,从逐次确认到完全绕过
  • Bash 工具的权限检查最复杂,因为它本质是执行一段程序
  • 自动检查不是万能的——人工窗口承认系统的不完备性