跳到主要内容

数据截至 (上游 commit 348d3875c1bf)

06 · 沙箱与安全边界

这章回答一个尖锐的问题:Chatbox 凭什么敢执行模型生成的 shell 命令? 答案是它没自己造轮子,而是把命令关进 Anthropic 的沙箱运行时,并叠了几层防护。

6.1 它要解决的小问题

第4章的 sandbox_bash 工具能让模型跑任意 shell。这显然危险:模型(或被注入的恶意 prompt)可能 cat ~/.ssh/id_rsarm -rf、外传密钥。所以执行前必须有真正的操作系统级隔离,而不只是"提示模型别乱来"。

6.2 思路:复用 Anthropic sandbox-runtime

沙箱跑在 main 进程(渲染进程没有 Node 的 spawn)。核心依赖是 @anthropic-ai/sandbox-runtime(src/main/sandbox/manager.ts:14)。它提供两个关键能力:

  • SandboxManager.initialize(config)——按配置(允许读写哪些路径、网络策略)初始化一个沙箱 profile。
  • SandboxManager.wrapWithSandboxArgv(command)——把一条命令包裹成沙箱约束下的 argv + env,然后才交给 spawn 跑(manager.ts:762-764)。

执行链路(execCode, manager.ts:651):

模型给的 command


wrapWithSandboxArgv(command) ← Anthropic runtime 加上 OS 级约束


spawn(wrapped, { shell, cwd, detached }) ← 在工作目录里跑


stdout/stderr 收集 → tailTruncate 截断 → 返回 {stdout, stderr, exitCode}

6.3 精华:默认拒绝清单

安全的关键在 buildConfig(manager.ts:375)和共享常量(src/shared/task-sandbox.ts)。默认就把高危目标拉黑:

// src/shared/task-sandbox.ts —— 默认拒绝清单
export const TASK_SANDBOX_DENY_READ_PATHS = ['~/.ssh', '~/.gnupg', '~/.aws', '~/.config/gh']
export const TASK_SANDBOX_DENY_WRITE_PATHS = ['.env', '.env.local', '.env.production']
export const TASK_SANDBOX_EXTRA_WRITE_PATHS = ['/tmp']

配置组装时:可写 = 用户选的工作目录 + /tmp,拒读 = SSH/GPG/AWS/gh 凭据目录,拒写 = 各种 .env(manager.ts:390-410)。工具说明里也对模型明说了这条边界:可写输出放进工作目录,macOS/Linux 上 /tmp 这类沙箱可写临时目录也行(toolsets/code-execution.ts 描述)。

这里还藏了一个容易踩的坑注释(manager.ts:399-400):

// WARN: `allowedDomains: ['*']` is NOT a wildcard — it's a literal match.
// Omit `allowedDomains` so wrapWithSandbox generates `(allow network*)`.

即:想"允许所有网络"不能写 ['*'](那会被当成字面域名),而要省略该字段。这种"运行时行为反直觉"的点被留成注释,是高质量边界代码的标志。

6.4 进程管控:超时、进程组 kill

模型跑的命令可能挂死或 fork 子进程,所以:

  • detached spawn + 进程树 kill:killProcessTree 先 SIGTERM、3 秒后 SIGKILL 杀整棵进程树(manager.ts:800-801;工具 terminateTrackedChild 同款 :118-119)。这能干掉命令 fork 出来的子孙进程,避免僵尸。
  • 超时:默认 execCode 30 秒(manager.ts:664),但工具层 code_execution 默认给到 120 秒(toolsets/code-execution.ts:213,DEFAULT_EXEC_TIMEOUT 定义于 src/shared/sandbox-provider.ts:6),超时 exitCode 记 124(manager.ts:842)。
  • 可中断:工具层把 AbortSignal 监听接到 platform.sandboxKill()(toolsets/code-execution.ts:195-210):会话一取消就杀沙箱进程,进来时已中止则直接返回 exitCode 130。

6.5 跨平台:macOS/Linux 走 OS 沙箱,Windows 原生直跑

checkAvailability(manager.ts:1190)按平台分流:macOS 直接可用;Linux 要求 bubblewrap+socat(checkDependencies,缺依赖返回原因);Windows 现在原生直跑、没有 OS 级沙箱(manager.ts:1212-1216 的注释与 docs/technical/windows-sandbox.md),bash 语言需要 PATH 里有 Git Bash 或 WSL,由 execCode 调用时检查;WSL 场景下 cd 会用 wslpath 翻译路径(exec-script.ts:23-24)。

6.6 输出截断:防上下文爆炸

命令输出可能是几 MB 的日志,直接喂回模型会爆上下文。truncate.ts 提供 tailTruncate(留尾部,看最新输出)和 headTruncate(留头部,如 cat/ls)。execCode 对 stdout/stderr 用 tailTruncate(manager.ts:840-841),文件读取类用 headTruncate(manager.ts:1062)。

6.7 文件操作的两条路

旧实现的 sandbox_write/sandbox_edit(把写文件翻译成 printf > file、编辑翻译成 sed -i 再过沙箱)已删除。现在沙箱内的文件操作由模型用 code_execution 以代码直接完成,统一收口于 execCode;沙箱外的真实修改走 file.tswrite_file/edit_file,由用户审批把关(toolsets/code-execution.ts 的工具说明明确引导模型这么分工)。命令拼接的转义辅助仍在(shellEscape,manager.ts:450)。

6.8 边界与风险

  • 隔离强度取决于 @anthropic-ai/sandbox-runtime 的底层实现(macOS Seatbelt / Linux 命名空间等);Chatbox 信任这个上游。
  • 拒绝清单是黑名单思路,新出现的敏感路径(如其他云厂商凭据)需要手动补。
  • Windows 原生路径没有 OS 沙箱(见 6.5),隔离只依赖进程树 kill 与超时,敏感目录靠拒读/拒写清单兜底。

6.9 代码地图

主题文件符号
沙箱管理(初始化/执行)src/main/sandbox/manager.tsinitSandbox / execCommand
命令包裹src/main/sandbox/manager.ts(SandboxManager.wrapWithSandbox)
默认拒绝清单src/shared/task-sandbox.tsTASK_SANDBOX_DENY_READ_PATHS
进程组 killsrc/main/sandbox/manager.tskillTree / killRunningCommand
跨平台可用性src/main/sandbox/manager.tscheckAvailability / toWSLPath
输出截断src/main/sandbox/truncate.tstailTruncate / headTruncate
沙箱工具(模型侧)src/renderer/packages/model-calls/toolsets/code-execution.tscode_execution(内联 abort→sandboxKill)
IPC 暴露src/main/sandbox/ipc-handlers.ts(sandbox:* handlers)