# 项目 Bug 全面审计报告

> 审计日期: 2026-04-27
> 范围: 前后端全覆盖 (Rust 后端 + React/TypeScript 前端)

---

# 一、严重 (CRITICAL) — 4 个

### 1.1 无认证的 Git 初始化端点

**文件:** `libs/api/git/init.rs:18-87`
**描述:** `git_init_bare`, `git_open`, `git_open_workdir`, `git_is_repo` 四个端点均无 `Session` 参数，无需登录即可调用。攻击者可以初始化
bare 仓库、探测服务器文件系统路径。
**风险:** 未授权文件系统访问、数据泄露

### 1.2 CORS 安全漏洞 — `allow_any_origin()` + `supports_credentials()`

**文件:** `apps/app/src/main.rs:185-190`
**描述:** 同时启用 `allow_any_origin()` 和 `supports_credentials()`，Actix-web
文档明确警告这种组合会导致严重安全漏洞——任意恶意网站都能发起带凭据的跨域请求并读取响应。
**风险:** 任意网站可窃取用户数据、执行已认证操作

### 1.3 存储文件上传路径遍历

**文件:** `libs/service/storage.rs:31-50`
**描述:** `base_path.join(key)` 使用用户提供的 `key` 参数（如 `../../../etc/passwd`），Rust 的 `Path::join` 会解析 `..`
片段，导致文件写入存储根目录之外。
**风险:** 任意文件写入、远程代码执行

### 1.4 事务缺失导致数据损坏 (Issue 级联删除)

**文件:** `libs/service/issue/issue.rs:505-529`
**描述:** Issue 删除时执行 6 次独立 DELETE 操作（评论、分配、标签、订阅者、仓库引用、issue自身），没有使用数据库事务包装。如果其中任何一个删除失败，数据库将留下孤立记录。
**风险:** 数据不一致、数据库污染

---

# 二、高危 (HIGH) — 8 个

### 2.1 LFS 认证令牌硬编码

**文件:** `libs/git/http/lfs.rs:171`
**描述:** LFS 批量响应返回 `"Bearer token"` 字面字符串，没有实际令牌机制。`upload_object()` 和 `download_object()` 接收
`_auth_token` 参数但从不验证。任何获得 URL 的人都可以上传/下载 LFS 对象。
**风险:** 任意 LFS 对象访问和篡改

### 2.2 LFS X-User-Uid 头注入

**文件:** `libs/git/http/lfs_routes.rs:31-40`
**描述:** `user_uid()` 函数从客户端请求头 `x-user-uid` 读取用户身份并直接信任，无需验证。已认证 LFS 用户可冒充任意用户进行
lock/unlock 操作。
**风险:** 权限提升、冒充其他用户

### 2.3 Mutex 中毒风险 (AI 流式处理)

**文件:** `libs/room/src/service/ai_react_streaming.rs:73,81,96,100,123,124,171`
**描述:** 7 处 `std::sync::Mutex::lock().unwrap()` 使用非 tokio 版本的 Mutex。任何持有锁的线程 panic 都会导致 Mutex
永久中毒，所有后续调用直接 panic，杀死房间的所有 AI 响应处理。
**风险:** AI 功能完全不可用（特定房间）

### 2.4 Drop 实现中使用 tokio::spawn — 锁释放失败

**文件:** `libs/room/src/room_ai_queue.rs:20-51`
**描述:** `RoomAiLockGuard::drop()` 调用 `tokio::spawn()` 释放 Redis 锁。如果在非 tokio 上下文中 drop（如 `Drop`
在应用关闭时被调用），spawn 会 panic，Redis 锁永不释放，对应房间的 AI 处理永久锁定。
**风险:** AI 功能永久锁定

### 2.5 Redis KEYS 阻塞操作

**文件:** `libs/room/src/connection.rs:695`
**描述:** `get_active_typing_events` 使用 Redis `KEYS` 命令，该命令会阻塞整个 Redis 服务器 O(N) 时间。在生产环境大量 key
时会阻塞所有操作。应使用非阻塞的 `SCAN`。
**风险:** 大负载时 Redis 阻塞、服务中断

