Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
混合矩阵式审计:10 个功能模块 × 五维检查清单 - 项目整体健康度: 76/100 - 2 个 P0 (M4 双数据库 + 反思引擎 LLM 未接入) - 15 个 P1 (跨 M2/M3/M4/M5/M6/M7/M11) - 三类断链模式: 写了没接/接了不对/双实现未统一 - 三阶段修复路线图: P0(2-3天) → P1(5-7天) → P2(5-7天)
9.6 KiB
9.6 KiB
模块 M3 Hands 自主能力 审计报告
审计版本: V12 审计日期: 2026-04-04 审计范围: 11 个自主能力包触发/审批/执行/回调完整流程
1. 模块概况
功能描述
9 个已实现 Hand(Browser/Researcher/Collector/Clip/Twitter/Whiteboard/Slideshow/Speech/Quiz),含触发、审批、执行、事件回调全流程。
涉及文件清单
前端
- Store:
desktop/src/store/handStore.ts,desktop/src/store/browserHandStore.ts - Client:
desktop/src/lib/kernel-hands.ts,desktop/src/lib/browser-client.ts - 自主管理:
desktop/src/lib/autonomy-manager.ts - UI:
desktop/src/components/Automation/(ApprovalQueue, AutomationPanel, ExecutionResult 等)
后端 (Rust)
- Tauri 命令:
desktop/src-tauri/src/kernel_commands/hand.rs,approval.rs - Kernel:
crates/zclaw-kernel/src/kernel/hands.rs,crates/zclaw-kernel/src/kernel/approvals.rs - Registry:
crates/zclaw-hands/src/registry.rs - Hand 实现:
crates/zclaw-hands/src/hands/(9 个 .rs 文件) - 配置:
hands/*.HAND.toml(9 个)
调用链路图
用户触发 Hand
→ AutomationPanel.triggerHand(id, params)
→ handStore.triggerHand()
→ canAutoExecute('hand_trigger', 5) [前端自主性检查]
→ client.triggerHand(name, params, autonomyLevel)
→ KernelClient.triggerHand() → invoke('hand_execute', { id, input, autonomyLevel })
→ Rust hand_execute:
├─ supervised 模式 → 创建 ApprovalEntry → 返回 pending_approval
├─ needs_approval + 非 autonomous → 创建 ApprovalEntry
└─ autonomous 或无需审批 → kernel.execute_hand()
→ HandRegistry.execute() → Hand::execute()
→ 创建 HandRun (Pending→Running→Completed/Failed)
→ emit("hand-execution-complete")
审批流程:
ApprovalQueue → handStore.respondToApproval()
→ invoke('approval_respond', { id, approved, reason })
→ kernel.respond_to_approval()
→ tokio::spawn 异步执行 Hand + 轮询完成
→ emit("hand-execution-complete")
前端事件接收:
kernel-hands.ts onHandExecutionComplete → listen("hand-execution-complete")
→ [问题: 前端未注册全局监听]
2. 五维检查结果
2.1 链路完整性
| 链路 | 起点 | 终点 | 状态 | 备注 |
|---|---|---|---|---|
| Hand 列举 | AutomationPanel | Registry.list() | ✅ 连通 | |
| Hand 触发(直接执行) | handStore | Hand::execute() | ⚠️ run_id 丢失 | hand.rs:184 丢弃 _run_id |
| Hand 触发(需审批) | handStore | ApprovalEntry | ✅ 连通 | |
| 审批确认 | ApprovalQueue | Hand::execute() | ✅ 连通 | 两条重复路径 |
| hand-execution-complete 事件 | Rust emit | 前端 listen | ⚠️ 无人接收 | onHandExecutionComplete 未被调用 |
| Browser Hand (Rust) | BrowserHand::execute() | 浏览器操作 | ❌ 断裂 | 只返回结构化指令不执行 |
| Browser Hand (前端) | browserHandStore | browser-client → Rust browser_* 命令 | ✅ 连通 | 但绕过审批流程 |
| Hand 取消 | handStore | cancel_flag AtomicBool | ✅ 连通 | |
| Hand 运行状态查询 | handStore | Rust hand_run_status | ✅ 连通 |
2.2 参数/类型一致性
| 接口 | 前端参数 | 后端参数 | 一致性 | 备注 |
|---|---|---|---|---|
| hand_execute | { id, input, autonomyLevel } |
id, input, autonomy_level |
✅ | camelCase 正确转换 |
| hand_approve | { handName, runId, approved, reason } |
hand_name, run_id, approved, reason |
✅ | |
| approval_respond | { id, approved, reason } |
id, approved, reason |
✅ | |
| hand_execute 返回值 | 期望 { instance_id, status } |
实际 { success, output, error, durationMs } |
❌ 不匹配 | 前端拿不到 run_id |
| TOML permissions/rate_limit | 定义了但未使用 | HandConfig 只有基础字段 | ❌ 配置未生效 |
2.3 边界与错误处理
| 场景 | 预期行为 | 实际行为 | 级别 |
|---|---|---|---|
| 触发不存在的 Hand | 返回错误 | ✅ ZclawError::NotFound | P4 正确 |
| 并发触发同一 Hand | 限流/拒绝 | ❌ 无保护,可无限并发 | P1 |
| Hand 执行超时 | 超时终止 | ❌ timeout_secs 定义但未使用 |
P1 |
| 审批不响应 | 超时清理 | ❌ 永不过期,pending 无限积累 | P2 |
| Hand 执行失败 | 错误传播到 UI | ✅ 错误传播链路完整 | P4 正确 |
| max_concurrent 限制 | 限制并发数 | ❌ TOML 定义但未实现 | P2 |
| Clip Hand 路径含单引号 | 正常执行 | ⚠️ FFmpeg concat 文件解析可能异常 | P3 |
2.4 状态管理
| Store/组件 | 状态机完整性 | 持久化 | 竞态风险 |
|---|---|---|---|
| handStore | ✅ idle/running/needs_approval/error/unavailable/setup_needed | ❌ 无持久化 | ⚠️ triggerHand 后依赖异步 loadHands 更新状态 |
| browserHandStore | ✅ sessions/execution/logs/templates | ❌ 无持久化 | ⚠️ 独立于 handStore,绕过审批 |
| autonomy-manager | ✅ 三级自主+三级风险 | ✅ localStorage | ❌ 仅前端,后端无强制 |
2.5 安全与资源
| 检查项 | 状态 | 说明 |
|---|---|---|
| 审批安全(跨 Hand 攻击防护) | ✅ | entry.hand_id == hand_name 校验 |
| Approval ID 不可预测 | ✅ | UUID v4 |
| Browser Hand 绕过审批 | ❌ P1 | browserHandStore 完全绕过 autonomy-manager |
| TOML requires_approval 形同虚设 | ❌ P1 | browserHandStore 不走审批流程 |
| 审批内存不释放 | ⚠️ P2 | Vec 无限增长 |
| Clip Hand 命令注入防护 | ✅ | 使用 Command::new + .args() 非 shell |
3. 问题清单
| ID | 描述 | 文件:行号 | 级别 | 修复建议 | 验证方法 |
|---|---|---|---|---|---|
| M3-01 | hand_execute 丢弃 run_id — 前端拿不到执行 ID,无法追踪运行状态 | hand.rs:184 |
P1 | 将 HandRunId 包含在返回结果中 | triggerHand 后检查返回值含 runId |
| M3-02 | Browser Hand 双路径断裂 — Rust execute() 只返回结构化指令不执行实际操作 | browser.rs:191-343 |
P1 | 统一路径:要么 Rust 端执行操作,要么移除 Rust BrowserHand | 通过 handStore 触发 browser 验证 |
| M3-03 | browserHandStore 绕过审批 — 无视 TOML requires_approval = true | browserHandStore.ts:222-339 |
P1 | browserHandStore 操作前检查 autonomy-manager | 在 autonomous=false 下执行浏览器操作 |
| M3-04 | max_concurrent 未实现 — TOML 定义但运行时无检查 | kernel/hands.rs |
P1 | 在 execute_hand 中添加并发计数检查 | 并发触发同一 Hand |
| M3-05 | timeout_secs 未实现 — Hand 可无限挂起 | hand.rs:47 |
P1 | 在 execute 中使用 tokio::time::timeout 包装 | 启动长时间 Hand 验证超时 |
| M3-06 | hand_execute 返回值类型不匹配 | kernel-hands.ts:94 vs Rust HandResult |
P2 | 统一返回类型定义 | 检查 triggerHand 返回值 |
| M3-07 | hand-execution-complete 事件前端无人监听 | kernel-hands.ts:195 |
P2 | 应用初始化时注册全局监听 | 审批执行完成后检查 UI 更新 |
| M3-08 | 审批条目永不过期,内存不释放 | approvals.rs:16-19 |
P2 | 添加 expires_at + 定期清理 | 检查长期运行后的 pending_approvals 大小 |
| M3-09 | 重复审批确认路径(hand_approve vs approval_respond) | hand.rs:196-295 vs approval.rs:54-142 |
P2 | 统一为单一审批路径 | 检查两路径是否行为一致 |
| M3-10 | tool_count/metric_count 硬编码为 0 | hand.rs:82-83 |
P2 | 从 Hand 实际能力中计算 | 检查 hand_list 返回值 |
| M3-11 | TOML permissions/rate_limit/audit 配置未被读取 | hands/*.HAND.toml vs hand.rs:10-32 |
P3 | 扩展 HandConfig 或在运行时解析 | 修改 TOML 验证行为无变化 |
| M3-12 | hand_trigger 在 autonomy-manager 中永远不自动允许 | autonomy-manager.ts:268 |
P3 | 修正映射逻辑 | 设为 autonomous 后触发 Hand |
| M3-13 | Clip Hand concat 文件路径单引号未转义 | clip.rs:448 |
P3 | 转义路径中的单引号 | 使用含单引号的路径执行 concat |
4. 改进建议
短期修复(按优先级)
- [P1] 修复
hand_execute返回run_id:在 HandResult 中添加run_id字段 - [P1] 统一 Browser Hand 路径:移除 Rust BrowserHand 的假执行,让 handStore 走 browserHandStore
- [P1] browserHandStore 集成审批流程:在 executeTemplate 前检查 autonomy-manager
- [P1] 实现
max_concurrent并发限制 - [P1] 实现
timeout_secs超时保护
长期架构建议
- 注册
hand-execution-complete全局监听器到 handStore - 统一审批路径(移除重复的 hand_approve / approval_respond)
- 添加审批 TTL 和自动清理
- 扩展 HandConfig 解析 TOML 中的权限/限流/审计配置
5. 健康度评分
| 维度 | 评分 | 说明 |
|---|---|---|
| 链路完整性 | 55/100 | Browser Hand 断裂、事件无人接收、run_id 丢失 |
| 参数一致性 | 70/100 | invoke 参数匹配但返回值不匹配、TOML 配置未生效 |
| 边界处理 | 50/100 | 无并发限制、无超时、审批不过期 |
| 状态管理 | 60/100 | 基本状态机存在但 browserHandStore 独立运作 |
| 安全资源 | 55/100 | Browser Hand 绕过审批是核心安全问题 |
综合健康度: 58/100
5 个 P1 级问题集中在此模块(run_id 丢失、Browser 断裂、绕过审批、无并发限制、无超时)。修复 P1 后预计可提升至 75+。