Files
zclaw_openfang/docs/features/SECONDARY_AUDIT_V11.md
iven 8898bb399e
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
docs: audit reports + feature docs + skills + admin-v2 + config sync
Update audit tracker, roadmap, architecture docs,
add admin-v2 Roles page + Billing tests,
sync CLAUDE.md, Cargo.toml, docker-compose.yml,
add deep-research / frontend-design / chart-visualization skills

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-02 19:25:00 +08:00

14 KiB
Raw Blame History

ZCLAW 深度二次审计补充报告

审计日期: 2026-04-02 基线: V11 全面审计报告22 项发现) 方法: 5 维并行深度审计 新增发现: 22 项2 P0 + 9 P1 + 10 P2 + 3 P3 + 1 确认项)


1. 审计维度

# 维度 发现数
1 Rust crate 交叉依赖 + 编译状态 5
2 前端 Store ↔ Tauri 命令参数签名全量比对 5
3 Sprint 1-4 修复代码质量验证 1
4 安全/竞态/资源泄漏扫描 9
5 测试覆盖缺口 + admin-v2 API 对齐 4

2. 新增 P0 发现2 项)

SEC2-P0-01: skill_execute 反序列化崩溃

维度 详情
问题 前端传递空 context: {},但 Rust SkillContext 要求 agent_id: Stringsession_id: String(非 Option。空 JSON 无法反序列化。
前端 desktop/src/lib/kernel-skills.ts:110-114
Rust desktop/src-tauri/src/kernel_commands/skill.rs:290-296
触发条件 用户在前端执行任何 Skill
影响 运行时 serde 反序列化失败Skill 执行必然报错

SEC2-P0-02: TaskTool::default() 潜在 panic

维度 详情
问题 TaskTool 实现了 Default traitdefault() 调用 unimplemented!()
文件 crates/zclaw-runtime/src/tool/builtin/task.rs:59
触发条件 任何泛型约束 T: Default 触发 TaskTool::default()
影响 运行时 panic虽然正常路径使用 TaskTool::new() 规避)
建议 移除 impl Default for TaskTool,或提供合理的默认值

3. 新增 P1 发现9 项)

SEC2-P1-01: FactStore trait 零实现

维度 详情
问题 FactStore trait 在 zclaw-memory/src/fact.rs:141 定义,全 workspace 无任何 impl FactStore for TMemoryStore 有同名方法但未实现 trait。dyn FactStore 模式完全不可用。
文件 crates/zclaw-memory/src/fact.rs
影响 架构层面功能缺口——trait 接口已定义但无法使用

SEC2-P1-02: agent-templates API 路径缺少 /api/v1 前缀

维度 详情
问题 前端 saas-client.ts 中 agent-templates 路径缺少 /api/v1 前缀
前端 desktop/src/lib/saas-client.ts:376,384'/agent-templates/available''/agent-templates/${id}/full'
后端 路由注册在 /api/v1/agent-templates/...
影响 请求发送到错误 URL必然返回 404

SEC2-P1-03: hand-execution-complete 事件无前端监听

维度 详情
问题 Rust 在 hand.rs:279approval.rs:130 emit hand-execution-complete,但前端无任何 listen() 调用
影响 Hand 异步执行完成后,前端无法收到通知,用户不知道执行结果

SEC2-P1-04: InMemoryStorage RwLock unwrap() 级联 panic 风险

维度 详情
问题 InMemoryStorage 中 6 处 std::sync::RwLock.unwrap()。若锁被 poisoned某线程 panic后续所有调用级联崩溃
文件 crates/zclaw-growth/src/viking_adapter.rs:137,143,148,190,202,208,214
风险 低概率但后果严重

SEC2-P1-05: HandRun 持久化错误静默忽略3 处)

维度 详情
问题 let _ = memory.save_hand_run(&run).await 等 3 处 HandRun 持久化错误被忽略
文件 crates/zclaw-kernel/src/kernel/approvals.rs:88,91,120
影响 DB 不可用时 run 状态丢失UI 无法显示执行结果
建议 至少 log warning

SEC2-P1-06: FTS 索引更新失败静默忽略3 处)

维度 详情
问题 全文搜索索引 DELETE/INSERT 操作失败被 let _ = 忽略
文件 crates/zclaw-growth/src/storage/sqlite.rs:384,390,605
影响 搜索结果不一致stale index

SEC2-P1-07: Worker dispatch 失败静默忽略4 处)

维度 详情
问题 let _ = state.worker_dispatcher.dispatch(...) 4 处忽略
文件 crates/zclaw-saas/src/knowledge/handlers.rs:220,262,331,539
影响 embedding 生成等后台任务可能静默丢失

SEC2-P1-08: Desktop 前端零测试

维度 详情
问题 desktop/src/ 中零个 .test.ts/.test.tsx 文件,包括 chatStore、agentStore 等核心 store
影响 前端任何回归只能通过手工测试发现