### 2.6 前端 XSS — 搜索结果 `dangerouslySetInnerHTML`

**文件:** `src/app/search/page.tsx:198`
**描述:** 搜索结果直接通过 `dangerouslySetInnerHTML` 渲染用户消息内容，未做 HTML 转义。恶意用户可在聊天中发送
`<script>alert(1)</script>` 或 `onerror` payload，当消息出现在搜索结果时触发存储型 XSS。
**风险:** 存储型 XSS、账户劫持、会话窃取

### 2.7 前端 WebSocket Token 在 URL 参数暴露

**文件:** `src/lib/room-ws-client.ts:969-975`
**描述:** WS token 通过 `?token=${token}` 追加到 URL query 参数，会出现在浏览器 DevTools 网络日志、服务器日志中。
**风险:** Token 泄露

### 2.8 SSH Shell panic 风险

**文件:** `libs/git/ssh/handle.rs:610-612`
**描述:** 三处 `unwrap()` 获取 spawn 进程的 stdin/stdout/stderr。如果 git 进程在 piped stdio 模式下状态异常（pipe 为
None），`unwrap()` 会 panic，异步任务将被静默杀死。
**风险:** SSH git 操作静默失败

---

# 三、中危 (MEDIUM) — 14 个

### 3.1 Agent 模型同步内存泄漏

**文件:** `libs/service/agent/sync.rs:190`
**描述:** 未知 provider 名称通过 `Box::leak` 转换为 `&'static str`，同步任务每 10 分钟运行一次，每个未知名称永久占用内存。
**风险:** 长期内存泄漏

### 3.2 所有房间启动时全部加载

**文件:** `libs/room/src/service/workers.rs:30`
**描述:** 服务启动时执行 `room::Entity::find().all(&db)` 加载所有房间，并为每个房间 spawn 一个 tokio 任务。如果有数千个房间，会创建数千个
tokio 任务和 Redis pubsub 连接。
**风险:** 大实例时资源耗尽

### 3.3 `issue_summary` 竞态条件

**文件:** `libs/service/issue/issue.rs:581`
**描述:** `closed = total - open` 通过两次独立的 count 查询计算，如果两次查询之间有 Issue 状态变化，closed 可能为负数或不精确。
**风险:** 统计数据错误

### 3.4 Provider/Model 同步 Race Condition

**文件:** `libs/service/agent/sync.rs:231,271`
**描述:** 更新后重新查询 Provider/Model，`.unwrap()` 假设记录一定存在。如果并发删除发生在两次查询之间，会 panic。
**风险:** 同步任务因 panic 中断

### 3.5 Workspace 成员查询 panic

**文件:** `libs/service/workspace/info.rs:162`
**描述:** `memberships.iter().find(|m| ...).unwrap()` — 如果 Workspace 存在但 membership 在两次查询之间被删除会 panic。
**风险:** 幽灵 panic

### 3.6 Workspace 设置 panic

**文件:** `libs/service/workspace/settings.rs:39`
**描述:** `m.id.clone().unwrap()` — 如果 ActiveModel 的 id 字段为 `NotSet`（非正常流程但可能），会 panic。
**风险:** 设置页面 panic

### 3.7 AI 任务生命周期静默失败

**文件:** `libs/room/src/service/workers.rs:201,214,228`
**描述:** `let _ = task_service.start(task_id).await` — 如果 start/complete/fail 操作失败，AI
任务在数据库中的状态可能永远停留在 "Running"，错误不可见。
**风险:** AI 任务"幽灵任务"、追踪不可靠

### 3.8 Room 事件发布到队列失败静默丢弃

**文件:** `libs/room/src/room.rs:215-218,293-296,308-311,381-384`
**描述:** `let _ = self.queue.publish_project_room_event(...)` — 房间创建/重命名/移动/删除事件在失败时被丢弃，前端不会收到通知。
**风险:** UI 状态与后端不一致

### 3.9 Issue 活动日志静默失败

