很多人第一次看权限预设,会把它读成一把从"严格"到"宽松"的滑杆,然后默认拉到底那一档等于"全部放行"。实际不是。预设是两项独立控制的打包名,其中一项叫审批策略,它的 never 值拒绝的是"审批请求",不是"工具执行"。
这两件事差得很远,而且在默认配置里就已经同时出现了。
一、预设不是一套机制,是两个设置的别名
权限预设本身不是第三套安全机制。它只是给两个设置的组合起一个名字:
| 控制 | 文档中的取值 | 回答的问题 |
|---|---|---|
| 沙箱模式 | read-only、workspace-write、danger-full-access |
子进程允许哪些文件效果? |
| 审批策略 | ask、never |
调用方发起审批请求时怎么处理? |
默认表里有一组值得盯着看的:workspace-write + ask,以及 danger-full-access + never。
部署可以自己配置预设表,所以不要假设所有安装的选项名和内容都一样——遇到不认识的组合时,要去看两项生效值,而不是只念预设名。
二、误解一:never 是"自动批准"
这是最容易错的一条。
ask 把请求交给已组合的应答者链:界面可以挂人工应答者,其他集成也可以挂机器决策。never 则是在分发答复者之前就返回 rejected——它不会弹提示,也不会批准。
所以"我设成 never 了,随便跑吧"得到的不是放行,而是每一个走到审批服务的请求都被直接拒掉。
再看四种结果,这个设计就很清楚了:
| 结果 | 含义 |
|---|---|
allowed-once |
只授予被询问的那一次操作 |
rejected |
明确拒绝 |
cancelled |
请求已撤回 |
unavailable |
没有合适的应答者,或应答者失败 |
调用方只在 allowed-once 时放行。应答者缺失、不负责这个请求、抛错、返回不合约的结果——全都落到 unavailable,也就是失败关闭,不会悄悄变成允许。
反过来,ask 也不保证一定弹提示。没接应答者的时候它就是 unavailable,白等一场。
三、误解二:danger-full-access 等于"什么都被批准了"
看看默认组合:danger-full-access 绕过沙箱隔离,never 拒绝进入审批服务的请求。
这两件事可以同时成立。 于是会出现一个很反直觉的场面:文件系统这边完全没有隔离,但任何试图申请审批的操作反而被拒。
判断一次任务的真实暴露范围时,必须分开看这两个控制:
- 沙箱决定子进程能把文件写到哪儿
- 审批决定某个具体操作有没有拿到许可
不需要发起审批的操作,仍然可能在完全文件访问下执行。 所以"没看到任何审批提示"绝不能当成"没有危险操作发生"的证据。
四、沙箱这一侧:名字说明的是文件效果
三个模式的名字很容易被过度解读:
| 模式 | 文件效果 | 容易忽略的地方 |
|---|---|---|
read-only |
请求后端拒绝写入 | 后端可能保留必要接收位置;Windows ACL 运行器不授予显式可写根目录,并报告部分强制执行 |
workspace-write |
允许写工作区根目录和后端定义的临时区域 | 实际强制程度取决于后端 |
danger-full-access |
绕过隔离,直接启动原始命令 | — |
两件事必须一起记:
第一,这些名字不承诺网络访问或进程可见性限制。 只读文件策略本身说明不了网络能不能通。"在工作区内只能写工作区"也不等于其他一切都隔离了。
第二,后端会报告 full 还是 partial。 文档列出的部分强制执行情况包括旧版 Landlock ABI,以及 Windows ACL 运行器的硬链接、未受限读取和 AppContainer ACL 边界。
需要绝对文件效果承诺时,必须核对实际后端和强制执行结果,模式名不够用。
五、一次等待中的调用,到底在等什么
如果你的 Agent 卡在一个等待中的工具调用上,那通常不是工具坏了,而是调用方在等应答者给出决定。
请求本身标识 Agent 和工具,可以带 callId 把提示关联到已经展示出来的那次调用,也可以说明原因。注意它有意不重复工具参数——答复者会把提示附着到已经流式展示的调用上。所以看提示的时候,要回到真实调用去看参数。
生效策略取自哪里?会话日志里最后一个 approval/policy 事件,没有事件才回退到服务配置,服务文档的默认值是 ask。这意味着部署的默认值和会话里的覆盖值都会起作用,只看界面标签不够。
还有一个排查时经常误判的细节:取消会撤回请求。 如果答复者还在等待,取消之后到达的迟到答复会被丢弃。所以别指望"我后来点了允许"就能放行一个已经撤回的操作。
六、审批不等于"用户问题"
这两套协议经常被当成一套:
- 审批:封闭结果,
allowed-once/rejected/cancelled/unavailable - 用户问题:可以带选项标签、描述和补充信息,可能用来收集信息或评审计划
定时问题的行为和审批不一样:无人回应时前台等待超时可以返回待处理结果,而问题之后仍然可以回答;关闭客户端面板不会发送答复。这套语义不能照搬到审批请求上。
而且——看到一个用户问题,不能证明某个审批请求已经获准。
七、审计记录:它证明什么,不证明什么
审批请求必须发生在会话的开放 turn 内。服务会追加 approval/asked,拿到结果后追加配对的 approval/decided。
关键点:这对事件只进日志,不作为两条消息进入模型对话。 模型看到的是调用方生成的工具结果和运行时上下文快照。
所以审计视图和模型对话不一定能一行对一行。审批标识把询问和决定配起来,callId 把提示关联到工具调用——但这些标识都不能证明操作真的完成了。要看工具结果,必要时看受影响的输出。
八、照着这张清单设权限
- 先用实际效果描述任务。 读文档、改源码、跑陌生安装程序,即使都在同一个工作区,需要的访问范围也不同。明确目标目录、可能读到的数据、是否要写、是否涉及外部访问。
- 查两项生效值,不是预设名。 界面上显示
custom,表示当前组合不匹配任何可用预设——它是派生出来的显示值,不是可以切换的目标,得去看具体的沙箱模式和审批策略。 - 确认后端报告的是
full还是partial,以及工作区目录对不对。别假设这些设置顺带限制了网络。 - 需要交互审批时,先确认有应答者。 没应答就是
unavailable,反复提交同一个请求不会变出授权。 - 看每个提示的具体操作和范围,只有这次操作可以接受时才允许。
allowed-once不是后续所有操作的通行证。 - 无人值守任务要能处理拒绝。
never下请求直接被拒,自动化流程必须有分支应对,而不是重试。 - 例外用完要恢复。 共享或无人值守环境尤其要记录"为什么当时用了更宽设置",让下一个人能判断这个例外是否还成立。
第 2 条和第 7 条最容易被跳过,但它们决定了排查时你说的"当时的权限配置"到底是真的还是猜的。报告问题时同时写预设名和两项生效值,别人复现就不用靠一个界面标签去猜运行条件。
九、边界
最后必须说清楚这几条,否则前面所有机制都会被读成安全承诺:
- 审批是对具体请求的决定,不保证操作或其后果安全。 要检查目标和参数,尤其是改文件、跑命令、访问外部系统的操作。
- 沙箱约束文件效果,不替代审批;审批回答一个请求,不替代沙箱。 两者都不能证明任意插件、网络目标或生成的命令是安全的。
- 只读和工作区访问仍可能暴露数据——在实际后端允许读取的范围内。
- 预设是否有效,取决于真正执行这些控制的后端和集成。 预设存在,不等于做过一次安全审计。
- 沙箱启动失败时,不要把"绕过隔离"当修复。 那是把问题挪走,不是解决。
相关阅读
术语速查
- 权限预设:把沙箱模式与审批策略打包的具名组合。
- 沙箱模式:描述子进程允许的文件效果的策略。
- 审批应答者:在
ask下对具体请求作决定的集成。 allowed-once:只针对当前询问操作的一次性许可。custom:当前组合不匹配可用预设时派生出的显示值。
本文由 DSH Plugin 社区(dsh-plugin.com)维护者撰写,首发于阿里云开发者社区。