Skip to content

3.2 沙箱与安全模型

本章概览:DSH 对"进程能碰哪些文件"的完整回答——一个 ctx.sandbox 能力缝、三档模式词汇、随调用携带的策略、跨家族共享的写围栏,以及"先拒绝、后批准一次更宽重试"的审批瀑布。读完可回答:什么在沙箱里、什么不在,被拒后模型如何合法地请求一次更宽的执行。

概述 / 定位

sandbox 能力族位于 packages/sandbox/ 下:

角色ctx 键
dsh-sandboxService Definition:进程沙箱服务 + 共享词汇ctx.sandbox
dsh-sandbox-localService Provider:平台本地隔离后端(注册 ctx.sandbox
dsh-sandbox-policy策略归属:部署默认 + 会话覆盖解析ctx.sandboxPolicy

消费方不止 shell:dsh-bash-sandbox / dsh-pwsh-sandbox 用它包住每条命令,dsh-fs-sandbox(在 fs 族)用同一套策略围栏文件写。模式词汇只约束文件效果(file effects only)——网络与进程可见性不在 SandboxMode 声称的范围内。

出处:packages/sandbox/README.mddeepseek-harness-src/packages/sandbox/README.md

核心机制

三档模式语义

SandboxMode 是闭集:read-only / workspace-write / danger-full-access。只有前两档是"受限"模式(ConfinedSandboxMode),可以发给 provider;danger-full-access 的消费方直接 spawn 原始 argv、根本不调用 ctx.sandbox

模式文件效果
read-only(默认)任何地方都不允许写;仅 /dev/null 节点可写,>/dev/null 保持可用
workspace-write只允许写到 workspaceRoot 与平台临时区(bwrap 下为 /tmp,Landlock 下为主机 /tmp,Seatbelt 下为 /private/tmp 加每用户临时目录)
danger-full-access不加限制;provider 从不被咨询

出处:packages/shell/bash-sandbox/README.mddeepseek-harness-src/packages/shell/bash-sandbox/README.md)与 docs/subsystems/sandbox.mddeepseek-harness-src/docs/subsystems/sandbox.md

confine(argv, policy) 契约

服务契约一句话:ctx.sandbox.confine(argv, policy) 返回替代你自己 argv 去 spawn 的 argv——包装后进程及其一切子进程都受限——外加所选后端的 enforcement 完整性、拒绝方言(denialSignatures)与结构化 runner 失败证据(runnerFailureRules);无后端可用时抛错,绝不把 argv 原样放行。拒绝分类是保守推断:bash 消费方从失败运行收集的 stderr 尾部匹配该后端独有的签名(bwrap 的 EROFS 文本、Landlock 的 EACCES、Seatbelt 的 EPERM)。

同世界限制(same-world confinement):后端共享宿主文件系统与内核(bwrap、Landlock、Seatbelt、Windows ACL 受限令牌)。容器、微 VM、远程执行器不是本缝的后端——它们作为环境自洽的整族替换 ctx.shell / ctx.fs 的 Service Provider,因为"bash 在容器里、fs 工具写宿主机"等于把代理劈成两个世界。

出处:packages/sandbox/sandbox/README.mddeepseek-harness-src/packages/sandbox/sandbox/README.md

策略 ride the call,而非 ride the provider

每个能力调用携带完整的 SandboxExecutionPolicy(mode + workspaceRoot + 可选 sessionId)。两个消费方可在同一瞬间以不同策略受限(bash 在 read-only 下跑,受限子代理保持自己的状态目录可写);一次被批准的一步到位更宽重试,就是一次带更宽 policy 的新调用——这在"provider 上固定一个模式"的模型下根本无法表达。因此 provider 不存模式,只存机制与缓存的 runner 裁决。

部署默认 + 会话覆盖(ctx.sandboxPolicy

sandbox-policy 是策略解析的唯一归属:部署默认模式(mode,默认 read-only,fail-safe)+ 回退根(workspaceRoot,默认 process.cwd())。会话级覆盖是一条 log-only 的 sandbox/mode 事件——开关即事件,无带外变更;生效值按 显式批准 mode ?? 会话事件折叠 ?? 部署默认 解析。workspace 根是会话创建时记录、之后不可变的 SessionHeader.cwd(以文件系统语义先 canonicalize、再做词法归一,使 symlink/..chdir 实际落点一致),因此不需要另一个事件。resolve({ session, mode? }) 是唯一解析入口,bash 与 fs 双方读同一份 policy——这正是防"围栏漂移"(fence drift)的关键。

出处:packages/sandbox/sandbox-policy/README.mddeepseek-harness-src/packages/sandbox/sandbox-policy/README.md

本地后端与 fail-closed

dsh-sandbox-local 每个 provider 生命周期选择并缓存一个平台 runner:Linux 功能探测 bwrap(探测真实构建并强制一个 profile,而非查 --version),失败则落到随包发布的 Landlock launcher(native/landlock-run,约 300 行 C,规则集随 execve 继承);macOS 用 Seatbelt(sandbox-exec);Windows 用 ACL 受限令牌 runner。不支持的平台与不可用的 runner 一律 fail closed——前台抛 SANDBOX_UNAVAILABLE,后台进程盖上 runnerFailed 事实。enforcement 是上报的事实:较老的 Landlock ABI 与 Windows ACL runner 的 Everyone/硬链接边界上报 partial,绝不虚报为 full。

出处:packages/sandbox/sandbox-local/README.mddeepseek-harness-src/packages/sandbox/sandbox-local/README.md

跨族:fs 写围栏与 bash 共享同一模式

dsh-fs-sandbox 扩展 LocalFileSystem,只对 writeText / editText 加逐调用模式围栏:read-only 一律拒绝(结构化 FS_SANDBOX_DENIED);workspace-write 只允许目标落在 workspace 根与平台临时区之下;danger-full-access 不围栏。可写根集合来自单一来源函数 writableRoots,Seatbelt profile 与 fs 围栏都从它推导,所以"fs 的围栏"与"bash 的 runner"不可能漂移到不同的根。其威胁模型是"可信代码对模型控制路径做策略围栏",不是内核安全边界——内核级隔离不可信代码仍是 ctx.shell 的职责;残余 TOCTOU 通过在写前立即重新 canonicalize 收窄。

出处:packages/fs/fs-sandbox/README.mddeepseek-harness-src/packages/fs/fs-sandbox/README.md)与 跨族 fs 沙箱设计笔记deepseek-harness-src/.agents/notes/implemented/feature/2026-07-14-cross-family-fs-sandbox.md