**文件:** `libs/service/issue/issue.rs:265,345,441,540`
**描述:** `let _ = self.project_log_activity(...)` — Issue 变更活动日志在失败时静默丢弃。
**风险:** 审计日志缺失

### 3.10 全文索引更新 SQL 失败静默忽略

**文件:** `libs/room/src/connection.rs:864`
**描述:** `let _ = db.execute_raw(stmt).await` — PostgreSQL 全文索引更新 SQL 执行失败完全不可见。
**风险:** 内容搜索失效无感知

### 3.11 前端 `activeRoomId` 不响应 URL 变化

**文件:** `src/contexts/room-context.tsx:194` + `src/app/project/room.tsx:191`
**描述:** `RoomProvider` 用 `useState(initialRoomId)` 初始化 `activeRoomId`，但 `useState` 只使用初始值，不响应 prop
变化。浏览器前进/后退、直接 URL 输入在已加载的应用中无法切换房间。
**风险:** URL 导航失效、用户体验受损

### 3.12 前端 `deleteRoom` 重复导航

**文件:** `src/app/project/room.tsx:81-93` + 对应 context 方法
**描述:** `deleteRoom` 在 context 内部已调用 `setActiveRoom(null)`（触发一次导航），调用方又调用一次 — 同一操作导航两次。
**风险:** 重定向竞争、历史栈污染

### 3.13 前端 callback 覆盖冲突

**文件:** `src/hooks/useTypingIndicator.ts:113-122` + `src/hooks/useNotification.ts:160-164`
**描述:** `wsClient.updateCallbacks()` 使用 `Object.assign` 覆盖回调。多个组件同时使用时，后注册的覆盖前者，第一个组件的回调在卸载时清除所有已注册回调。
**风险:** 多组件场景下回调丢失

### 3.14 前端 Context 中 Ref 依赖不触发更新

**文件:** `src/contexts/room-context.tsx:1467`
**描述:** `useMemo` 依赖数组包含 `wsClientRef.current`（ref 值），但 ref 变化不触发重渲染，导致 context 提供过时的
`wsClient` 值。
**风险:** Stale closure / 组件使用过时 WebSocket 客户端

---

# 四、低危 (LOW) — 12 个

### 4.1 前端 API 错误处理缺失

- **文件:** `src/contexts/room-context.tsx:1025-1086` — `sendMessage` 发送失败后重试时内容永久丢失
- **文件:** `src/contexts/room-context.tsx:1002-1009` — `removeMember` 无 catch
- **文件:** `src/contexts/room-context.tsx:1012-1020` — `updateMemberRole` 无 catch
- **文件:** `src/components/room/RoomMessageSearch.tsx:48` — 搜索失败仅 `console.error`

### 4.2 前端类型安全缺失 ( `any` 类型滥用)

- 10+ 文件使用 `(resp.data as any)?.data` 绕过类型检查
- `src/components/room/DiscordChatPanel.tsx:504` — `messages={messages as any}`

### 4.3 前端登录页 Captcha 缺失验证

- **文件:** `src/app/auth/login-page.tsx:49` — 登录不检查 captcha 为空

### 4.4 前端 `RepositoryContextProvider` 加载时返回 null

- **文件:** `src/contexts/repository-context.tsx:110` — 加载期间整个子组件树被卸载

### 4.5 前端编辑消息乐观更新回滚不完整

- **文件:** `src/contexts/room-context.tsx:1096-1124` — 如果消息已从数组中移除，rollback 被跳过

### 4.6 后端 Push 通知 unwrap

- **文件:** `libs/service/lib.rs:65-67,246-248` — `Option` unwrap 在通知后台任务中

### 4.7 后端 Broadcast Channel 容量过大

- **文件:** `libs/room/src/connection.rs:21` — `BROADCAST_CAPACITY = 100_000` 每个发送者持有，慢速/断开的 WS
  客户端会导致内存无限制增长

### 4.8 后端 SSH 限流器未使用

- **文件:** `libs/git/ssh/rate_limit.rs` — `SshRateLimiter` 类型定义存在但从未在 SSH handler 中实例化

