feat(embed): 增加逐轮评测工具安全边界 - #15
Conversation
Signed-off-by: c24894 <chen.zhusen@h3c.com>
|
评审结论:当前版本不建议直接合入,建议先修复以下安全边界并补真实入口测试。
当前 head |
Signed-off-by: c24894 <chen.zhusen@h3c.com>
|
已在
本地验证:
本机执行 test binary 时 LLVM/link 阶段两次因内存不足退出,因此这里只声明测试目标已编译,不把本地测试执行写成通过;新一轮 GitHub CI 将执行完整门禁。 |
JensenChen28
left a comment
There was a problem hiding this comment.
审核结论:通过(GitHub 不允许自批,以 Comment 形式记录 Approve 意见)
已通读全部 diff 并抽查关键路径源码核实,安全闭环成立,无阻塞缺陷。两个建议确认项不阻塞合并。
已验证的安全闭环
- 三层白名单闭环:目录投影(
project_exact_allowed_tools)→ turn_loop 规划层exact_dispatch_error→execute_tool_with_lock真实分发层,最终检查位于所有 dispatch 后端(MCP/JS/code_execution/registry)之前。伪造调用测试覆盖mcp__forged__call、js_execution、code_execution、exec_shell、agent、动态工具名。 - goal continuation 非旁路:续轮决策在
handle_send_message内部做出,此时 per-turn 策略尚未复位,exact_dispatch_error("update_goal")正确阻断自分发。轮后宿主主动发的ContinueGoal不受限,但宿主本身即授权方,语义成立。 - Hook 覆盖充分:引擎核心只触发
ToolCallBefore(已按allows_hooks门禁,默认关闭);ToolCallAfter/TurnEnd 等均在 TUI 层触发,嵌入式 GAIA 宿主不经过。 - 策略不可序列化(无 serde),无法从 transcript 注入。
- CI
pinvou-fork-cicheck job 已实际执行cargo test -p codewhale-tui --lib forkguard_ --locked并通过,PR 描述中"本地未完成定向测试"的风险已由 CI 补齐。
建议确认项(非阻塞)
control_plane_restricted锁存不对称(engine.rs SendMessage 处理器):per-turn 策略结束后active_turn_tool_security复位为 None,但control_plane_restricted保持 true 直到下一条SendMessage出队。锁存语义本身有意且正确(防排队控制操作借终态事件提权,注释已说明),但两个字段状态不一致:轮次结束后宿主直接发RunShellCommand/SpawnSubAgent会被拒,需先发任意消息。GAIA 场景 config 级常驻不受影响;建议在turn_tool_security的 doc comment 里写明该锁存语义,避免 per-turn 用法踩坑。- 审计全量脱敏牺牲可观测性:
tool_audit_event_for_policy将整个事件替换为{"event":"restricted_tool_event","details":"redacted"},连 tool_id/tool_name/decision 都丢失。read_file这类工具名本身不敏感,敏感的是 input/path/error。建议至少保留 event 类型 + 工具名 + decision,便于评测后统计"调用了什么、哪些被拒"。
小问题(nit,可后续处理)
- engine.rs composer-bang 路径两处
emit_tool_audit(tool.user_provenance_preapproved、tool.spillover)未走_for_policy包装——仅含 tool_id/tool_name 且为用户发起路径,GAIA 嵌入不触达,可顺手统一。 - 动态工具处理不一致:
SendMessage带动态工具直接 fatal,ContinueGoal静默丢弃(.with_dynamic_tools(&[])),行为均安全但值得一句注释说明。 project_exact_allowed_tools按值收Option<Vec<String>>但只读,可改为按引用。
父仓 docs/fork-modifications.md r7 已登记此候选链(1eca6103a + 169c24cc5),上游回馈计划已注明,fork 侧义务完整。
TurnToolSecurityPolicy 新增 with_read_only_dispatch:受限轮把模型可见 File schema 投影为只读 action,审批前与最终分发前双重拒绝写动作;两条 核心回归测试补 forkguard_ 前缀以便父仓 layer-2 过滤器选中。 Signed-off-by: c24894 <chen.zhusen@h3c.com>
Op::ContinueGoal 与 Op::ReloadMcp 补 control_plane_restricted 锁存拒绝, 避免受限轮后的自调度续轮以无限制权限运行或拉起外部进程;受限审计 改为只保留 event/tool_name 非私有身份字段,输入输出路径继续脱敏; 补 forkguard 回归与只读 schema 测试的借用修复。 Signed-off-by: c24894 <chen.zhusen@h3c.com>
受限轮次结束后继续锁住子代理完成、后台 Shell 唤醒和编辑重放,直到显式新消息安装新的逐轮权限。 同时将宿主只读调度投影到 ShellPolicy,确保 Bash 使用现有的只读直接 argv 加固路径,并补充回归测试。 Signed-off-by: hexin <372726039@qq.com>
|
补充评审(基于当前 head 21e5f66): 这两处权限边界建议在合入前修复:
我已在本地准备修复:把空闲子代理和 Shell 唤醒延迟到显式新消息安装替代权限,拒绝受限状态下的编辑重放并正确消费 goal continuation 标记,同时把宿主只读策略收窄到 ShellPolicy::ReadOnly。新增回归后 34/34 forkguard 测试通过,integration 263/263、cucumber 14/14、PTY 65/65 通过;补丁目前尚未推送到该 PR。 |
No-Issue: GAIA benchmark 的底座前置安全能力,当前没有对应独立 Issue。
背景
GAIA 等评测任务会把外部题目与附件送入模型轮次。仅过滤模型可见工具目录不足以形成执行边界,还需要在真实分发、MCP、控制面与受信路径层面保持同一份逐轮权限。
变更
None保持现有产品行为。验证
cargo fmt --all -- --check:通过。git diff --check origin/pinvou3-clean...HEAD:通过。cargo test -p codewhale-tui forkguard_ --lib -- --test-threads=1:首次依赖下载长时间无进展后主动中止;未进入代码编译与测试阶段,未观察到代码失败。已知风险
codewhale-tui首次依赖下载后的定向测试,需由 CI 补齐。