docs: audit reports + feature docs + skills + admin-v2 + config sync
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
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>
This commit is contained in:
214
docs/features/AUDIT_ROUND3_V11.md
Normal file
214
docs/features/AUDIT_ROUND3_V11.md
Normal file
@@ -0,0 +1,214 @@
|
||||
# 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
|
||||
|
||||
1. **AUD3-FE-01**: chatStore.sendMessage 并发保护(HIGH)
|
||||
2. **AUD3-FE-02/API-01**: token refresh mutex(HIGH)
|
||||
3. **SEC2-P1-01**: FactStore trait 零实现(P1)
|
||||
4. **SEC2-P1-08**: Desktop 前端零测试(P1)
|
||||
5. **AUD3-FE-03**: initializeStores 重复调用(MEDIUM)
|
||||
6. **AUD3-FE-05**: mixin 模式类型安全(MEDIUM)
|
||||
7. **SEC2-P2-03**: sqlx-postgres 未来兼容性(P2)
|
||||
8. **SEC2-P2-05**: ~10 处 tokio::spawn 未绑定(P2)
|
||||
9. **AUD3-DB-01**: 无 down migration(MEDIUM)
|
||||
10. **SEC2-P1-01**: FactStore trait 零实现(P1)
|
||||
|
||||
---
|
||||
|
||||
## 8. 积极发现总结
|
||||
|
||||
三轮审计确认以下方面**设计良好、实现规范**:
|
||||
|
||||
1. **无 Store 循环依赖** — 单向树状依赖结构
|
||||
2. **事件监听器全部有 cleanup** — listen/unlisten 配对完整
|
||||
3. **认证中间件全覆盖** — 公共/受保护/Relay 三层路由无遗漏
|
||||
4. **SQL 参数化规范** — 除 2 处硬编码 format! 外全部使用 bind()
|
||||
5. **Lock ordering 安全** — 无嵌套锁获取
|
||||
6. **Feature gate 一致** — 传播链正确
|
||||
7. **Admin API 100% 对齐** — 前后端路由完全匹配
|
||||
8. **编译通过** — cargo check 仅 1 warning
|
||||
9. **密码安全** — Argon2id + password_version + JWT 失效
|
||||
10. **SSE 背压设计** — 有界 channel + 信号量 + CancellationToken
|
||||
Reference in New Issue
Block a user