Skip to content

feat(security): 危险工具审批门与 Bash 工作区沙箱策略#175

Open
Bsyy95 wants to merge 1 commit into
Stack-Cairn:mainfrom
Bsyy95:feat/tool-approval-gate
Open

feat(security): 危险工具审批门与 Bash 工作区沙箱策略#175
Bsyy95 wants to merge 1 commit into
Stack-Cairn:mainfrom
Bsyy95:feat/tool-approval-gate

Conversation

@Bsyy95

@Bsyy95 Bsyy95 commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

摘要

LiveAgent 的定位是"可远程遥控的本地全能代理"(Gateway 远程会话、cron 定时任务、子代理常驻运行),但目前任何危险工具调用都在模型发起的瞬间直接执行:递归 Delete、SSHManager 的变更操作、以及 cwd 指向工作区外的 Bash / ManagedProcess——全程没有任何用户确认环节。同时文件工具的工作区沙箱对 Bash 形同虚设:cwd 恒以 allowExternal: true 解析,命令可以在整台机器的任意目录运行。

MCP 第三方工具、ClawHub 安装的 Skills、网页搜索结果都是提示注入的入口;"人不在电脑前、代理自己在跑"恰恰是本项目的核心场景。本 PR 增加两层默认关闭(不改变存量行为)的安全能力,对标 Claude Code 的 permission prompt 与 Codex CLI 的 approval modes,并针对远程/委托场景做了更严格的处理。

改动内容

1. 危险操作审批门(system.toolApprovalMode = "dangerous"

  • 新增纯函数策略模块 lib/chat/runner/toolApprovalPolicy.ts,判定三类危险调用:
    • Delete(递归且不可撤销);
    • SSHManager 变更类 actionexec / send_input / sftp_delete / sftp_rename / sftp_upload / sftp_download / sftp_write_text / sftp_mkdir)——只读查询不打扰;
    • Bash / ManagedProcess 的工作区外 cwd:无 IO 的保守同步判定(绝对路径前缀比较、~file://、含 .. 段的相对路径;skill:// 除外),宁可多问一次也不漏逃逸。
  • 运行器在 beforeToolCall 处设门(复用 pi-agent-core 的异步 hook 与 {block, reason} 契约):
    • 桌面会话:弹出模态确认卡片,运行暂停等待"允许 / 拒绝";并行工具的多个确认按 FIFO 队列逐个弹出;运行被取消时挂起的确认自动按拒绝落地并撤下卡片(监听 abort signal)。
    • 无人值守会话(Gateway 远程发起、子代理 worktree/readonly 运行):不提供确认回调,危险调用直接拒绝——"有人能确认就问人,没人能确认就不执行"。沿用了 sshManagerRemoteAllowedgatewayBridgeRequest 判定远程来源的既有先例。
  • 拒绝文案是教学式的:告知模型不要原样重试,应调整方案或询问用户;无人值守拒绝会说明原因(远程/委托运行)并给出出路(桌面端执行或调整设置)。

2. Bash 工作目录范围(system.bashCwdPolicy = "workspace-only"

  • createShellTools 新增 externalCwdAllowed;锁定时 Bash / ManagedProcess 的 cwd 解析不再放行外部路径,在解析期即失败,报错追加一句提示说明限制来自设置(避免模型误以为路径拼写错误)。审批门是"问一下",这层是硬边界,两者可独立启用。

3. 设置与 UI

  • SystemSettings 新增 toolApprovalModeoff | dangerous,默认 off)与 bashCwdPolicyunrestricted | workspace-only,默认 unrestricted);normalize 齐全。
  • 系统设置页新增"危险操作确认"卡片(开关 + Bash 工作目录范围选择),确认弹窗为独立组件 ToolApprovalModal(危险类型说明 + 参数摘要 + 允许/拒绝),zh/en 文案齐全。
  • WebUI 侧 settings normalizer 同步新增字段,浏览器端保存设置不会把桌面端配置抹掉。

默认行为

两项均默认关闭,存量用户零感知;建议在 README/文档中引导远程部署用户开启。后续可以讨论把远程会话的默认值收紧一档。

测试

  • 新增 tool-approval-policy.test.mjs(7 组):策略关闭零打扰、Delete/SSH action 分级、外部 cwd 判定矩阵(分隔符/大小写/~/file:///.. 逃逸/skill:// 豁免)、拒绝文案、摘要截断。
  • shell-tools.test.mjs 新增 2 组:workspace-only 下外部 cwd 在解析期拒绝且不触发 shell 调用(含设置来源提示断言)、默认行为不变。
  • normalization.test.mjs 新增设置归一化用例(默认值 / 合法值 / 非法值回退)。
  • pnpm test:frontend 1053 个测试全部通过;GUI 与 WebUI 的 build / lint 通过;WebUI 368 个测试通过;check-mirror(87 文件)、git diff --check 干净。

Dangerous tool calls executed the moment the model asked for them:
recursive Delete, mutating SSHManager actions, and Bash/ManagedProcess
with a cwd outside the workspace all ran with no user consent, while the
Gateway lets remote sessions and cron/subagent runs drive the same tools
unattended. The file-tool sandbox was also trivially bypassable because
Bash cwd always resolved with allowExternal.

This adds two opt-in safety layers (defaults preserve current behavior):

Tool approval gate (system.toolApprovalMode = "dangerous"): a pure
policy module classifies dangerous calls - Delete, mutating SSHManager
actions, and shell calls whose cwd conservatively resolves outside the
workspace (absolute paths, ~, file://, ".." escapes). The agent runner
gates them in beforeToolCall: with an approval callback the run pauses
on a desktop confirmation modal (FIFO queue for parallel calls, abort
signal settles pending requests as denied); without a callback - remote
gateway sessions and delegated subagent runs - the call is denied with
an explanatory tool error. Deny/unattended texts teach the model to
adjust course instead of retrying.

Bash cwd policy (system.bashCwdPolicy = "workspace-only"): Bash and
ManagedProcess cwd resolution drops allowExternal so out-of-workspace
working directories fail at resolve time with a hint that the
restriction comes from settings.

Settings UI gains a security card (approval toggle + cwd scope picker)
with zh/en strings; the WebUI settings normalizer mirrors the new fields
so browser-side saves keep them intact.

Co-authored-by: Cursor <cursoragent@cursor.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant