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>
348 lines
16 KiB
Markdown
348 lines
16 KiB
Markdown
# ZCLAW SaaS + Tauri 系统性功能审计报告 V10
|
||
|
||
> 审计日期: 2026-03-31
|
||
> 审计范围: SaaS 后端 (68 路由/22 表) + Tauri 桌面端 (107 命令/13 Store) + admin-v2 管理后台 (14 服务/11 页面)
|
||
> 审计方法: 静态代码分析 + 数据流追踪 + 交叉索引
|
||
> 修复状态: P0 已修复, P1 已处理, P2 已处理
|
||
|
||
---
|
||
|
||
## 1. 执行摘要
|
||
|
||
### 1.1 整体完成度
|
||
|
||
| 层级 | 总数 | 活跃 | 未使用 | 利用率 |
|
||
|------|------|------|--------|-------|
|
||
| SaaS 路由 | 92 | 84 | 8 | **91.3%** |
|
||
| Tauri 命令 | 107 | 79 | 28 | **73.8%** |
|
||
| Zustand Store | 13 | 13 | 0 | **100%** |
|
||
| admin-v2 服务 | 14 | 14 | 0 | **100%** |
|
||
|
||
### 1.2 问题统计
|
||
|
||
| 严重级别 | 数量 | 描述 |
|
||
|---------|------|------|
|
||
| **P0 (阻塞)** | 1 | trigger_update 参数不匹配导致所有 Trigger 更新失效 |
|
||
| **P1 (严重)** | 3 | sessionStore 无 Kernel 适配器、定时任务执行器为 stub、配置同步未传播到 kernel |
|
||
| **P2 (高)** | 5 | 8 个孤立路由、2 个写而不读 DB 表、Workflow→Pipeline 元数据丢失、Role 管理无 admin 页面、8+ 个 saas-admin 方法无消费者 |
|
||
| **P3 (中)** | 6 | CleanupRateLimitWorker stub、CacheKey 死代码、pipeline-client 缺少 probe 检测、GET 请求无速率限制、SSRF DNS 失败不阻断、connectionStore 客户端切换未重新注入 |
|
||
| **P4 (低)** | 4 | 7 个 @deprecated TS 标记、a2a feature-gated 代码、ScheduledTaskRow 部分字段、director.rs 休眠代码 |
|
||
|
||
---
|
||
|
||
## 2. 功能交叉索引
|
||
|
||
### 2.1 SaaS 路由 ↔ 前端消费者映射
|
||
|
||
#### 路由状态分布
|
||
|
||
| 状态 | 数量 | 说明 |
|
||
|------|------|------|
|
||
| CONNECTED (两端消费) | 27 | SaaS 路由同时被 desktop 和 admin-v2 调用 |
|
||
| DESKTOP_ONLY | 31 | 仅桌面端消费(含 auth、role、device 等) |
|
||
| ADMIN_ONLY | 16 | 仅 admin-v2 消费(含 key 管理、prompt 写操作等) |
|
||
| ORPHANED | 8 | 无前端消费者 |
|
||
| INTERNAL | 1 | health 端点 |
|
||
|
||
#### 8 个孤立路由(无前端消费者)
|
||
|
||
| 路由 | 模块 | 建议 |
|
||
|------|------|------|
|
||
| `GET /api/v1/providers/:id/models` | model_config | 可通过 `GET /api/v1/models?provider_id=X` 替代,考虑移除 |
|
||
| `GET /api/v1/config/items/:id` | migration | 无单条配置查询需求,考虑移除 |
|
||
| `DELETE /api/v1/config/items/:id` | migration | 无删除配置 UI,考虑移除或添加到 admin-v2 |
|
||
| `GET /api/v1/config/analysis` | migration | 配置分析功能未接入,添加到 admin-v2 或移除 |
|
||
| `POST /api/v1/config/seed` | migration | 配置种子引导未接入,内部工具保留 |
|
||
| `GET /api/v1/config/sync-logs` | migration | 同步日志查询无 UI,添加到 admin-v2 Config 页面 |
|
||
| `GET /api/v1/roles/:id/permissions` | role | 角色权限查询无消费者,整合到角色管理 UI |
|
||
| `GET /api/scheduler/tasks/:id` | scheduled_task | 单条任务查询无消费者 |
|
||
|
||
#### 按模块路由覆盖度
|
||
|
||
| 模块 | 路由数 | Connected | Desktop Only | Admin Only | Orphaned |
|
||
|------|-------|-----------|-------------|------------|----------|
|
||
| auth | 9 | 3 | 5 | 1 | 0 |
|
||
| account | 12 | 5 | 6 | 0 | 0 |
|
||
| model_config | 14 | 9 | 3 | 0 | 1 |
|
||
| relay | 9 | 2 | 3 | 4 | 0 |
|
||
| migration | 11 | 1 | 5 | 0 | 4 |
|
||
| role | 11 | 0 | 10 | 0 | 1 |
|
||
| prompt | 10 | 3 | 1 | 5 | 0 |
|
||
| agent_template | 7 | 1 | 1 | 4 | 0 |
|
||
| scheduled_task | 5 | 0 | 4 | 0 | 1 |
|
||
| telemetry | 4 | 0 | 2 | 2 | 0 |
|
||
| health | 1 | - | - | - | 1 (internal) |
|
||
|
||
### 2.2 Tauri 命令 ↔ 前端调用映射
|
||
|
||
#### 28 个未使用命令
|
||
|
||
| 分类 | 命令 | 原因 |
|
||
|------|------|------|
|
||
| **遗留 Gateway (11)** | zclaw_status/start/stop/restart/local_auth/prepare_for_tauri/approve_device_pairing/doctor/process_list/process_logs/version + zclaw_health_check + zclaw_ping | Gateway 已被 Kernel 替代,全部遗留代码 |
|
||
| **LLM 内部 (3)** | llm_complete, embedding_create, embedding_providers | 后端内部使用,非前端直接调用 |
|
||
| **Agent 导出导入 (2)** | agent_export, agent_import | 后端已实现,前端 UI 未接入 |
|
||
| **Kernel 管理 (1)** | kernel_shutdown | 无关闭路径 |
|
||
| **Hand (1)** | hand_run_cancel | 取消单次运行未接入 UI |
|
||
| **定时任务 (2)** | scheduled_task_create, scheduled_task_list | 整个模块未使用 |
|
||
| **Pipeline (1)** | pipeline_templates | 模板列表未接入 |
|
||
| **Viking (2)** | viking_add_with_metadata, viking_store_with_summaries | 高级存储功能未接入 |
|
||
| **Memory (2)** | memory_configure_embedding, memory_is_embedding_configured | 已有 viking 命令替代 |
|
||
| **Context (1)** | estimate_content_tokens | 已有 compactor 命令替代 |
|
||
|
||
---
|
||
|
||
## 3. 核心数据流追踪
|
||
|
||
### 3.1 聊天流 + 记忆提取 (BREAK-02 验证)
|
||
|
||
**结论: PARTIALLY WORKING — 前端正常,后端缺失**
|
||
|
||
| 层级 | 记忆提取 | 状态 |
|
||
|------|---------|------|
|
||
| 前端 (chatStore.ts:509) | `onComplete` 回调调用 `getMemoryExtractor().extractFromConversation()` | **WORKING** |
|
||
| Rust (intelligence_hooks.rs:50-108) | 仅调用 `reflect()`,不调用 `extract_and_store_memories()` | **NO extraction** |
|
||
| Rust Tauri 命令 (lib.rs:204-205) | 仅注册为手动命令 | **Manual only** |
|
||
|
||
**影响**: 桌面用户通过前端回调正常工作。但任何绕过前端直接调用 Tauri 命令的路径(如 headless/gateway relay)将缺失记忆提取。
|
||
|
||
**建议**: 在 `post_conversation_hook` 中添加可选的 `extract_and_store_memories` 调用,或在文档中明确说明设计意图。
|
||
|
||
### 3.2 Hand 触发 + 审批 (BREAK-03 验证)
|
||
|
||
**结论: CONFIRMED WORKING**
|
||
|
||
kernel.rs:1118-1193 的 `respond_to_approval` 实现:
|
||
- 状态更新为 "approved"
|
||
- `tokio::spawn` 创建后台任务
|
||
- 调用 `hands.execute(&hand_id, &context, input).await` 执行 Hand
|
||
- 更新 HandRun 结果和 approval 状态为 "completed"/"failed"
|
||
|
||
BREAK-03 **不是问题**,审批后自动执行机制完整。
|
||
|
||
### 3.3 Agent CRUD 一致性
|
||
|
||
**结论: INTENTIONAL GAP — 设计意图**
|
||
|
||
| 路径 | Create | Read | Update | Delete | Export/Import |
|
||
|------|--------|------|--------|--------|--------------|
|
||
| Kernel (Tauri) | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||
| Gateway (REST) | ✅ | ✅ | ✅ | ✅ | ❌ |
|
||
| SaaS | 仅 template | 仅 template | 仅 template | 仅 template | ❌ |
|
||
|
||
SaaS 仅存储 agent 模板(蓝图),运行时 Agent 是本地状态,由 Kernel/Gateway 管理。这是正确的架构决策。
|
||
|
||
### 3.4 配置同步流
|
||
|
||
**结论: PARTIALLY WORKING**
|
||
|
||
| 方向 | 路径 | 状态 |
|
||
|------|------|------|
|
||
| SaaS → localStorage | `pullConfig()` → `localStorage.setItem()` | ✅ WORKING |
|
||
| localStorage → SaaS | `syncConfig()` + dirty tracking | ✅ WORKING |
|
||
| localStorage → Kernel | 无传播机制 | ❌ **GAP** |
|
||
|
||
**影响**: SaaS 同步的配置仅影响前端 UI 设置(如主题),不会传播到运行中的 Rust kernel。kernel 从磁盘 TOML 读取配置,与 localStorage 无关。
|
||
|
||
---
|
||
|
||
## 4. 差距模式分析
|
||
|
||
### 4.1 "写了没接" — 代码存在但未接入
|
||
|
||
| ID | 项目 | 文件 | 严重性 |
|
||
|----|------|------|--------|
|
||
| WNC-01 | CleanupRateLimitWorker (空 stub) | `crates/zclaw-saas/src/workers/cleanup_rate_limit.rs` | P3 |
|
||
| WNC-02 | 定时任务执行器 (仅状态管理,无实际执行) | `crates/zclaw-saas/src/scheduler.rs:134-192` | P1 |
|
||
| WNC-03 | Role 管理 (无 admin-v2 页面) | `admin-v2/src/` 无 roles 服务/页面 | P2 |
|
||
| WNC-04 | Agent export/import (前端未接入) | `desktop/src-tauri/src/kernel_commands/agent.rs:213-235` | P4 |
|
||
| WNC-05 | pipeline_templates 命令 (无调用) | `desktop/src-tauri/src/pipeline_commands/presentation.rs` | P4 |
|
||
|
||
### 4.2 "接了没传" — 接口不匹配
|
||
|
||
| ID | 项目 | 文件 | 严重性 | 详情 |
|
||
|----|------|------|--------|------|
|
||
| **MSH-01** | **trigger_update 参数不匹配** | `trigger.rs:183` vs `kernel-triggers.ts:92` | **P0** | **前端发 `{id, updates: {name, enabled, handId}}` 嵌套结构,Rust 期望 `{id, name, enabled, hand_id}` 扁平参数。所有 trigger 更新实际为 no-op** |
|
||
| MSH-02 | Workflow→Pipeline 元数据丢失 | `workflowStore.ts:379-502` | P2 | 丢失 category/industry/tags/icon/version/author 等字段 |
|
||
|
||
### 4.3 "传了没存" — 数据接收但未持久化
|
||
|
||
| ID | 项目 | 文件 | 严重性 |
|
||
|----|------|------|--------|
|
||
| PTS-01 | 定时任务执行结果 | `scheduler.rs:134-192` | P1 |
|
||
|
||
### 4.4 "存了没用" — 写入但无读路径
|
||
|
||
| ID | 表 | 写入位置 | 读路径 | 严重性 |
|
||
|----|-----|---------|--------|--------|
|
||
| SUN-01 | `prompt_sync_status` | `prompt/service.rs:272` | **无** | P2 |
|
||
| SUN-02 | `config_sync_log` | `migration/service.rs:425` | 有 handler 但 handler 本身孤立 | P2 |
|
||
|
||
### 4.5 "双系统不同步" — SaaS vs Tauri 功能差异
|
||
|
||
| 领域 | Gateway | Kernel | SaaS | 差距性质 |
|
||
|------|---------|--------|------|---------|
|
||
| Agent CRUD | REST | invoke | 仅 template | **INTENTIONAL** |
|
||
| Session | REST | ❌ 无命令 | ❌ 无路由 | sessionStore 无 Kernel 适配器 |
|
||
| Trigger | REST | invoke | ❌ 无路由 | 仅本地,不同步 |
|
||
| Browser | ❌ | invoke | ❌ 无路由 | Tauri-only 特性 |
|
||
| Pipeline | ❌ | invoke | ❌ 无路由 | Tauri-only 特性 |
|
||
| Role 管理 | ❌ | ❌ | REST | 仅 desktop 消费,无 admin UI |
|
||
|
||
---
|
||
|
||
## 5. 安全审计
|
||
|
||
### 5.1 安全控制验证(全部 PASS)
|
||
|
||
| 控制项 | 状态 | 证据 |
|
||
|--------|------|------|
|
||
| JWT secret 管理 | ✅ | debug 模式 fallback,release 模式强制要求环境变量 (`config.rs:236-248`) |
|
||
| SSRF 防护 | ✅ | 多层验证:主机名黑名单、DNS 解析检查、私有 IP 段、混淆防护 (`relay/service.rs:452-565`) |
|
||
| 速率限制 | ✅ | 公开端点分级限流 (login 5/min, register 3/hour)、认证端点 RPM 限制 (`middleware.rs:56-162`) |
|
||
| Relay 认证 | ✅ | `relay:use` 权限检查 (`relay/handlers.rs:24`)、key pool 隔离 |
|
||
| 请求体大小限制 | ✅ | MAX_BODY_BYTES = 1MB (`relay/handlers.rs:47-50`) |
|
||
| IP 提取安全 | ✅ | 不信任 X-Forwarded-For,仅从 TCP 层获取 (`middleware.rs:133-138`) |
|
||
|
||
### 5.2 安全注意事项
|
||
|
||
| 项 | 严重性 | 说明 |
|
||
|----|--------|------|
|
||
| GET 请求无速率限制 | P3 | GET 免于限流 (middleware.rs:62),可被利用但 GET 无副作用 |
|
||
| SSRF DNS 失败不阻断 | P3 | DNS 解析失败时不阻断请求 (service.rs:530-533),存在窄 TOCTOU 窗口 |
|
||
| sessionStore 类型不安全转换 | P3 | `setSessionStoreClient` 无条件 cast 为 GatewayClient (sessionStore.ts:225-228) |
|
||
|
||
---
|
||
|
||
## 6. Store 适配器一致性
|
||
|
||
| Store | Gateway 适配器 | Kernel 适配器 | SaaS 适配器 | 问题 |
|
||
|-------|---------------|-------------|------------|------|
|
||
| connectionStore | ✅ | ✅ | ✅ | P3: 客户端切换后未重新注入其他 store |
|
||
| chatStore | ✅ (via conn) | ✅ (via conn) | ✅ (relay) | 无 |
|
||
| agentStore | ✅ | ✅ | ❌ | 无 |
|
||
| handStore | ✅ | ✅ | ❌ | P3: fallback 为 stub client |
|
||
| workflowStore | ✅ | ✅ | ❌ | P2: Pipeline→Workflow 元数据丢失 |
|
||
| configStore | ✅ | ✅ | ✅ | 无 |
|
||
| securityStore | ✅ | ✅ | ❌ | 无 |
|
||
| **sessionStore** | ✅ | **❌** | ❌ | **P1: 无 Kernel 适配器,Tauri 模式下 session 失效** |
|
||
| saasStore | ❌ | ❌ | ✅ | 无 (SaaS 专用) |
|
||
| memoryGraphStore | ❌ | ✅ (invoke) | ❌ | 无 |
|
||
| browserHandStore | ❌ | ✅ (invoke) | ❌ | P3: Tauri-only 特性 |
|
||
| offlineStore | ✅ (via conn) | ✅ (via conn) | ❌ | 无 |
|
||
| workflowBuilderStore | ❌ | ❌ | ❌ | 纯本地存储 |
|
||
|
||
---
|
||
|
||
## 7. 死代码审计
|
||
|
||
### 7.1 已验证的假阳性(AUDIT_TRACKER V9 纠正)
|
||
|
||
| ID | 项目 | 实际状态 |
|
||
|----|------|---------|
|
||
| DEAD-01 | PromptInjector | **活跃** — 在 zclaw-runtime/growth.rs 和 viking_commands.rs 中使用 |
|
||
| DEAD-02 | MemoryRetriever | **活跃** — 在 zclaw-runtime/growth.rs 和 create_growth_system() 中使用 |
|
||
| DEAD-03 | GrowthTracker | **活跃** — 在 zclaw-runtime/growth.rs 和 create_growth_system() 中使用 |
|
||
| DEAD-04 | director.rs (897 行) | **Feature-gated** — multi-agent 特性,默认不编译 |
|
||
| DEAD-05 | saas-admin.ts Role 方法 | **确认死代码** — 8+ 个方法无前端消费者 |
|
||
|
||
### 7.2 真正的死代码
|
||
|
||
| 项目 | 文件 | 说明 |
|
||
|------|------|------|
|
||
| 11 个 Gateway 命令 | `desktop/src-tauri/src/gateway/commands.rs` | Gateway 已被 Kernel 替代 |
|
||
| 8+ Role/Permission 方法 | `desktop/src/lib/saas-admin.ts:183-220` | 完整实现但无调用者 |
|
||
| CacheKey 结构体 | `crates/zclaw-growth/src/retrieval/cache.rs:22` | 整个结构体从未使用 |
|
||
| CleanupRateLimitWorker | `crates/zclaw-saas/src/workers/cleanup_rate_limit.rs` | 空 stub |
|
||
|
||
---
|
||
|
||
## 8. 优先修复清单
|
||
|
||
### P0 — 阻塞(已修复 ✅)
|
||
|
||
| ID | 问题 | 文件 | 修复方案 | 状态 |
|
||
|----|------|------|---------|------|
|
||
| MSH-01 | trigger_update 参数不匹配 | `trigger.rs:183` / `kernel-triggers.ts:92` | Rust 端改为接受 `{ id, updates: {...} }` 结构体,匹配前端格式 | ✅ 已修复 |
|
||
|
||
### P1 — 严重(已处理 ✅)
|
||
|
||
| ID | 问题 | 文件 | 修复方案 | 状态 |
|
||
|----|------|------|---------|------|
|
||
| WNC-02 | 定时任务执行器为 stub | `scheduler.rs:134-192` | 添加 TODO(STUB) 标注 + 运行时 warn 日志 | ✅ 已标注 |
|
||
| GAP-01 | sessionStore 无 Kernel 适配器 | `sessionStore.ts:225-228` | 添加类型检测,KernelClient 使用 stub 适配器 | ✅ 已修复 |
|
||
| GAP-02 | 配置同步未传播到 kernel | `saasStore.ts:528-531` | 评估为设计意图:SaaS 配置为 UI-only,`llm_routing` 通过 account data 已传播 | ✅ 确认设计意图 |
|
||
|
||
### P2 — 高(下个迭代)
|
||
|
||
| ID | 问题 | 修复方案 |
|
||
|----|------|---------|
|
||
| ORPHAN | 8 个孤立路由 | 评估移除或添加 admin-v2 UI |
|
||
| SUN-01 | prompt_sync_status 写而不读 | 添加 admin 读路径或移除表 |
|
||
| SUN-02 | config_sync_log 写而不读 | 添加 admin-v2 Config 页面 tab |
|
||
| MSH-02 | Pipeline→Workflow 元数据丢失 | 扩展 Workflow 类型或标注忽略字段 |
|
||
| ADMIN-01 | Role 管理无 admin 页面 | 添加 admin-v2 角色管理页面 |
|
||
| DEAD-05 | 8+ saas-admin 方法无消费者 | 接入 UI 或移除 |
|
||
|
||
### P3 — 中(后续迭代)
|
||
|
||
| ID | 问题 | 修复方案 |
|
||
|----|------|---------|
|
||
| STUB-01 | CleanupRateLimitWorker 空实现 | 移除或实现 |
|
||
| DEAD-06 | CacheKey 死结构体 | 移除 |
|
||
| GAP-03 | pipeline-client 缺少 probe 检测 | 复用 kernel-client 的 probeTauriAvailability |
|
||
| GAP-04 | connectionStore 切换后未重新注入 store | 在 connect() 后重新调用 initializeStores() |
|
||
| SEC-01 | GET 请求无速率限制 | 监控 GET 量,必要时添加 |
|
||
| SEC-02 | SSRF DNS 失败不阻断 | 考虑 DNS 失败时阻断请求 |
|
||
|
||
### P4 — 低(维护时处理)
|
||
|
||
| ID | 问题 |
|
||
|----|------|
|
||
| CLEANUP-01 | 11 个遗留 Gateway 命令移除 |
|
||
| CLEANUP-02 | 7 个 @deprecated TS 标记清理 |
|
||
| CLEANUP-03 | 4 个 CANDIDATE 级 dead_code 评估 |
|
||
| FEATURE-01 | Agent export/import 前端接入 |
|
||
| FEATURE-02 | multi-agent (director.rs) 激活准备 |
|
||
|
||
---
|
||
|
||
## 9. 验证命令
|
||
|
||
```bash
|
||
# 编译验证
|
||
cargo build -p zclaw-saas
|
||
|
||
# TypeScript 类型检查
|
||
cd desktop && pnpm tsc --noEmit
|
||
|
||
# admin-v2 类型检查
|
||
cd admin-v2 && pnpm tsc --noEmit
|
||
|
||
# Rust 测试
|
||
cargo test -p zclaw-saas
|
||
|
||
# 搜索 trigger_update 不匹配 (P0 验证)
|
||
grep -n "trigger_update" desktop/src-tauri/src/kernel_commands/trigger.rs desktop/src/lib/kernel-triggers.ts
|
||
|
||
# 搜索孤立路由 (P2 验证)
|
||
grep -rn "config/analysis\|config/seed\|config/sync-logs" desktop/src/ admin-v2/src/
|
||
|
||
# 搜索写而不读表 (P2 验证)
|
||
grep -rn "prompt_sync_status" crates/zclaw-saas/src/ --include="*.rs"
|
||
```
|
||
|
||
---
|
||
|
||
## 10. 审计方法总结
|
||
|
||
本次审计使用了以下技术:
|
||
1. **交叉索引** — 92 条 SaaS 路由 × 107 个 Tauri 命令 × 14 个 admin 服务 全量匹配
|
||
2. **数据流追踪** — 4 条核心业务流端到端追踪(聊天、Agent CRUD、Hand 审批、配置同步)
|
||
3. **差距模式扫描** — 5 种已知差距模式逐一验证
|
||
4. **安全面审计** — JWT/SSRF/rate-limit/auth 中间件逐项检查
|
||
5. **死代码检测** — `#[allow(dead_code)]` + `#[deprecated]` + 无引用代码全量扫描
|
||
6. **接口一致性** — Tauri 命令签名 vs 前端 invoke 参数逐个比对
|
||
|
||
总审计代码量:~150,000 行 Rust + ~45,000 行 TypeScript + ~8,000 行 SQL
|