本节摘要:权限与审批门的设计原理——为什么默认拒绝、如何分级、审批如何避免训练出「无脑点同意的人」——《Harness 工程:从零打造智能体运行环境》第 5 章已经讲透,本书不重写;本节只管把这些原理装进第 2.3 节的网关。内容有三块:其一,命令规则的三种粒度(精确匹配、子命令匹配、模式匹配)与各自的陷阱——前缀放行 git 等于放行 git 的一切,是最常见的自毁式白名单;其二,路径作用域的落地:绝对路径收敛到工作区、封禁波浪号与点点穿越,并把「参数里像路径的东西」都当路径检查;其三,审批门的落位:deny 与 needs_approval 是两个不同出口,四类高危动作默认进审批,人类回执后继续或终止流程。
策略的强度取决于粒度。三种粒度从粗到细:
| 粒度 | 写法示例(fnmatch 模式) | 适用 | 陷阱 |
|---|---|---|---|
| 精确匹配 | pytest、git status |
高频只读命令 | 无(最安全,尽量用) |
| 子命令匹配 | git diff *、git log * |
某命令的特定子集 | 子命令仍带参数,注意参数里的路径 |
| 模式匹配 | python *.py |
带参数的常用组合 | 通配符过宽等于没拦 |
三个真实陷阱,每个都对应一次可预见的失守:
陷阱一:前缀放行。 允许 git * 看似方便,实际放行了 git push(外发)、git clean -fdx(删库)。正确做法是精确到子命令:git status、git diff *、git log * 各自单列,用不到的子命令不进名单。
陷阱二:模式过宽。 python * 意味着 python -m http.server 也被放行——沙箱断网时它是哑弹,一旦第 4 章讨论的「受控出站」放开,它就是隧道。收紧为 python *.py,再对入口文件名做模式约束。
陷阱三:shell 包装穿透。 只要允许任何能起 shell 的形式(sh -c ...、bash ...、python -c ...),后续所有规则都在字符串内部失效。第 2.3 节拒收 shell 字符串是第一道锁;策略里不列 shell 与 -c 类入口是第二道锁。
💡 写策略的顺序建议:从「完全空白 + deny 兜底」起步,让真实工作流把命令一条条「申请」进名单——自下而上长出来的白名单,比抄来的模板清单贴合得多,也短得多。
命令白名单管「做什么」,路径作用域管「对哪里做」。落位实现(可直接并入 gateway.py 的 check 函数):
# policy_paths.py —— 路径作用域检查:一切像路径的参数都必须收敛在工作区内(示意实现) def path_scope_ok(argv: list) -> tuple: for a in argv[1:]: if a.startswith("~"): return False, f"封禁家目录引用: {a}" parts = a.split("/") if ".." in parts: return False, f"封禁点点穿越: {a}" if a.startswith("/") and not (a == "/workspace" or a.startswith("/workspace/")): return False, f"绝对路径越界: {a}" return True, "ok"
三条规则各有对手:~ 展开直取家目录的 .ssh、.aws;.. 穿越(/workspace/../etc/passwd)绕过前缀判断;工作区外绝对路径直接打击 /etc、/var。加上第 2.1 节的只读根,这里形成双保险:策略层挡「明显越界」,文件系统层挡「漏网的写」。
⚠️ 已知局限:这套检查是文本级的,不解析引号内的语义,也不追符号链接(symlink 可能指向工作区外)。生产化应在沙箱内做真实路径解析(realpath 归一后再判前缀),文本级检查当作第一道滤网——示意实现与生产实现的差距,正在于这类边角。
策略评估不该只有「行 / 不行」两态。三个出口各司其职:
(文字流程图)网关决策的三出口 命令 ──▶ deny 规则命中 ─────────────▶ 出口一 deny:直接拒绝并落审计 ──▶ allow 命中且路径合规 ─────────▶ 出口二 allow:进沙箱执行 ──▶ 未命中任何名单(灰区)────────▶ 出口三 needs_approval:挂起,等人 │ 人批准 ──▶ 进沙箱执行(审计记 reviewer) 人拒绝 ──▶ 记 deny(审计记 reviewer)
四类默认进审批的高危动作(抄走即用):
| 类别 | 触发示例(示意) | 为什么不能自动 |
|---|---|---|
| 工作区外写 | 路径不在 /workspace 内的写操作 |
破坏范围不可控 |
| 网络出站 | curl、pip install(联网下载) |
数据外传与供应链入口 |
| 删除与覆盖 | rm、git push --force |
不可逆 |
| 安装与执行新代码 | pip install <包名>、运行下载来的脚本 |
供应链投毒入口(第 4 章主题) |
审批门的落位要点:回执里必须带完整命令与完整参数让人看——恶意 Skill 事件(CVE-2026-100602,官方记录)的问题恰恰出在审批流程缺失「预览」环节,人在没看清将执行什么的情况下点了同意;此外同一命令短时间重复触发审批要合并提示,否则人会被训练成条件反射式同意。这两个「人的因素」的原理展开见《Harness 工程:从零打造智能体运行环境》第 5 章。
# policy.py —— 第一版策略文件:精确优先、deny 兜底、灰区进审批(示意,按项目裁剪) ALLOW = [ "ls", "ls *", "cat *", # 只读浏览 "git status", "git diff", "git diff *", "git log", "git log *", # 精确到子命令 "python *.py", "pytest", "pytest -q", "pytest tests/*", # 受限执行 "pip install -r requirements.txt", # 锁定文件式安装(内容治理见第 4 章) ] DENY = [ "*curl*", "*wget*", "*nc *", "*ssh*", # 网络工具 "*sudo*", "*chmod*", "*chown*", # 提权与改权 "rm *", "* > /dev/*", "*mkfs*", "*dd if=*", # 破坏性 "git push*", # 外发 ] APPROVAL_HINTS = ["pip install", "rm", "git push", "curl", "wget"] # 灰区优先提示
验证方法照抄第 2.3 节冒烟测试:每类各放一条命令,期望分别命中 allow、deny、路径越界三个分支。
策略挡在事前,但「deny 了多少次、批了多少次、deny 的都是什么」同样是金矿——这需要把每个决策点变成结构化、防篡改、可回放的记录。下一节补上最后一块拼图:审计日志与回放。