第 8 章 Permissions 与 Approval

这个功能解决什么问题

给 AI agent 开放系统操作权限,本质上是一个信任问题。信任得太少,agent 没法干活;信任得太多,出了事没有后悔药。

Claude Code 面对这个问题的答案是:不做全局信任,做细粒度规则

最直观的体验

用 Claude Code 改代码,有时会弹出一个确认框:"准备写入 /src/config.ts,是否允许?"

你可以:

  • 点允许,这次通过
  • 点允许,并勾选"以后 /src/ 下的文件都自动允许"
  • 点拒绝,Claude Code 会记录这次拒绝,但任务不会整个中断

这个确认框就是 Approval,而背后决定"要不要弹出这个框"的逻辑就是权限系统。

权限是怎么工作的

每个工具在执行之前都会先问自己一个问题:我现在要做的这件事,用户允许吗?

这个问题的答案来自规则匹配,有三种结果:

  • allow:直接执行,不打扰用户
  • deny:直接拒绝,不执行
  • ask:弹出确认框,等用户决定

规则按 deny > allow > ask 优先级匹配,第一条命中的生效。如果一条规则都没匹配上,默认进入 ask。

规则是用户自己写的(或者通过确认框积累下来的),长这样:

allow:Bash(git *:*)          # git 命令全部放行
allow:FileEdit(/src/**:*)    # src 目录下的文件可以随便改
deny:Bash(rm -rf *:*)        # 这条命令直接拒绝
ask:WebFetch(*.external.com) # 外部网址每次都要问

为什么不做成一个总开关

最简单的设计是:要么"完全信任模式"(什么都不问),要么"严格模式"(每步都确认)。

但这两种都不好用:

  • 完全信任:你不知道 agent 在干什么,改错了才发现
  • 每步确认:做一件事要点几十次确认,根本没法用

Claude Code 选择的路是逐步积累授权。第一次做某件事问你,你点允许并选择"记住规则",下次同类操作就不再问了。用着用着,你的规则表就变成了一份个人化的"信任配置",反映你真实的风险偏好,而不是一刀切的全开或全关。

为什么权限判断在每个工具里,而不是一个统一的地方

读文件、写文件、跑 shell 命令、访问网址——这四件事的风险完全不同,需要判断的信息也不同:

  • 写文件看的是路径(是不是 /etc/?是不是生产配置?)
  • 跑命令看的是命令内容(是搜索还是删除?)
  • 访问网址看的是域名(是可信站点还是随机 URL?)

如果放在一个统一的地方判断,要么判断逻辑过于复杂,要么精度不够。放在每个工具里,每个工具按自己的业务语义做判断,更准确,也更容易维护。

拒绝之后发生什么

权限被拒绝的时候,界面上能看到"这一步被拒了,原因是什么",不是悄悄失败。你可以修改规则之后重试,不用从头再来,整个任务也不会因为一次拒绝就中断。

拒绝不是终点,而是一个可以继续调整的节点。

类似的细粒度也体现在 Shell 命令上:find -exec 会被拦住但 find -name 不会,sed -i 被单独标记为有副作用。格式化工具执行完后,系统还会主动刷新编辑工具的文件缓存。甚至 Windows 路径攻击(UNC 路径触发凭证泄露)也在防御范围内。

权限系统的边界

权限系统能做的是:控制 Claude Code 本身发起的操作。

它管不了的是:你自己写的规则有没有漏洞,或者 MCP server 在它自己进程里做了什么。权限规则是针对 Claude Code 工具调用的过滤层,不是沙盒隔离。

如果你需要更强的隔离,sandbox(通过容器隔离执行环境)是另一层机制,和权限规则独立工作。