gitdataai/BUG_AUDIT_REPORT.md

345 lines
17 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 项目 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 会 panicRedis 锁永不释放,对应房间的 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)