feat(sandbox): 对话框内命令执行方式选择器 + OS 级沙箱执行 - #505
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>
|
PR governance checks passed. Awaiting human review. |
…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>
su-fen
left a comment
There was a problem hiding this comment.
结论:当前不建议直接合入,建议先 Request changes。
阻塞问题:
-
P1:远程 WebUI 的安全模式不会生效。
buildGatewaySystemSettings只发送executionMode和workdir,没有发送commandSafetyMode(crates/agent-gateway/web/src/app/chatEventUtils.ts:102)。桌面端远程请求类型和桥接逻辑也没有该字段,最终执行仍读取桌面本地的settings.system.commandSafetyMode(crates/agent-gui/src/pages/chat/runtime/useSendChatTurn.ts:1588)。因此 WebUI 选择“沙箱/沙箱·断网”时,远程命令可能仍按auto执行,安全语义失效。 -
P1:工作区根目录可以重新暴露敏感目录。 macOS 先 deny 敏感目录,随后对整个
write_root做 read/write re-allow(crates/agent-gui/src-tauri/src/runtime/sandbox.rs:175);Linux 先用--tmpfs掩蔽,再用--bind write_root write_root重新暴露(crates/agent-gui/src-tauri/src/runtime/sandbox.rs:275)。当用户把工作区设为 home 目录甚至/时,~/.ssh、~/.liveagent等会重新可读写,沙箱边界被绕过。应拒绝与敏感目录存在祖先/包含关系的工作区,或改为不会重新暴露敏感子目录的挂载/profile 规则。 -
P1:
isolated=true的 ManagedProcess 生命周期被破坏。 Linux bwrap 始终加入--die-with-parent(crates/agent-gui/src-tauri/src/runtime/sandbox.rs:255),但现有语义要求 isolated 进程在 LiveAgent 退出后继续运行;调用路径没有根据isolated调整沙箱参数(crates/agent-gui/src-tauri/src/runtime/managed_process.rs:525)。应修复生命周期语义并补充 isolated + sandbox 的回归测试。
GitHub CI 检查目前全部通过,但现有 CI 没有实际执行 Linux bwrap 或 macOS Seatbelt 的运行时冒烟测试,建议补充后再合入。
…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
Closes #506
概述
新增「命令执行方式」选择器,放在聊天输入栏工具条(推理选择器旁),单一互斥维度四档:
沙箱实现
新增
src-tauri/src/runtime/sandbox.rs,按平台分后端:/usr/bin/sandbox-exec+ 动态 Seatbelt profile。allow-default + 全盘写 deny + 工作区/临时目录写 allow + 敏感目录读 deny + 工作区 re-allow。规则为"最后命中者优先",工作区 re-allow 必须排在敏感目录 deny 之后(默认工作区在 ~/.liveagent 内),顺序有单测锁定。--ro-bind / /+ 工作区可写 bind + tmpfs 掩蔽敏感目录 + 可选--unshare-net),启动时真实探测可用性(容器/受限内核下 bwrap 可能存在但无法建 namespace)。掩蔽范围:
~/.ssh、~/.aws、~/.gnupg、~/.config/gh、~/.liveagent(provider 密钥库所在)。拦截点:
spawn_platform_shell_command唯一 spawn 漏斗(Bash + ManagedProcess 共用),沙箱包裹发生在 shell candidate 选定后、Command::new前;包裹失败直接报错。Hook/Cron 脚本是用户显式配置的自动化,不属于模型驱动面,不套沙箱。配套改动
ShellRunResponse新增sandbox字段;Bash 结果头部显示sandbox: seatbelt,模型与用户都能确认命令在沙箱内执行system_sandbox_capability命令 +@liveagent/adapters/sandboxCapability双端适配(WebUI 返回 null,由桌面端裁决)system.commandSafetyMode字符串枚举:GUI/WebUI settings 双份同步、Rust 归一化 + 保存白名单、i18n 四 block 同加验证
后续(不在本 PR)
🤖 Generated with Claude Code
Screenshots / preview
默认(自动执行) — 输入栏工具条新增命令执行方式选择器:
下拉菜单 — 四档模式,带图标与说明,Windows 上沙箱两项禁用并提示:
沙箱模式激活 — pill 转为绿色盾牌,此后 Bash / ManagedProcess 均在 OS 沙箱内执行: