ARTICLE · 1004641
DeepSeek Harness 源码导读 05|给 Agent 工具前,先画清边界
“运行测试”通常很安全。 |
但模型可能生成npm test,也可能顺手加上清理目录、安装依赖或访问网络。对人类来说,这些命令风险不同;对操作系统来说,它们都只是准备启动的进程。
DeepSeek Harness 没有把安全压在一个万能开关上,而是分成权限策略、用户审批、工具守卫和进程沙箱几层。要看懂这套设计,先别把它们混着叫“权限”。
先用一张表把四层钉住:
前面三层发生在 Harness 的工具语义里,最后一层落到宿主平台的进程约束。审批按钮点了“允许”,只代表策略同意继续走,并不等于操作系统已经给命令一张无限通行证。
01权限策略决定怎样处理一类操作
权限预设用于给会话选择一套整体姿态。交互式编码、只读分析和无人值守运行,可以采用不同规则。
当一次工具调用进入tools/pre-execute,策略会结合工具、参数和当前会话作出判断:允许、拒绝,或询问用户。
例如读取工作区文件可以直接允许,写入工作区可能需要确认,危险范围的命令则直接拒绝。
这里的结果仍不是操作系统保证。它是工具执行前的一次策略决定。
策略也不能只靠模型遵守。“不要修改文件”写在提示词里,是给模型看的行为指导;真正的写入限制要由工具守卫或沙箱执行。把提示词当防火墙,和在纸上写“闲人免进”差不多,主要作用是礼貌。
Permission Preset 其实捆绑了两个旋钮
Web 界面里看到的“权限预设”不是新的强制执行层。它把两个彼此独立的状态组合成一个易选的名字:
默认组合中,workspace-write对应workspace-write + ask,danger-full-access对应danger-full-access + never。这里的never不是“永远批准”,而是“永远不询问,任何 ask 都确定性拒绝”。
预设切换会把用户选择以及两个旋钮的变化写进会话日志。真正执行时,Shell 读取当前沙箱模式,审批服务读取当前审批策略。若两个旋钮被单独改成不匹配任何具名预设的组合,界面会派生出custom,但custom本身不是可以写入的第三套策略。
这个设计避免把“能写到哪里”和“是否需要人确认”绑死。例如不同部署可以定义自己的只读 + ask、工作区写入 + never 等组合,而底层消费方仍只理解各自的明确状态。
02审批只回答一个具体问题
当策略返回ask,ctx.approval会为这一次操作发起审批。
审批结果只有四种:
只有allowed-once是放行。
这意味着在无人值守环境里,如果系统无法询问任何人,默认结果是拒绝。它不会因为凌晨三点没人点按钮,就把沉默理解成热烈支持。
会话级审批策略目前有ask和never。ask把问题交给应答者链;never不弹问题,所有询问直接得到rejected。后者适合 CI 等不允许等待人工决定的场景。
每次审批都会获得新的请求 ID,并在会话日志里写下approval/asked与approval/decided。授权与工具调用可以配对审计,但一次允许不会自动升级成永久通行证。
03工具守卫为什么还要再检查
审批之后还有注册表守卫。
守卫采用单调规则:可以拒绝,也可以不表态,但不能撤销已经存在的拒绝。身份字段和冻结参数也不能被中途替换。
这给系统留出了多层叠加空间。平台规则、工作区规则和某个插件自带的限制可以同时工作,后注册的扩展不能轻易把前面的安全决定擦掉。
例如工作流可以临时把某个子任务限制为只读工具集。即使全局会话拥有编辑工具,这个作用域内的守卫仍能拒绝写入。

04沙箱管的是进程的文件效果
命令真正启动前,进程沙箱把原始 argv 包装成平台后端能够约束的 argv。
DeepSeek Harness 定义了三种模式:
前两种会交给沙箱提供方。danger-full-access则直接运行原始命令,不调用约束后端。
请注意范围:这里约束的是文件系统效果。它不自动等于断网,也不承诺隐藏其他进程。网络与进程可见性不属于这个SandboxMode的定义。
三种模式可以按效果理解:
read-only | ||||
workspace-write | ||||
danger-full-access |
表里的“拒绝”只描述模式承诺,实际完整度还要看后端返回的full或partial。此外,它只覆盖进程文件效果;命令是否能联网、能否看到其他进程,要由其他部署措施负责。
这个细节很容易被宣传口号抹平。看到“sandbox”就脑补成全封闭虚拟机,最后往往是安全评审替你补课,而且收费方式通常是加班。
05三个平台怎样实现
本地提供方会按平台选择后端。
Linux 可以使用 bwrap 或 Landlock,macOS 使用 Seatbelt,Windows 使用 ACL 和受限令牌相关机制。
不同后端的能力并不完全相同,所以返回结果会标明full或partial。
full表示后端完整执行了该模式承诺的文件效果限制。partial表示当前后端或内核只能覆盖一部分,例如较旧的 Landlock ABI,或 Windows ACL 在某些边界上的限制。
需要绝对保证的调用方必须检查这个事实,不能把partial当成“差不多 full”。安全语境里,“差不多”通常是事故报告的第一版标题。
如果受限模式下没有可用后端,提供方会失败关闭,也就是报错,而不是悄悄裸跑。系统还会区分两类失败:沙箱本身没能启动,和沙箱正常工作并拦住了命令。两者都会失败,但排查方向完全不同。
沙箱不是在命令字符串外面贴一个标签
消费方把准备执行的 argv 和逐调用策略交给ctx.sandbox.confine()。提供方返回真正应该 spawn 的新 argv,以及当前后端的强制执行完整度和错误识别规则。
例如消费方原本准备运行:
Linux 后端可能把它包装成 bwrap 或 Landlock runner 的 argv;macOS 会使用 Seatbelt profile;Windows 后端会准备 ACL 与受限令牌相关执行路径。Shell 最终启动的是包装后的 argv,而不是先启动原命令再希望它自觉。
运行失败后还要分类:
runner failure:约束程序自己没能启动,原命令可能根本没运行。 sandbox denial:runner 正常工作,并阻止了越界文件效果。 command failure:命令确实运行,但测试失败或返回其他非零状态。
三者在界面上都可能是红色结果,含义却完全不同。只根据退出码判断,会把“沙箱成功拦截”和“沙箱根本没工作”混成一件事。

