从0打造编码Agent 岔路 阅读 ≈ 15 分钟
A3 权限与审批:它要执行 rm -rf 怎么办? 岔路课

Pi 为什么说权限弹窗是「安全剧场」?

安全剧场
Pi 的核心没有任何权限系统,作者把权限弹窗称作「mostly security theater」。这不是说权限不重要:它把确认框做成一个 34 行的示例扩展,把真正的边界交给容器。和第 3 课的审批疲劳放在一起读,是同一个问题的另一种回答。
❓

本课一问:第 3 课说「问多了人就不看」,主流 harness 的回答是分层放行、模型审批。Pi 的作者给了另一个回答:权限弹窗多半是「安全剧场」。他为什么这么说,Pi 又拿什么代替?

这一课先不碰:容器与沙箱的具体做法(A4);Pi 在其他方面「不做什么」的清单(课程其余章节各自讲)。

从已有出发

第 3 课的组件里有一行 Pi:默认没有权限层,修 test_parse_date 的每次调用都直接执行;加上 permission-gate 扩展之后,我们真实运行里的测试命令一条都不命中它的正则,弹窗仍然是 0。

从已有出发 · 累计数据流 点已学环节看它回答什么
每一轮循环都经过

这不是 Pi 的疏忽,而是一个写进仓库的立场。

先自己设计:把权限弹窗当成安全边界

最常见的想法:危险操作前弹一个确认框,用户点「是」才执行——有了这道闸,就算安全了。

  1. ① 朴素方案
  2. ② 它在哪失败
  3. ③ 改进方案
朴素方案把权限弹窗当成安全边界
这道闸靠什么真实情况出处
人会认真看每个弹窗用户批准了约 93% 的弹窗Anthropic 官方自报(第 3 课)
换成模型来看真实越权动作上漏判 17%(n = 52)Anthropic 官方自报(第 4 课)
正则能认出危险命令Pi 的 permission-gate 在 6 条危险命令里放过 4 条pin 源码实跑(第 1 课)

立场:进程内的闸门,给不了真正的边界

三处一手材料(均钉 pin 31b513e3;2026-09-22 起现行 README 已删掉 Philosophy 一节,引用只能钉这个 SHA):

把这三处连起来,论证有两步:

  1. 弹窗挡不住有意的绕过:第 1 课已经看到,规则只认命令文字;第 4 课看到,连专门的审批模型也有 17% 漏判。
  2. 弹窗还会制造虚假的安全感:第 3 课的 93% 批准率说明,用户在大量点「是」;既然这道闸靠不住,不如坦白告诉用户「这是 YOLO 模式」,需要边界就用容器。

不做,不等于做不出来

Pi 的仓库里恰好有这个功能的示例扩展——作者不是做不出,而是不放进核心:

核心不做的功能仓库里的示例扩展行数(pin 上 wc 实测)
权限确认框examples/extensions/permission-gate.ts34
子 agentexamples/extensions/subagent/index.ts + agents.ts1,141

权限门只要 34 行:注册一个 tool_call 处理函数,bash 命令命中 3 条正则(rm -rf、sudo、chmod/chown 777)就弹一个是 / 否,没有 UI 时直接拦下。它能挂上去,靠的是核心留的拦截点 beforeToolCall(packages/agent/src/agent-loop.ts:619-647):返回 block 的调用会变成一条错误 tool result 回给模型,而不是抛异常中断循环。

第 1 课的组件把这 3 条正则原样跑过:11 条命令里它命中 3 条,4 条本意危险的命令它放过了。作者当然知道——所以它只是示例,不是边界。

⚠

常见误解:「Pi 认为权限不重要。」

它的立场是:这个功能只值一个 34 行的扩展,不该占核心的复杂度和系统提示词;真正的边界应该是容器。要确认框的人可以装扩展,要隔离的人应该上容器,两件事都不该假装成对方。

和另外几家放在一起

