docs/technical/windows-sandbox.md
Last updated: 2026-07
状态:已实现最小原生支持(放弃文件隔离)。本文记录 Windows 上代码执行的根因分析、 方案对比,以及当前落地方案与未来的强隔离演进方向。
当前决定(本次实现):Windows 作为原生应用,代码执行原生直跑、无操作系统沙箱 (放弃文件/网络隔离),换取无管理员、无注销重登的良好本地体验。强隔离(SRT-Windows / Codex unelevated)记录在 §3.2 / §3.3 作为后续演进,本次不做。
Chatbox 的 Agent Mode 代码执行(见 Chat 代码执行)在 macOS/Linux 上
通过 @anthropic-ai/sandbox-runtime(下称 SRT)实现隔离,但在 Windows 上完全不可用:
Sandbox init failed: Sandbox dependencies not available: Unsupported platform
0.0.34(2026-02)。该版本 isSupportedPlatform() 只认 macos /
linux;在原生 Windows 进程(process.platform === 'win32')调用 initialize() /
wrapWithSandbox() 会直接抛 Unsupported platform。process.platform 即为 linux)。
Chatbox 的 Electron 主进程在 Windows 上是原生 win32 进程,从未真正"进入 WSL 跑 runtime"。toWSLPath()、checkAvailability() 查 wsl --status 是半成品:只转换了
路径、探测了 WSL,却仍在 win32 进程内调用 runtime,必然失败。command node in WSL、
toSandboxShellPath 等)。这些 Windows 专属改动在本方案下作废,需清理(见 §7)。SRT 自 0.0.53(2026-06-04) 起新增原生 Windows 支持,0.0.55 为最新:
isSupportedPlatform() 现返回 macos || windows。wrapWithSandboxArgv() 返回 {argv, env},以 {shell:false} 启动
(Windows 上 wrapWithSandbox() 会抛错,强制走 argv 形式)。srt-win.exe:npm 包只含源码,无预编译二进制;官方平台包
@anthropic-ai/sandbox-runtime-win32-* 目前 404 未发布。致命的 UX 问题:WFP 过滤器只在 discriminator group 进入调用者 token 后才生效,而这 需要一次注销 / 重新登录("the logout/login dance",源码注释原话)。在此之前 WFP filter-0 放行所有流量,即网络未隔离。首次启用还需管理员权限创建组 + 安装 WFP 过滤器。 对消费级桌面应用,"装完要注销重登"是不可接受的门槛。
OpenAI Codex(Apache-2.0,openai/codex → codex-rs/windows-sandbox-rs)已落地、文档化,
原生 Windows、不依赖 WSL,提供两种模式:
elevated(强) | unelevated(无需管理员) | |
|---|---|---|
| 文件系统 | 专用低权限 sandbox 本地用户 + ACL 边界 | 从当前用户派生的受限令牌 + ACL 边界 |
| 网络 | 防火墙规则(offline-user firewall) | 环境变量级 offline 控制 |
| 管理员 | 需要(UAC:建用户/组、防火墙、登录权限) | 不需要 |
| 注销/重登 | 否 | 否 |
| UI 隔离 | 默认 private desktop | 默认 private desktop |
| 终端 | ConPTY(Win10 1809+,Win11 推荐) | 同左 |
分发的二进制(@openai/codex@…-win32-x64 包内已预编译):
codex-windows-sandbox-setup.exe — 一次性 setup(manifest 为 asInvoker,按需提权)。codex-command-runner.exe — 每条命令的 runner:派生受限令牌,ConPTY/管道 spawn,IPC 帧通信。源码模块映射:token.rs/cap.rs(受限令牌)、acl.rs/workspace_acl.rs/deny_read_acl.rs
(FS ACL)、identity.rs/hide_users.rs(sandbox 用户)、wfp.rs(防火墙)、desktop.rs
(private desktop)、conpty/(终端)、setup.rs/elevated_impl.rs(提权 setup)。
@vscode/sandbox-runtime、opencode-sandbox、@xmz-ai/sandbox-runtime):
均为 macOS/Linux 路线的包装层,不含 Windows helper,不解决核心问题。考虑到强隔离方案改动巨大,本次先让 Windows 能原生执行代码,放弃文件/网络隔离:
0.0.34 → 0.0.54(受 pnpm minimumReleaseAge: 10080=7 天约束,0.0.55 仅 2 天被拦;
0.0.54 通过且已含 Windows 后端,为未来留门)。此升级只惠及 macOS/Linux,已在 macOS 实测无回归。code_execution 在会话工作目录里直接跑:
node:用打包的 Electron 二进制 + ELECTRON_RUN_AS_NODE,程序经 stdin 喂入(无需 shell 转义/路径转换)。powershell:优先使用 CHATBOX_POWERSHELL_PATH 指定的程序或 PowerShell 7(pwsh.exe),再回退到
Windows 自带的 powershell.exe。使用 -NoLogo -NoProfile -NonInteractive -Command - 从 stdin 执行,
原生继承 spawn({ cwd }) 的 Windows 工作目录和路径语义。bash:优先使用 CHATBOX_GIT_BASH_PATH 指定的 Git Bash,然后检查 Git for Windows、
PortableGit / Scoop 等常见位置和 git.exe 旁的 bash.exe;再兼容 PATH 上的其他 POSIX shell,
最后才回退到 wsl bash。解析结果显式区分 git-bash / path-bash / wsl,脚本均经 stdin
喂入。无 bash 时返回清晰错误。spawn({ cwd }) 传入,不要求模型先执行 cd / Set-Location。Windows 上优先提示模型
使用 PowerShell 执行终端命令和原生路径操作;Bash 只用于 POSIX 专属脚本。工作目录内部优先使用相对路径,
工作目录外(包括用户授权的真实目录)使用绝对路径和结构化文件工具。Bash 提示会区分 Git Bash 的
C:/... 与 WSL 的 /mnt/c/...,并禁止直接传入 C:\\...。若模型仍用原生路径执行 cd,shim 会通过
cygpath / wslpath 做窄范围兜底。read_file、list_files、
search_files 或 create_download。checkAvailability(win32) → available(原生、无隔离),移除旧的 wsl --status 探测。C:\...、WSL /mnt/c/...、Git Bash /c/... 和 Cygwin /cygdrive/c/... 路径统一成原生路径,
并用不区分大小写、按路径段匹配的方式判断是否位于用户授权目录中。write_file / edit_file 对授权目录内的路径免二次审批,但实际写入仍经
主进程按会话保存的授权根目录校验;授权时会固定目录的 canonical target,根目录、相邻前缀、..
越界、授权后被替换的根目录以及指向目录外的 symlink/junction 不会获得免审批写入。绑定目录内任意
层级的 .env、.env.local、.env.production 仍保持拒写。安全取舍(须知):Windows 上 code_execution 运行模型生成的任意代码,以当前用户完整权限执行——
可读 ~/.ssh、删改用户文件、自由联网。相对 macOS(Seatbelt)/ Linux(bubblewrap)的
denyRead/allowWrite 是实质降级。UI/文档须如实标注,并保持在显式开启入口之后。绑定工作目录只表示
用户授予文件工具在该目录内免二次审批,不是 Windows OS 沙箱;write_file / edit_file 对临时目录、
绑定目录以外绝对路径的用户批准提示仍然有效(工具层,与 OS 沙箱无关)。
升级到的 SRT 0.0.53+ 自带 Windows 后端(WFP + deny-only 组),但首装需管理员 + 注销/重登,
并要自行编译/签名/分发 srt-win.exe。UX 门槛高,暂不采用。
Codex(Apache-2.0)的 unelevated 模型——从当前用户派生受限令牌 + 对工作目录/敏感路径做
ACL 边界 + 环境级网络 offline——是唯一同时满足"无需管理员、无需注销重登、且有真实文件系统隔离"
的方案。是后续把"放弃文件隔离"补回强隔离的推荐方向。下文 §4 起的架构设计即针对该方向,作为未来实现参考。
在 src/main/sandbox/ 现有 SandboxProvider / SandboxManager 抽象下,新增 Windows 原生后端,
与现有 macOS/Linux(SRT)后端并存:
execCommand(command)
├─ darwin/linux → SRT wrapWithSandbox → spawn({shell:true}) (现状不变)
└─ win32 → WindowsSandbox.wrapArgv → spawn(helper, {shell:false})
CreateRestrictedToken 施加:
DISABLE_MAX_PRIVILEGE(剥离特权);SetTokenInformation / TokenIntegrityLevel)。CreateProcessAsUser 启动;继承 stdio(pipes)或 ConPTY(tty)。workingDirectory 模型一致)。TASK_SANDBOX_DENY_READ_PATHS(~/.ssh、~/.aws 等)施加 deny-read ACE,
并对 reparse-point 的 canonical target 一并施加(参考 Codex deny_read_acl.rs,防符号链接绕过)。.env 等保持拒写。env.rs)。明确这是弱网络隔离,
能阻断遵守 proxy 约定的工具,但不是内核级强制。文档与 UI 需如实标注。tty=true 走 ConPTY(Win10 1809+);tty=false 走 pipes。复用现有 stdout/stderr 截断与
10MB 缓冲、超时 → taskkill /T /F(#813 已落地的 killProcessTree)。两条路线(§8 决策):
codex-command-runner.exe(unelevated 路径)。最快,但引入对 Codex CLI 内部 IPC 协议
的依赖,升级/裁剪成本不可控,且二进制入仓 + 签名负担。CreateRestrictedToken + ACL + CreateProcessAsUser + ConPTY 桥接。可控、可裁剪、与 Chatbox
IPC 对齐,但工作量大、需 Windows 构建链与代码签名。无论 A/B,都通过 electron-builder app.asar.unpacked 分发 helper,运行时按
SRT_WIN_PATH 式的解析定位。
checkAvailability() 的 win32 分支移除 wsl --status,改为:探测 helper 存在 + ConPTY 可用
(Win 版本 ≥ 10 1809)。不可用时干净禁用代码执行工具(不构建、不崩、UI 标注原因)。unelevated 的边界强度 < macOS Seatbelt / Linux bubblewrap。受限令牌 + ACL 能挡住"误删用户文件 /
读取敏感目录 / 越权写",但 deny-only 组对持有同用户其他句柄的高级绕过不是铁壁。elevated 模式(管理员一次性 setup),或把强隔离任务
下放到远端/容器/microVM sandbox。注:§4–§6(受限令牌 / ACL / WFP / 提权 setup / 能力探测的强隔离版本)描述的是 §3.3 未来 Codex unelevated 方向的设计,本次未实现,保留作参考。
PR #813(已合并,commit c955cbf94)基于错误的 WSL 前提。本次已清理其 Windows 专属部分:
sandbox:node-command 的 command node(WSL)分支、toSandboxShellPath()/toWSLPath()
及其在 env 改写与 readFile/listDir/grepFiles/findFiles 中的调用、checkAvailability() 的
wsl --status 分支。killProcessTree()(taskkill /T /F,原生 Windows 仍需要)、detached 仅 POSIX 启用。release/app/package.json:SRT ^0.0.34 → ^0.0.54。manager.ts:win32 initSandbox 跳过 SRT;新增 execCode(node 经 execPath+ELECTRON_RUN_AS_NODE、
PowerShell 经 resolveWindowsPowerShell()、bash 经 resolveWindowsBash(),均经 stdin 喂入);PowerShell 7
优先并回退 Windows PowerShell,Git Bash 优先并显式区分 PATH Bash / WSL;
checkAvailability(win32) 返回 available;清理 WSL 遗留。ipc-handlers.ts:sandbox:exec-code 成为全平台唯一代码执行入口,移除 sandbox:exec 与
sandbox:node-command。interfaces.ts / desktop_platform.ts:桌面端统一使用 sandboxExecCode。local-provider.ts:exec() 在所有桌面平台路由到 sandboxExecCode;Windows bash 保留其 PATH 中的
node,避免 WSL 执行宿主 Electron 路径。AgentModePanel.tsx:Windows 开放绑定工作目录入口。windows-path.ts / filesystem.ts:统一 Windows 原生与 shell 路径、大小写无关边界判断,并把授权目录
的文件工具写入路由到主进程。manager.ts:会话记录经过筛选的授权目录;写入/编辑执行词法边界与 canonical symlink/junction 校验。manager.ts / ripgrep-search.ts:文件分页读取和单目录列表使用 Node helper;文件发现与内容搜索统一走
内置 ripgrep;相对下载路径基于会话工作目录解析并校验允许根目录,不再依赖 Bash realpath。code-execution.ts:本地 create_download 直接调用 persistArtifact,Windows 输出固定保存在工作目录。resolveWindowsPowerShell / resolveWindowsBash 决策、Windows 路径别名/大小写/越界、授权目录
symlink 逃逸、无 Bash 文件工具与大输出分页单测;macOS SRT 路径与 stdin 执行机制已实测。windows-2022 已覆盖原生 Node/PowerShell/Bash stdin 执行、PowerShell 7
/ Windows PowerShell 解析、Git Bash 解析、原生 C:\\... 路径、空格/中文目录、文件读写/搜索以及
shell 路径回转,并在 PR #901 验证无 Bash 文件读取/列表、内置 ripgrep 查找与相对路径产物持久化;WSL 仍只做决策单测,
未在 hosted runner 上安装发行版做端到端验证。