06为什么审批不能代替沙箱
用户看到的审批问题通常是“是否允许这次命令”。即使用户同意,命令仍可能产生超出预期的文件效果。
沙箱提供第二层约束:允许执行,不等于允许写遍整台机器。
反过来也一样。workspace-write沙箱能限制写入范围,却不判断“删除工作区内所有文件”是否符合用户意图。工作区里的删除操作仍可能需要权限策略和审批。
所以四层各有职责:
少一层不一定立刻出事,但不能假装另外三层会自动补位。
提权重试必须是一项新调用
假设workspace-write下的测试确实需要写入工作区外缓存。合理流程不是在原调用执行到一半时偷偷扩大权限,而是让原调用明确失败或提出升级请求,得到批准后,以显式模式覆盖发起一项新的执行。
新调用会重新拥有自己的调用身份、审批记录、沙箱策略和结果。这样审计日志能回答:哪一次在受限模式下失败,谁批准了哪次提升,提升后的命令产生了什么结果。
如果在同一进程运行中途改规则,系统既难以说明前半段和后半段分别受什么约束,也很难可靠重放。安全状态变化越重要,越需要清楚的调用边界。
07用三个命令做对照
读取package.json,通常只需要权限策略允许和只读文件能力。
运行测试,可能进入受限进程,并根据项目策略直接允许或询问一次。
向工作区外写文件,则可能先被工具策略拒绝;即使获得审批,受限沙箱仍会按照实际模式拦截越界文件效果。
同样是“命令”,路径不同,经过的控制也不同。判断风险要看具体调用,不能只看工具名叫 Bash 就一律放行或一律封死。
把“运行测试并修复”完整走一遍
模型请求 bash("pnpm test"),Agent Loop 先写tool/call。权限插件在 tools/pre-execute检查命令和会话策略,决定直接允许、拒绝或ask。若需要询问,审批服务写 approval/asked,等待当前会话的应答者,再写approval/decided。只有 allowed-once才继续;rejected、cancelled、unavailable都让工具主体跳过。注册表单调守卫再次检查不可撤销的限制,例如当前子 Agent 是否只允许只读工具。 Shell 消费方解析这一次调用的沙箱策略,把原 argv 交给平台后端包装。 子进程运行,消费方根据后端方言区分 runner 故障、沙箱拦截和普通测试失败。 规范结果进入 tools/post-execute,随后冻结为tool/result,模型在下一步骤中看到结果。
这八步解释了为什么安全问题不能只问“有没有弹窗”。弹窗只占第三步,前后还有策略、不可撤销限制、平台约束和结果归因。
08给实际使用者的四个建议
第一,首次使用先从只读任务开始,观察工具请求和审批内容。
第二,无人值守运行使用确定性拒绝策略。不要让自动化停在一个永远没人回答的弹窗前,也不要让它默认同意。
第三,只有明确需要时才提升到danger-full-access,并把它当成新的高权限调用,而不是“重试一下”。
第四,检查当前平台的 enforcement 是full还是partial。模式名称和后端实际保证是两件事。
09读源码的顺序
练习时,可以让 Agent 先运行只读命令,再请求一次写入工作区的操作,观察策略、审批和最终工具结果分别出现在哪里。不要拿重要目录做实验,测试仓库的存在就是为了保护周一早上的心情。
10三句话收住
权限策略、审批、工具守卫和沙箱是四层控制,不是四个名字指向同一个按钮。
审批只授权一次具体操作,无人回答时失败关闭。
沙箱主要约束文件效果,不自动提供完整网络和进程隔离。
下一篇,我们回到整个项目最特别的设计:为什么连 Agent Loop、模型适配器和会话日志都被做成插件,以及 profile 怎样把 247 个包拼成一个可运行产品。
谢谢你读我的文章。 如果觉得不错,随手点个赞、在看、转发三连吧🙂 如果想第一时间收到推送,也可以给我个星标⭐~ |