核心里的权限真正的边界放在哪
Pi无;拦截点 beforeToolCall + 34 行示例扩展容器 / 微虚拟机 / OpenShell(用户自己选)
Claude Code规则引擎 + 六种模式 + auto 分类器内置 OS 沙箱(官方自认「not a complete isolation boundary」);bypassPermissions 只建议在隔离环境用
Hermes4,553 行的 approval.py它的 SECURITY.md 说唯一的安全边界是操作系统;默认的 local 后端在宿主机上直接执行,另有 docker、singularity 等可选的容器与云沙箱后端

三家说的是同一件事的不同侧面:进程内的判断(规则、弹窗、模型审批)管的是「想做什么」,能兜底的只有进程外的隔离。区别只在于要不要在核心里再放一层进程内的判断,以及放多厚。

权限设计的演进:每一代都被上一代的某个真数字逼出来
  1. Pi 核心、bypassPermissions、CoreCoder --yes
    解决了零打断,模型想跑什么马上跑。
    卡在L1 的 6 条本意危险的构造命令(含 rm -rf /)会全部直接执行。
  2. lcc s01 / s03、CoreCoder bash 正则、Pi permission-gate
    解决了把已知危险的写法挡在执行之前,代码只有几行。
    卡在lcc s03 三道闸(pin 源码实跑):6 条危险命令里漏拦 4 条,5 条日常命令里误拦 2 条(cat sudoku_solver.py 因含子串 sudo 被拒)。
  3. 第一代 每次都问人
    CoreCoder 交互模式、Claude Code Manual、lcc s03 的第三道闸
    解决了判断交给人,规则写不全也不怕。
    卡在我们 26 次真实运行的 139 次工具调用,在 CoreCoder 交互模式下有 74 次要停下来问(53%);Anthropic 自报用户批准了约 93% 的弹窗——问多了,人就不看了。
  4. Claude Code(deny → ask → allow)、OpenCode(last-match)、Codex(最严格者胜)
    解决了把常见的放行和拒绝写成规则,只有规则没覆盖的才问人。
    卡在同一份规则换一种求值语义可能换一个答案:同一份 rules-standard.json 改用 first-match,真实运行 pd-firstmatch-s1 里模型的 python -m unittest -v test_dateparse.py 被规则 #8 拒掉。
  5. Claude Code auto、Codex Auto-review(代码名 Guardian)、Hermes smart
    解决了只读直放、项目内编辑免审,其余交给另一个模型判断;弹窗接近清零。
    卡在Anthropic 自报:auto 分类器在真实越权动作上仍有 17% 漏判(n = 52),官方原话「does not guarantee safety」。
  6. 下一章 与 OS 沙箱分层
    Claude Code sandbox、Codex Seatbelt / bubblewrap
    解决了规则管「允许调用什么」,沙箱管「调用之后能碰到什么」。
    卡在L1 的 python3 -c "import shutil; shutil.rmtree(…)" 在四个黑名单里被 4 个全部放行;规则看不见脚本在运行时做什么 → A4。

数字口径:黑名单与「每次都问」由原实现在 pin 源码上实跑(a03-permission-fixtures.json,2026-09-26);真实运行 = 本课程项目 miniagent 用 Qwen/Qwen3-8B-MLX-4bit 本机录制;93% 与 17% 为 Anthropic 官方自报。

证据三问

问本课的回答
① 它解决什么问题?避免给用户一道「看起来安全、其实靠不住」的闸
② 为什么这样可能对?进程内的判断拦不住有意的绕过,又会因为审批疲劳失效;OS 或容器边界不依赖命令写法
③ 实践上?作者自述、仓库文档与示例扩展行数都是一手材料;是否比「规则 + 弹窗 + 沙箱」的纵深防御更好,没有对照实验

一句话

📌

Pi 的立场不是「权限不重要」,而是「进程内的弹窗给不了边界,只值一个 34 行的扩展;要边界就上容器」。

🚦

回到主线:第 4 课:让另一个模型来审批,审批者该有多大权限?

下一个缺口

回到主线:让另一个模型来审批,审批者该有多大权限?Pi 选择不审;Claude Code 和 Codex 选择让模型审。如果要审,审批者自己也得被关在一个更小的笼子里。

你可能会问