审批瀑布:缺席即 deny

提权经 ctx.approval.request() 走:返回 allowed-once / rejected / cancelled / unavailableapproval/request 是 waterfall,监听者可作答或 next() 委托;没有可用 answerer 时 fail closedunavailable),服务自身从不主动弹人。allowed-once 的授权只作用于那一次调用、不持久化;approval/askedapproval/decided 是 log-only 审计事件,模型只看到消费方最终的工具结果。会话审批策略 ApprovalPolicy'ask' / 'never''never' 在交互式分发之前就确定性拒绝。

出处:packages/interaction/user-approval/README.mddeepseek-harness-src/packages/interaction/user-approval/README.md

权限预设:一个选择器绑两个旋钮

dsh-permission-presets 提供面向用户的预设表,把沙箱模式与审批策略捆绑成一个选择:

预设sandbox/modeapproval/policy
workspace-write(默认)workspace-writeask
danger-full-accessdanger-full-accessnever

set(session, name) 先记录一条 log-only 的 permissionPresets/preset 事件,再仅对实际变更的旋钮调用其 setter;current(events) 优先匹配仍生效的记录选择,否则返回 custom(可展示、不可选中)。它硬性要求 confining 的 ctx.shell 执行器与 ctx.approval 都在场。

出处:packages/interaction/permission-presets/README.mddeepseek-harness-src/packages/interaction/permission-presets/README.md

模型可见的失败与拒绝文案

  • SANDBOX_UNAVAILABLE(受限模式无可用后端时前台传播的精确错误):
markdown
sandbox mode "<mode>" is requested but no sandbox backend is usable on this host; refusing to run the command unconfined. Install bubblewrap or run a Landlock-enforcing kernel (Linux), ensure sandbox-exec is usable (macOS), or ensure the ACL restricted-token runner can start (Windows) — otherwise switch the consumer to danger-full-access.
  • 拒绝标记[sandbox: file access denied under <mode> mode];可提权时追加 [sandbox: escalation available — retry this exact command once with sandbox_permissions (the narrowest wider mode that suffices) + justification; the approval prompt asks the user]
  • runner 自身失败[sandbox: the sandbox runner itself failed under <mode> mode — the command did not run; this is a sandbox problem, not a command failure]

出处:packages/sandbox/sandbox/README.mdpackages/shell/bash-sandbox/README.md(均在 deepseek-harness-src/packages/ 下)

关键设计决策

  • 策略随调用携带:同一 provider 并发服务不同策略,提权重试就是一次更宽的新调用——这是"config-fixed 模式"被否掉的直接理由(沙箱设计笔记deepseek-harness-src/.agents/notes/implemented/feature/2026-07-06-sandbox.md)。
  • fail-closed,绝不静默降级:无后端时抛 SANDBOX_UNAVAILABLE,明文直通在受限策略下永远非法。
  • 文件效果是全部词汇:不为后端不强制的东西做声称;网络与进程可见性明确不在模式语义内。
  • 同世界限制是有意取舍:容器/微 VM 是整族替换而非本缝后端,保证"执行世界"环境自洽。
  • 审批缺席即拒绝、授权仅一次(allowed-once)、不持久化;重试必须是"同一命令 + 严格更宽模式 + justification"且以审批提示为同意步骤,绝不自动重试(自动重试会产生日志无法重建的隐藏重入)。
  • 可写根单一来源writableRoots):fs 围栏与 Seatbelt profile 同源推导,防跨族漂移。

扩展点

  • 新平台 runner:在 dsh-sandbox-local 的选择链中加后端(Windows 链已由 ACL 受限令牌 runner 填上);自定义 runner 可经 runnerCommand 显式断言(操作者声明其 bwrap 兼容)。
  • 新消费方:任何子进程消费方调用 confine(argv, policy) 并直接 spawn 返回值即可受限;subagent-acp 子代理受限是设计笔记中留待后续的阶段。
  • 自定义权限预设表:扩展 dsh-permission-presetspresets 配置,UI 适配器可把它暴露成一个选择器。
  • 策略层拦截tools/pre-execute waterfall 承载每调用 allow/deny/ask;prepend 一个 policy answerer 可在审批链最前自动拒绝部署永不想要的模式。

参考资料

本文档为学习用途的原创讲解,基于 MIT 许可的开源项目 deepseek-ai/deepseek-harness 编写;所有引用均注明出处。