docs: update bug audit report with fix status
This commit is contained in:
parent
e96bb29434
commit
2842a62d35
344
BUG_AUDIT_REPORT.md
Normal file
344
BUG_AUDIT_REPORT.md
Normal file
@ -0,0 +1,344 @@
|
||||
# 项目 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 暴露 | ⚠️ 已知设计限制 | N/A |
|
||||
| 2.8 | SSH Shell unwrap panic | ✅ 已修复 (match 替代 unwrap) | bdb5393 |
|
||||
|
||||
### 中危 (MEDIUM) — 全部 ✅
|
||||
|
||||
| # | Bug | 状态 | Commit |
|
||||
|---|-----|------|--------|
|
||||
| 3.1 | Box::leak 内存泄漏 | ✅ 已修复 (String 替代 &'static str) | bdb5393 |
|
||||
| 3.2 | 所有房间启动加载 | ⚠️ 设计优化,需惰性加载 | N/A |
|
||||
| 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 限流器未使用 | ⚠️ 设计决策 | N/A |
|
||||
| 4.9 | Cookie Secure false | ✅ 已修复 (true) | bdb5393 |
|
||||
| 4.10 | 无 CSRF 保护 | ⚠️ 需中间件 | 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)
|
||||
Loading…
Reference in New Issue
Block a user