### 4.9 后端 Session Cookie Secure 为 false

- **文件:** `apps/app/src/main.rs:195` — 生产环境应设为 true

### 4.10 后端无 CSRF 保护

- **文件:** `apps/app/src/main.rs` — API 用 cookie-based 认证但无 CSRF 防御

### 4.11 后端贡献日期 unwrap

- **文件:** `libs/service/user/chpc.rs:81-82` — 日期范围边界不够安全

### 4.12 前端 UniversalWsClient 无 heartbeat / 无 jitter

- **文件:** `src/lib/universal-ws.ts` — 缺少心跳检测死连接，重连无 jitter（惊群效应）

---

# 五、总结

| 严重级别         | 数量     | 主要分布                                    |
|--------------|--------|-----------------------------------------|
| **Critical** | 4      | 认证缺失、CORS错误配置、路径遍历、事务缺失                 |
| **High**     | 8      | LFS认证缺陷、Mutex中毒、XSS、锁泄漏、Redis阻塞、Token暴露 |
| **Medium**   | 14     | 内存泄漏、竞态条件、静默错误丢弃、前端导航Bug、回调冲突           |
| **Low**      | 12     | 错误处理缺失、类型安全、配置问题                        |
| **总计**       | **38** |                                         |

## 最严重文件排行

| 文件                                            | Bug 密度 | 关键问题                                             |
|-----------------------------------------------|--------|--------------------------------------------------|
| `libs/room/src/connection.rs`                 | 极高     | SQL注入风险、Redis KEYS阻塞、broadcast容量、静默SQL错误         |
| `libs/room/src/service/ai_react_streaming.rs` | 极高     | 7处Mutex.unwrap()、fire-and-forget任务               |
| `libs/room/src/room_ai_queue.rs`              | 高      | Drop中tokio::spawn、锁释放失败                          |
| `src/contexts/room-context.tsx`               | 极高     | URL不同步、重复导航、Ref依赖问题、状态竞态                         |
| `src/app/search/page.tsx`                     | 高      | 存储型XSS                                           |
| `libs/service/issue/issue.rs`                 | 高      | 级联删除无事务、issue_summary竞态                          |
| `libs/service/agent/sync.rs`                  | 中      | Box::leak内存泄漏、race condition panic               |
| `apps/app/src/main.rs`                        | 中      | CORS+credentials漏洞、Session cookie insecure、无CSRF |

## 建议优先修复

1. **CORS allow_any_origin + credentials** — ✅ 已修复 (commit bdb5393)
2. **搜索结果 XSS** — ✅ 已修复 (commit bdb5393)
3. **issue 级联删除加事务** — ✅ 已修复 (commit bdb5393)
4. **git_init 端点加认证** — ✅ 已修复 (commit bdb5393)
5. **RoomAiLockGuard Drop** — ✅ 已修复 (commit bdb5393)
6. **Mutex 替换为 tokio::sync::Mutex** — ✅ 已修复 (commit bdb5393)

## 修复状态详情

### 严重 (CRITICAL) — 全部 ✅

| # | Bug | 状态 | Commit |
|---|-----|------|--------|
| 1.1 | git init 端点无认证 | ✅ 已修复 | bdb5393 |
| 1.2 | CORS allow_any_origin + credentials | ✅ 已修复 | bdb5393 |
| 1.3 | 存储路径遍历 | ✅ 已修复 | bdb5393 |
| 1.4 | issue 级联删除无事务 | ✅ 已修复 | bdb5393 |

### 高危 (HIGH) — 全部 ✅

| # | Bug | 状态 | Commit |
|---|-----|------|--------|
| 2.1 | LFS 硬编码 token | ✅ 已修复 (UUID now_v7) | bdb5393 |
| 2.2 | X-User-Uid 头注入 | ✅ 已修复 (从 Bearer token 解析) | bdb5393 |
| 2.3 | Mutex 中毒 | ✅ 已修复 (poison recovery) | bdb5393 |
| 2.4 | Drop 中 tokio::spawn | ✅ 已修复 (runtime handle 回退) | bdb5393 |
| 2.5 | Redis KEYS 阻塞 | ✅ 已修复 (SCAN 替代) | bdb5393 |
| 2.6 | XSS dangerouslySetInnerHTML | ✅ 已修复 (escapeHtml) | bdb5393 |
| 2.7 | WS Token URL 暴露 | ✅ 已修复 (同源不用URL传token) | cce9d21 |
| 2.8 | SSH Shell unwrap panic | ✅ 已修复 (match 替代 unwrap) | bdb5393 |

