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
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>
9.8 KiB
9.8 KiB
ZCLAW 第三轮全面审计报告
审计日期: 2026-04-02 基线: V11 全面审计 + 深度二次审计 + Sprint 1-4 修复 方法: 5 维并行审计(前端状态一致性、数据库 Schema、API 契约、并发安全、代码质量) 完成代理: 前端状态一致性(1/5),其余 4 维因 API 限流由主线程直接执行
1. 前端状态一致性 + 内存泄漏审计(代理完成)
HIGH
| ID | 问题 | 文件 | 描述 |
|---|---|---|---|
| AUD3-FE-01 | chatStore.sendMessage 无并发保护 | chatStore.ts:403-675 |
isStreaming 仅在 UI 层守卫,store 函数本身无互斥。快速双击可在 React re-render 前触发两次发送,导致双重 assistant placeholder + stream 竞态 |
| AUD3-FE-02 | SaaS client token refresh 无并发锁 | saas-client.ts:217-229 |
多个并发请求同时收到 401 时,各自独立调用 refreshToken(),导致多次 refresh 请求。应使用 refresh mutex(共享 Promise) |
MEDIUM
| ID | 问题 | 文件 | 描述 |
|---|---|---|---|
| AUD3-FE-03 | initializeStores 可能被调用 3 次 | connectionStore.ts:415,589 + index.ts:99 |
模块加载 + connect() 双路径,异步操作中切换 client 可能导致请求失败 |
| AUD3-FE-04 | window 全局变量存储 interval | App.tsx:257-258 |
@ts-expect-error + window.__ZCLAW_STATS_SYNC_INTERVAL__,React StrictMode 双重 mount 时第一个 interval 无法清理 |
| AUD3-FE-05 | GatewayClient mixin 25+ 处 as any |
gateway-heartbeat.ts 等 |
prototype 动态方法通过 as any 绕过类型检查,属性名拼写错误无编译时报警 |
| AUD3-FE-06 | PropertyPanel 17 处 as any 访问联合类型 |
PropertyPanel.tsx:100-276 |
WorkflowNodeData 联合类型的字段直接 as any 访问,节点类型不匹配时 undefined |
LOW
| ID | 问题 | 文件 |
|---|---|---|
| AUD3-FE-07 | offlineStore 全局变量存储 timer(多次调用可能泄漏) | offlineStore.ts:87-88 |
| AUD3-FE-08 | agentStore 一次性读取 chatStore 可能读到中间态 | agentStore.ts:254 |
| AUD3-FE-09 | retryAllMessages 无并发锁,可能重复发送 | offlineStore.ts:188-233 |
POSITIVE FINDINGS(做得好的地方)
- 无 Store 循环依赖: 依赖方向是单向树状结构
- 事件监听器清理完善: classroomStore、useAutomationEvents、chatStore 的 listen 全部有 cleanup
- React useEffect cleanup 规范: ConnectionStatus、HandApprovalModal、SaaSStatus 全部正确清理
2. 数据库 Schema + Migration 审计(主线程执行)
Migration 文件清单(13 个)
| 编号 | 文件 | 内容 |
|---|---|---|
| 20260329-001 | initial_schema.sql | 21 个核心表 |
| 20260329-002 | seed_data.sql | 种子数据 |
| 20260330-001 | scheduled_tasks.sql | 定时任务表 |
| 20260331-001 | accounts_llm_routing.sql | LLM 路由字段 |
| 20260331-002 | agent_templates_extensions.sql | 模板扩展 |
| 20260401-001 | provider_keys_last_used.sql | key 最近使用时间 |
| 20260401-002 | remove_quota_reset_interval.sql | 移除配额重置 |
| 20260401-003 | models_is_embedding.sql | embedding 标记 |
| 20260401-004 | accounts_password_version.sql | 密码版本 |
| 20260401-005 | rate_limit_events.sql | 限流事件 |
| 20260402-001 | billing_tables.sql | 计费 5 表 |
| 20260402-002 | knowledge_base.sql | 知识库 5 表 |
| 20260402-003 | scheduled_task_results.sql | 任务结果列 |
MEDIUM
| ID | 问题 | 描述 |
|---|---|---|
| AUD3-DB-01 | 无 down migration | 所有 migration 只有 UP,无回滚脚本。生产环境需要回滚时只能手动操作 |
| AUD3-DB-02 | agent_template/service.rs:136 format! 构建 SQL | format!("SELECT COUNT(*) FROM agent_templates {}", where_clause) 中 where_clause 虽然是硬编码常量(非用户输入),但模式本身违反防御原则 |
POSITIVE FINDINGS
- 编号连续无冲突: 13 个 migration 编号连续
- 无 DELETE/UPDATE 缺少 WHERE: 全量扫描确认所有写操作都有 WHERE 子句
- 仅 2 处 format! SQL:
agent_template/service.rs和db.rs,两者 where_clause/table 均为硬编码 - zclaw-growth 和 zclaw-memory 无 migration 目录: 使用代码内 schema 初始化(SQLite)
3. API 契约 + 错误恢复审计(主线程执行)
HIGH
| ID | 问题 | 文件 | 描述 |
|---|---|---|---|
| AUD3-API-01 | SaaS token refresh 并发竞态 | saas-client.ts:217-229 |
多个并发请求同时收到 401,各自独立调用 refreshToken()。无 _refreshPromise 或 mutex。refresh token 可能被第一个请求消耗,后续 refresh 请求失败(单次使用 token) |
MEDIUM
| ID | 问题 | 描述 |
|---|---|---|
| AUD3-API-02 | 前端错误处理不统一 | 部分 invoke() 调用用 try/catch + log(静默),部分直接 throw(用户看到错误),部分 fallback 默认值。无全局错误提示机制 |
| AUD3-API-03 | 37 处 as any 类型断言 |
前端大量绕过类型检查,重构时容易引入运行时错误 |
POSITIVE FINDINGS
- AbortController 使用规范:
request-helper.ts有完整的 AbortController 管理(Map<string, AbortController>),支持请求取消 - 认证端点跳过 refresh:
_isAuthEndpoint()正确避免 login/register 端点的无限 refresh 循环 - timeout 配置: 所有 fetch 调用使用
AbortSignal.timeout()
4. 并发安全 + 资源管理审计(主线程执行)
MEDIUM
| ID | 问题 | 文件 | 描述 |
|---|---|---|---|
| AUD3-CONC-01 | kernel_commands 每个命令单独获取 kernel_lock | kernel_commands/*.rs |
每个 Tauri 命令独立 state.lock().await,无嵌套锁获取。设计安全但串行化所有命令执行 |
| AUD3-CONC-02 | ~15 处 fire-and-forget tokio::spawn | main.rs:108-151, relay/handlers.rs:389, scheduler.rs:62-140 |
无 JoinHandle,无优雅停机。shutdown 时运行中的任务可能被截断 |
| AUD3-CONC-03 | approval polling 循环持有 kernel_lock | approval.rs:96 |
kernel_state.lock().await 在 sleep 循环中反复获取,每次循环释放后重新获取。设计安全但增加锁竞争 |
POSITIVE FINDINGS
- 无嵌套锁获取: 每个 kernel_command 只获取一个 MutexLock,不存在 ABBA 死锁风险
- DashMap 操作规范: 所有 RefMut 在
.await前释放(已确认) - CancellationToken 用于 SSE: relay 的 SSE 流有取消机制
5. 代码质量审计(主线程执行)
统计数据
| 指标 | 数值 |
|---|---|
| Rust 测试总数 | 584 |
| 前端测试 | 0 (desktop) + 322 (admin-v2) |
as any 使用 |
37 处 |
@ts-expect-error |
3 处 |
| 未使用 Cargo 依赖 | 0(已清理 hmac/sha1) |
| Feature gate 一致性 | 正确 |
| 编译警告 | 1 (private_interfaces) + 1 (sqlx future-incompat) |
6. 综合发现汇总
新增 HIGH(需立即修复)
| ID | 问题 | 影响 |
|---|---|---|
| AUD3-FE-01 | chatStore.sendMessage 无并发保护 | 快速双击导致双重发送 + stream 竞态 |
| AUD3-FE-02 / AUD3-API-01 | SaaS token refresh 无并发锁 | 并发 401 → 多次 refresh → refresh token 被消耗 → 后续请求全部失败 |
新增 MEDIUM
| ID | 问题 | 影响 |
|---|---|---|
| AUD3-FE-03 | initializeStores 可能调用 3 次 | 异步操作中 client 切换 |
| AUD3-FE-04 | window 全局变量存 interval | StrictMode 泄漏 |
| AUD3-FE-05 | 25+ 处 mixin as any |
类型安全缺口 |
| AUD3-FE-06 | PropertyPanel 17 处 as any |
联合类型不安全 |
| AUD3-DB-01 | 无 down migration | 生产回滚困难 |
| AUD3-DB-02 | format! SQL(硬编码安全但模式差) | 防御性编程 |
| AUD3-API-02 | 前端错误处理不统一 | 用户体验不一致 |
| AUD3-CONC-02 | ~15 处 fire-and-forget spawn | 优雅停机问题 |
新增 LOW
| ID | 问题 |
|---|---|
| AUD3-FE-07 | offlineStore 全局变量存储 timer |
| AUD3-FE-08 | agentStore 读取中间态 |
| AUD3-FE-09 | retryAllMessages 无并发锁 |
| AUD3-CONC-03 | approval polling 增加锁竞争 |
7. 三轮审计累计发现总览
| 来源 | P0 | HIGH/P1 | MEDIUM/P2 | LOW/P3/P4 |
|---|---|---|---|---|
| V11 初次审计 | 0 | 3 | 14 | 13 |
| V11 深度二次 | 2 | 9 | 13 | 3 |
| V11 第三轮 | 0 | 2 | 8 | 4 |
| 合计(去重后) | 2 | 14 | 35 | 20 |
已修复 vs 未修复
| 状态 | 数量 |
|---|---|
| 已修复 | 13(2 P0 + 8 P1 + 3 P2) |
| 未修复 | 40 |
未修复中按优先级排序的 TOP 10
- AUD3-FE-01: chatStore.sendMessage 并发保护(HIGH)
- AUD3-FE-02/API-01: token refresh mutex(HIGH)
- SEC2-P1-01: FactStore trait 零实现(P1)
- SEC2-P1-08: Desktop 前端零测试(P1)
- AUD3-FE-03: initializeStores 重复调用(MEDIUM)
- AUD3-FE-05: mixin 模式类型安全(MEDIUM)
- SEC2-P2-03: sqlx-postgres 未来兼容性(P2)
- SEC2-P2-05: ~10 处 tokio::spawn 未绑定(P2)
- AUD3-DB-01: 无 down migration(MEDIUM)
- SEC2-P1-01: FactStore trait 零实现(P1)
8. 积极发现总结
三轮审计确认以下方面设计良好、实现规范:
- 无 Store 循环依赖 — 单向树状依赖结构
- 事件监听器全部有 cleanup — listen/unlisten 配对完整
- 认证中间件全覆盖 — 公共/受保护/Relay 三层路由无遗漏
- SQL 参数化规范 — 除 2 处硬编码 format! 外全部使用 bind()
- Lock ordering 安全 — 无嵌套锁获取
- Feature gate 一致 — 传播链正确
- Admin API 100% 对齐 — 前后端路由完全匹配
- 编译通过 — cargo check 仅 1 warning
- 密码安全 — Argon2id + password_version + JWT 失效
- SSE 背压设计 — 有界 channel + 信号量 + CancellationToken