feat(sandbox): 统一 OS 沙箱与 Windows workspace-write 后端 - #530
Conversation
新增命令执行方式(commandSafetyMode),在聊天输入栏工具条切换,单一互斥维度: - 逐条审批(ask):非只读工具每次执行前都请求批准 - 自动执行(auto,默认):按设置中的工具权限策略直接执行 - 沙箱(sandbox):Bash/ManagedProcess 在 OS 级沙箱内执行 - 沙箱·断网(sandboxOffline):在沙箱基础上禁止命令联网 沙箱实现(runtime/sandbox.rs): - macOS:/usr/bin/sandbox-exec + 动态 Seatbelt profile(allow-default + 全盘写 deny + 工作区/临时目录写 allow + 敏感目录读 deny + 工作区 re-allow, 规则顺序有单测锁定) - Linux:bwrap(--ro-bind / / + 工作区 bind + tmpfs 掩蔽敏感目录 + 可选 --unshare-net),带真实可用性探测 - Windows:暂不支持(受限令牌 + Job Object + WFP 路线待实现),选择器中 沙箱两项禁用并提示,执行层 fail-closed 兜底 - 掩蔽范围:~/.ssh、~/.aws、~/.gnupg、~/.config/gh、~/.liveagent - 拦截点:spawn_platform_shell_command 唯一漏斗(Bash + ManagedProcess 共用), 沙箱包裹失败直接报错,绝不静默降级 - Hook/Cron 脚本为用户显式配置的自动化,不套沙箱 配套: - ShellRunResponse 新增 sandbox 字段,Bash 结果头部显示 sandbox: seatbelt - Bash 工具描述在沙箱开启时告知模型围栏规则 - system_sandbox_capability 命令 + adapters 探测(WebUI 返回 null 透传) - 设置持久化(含保存白名单)、GUI/WebUI settings 双份同步、i18n 四 block 同加 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…besandbox # Conflicts: # crates/agent-gateway/web/src/app/GatewayApp.tsx # crates/agent-gateway/web/src/i18n/config.ts # crates/agent-gateway/web/src/lib/settings/index.ts # crates/agent-gui/src-tauri/src/lib.rs # crates/agent-gui/src/i18n/config.ts # crates/agent-gui/src/lib/settings/index.ts # crates/agent-ui/src/pages/chat/ChatComposerBar.tsx
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ss lifecycle Address two P1 review blockers on the sandbox write fence: P1#2 — Workspace root could re-expose sensitive dirs. The write fence re-allows write_root last (macOS re-allow rule, Linux --bind after --tmpfs), so a workspace of $HOME or / would re-open ~/.ssh, ~/.liveagent, etc. Add a fail-closed validate_workspace() in the shared wrap_command choke point: reject any workspace that encloses/equals a sensitive dir, and any workspace nested inside a credential dir (~/.ssh/.aws/.gnupg/.config/gh). The app-config dir (~/.liveagent) is exempt in the descendant direction so the default workspace ~/.liveagent/default-project still runs. P1#3 — isolated ManagedProcess lifecycle was broken on Linux. bwrap always added --die-with-parent, killing isolated long-lived processes when LiveAgent exits. Thread an `isolated` flag through SandboxSpec (default false for the Bash tool path, true for isolated managed processes) and omit --die-with-parent when set, mirroring the existing conditional --unshare-net. Adds cross-platform tests for workspace validation and the bwrap flag. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
远端 WebUI / 队列 / 编辑重发的请求经网关下发时,useSendChatTurn 硬编码使用 本地 settings.system.commandSafetyMode,忽略了请求自带的安全模式,导致远端 选定的沙箱模式无法生效。 - proto: ChatRequest 新增 command_safety_mode 字段(regen Go/TS/Rust) - Go 网关: NormalizeCommandSafetyMode 白名单归一(非法/空→"")并全链路透传 - WebUI: buildGatewaySystemSettings / payload / adapters 补齐字段 - 桌面端: 网关事件 → bridge override → useSendChatTurn 优先级链 (override ?? 网关直带 ?? 本地设置),fail-closed 回落本地而非默认 auto - 队列: QueuedChatTurn 顶层携带 commandSafetyMode,保留入队时的安全模式 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…besandbox # Conflicts: # crates/agent-gui/src-tauri/src/commands/runtime/shell.rs # crates/agent-gui/src/lib/tools/shellTools.ts # crates/agent-gui/src/pages/ChatPage.tsx # crates/agent-ui/src/pages/chat/ChatComposerBar.tsx
在基础 OS 沙箱之上实现 Windows 端写围栏,替换原先的 unsupported 桩。 免管理员、零配置、fail-closed,语义为 workspace-write。 机制(Option A,自我再执行 launcher): - sandbox.rs windows `wrap_command` 返回 (current_exe, ["__sandbox_exec","--write-root",root,"--",program,args...]) - windows_sandbox.rs `run_sandbox_launcher_if_requested()` 作为 run() 首行(Tauri 初始化前)识别 marker:盖 (OI)(CI) 继承写 ACE(合成 workspace SID)+ 建 WRITE_RESTRICTED 受限令牌(合成 SID 作 restricting SID ⇒ 广读、写限工作区)+ CreateProcessAsUserW 拉真实 shell, WaitForSingleObject(INFINITE) 后透传退出码;任何失败退 127,绝不直跑。 - Job Object KILL_ON_JOB_CLOSE 兜底;launcher 阻塞等待使 taskkill /T 沿完整 PPID 链级联,进程树 kill 不受多一跳影响。 能力/门控: - capability() 返回 supported:true, mechanism:"restricted-token", network_control:false。sandbox 模式在 Windows 放开;sandboxOffline (断网 + 读掩蔽免管理员做不到)保持 fail-closed 禁用,执行层对 !allow_network 直接报错兜底(设置可能自 macOS 同步)。 - 前端 CommandSafetyModeSelector 改为按 network_control 单独禁用 sandboxOffline,sandbox 仍可用;i18n 增 sandboxOfflineUnavailable。 - SandboxCapability 增 network_control:boolean,Rust→双端 TS adapter 贯通。 安全加固(采纳两轮 review): - resolve_program_in_path:lpApplicationName 用 PATH 内绝对路径解析, 工作区 cwd 绝不被搜索(挡 planted-binary),argv[0] 保留原名。 - ensure_write_ace 跳过 null-DACL(避免 deny-all)、setup_fenced_temp 拒绝 reparse point、INVALID_HANDLE_VALUE 守卫。 验证:probe crate cargo check --target x86_64-pc-windows-msvc 零错误; host cargo test --lib sandbox 12/12;GUI 715 / WebUI 578 前端测试 + 双端 tsc + biome 全绿。⚠️ token/ACE/CreateProcessAsUserW 的 unsafe FFI 仅编译 级验证,从未在真 Windows 上功能测试,合入前需真机验证边界。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…518 沙箱可选并被选中(✓),沙箱·断网因 Windows 免管理员方案无法断网而 fail-closed 禁用。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…besandbox Co-authored-by: Cursor <cursoragent@cursor.com> # Conflicts: # crates/agent-gateway/web/src/lib/proto/gen/proto/v2/gateway_pb.ts
…h DLL-init failure Two Windows sandbox changes, both admin-free: 1. Fix Git Bash dying with 0xC0000142 (STATUS_DLL_INIT_FAILED) under the restricted-token sandbox: append a logon-SID GENERIC_ALL ACE to the restricted token's default DACL (Chromium AddSidToDefaultDacl pattern) so msys/cygwin named kernel objects pass the write-restricted second access check; set lpDesktop explicitly; replace bInheritHandles=TRUE with STARTUPINFOEXW + PROC_THREAD_ATTRIBUTE_HANDLE_LIST (3 std handles only); translate loader NTSTATUS exit codes into readable stderr hints. shell_runner additionally probes each candidate once per (shell, mechanism) under sandbox mode and falls through to pwsh/powershell/cmd on loader-level incompatibility instead of returning a dead shell. 2. sandboxOffline now works on Windows: zero-capability AppContainer (CreateProcessW + SECURITY_CAPABILITIES) gives kernel-enforced network denial including loopback via WFP, no elevation needed; write fence reuses the inheritable-ACE stamping with the AC SID as trustee, and AC default-deny read masks sensitive dirs as a side benefit. Offline env overlay (blackhole proxy, CARGO_NET_OFFLINE, PIP_NO_INDEX, NPM_CONFIG_OFFLINE) added as defense in depth. Launcher protocol gains --net on|off and --isolated; isolated processes now skip the KILL_ON_JOB_CLOSE job object across the re-exec boundary (previously the flag was dropped, matching bwrap --die-with-parent semantics on Linux).
network_control is now true on Windows, so the capability-driven selector enables sandboxOffline there; drop the hardcoded "Windows can't sever network" from the unavailable hints (kept generic for platforms that still lack network control) and refresh the CommandSafetyMode comment.
|
对 一、沙箱围栏可被绕过1. 模型可经 MCP 工具拿到无围栏的进程 spawn 原语 —
更关键的是 2. cron bash 任务完全不受沙箱约束,且重启后仍在 —
用户为了约束一个不可信任务而选择 3. 沙箱开关完全由渲染进程的两个 bool 决定,后端无独立下限 —
仓库里已有正确范式可循: 二、fail-open:失效路径未按 fail-closed 收敛4. Windows 其余平台都做真实探测:macOS 在 理解注释的论证是两个后端免依赖免提权、恒可用。但若 5.
于是当 6. 未识别的安全模式静默降级为
任何未来新增的模式值、任何回退到旧版本、任何手改配置的笔误,都会解析成宽松的 三、UI 安全承诺与 Windows 联网后端实现不符7. 两种语言的文案都承诺"~/.ssh 等敏感目录不可读",但 Windows 联网后端(restricted-token)没有实现任何读掩蔽 — macOS 用 问题在于文案把一个已知缺口转成了肯定的安全承诺:Windows 用户读到该描述,据此认为凭据受保护,于是在 sandbox 模式下运行不可信的模型生成代码。这段代码可以随意读取 四、Windows 后端实现细节8.
另外 五、设计问题(非缺陷,供讨论)9. 安全模式优先级链缺少 max-strictness 钳制 —
但链路上没有任何一处与本地设置做严格度比较。WebUI 经 审查覆盖沙箱边界相关改动;未逐行审查 UI 文案与文档的其余部分。若某条判断有误请指出。 |
Closes #506
Closes #520
Summary
将基础 OS 沙箱与 Windows workspace-write 后端合并为一个面向
main的 PR,并修复模型驱动 shell 的可恢复 Bash 绕过问题。根因是 chat 的 Bash 在启用 resumable session 时走
shell_session_start,旧链路没有传递 sandbox policy,Rust 最终以sandbox_spec=None启动普通 shell。RANDOM、&&、Git Bash 和D:/...路径本身都不是绕过技巧;缺失的策略参数才是漏洞。现在一次性 Bash、resumable Bash 和 ManagedProcess 共用同一个平台 spawn funnel。resumable session 初始启动和后续
ProcessWait都保持同一隔离进程,响应返回实际生效的机制,便于审计。本 PR supersedes #505 和 #518;两个旧 PR 已关闭。
Coverage Matrix
useSendChatTurn->runAgentConversationTurn-> builtin registrysandbox/sandboxOfflinereaches every shell toolcommand_safety_mode-> desktop listener -> turnExplicit user-authored cron scripts and hooks remain outside this model-driven Bash contract and retain their existing explicit-script semantics.
Change Scope
sandboxPolicy.ts.shell_run,shell_session_start, andmanaged_process_startalways send explicitsandboxandsandbox_allow_networkbooleans.sandbox-exec); Linux uses bubblewrap (bwrap).sandboxOfflineremains disabled and fails closed because this backend cannot enforce network isolation.Screenshots / Preview
Default command mode
Command mode menu
Sandbox mode selected
Windows capability menu
Windows composer with workspace-write sandbox
Verification
pnpm --filter liveagent exec tsc --noEmit— passedgo test ./...passedgit diff --check— passedThe Unix E2E proves workspace writes succeed while sibling writes fail and the response reports the active mechanism. Windows FFI is compile-checked and covered by pure-logic tests, but a real Windows host/Tauri launcher E2E is still required before release; this macOS environment cannot provide that evidence.
Pre-submit Checklist
feature/sandbox-unified