SEC2-P1-09: record_key_usage 错误忽略导致计费数据丢失风险

维度 详情
问题 let _ = record_key_usage(...) 忽略写入错误
文件 crates/zclaw-saas/src/relay/service.rs:376
影响 可能导致计费数据丢失

4. 新增 P2 发现10 项)

ID 问题 文件
SEC2-P2-01 hmac/sha1 在 zclaw-hands Cargo.toml 中声明但代码零引用 crates/zclaw-hands/Cargo.toml
SEC2-P2-02 serde_yaml 版本不一致desktop 用 0.9pipeline 用 2不同 package Cargo.toml
SEC2-P2-03 sqlx-postgres v0.7.4 使用了未来 Rust 版本将拒绝的语法 Cargo.lock
SEC2-P2-04 generate_embedding.rs:107 embedding 生成被注释掉(// TODO crates/zclaw-saas/src/workers/generate_embedding.rs
SEC2-P2-05 ~10 处 tokio::spawn 返回 JoinHandle 未绑定,无法优雅停止 kernel + saas 多处
SEC2-P2-06 Telemetry 审计日志批量 INSERT 绑定可能与 SQL 模板不匹配 crates/zclaw-saas/src/telemetry/service.rs:205-213
SEC2-P2-07 Scheduler 串行执行 trigger长 hand 阻塞后续调度 crates/zclaw-kernel/src/scheduler.rs:117-153
SEC2-P2-08 format!("FROM {}", table) 模式虽当前安全(硬编码),但违反防御原则 crates/zclaw-saas/src/db.rs:874,880
SEC2-P2-09 hand_run_status 前端传递多余 handName 参数Rust 只接收 run_id desktop/src/lib/kernel-hands.ts:95
SEC2-P2-10 kernel_apply_saas_config TOML 写入不支持多行值 desktop/src-tauri/src/kernel_commands/lifecycle.rs

5. 新增 P3 发现3 项)

ID 问题 文件
SEC2-P3-01 A2A Router 4 个 RwLock 获取顺序未文档化 crates/zclaw-protocols/src/a2a.rs:239-245
SEC2-P3-02 Admin-v2 Role 类型:后端 is_system 字段前端未映射,前端 account_count 后端无字段 admin-v2/src/types/index.ts vs role/types.rs
SEC2-P3-03 Admin-v2 Billing/Knowledge/Roles 三个页面缺测试Billing、Knowledge 页面 + Roles 新页面) admin-v2/tests/

6. 确认项(审计验证结果)

6.1 编译状态

  • cargo check --workspace 编译成功33.60s
  • 2 个 warningRegisterDeviceRequest 可见性P2sqlx-postgres 未来兼容性P1

6.2 Sprint 1-4 修复验证

修复 验证结果
trigger_update 参数扁平化 通过 — 前后端参数完全匹配
SaaS config sync 传播 通过 — kernel_apply_saas_config 正确桥接
Deprecated 代码清理 通过 — 无残留引用
Admin Roles 页面 通过 — API 路径与 SaaS 路由匹配
ToolDefinition 去重 通过 — 使用 pub use
Knowledge handler 修复 通过 — 连接到 service 层
Task 结果持久化 通过 — migration + 读写一致

6.3 Admin-v2 ↔ SaaS API 对齐

结论100% 对齐。 所有 admin-v2 前端调用的 API 端点在后端都有对应路由注册,包括:

  • 9 个 knowledge 路径categories/items/analytics/search/recommend/import/versions/rollback/batch
  • 5 个 role 路径 + 5 个 permission-template 路径
  • 6 个 billing 路径
  • 2 个 telemetry 路径

6.4 Feature Gate 一致性

传播链完全正确:

desktop --multi-agent--> zclaw-kernel --multi-agent--> zclaw-protocols --a2a--> [a2a module]
zclaw-skills --wasm--> [wasmtime, wasmtime-wasi]

6.5 认证覆盖

SaaS 后端三层路由分离无遗漏:公共路由 + 受保护路由 + Relay 独立中间件链。


7. 测试覆盖统计

7.1 Rust 测试分布

Crate 测试数 关键缺口
zclaw-hands 155 -
zclaw-saas 92 集中在工具函数handler/scheduler 0 测试
zclaw-growth 75 -
zclaw-pipeline 59 -
zclaw-types 57 -
zclaw-kernel 52 capabilities/registry/trigger_manager 0 测试
zclaw-runtime 42 -
zclaw-memory 25 -
zclaw-skills 22 -
zclaw-protocols 5 极低
总计 584

7.2 零测试关键模块

模块 公开函数数 严重度
kernel_commands/ (41 Tauri 命令) 41 P0
browser/commands.rs (23 命令) 23 P0
SaaS scheduler.rs 3 公开函数 P1
SaaS knowledge/handlers.rs (561 行) ~15 P1
SaaS relay/service.rs ~20 P1
SaaS billing/ ~10 P1
Desktop frontend 全部 P1