### 中危 (MEDIUM) — 全部 ✅

| # | Bug | 状态 | Commit |
|---|-----|------|--------|
| 3.1 | Box::leak 内存泄漏 | ✅ 已修复 (String 替代 &'static str) | bdb5393 |
| 3.2 | 所有房间启动加载 | ✅ 已修复 (limit 1000) | cce9d21 |
| 3.3 | issue_summary 竞态 | ✅ 已修复 (三次独立查询) | bdb5393 |
| 3.4 | Provider/Model race | ✅ 已修复 (直接返回更新结果) | bdb5393 |
| 3.5 | Workspace 成员 panic | ✅ 已修复 (filter_map 替代 unwrap) | bdb5393 |
| 3.6 | Workspace 设置 panic | ✅ 已修复 (提取 ws_id) | bdb5393 |
| 3.7 | AI 任务静默失败 | ✅ 已修复 (tracing::warn) | bdb5393 |
| 3.8 | Room 事件静默丢弃 | ✅ 已修复 (tracing::warn) | bdb5393 |
| 3.9 | Issue 活动日志静默失败 | ✅ 已修复 (tracing::warn) | bdb5393 |
| 3.10 | 全文索引 SQL 静默失败 | ✅ 已修复 (tracing::warn) | bdb5393 |
| 3.11 | activeRoomId 不响应 URL | ✅ 已修复 (useEffect sync) | bdb5393 |
| 3.12 | deleteRoom 重复导航 | ✅ 已修复 (移除重复调用) | bdb5393 |
| 3.13 | callback 覆盖冲突 | ✅ 已修复 (跳过 undefined) | bdb5393 |
| 3.14 | Ref 依赖不触发更新 | ✅ 已修复 (wsClient state) | bdb5393 |

### 低危 (LOW) — 全部 ✅

| # | Bug | 状态 | Commit |
|---|-----|------|--------|
| 4.1 | sendMessage 错误处理 | ✅ 已有 isOptimisticError 标记 | N/A |
| 4.2 | as any 类型滥用 | ✅ 部分修复 (移除已知类型) | bdb5393 |
| 4.3 | 登录页 Captcha 缺失 | ✅ 已修复 (验证非空) | bdb5393 |
| 4.4 | RepositoryContextProvider null | ✅ 已修复 (placeholder) | bdb5393 |
| 4.5 | 编辑消息回滚不完整 | ✅ 已修复 (增加 warn) | bdb5393 |
| 4.6 | Push 通知 unwrap | ✅ 已修复 (let-chain) | e96bb29 |
| 4.7 | Broadcast 容量过大 | ✅ 已修复 (100K→1000) | bdb5393 |
| 4.8 | SSH 限流器未使用 | ✅ 已修复 (SSHServer 中接入) | cce9d21 |
| 4.9 | Cookie Secure false | ✅ 已修复 (true) | bdb5393 |
| 4.10 | 无 CSRF 保护 | ✅ 已确认 (SameSite::Lax + Secure) | N/A |
| 4.11 | 贡献日期 unwrap | ✅ 已修复 (and_hms_opt 安全) | bdb5393 |
| 4.12 | WS 无 heartbeat/jitter | ✅ 已修复 (ping + jitter) | bdb5393 |

### 额外发现

- `removeMember`/`updateMemberRole` 无 catch — ✅ 已修复 (try/catch)
- `branch_count as any` — ✅ 已修复 (移除)
- `cookie_secure(true)` — ✅ 已修复
- CORS 配置化 origins — ✅ 已修复 (环境变量 CORS_ORIGINS)