7.3 Admin-v2 测试

  • 测试框架: Vitest + jsdom + MSW
  • 测试用例: 322 个
  • 覆盖页面: 10/13缺失 Billing、Knowledge、Roles
  • 覆盖 service: 1/17仅 request.ts

8. 积极发现

  1. SQL 参数化规范SaaS 层绝大多数 SQL 使用 sqlx::query(...).bind(...) 参数化
  2. 密钥保护JWT secret 使用 secrecy::SecretStringAPI key 日志仅输出 ID
  3. DashMap 死锁已规避:所有 DashMap RefMut 在 .await 前释放
  4. SSE 背压设计:有界 channel + 信号量限制 + CancellationToken
  5. 密码版本控制password_version 确保 JWT 失效
  6. MCP Transport:实现 Drop trait 清理子进程
  7. DB 连接池监控30s 周期日志 + 80% 告警
  8. Admin-v2 API 对齐:所有前端调用都有后端路由对应

9. 综合风险矩阵V11 + 二次审计合并)

严重度 V11 原始 二次审计新增 合计
P0 1 (误报) 2 2
P1 3 9 12
P2 6 10 16
P3 8 3 11
P4 5 0 5
总计 23 24 46

10. 支付安全深度审计 V32026-04-02

审计范围: crates/zclaw-saas/src/billing/ 全部文件payment.rs, service.rs, handlers.rs, types.rs, mod.rs 审计方法: 3 维并行Security Engineer + Code Reviewer + Senior Developer 基线: V2 审计后的 25 项修复全部落地

10.1 审计结论

指标 结果
CRITICAL 0
HIGH 0
MEDIUM 1(已修复)
LOW 3pre-existing / cosmetic

10.2 已修复项(本次修复)

ID 严重度 问题 修复
PAY-FIX-01 MEDIUM truncate_str 使用 s.len()(字节数)判断是否截断,但 chars().take() 按字符截断。中文等多字节字符串在 200 字节内但超过 200 字符时截断错误 改为先 chars().collect::<Vec<char>>() 再按 .len() 判断字符数

10.3 已验证通过的关键安全措施25 项)

事务与竞态保护6 项)

  • create_payment 在事务中创建 invoice + payment原子性
  • handle_payment_callback 使用 SELECT ... FOR UPDATE 锁定行(防 TOCTOU
  • get_or_create_usage 使用 INSERT ON CONFLICT DO NOTHING(防重复创建)
  • 金额交叉验证:生产环境 callback_amount 为 None 时拒绝
  • 幂等保护:已处理的 paymentstatus != pending直接返回
  • 事务失败时正确 rollback

支付回调安全6 项)

  • Alipay 验签:生产环境强制 RSA2 验签,缺公钥拒绝
  • WeChat 解密AES-256-GCM + nonce 长度校验12 字节)
  • trade_no 缺失时返回错误而非静默忽略
  • 日志安全:sanitize_log 只保留字母数字和 - _
  • 通用错误对外返回(不泄露内部细节)
  • 回调路由正确分离到 public_routes无需认证

密钥保护4 项)

  • PaymentConfig 自定义 Debug impl敏感字段显示 ***REDACTED***
  • alipay_private_keyalipay_public_keywechat_api_v3_key 标记 skip_serializing
  • JWT 密钥使用 secrecy::SecretString
  • API key 日志仅输出 ID 不输出值

货币精度2 项)

  • 分→元转换使用整数运算(amount_cents / 100 + amount_cents % 100
  • WeChat 金额保持整数分(无浮点转换)

HTML 安全2 项)

  • Mock 支付页面使用 html_escape() 转义用户输入
  • Mock 确认页面的 msg 变量由服务端控制(安全)

SQL 安全3 项)

  • 所有 SQL 使用参数化查询(sqlx::query(...).bind(...)
  • increment_dimension 使用白名单分支(非动态列名)
  • failure_reason 截断到 200 字符

整体架构2 项)

  • 路由分离:routes() 需认证,callback_routes() 公开
  • 批量递增 increment_dimension_by 替代循环查询

10.4 剩余 LOW 项(不影响安全,可后续优化)

ID 说明 建议
PAY-LOW-01 reqwest::Client::new() 每次 WeChat 支付创建新实例 改为 AppState 中共享 Client
PAY-LOW-02 BootstrapScreen 缺少 dark mode 变体 添加 dark:bg-gray-900
PAY-LOW-03 aside.w-64.sidebar-open class 定义但无 JS 切换逻辑 添加汉堡菜单或移除

10.5 审计签名

  • Security Engineer: 0 CRITICAL, 0 HIGH — 支付链路安全措施完备
  • Code Reviewer: 25 项修复全部正确实现,无回归
  • Senior Developer: 1 MEDIUM 已修复truncate_str代码质量良好