docs: add cybersecurity notes
zhenyi committed
Added cybersecurity.md cybersecurity.md +407 −0 View cybersecurity.md
@@ -0,0 +1,407 @@
1# 网络安全审计报告 — `/home/zhenyi/work/untitled`
2
3> **审计范围**:完整 Rust workspace(Git 远程托管平台 + 聊天 + LiveKit 视频 + 支付),共 30+ crate、约 6.5 万行代码,5 个二进制(apihost / gitpro / githook / livechat / mails)。
4>
5> **方法**:OWASP Top 10:2021 / CWE Top 25 静态审计;8 个并行专职 agent(git 传输层 / 基础设施与配置 / 聊天·媒体·直播·WS / Web API / 认证与令牌 / 供应链与后门 / 合规与隐私 / 交叉验证与攻击链),加上 `cargo audit`、`cargo deny` 依赖审计,主会话对最高风险项的逐条源码复核,以及独立多 agent 交叉验证(结论与修订见 [§13](#13-交叉验证修订记录),2026-08-08 修订)。
6>
7> **日期**:2026-08-08 | **扫描器**:Claude Code(cybersecurity skill)
8>
9> **结论**:代码库**没有**发现恶意后门、C2 信标、混淆代码或供应链篡改(`unsafe` 全库为 0)。但存在**多个可直接利用的授权绕过漏洞**(CRITICAL × 3,HIGH × 9),其中仓库 REST API 的鉴权失效是核心根因,可导致任意仓库被删除/接管、所有私有仓库被读取、任意用户提权为站点管理员。
10
11---
12
13## 目录
14
151. [总体风险评估](#1-总体风险评估)
162. [关键发现摘要(按严重程度)](#2-关键发现摘要按严重程度)
173. [CRITICAL 漏洞详情](#3-critical-漏洞详情)
184. [HIGH 漏洞详情](#4-high-漏洞详情)
195. [MEDIUM 漏洞详情](#5-medium-漏洞详情)
206. [LOW 与潜在问题](#6-low-与潜在问题)
217. [供应链与依赖审计](#7-供应链与依赖审计)
228. [后门与 C2 扫描结论](#8-后门与-c2-扫描结论)
239. [合规与隐私(GDPR/CCPA/ISO 27001)](#9-合规与隐私gdprccpaiso-27001)
2410. [完整攻击链](#10-完整攻击链)
2511. [确认已有的防护措施](#11-确认已有的防护措施)
2612. [修复优先级路线图](#12-修复优先级路线图)
2713. [交叉验证修订记录](#13-交叉验证修订记录)
28
29---
30
31## 1. 总体风险评估
32
33| 维度 | 评分 (0-100) | 说明 |
34|---|---|---|
35| **授权 / Access Control** | **15 / 100** | 仓库/Issue REST API 鉴权形同虚设,SSH 作用域短路,git Basic 认证可越权提权 |
36| **认证 / Authentication** | 45 / 100 | 密码/令牌哈希良好,但 deny-list 覆盖不一致、签名密钥生命周期损坏 |
37| **聊天 / Chat & WS** | 55 / 100 | 存储层有严格成员校验,但幂等键、mentions、Live 存在越权点 |
38| **Git 传输 / Git & SSH** | 40 / 100 | 智能 HTTP 鉴权正确,但 SSH 绕过 + 推送保护失效 + DoS |
39| **基础设施 / Infra & Config** | 50 / 100 | 无 TLS、密钥明文落库、Redis/出口默认明文 |
40| **供应链 / Supply Chain** | 80 / 100 | 3 个已知 CVE(rsa ×2 无修复、rkyv 需 major 升级至 ≥0.8.17),无后门,锁文件干净 |
41| **隐私合规 / Privacy** | 40 / 100 | 无删除权、无数据可携带、同意记录可伪造、PII 泄漏 |
42
43**总体风险等级:高危(High)** — 存在可立即利用、影响全部租户的授权绕过。
44
45---
46
47## 2. 关键发现摘要(按严重程度)
48
49| # | 严重程度 | 标题 | 根因文件 | 影响 |
50|---|---|---|---|---|
51| **C1** | CRITICAL | **任意认证用户可销毁/转移/重命名任意仓库** | `lib/repo/features/lifecycle/usecase/*.rs` | 数据丢失 / 仓库劫持 |
52| **C2** | CRITICAL | **git Basic 认证可越权为站点管理员(JWT-as-password 用户名混合)** | `dvcs/gitauth/service.rs:68-80` | 任意用户对所有仓库获得 Admin |
53| **C3** | CRITICAL | **任何 SSH 用户可读所有私有仓库(作用域短路绕过仓库 ACL)** | `lib/repo/permission.rs:35-43` + `lib/repo/actor.rs:71-81` | 全库私有数据外泄 |
54| **H1** | HIGH | **任意用户可添加/删除 collaborator(含自封 admin)并管理 deploy key/分支** | `lib/repo/features/collab/usecase.rs:33-38` | 提权 + 写任意仓库 |
55| **H2** | HIGH | **私有仓库元数据全量泄漏(list/get 无视 actor)** | `lib/repo/features/lifecycle/usecase/{list,get}.rs` | 私有库清单/名称泄漏 |
56| **H3** | HIGH | **Issue 更新/删除/关闭无归属或成员校验** | `lib/issues/features/issue/usecase/*.rs` | 跨频道修改/删除工单 |
57| **H4** | HIGH | **私有用户 PII(真名/出生日期)可被任意登录用户读取** | `lib/user/features/profile/usecase.rs` | 隐私泄漏 |
58| **H5** | HIGH | **撤销 deny-list 未在 repo/issues API、git HTTP/SSH、chat WS 上执行** | `lib/repo/service.rs`、`lib/issues/service.rs`(project 已正确接线) | 登出/改密/封禁后令牌仍有效 |
59| **H6** | HIGH | **分支/标签保护在推送路径上从不执行** | `dvcs/gitsync/post_receive.rs`(只有 capture hook) | `git push --force` 到受保护分支 |
60| **H7** | HIGH | **SSH 通道缓冲无上限 → 内存耗尽 DoS** | `dvcs/gitssh/handler/channel.rs:8-12` | OOM |
61| **H8** | HIGH | **聊天消息幂等键全局无主键 → 跨用户去重抑制 + 消息 ID 泄漏** | `chat/chatlayer/storage_bridge.rs:52-85` | 消息被丢弃 / 存在性探测 |
62| **H9** | HIGH | **Live 会议销毁副作用先于鉴权(任何成员可踢掉会议)** | `chat/live/service.rs:211-242` | 拒绝服务(Live) |
63| **H10** | HIGH | **所有 HTTP 服务无 TLS,git Basic 认证明文传输密码** | `config/default.yaml` + `app/*/main.rs` | 全站密码嗅探 |
64
65---
66
67## 3. CRITICAL 漏洞详情
68
69### C1 — 任意认证用户可销毁/转移/重命名任意仓库(CWE-306)
70
71**根因**:`lib/repo` 的 Web API 层完全绕过了 `repo::permission::require`(在 `lib/repo/features` 中被 grep 确认无任何引用),改用只检查 `actor.is_nil()` 的桩守卫。
72
73- `lib/repo/features/lifecycle/usecase/delete.rs:19-23` — `require_authenticated(actor)?` 后直接 `Entity::delete_by_id(repo_id).exec(&db)`,**无所有权/空间成员检查**。
74- `update.rs:20-55` — 同上,可翻转 `is_private`、`default_branch`。
75- `transfer.rs:21-32` — `active.channel_id = Set(input.new_channel_id)` → 攻击者把仓库搬到自己的频道(**仓库劫持**)。
76- 守卫桩:`lib/repo/features/lifecycle/usecase/helpers.rs:8-13`(仅拒绝 nil UUID)。
77
78**攻击路径**:`GET /api/v1/repos`(见 H2)枚举所有 `channel_id`+`id` → `DELETE /api/v1/repo/{channel_id}/{repo_id}` 删除任意仓库,或 `POST .../transfer` 接管。
79
80**修复**:所有仓库变更路由改走 `repo::permission::require(actor, repo.access, ...)`,delete/transfer 要求 owner 或 channel-write,update 要求 owner/space-admin。
81
82---
83
84### C2 — git Basic 认证可越权为站点管理员(JWT-as-password 身份混合)(CWE-269)
85
86**根因**:`dvcs/gitauth/service.rs:68-80` 中 `authenticate()` 按 Basic 认证的 **username** 查 DB(第 61-64 行),而 `verify_credentials()`(第 112-115 行)—— 如果 `password` 字段是一个**合法 access token**,直接返回 `claims.user_id()`,**从不与 username 记录比对**。随后 actor 用 `user_id: Some(claims.user_id())`(攻击者自己的 id)**但** `is_site_admin: user.is_admin`(被冒充用户名对应的管理员标记,第 77 行)构造。
87
88**提权点**:`lib/repo/permission.rs:25-27` — `if actor.is_site_admin { return RepoAccess::Admin }`。(初版误标 130-132,该文件仅 111 行;交叉验证修正)
89
90**攻击路径**:
911. 攻击者用自己的账号登录获得自己的 access token。
922. `curl -u <站点管理员用户名>:<自己的access_token> https://host/priv-repo.git/info/refs?service=git-upload-pack`
933. `authenticate` 返回 `user_id` = 攻击者、`is_site_admin = true` 的 actor。
944. `authorize()` → `effective_access` 返回 `Admin` → 对所有仓库(含私有)读/写/删,且 LFS 同样生效。
95
96**修复**:JWT 路径成功后,actor 必须完全从验证后的身份构建:使用 `claims.is_admin`,或从 `claims.sub` 重取用户并拒绝 `claims.sub ≠ username-row.uid` 的情况;**永不**混合 username 行的 `is_admin` 与另一身份的 `user_id`。强烈建议彻底放弃 JWT-as-password 路径(改用独立的 git 令牌)。
97
98---
99
100### C3 — 任何 SSH 用户可读所有私有仓库(作用域短路绕过仓库 ACL)(CWE-863 / CWE-276)
101
102**根因**:
103- `lib/repo/actor.rs:71-81` — `ActorContext::for_user` 给**每个**用户都盖上 `TokenScope::RepoRead`。
104- `lib/repo/permission.rs:35-43` — `effective_access` 在 `has_scope(RepoRead)` 时**立即返回 `Read`**,早于任何 `space_role` / `collaborator_mode` / `is_public` 检查;`collaborator_mode` 只用于升级到 Write,从不用于拒绝。
105- `dvcs/gitssh/handler/command.rs:47-49` — SSH 路径用 `for_user(u.uid)` 构造 actor(HTTP 路径在 `dvcs/gitauth/service.rs:73-79` 传空 scopes,故 HTTP 安全)。
106
107**攻击路径**:任何注册用户添加 SSH key → `git clone ssh://host/任意/私有仓库.git` → `authorize(..., Read)` → `effective_access = Read` 无论仓库私有性 → 克隆成功。**全站每个私有仓库对每个认证用户可读**(只读外泄,写入仍受控)。
108
109**修复**:`for_user` 不要授予 `RepoRead` 作用域(与 HTTP actor 一致——空 scopes,让 space/collab 决定);或重排 `effective_access`,让仓库级检查先跑,作用域只用于升级 token/deploy-key 流。
110
111> **主会话独立复核确认**:已在 `lib/repo/permission.rs`、`lib/repo/actor.rs`、`dvcs/gitssh/handler/command.rs` 源码中逐行验证该路径真实存在。
112
113---
114
115## 4. HIGH 漏洞详情
116
117### H1 — 任意用户可添加/删除 collaborator(含自封 admin)并管理 deploy key/分支(CWE-306 → 提权)
118
119- `lib/repo/features/collab/usecase.rs:33-38` — `require_admin(actor)` 是桩,任意认证用户通过;`add_collaborator`(96-135)/`delete_collaborator`(137-157)直接写 `repo_collaborations`。
120- `lib/repo/handler/collab/add.rs:19-28` — `PUT /api/v1/repo/{channel}/{repo}/collaborators/{user_id}` body `{"permission":"admin"}`。
121- 同类桩守卫:`deploy_key/usecase.rs:12-17,95`、`branch/usecase.rs:16-21`、`branch_protection/usecase.rs`、`tag_protection/usecase.rs`。
122- **攻击路径**:把自己加为任意仓库的 `admin` collaborator → git `receive-pack` 获得写权限(collab 表被 `gitauth/permission.rs:53-71` 读取)→ 部署代码、转移/删除仓库、翻转分支保护、投毒下游消费者。
123
124### H2 — 私有仓库元数据全量泄漏(CWE-200 / CWE-639)
125
126- `lib/repo/features/lifecycle/usecase/list.rs:14` — `let _ = actor;`,查询无 actor 过滤,`GET /api/v1/repos` 返回**所有**仓库行(含 `is_private`)。
127- `get.rs:19-21` — 仅对匿名用户挡私有,任何认证用户可读任意私有仓库元数据(名称、描述、默认分支、clone/SSH URL)。
128- 这也为 C1 提供了 `channel_id`/`repo_id` 枚举源。
129
130### H3 — Issue 更新/删除/关闭无归属或成员校验(CWE-306)
131
132- `lib/issues/features/issue/usecase/delete.rs:18-24` — `require_authenticated` 后硬删;`update.rs:21-58` 同;`close.rs` 同。仅 comment 有真实 owner 检查。
133- `lib/issues` 层从不校验 channel/space 成员(唯一助手 `require_authenticated` 见 `helpers.rs:9-14`)。
134- 任意登录用户给定 channel+issue id 即可改/删/关他人工单。
135
136### H4 — 私有用户 PII(真名/出生日期)可被任意登录用户读取(CWE-200)
137
138- `lib/user/handler/profile/profile_view.rs:23-30` — `AuthUser` 存在即调 `svc.get_profile(uid)`。
139- `lib/user/features/profile/usecase.rs:124-137` — `fetch_profile` 无 viewer/可见性检查(对比 `fetch_user_public`:41-62 + `can_view`)。
140- `ProfileView::from` 无条件输出 `real_name`、`date_of_birth`、`location` 等,`show_real_name`/`show_location` 标志**从不执行过滤**。
141- 用户设为 `visibility: Private` 仍会暴露真名 + 出生日期。
142
143### H5 — 撤销 deny-list 未在 repo/issues API、git HTTP/SSH、chat WS 上执行(CWE-613)
144
145- `lib/repo/service.rs:47-54`、`lib/issues/service.rs:41-45` — 实现 `HasJwtState` 但**不覆盖** `access_blacklist()` → 默认 `None`(`lib/jwt/middleware.rs:16-18`),repo/issues API 的 JWT 不做撤销检查。
146- `dvcs/gitauth/service.rs:130-144`(git JWT-as-password)、`chat/chatlayer/auth_bridge.rs:26-54`(chat WS/服务层)、`dvcs/gitssh/handler/auth.rs:32-34`(SSH 公钥:仅拒绝 `!is_active`)— 均无 blacklist、无 `pwd_version` 校验。
147- 已正确接线(确认):`lib/auth`、`lib/user`、`lib/space`、`chat/live`、**`lib/project`**(`lib/project/service.rs:42-45` 覆写 `access_blacklist`,project 路由实际执行撤销)。
148- **影响**:受害者改密/登出/被挂起后,其令牌在 repo/issues API、git HTTP(JWT)、chat WS 上仍有效(`access_ttl` 默认 15 分钟);SSH 为公钥认证,封禁只置 `is_restricted`(`lib/user/features/suspensions/usecase.rs:67`)而非 `is_active`,被封用户 SSH key 继续有效。
149- **交叉验证修正**:初版把 `lib/project` 误列为未接线并计入受害面——实际 project 已正确接线;真实缺口是 **repo + issues(HTTP)+ git HTTP/SSH + chat WS**。因撤销窗口受 `access_ttl`(15 分钟)约束,严重度建议 **MEDIUM–HIGH**。
150
151### H6 — 分支/标签保护在推送路径上从不执行(CWE-284)
152
153- 推送路径跑裸 `git receive-pack`,仅装了一个 **capture** `post-receive` hook(`dvcs/gitsync/post_receive.rs:25-38`,只把 ref 更新追加到 `_gta_post_receive`)。
154- `hook_pre_receive`(`dvcs/git/hook/usecase.rs:6-12`)是恒返回 `accepted:false` 的桩,且 **从不被调用**(grep 只有 trait 定义 + 该实现)。
155- `lib/repo/features/branch_protection/*` 只管理 DB 策略行,`git push` 时不读取。
156- **影响**:任何有 `RepoAccess::Write` 的用户可 `git push --force`/删除受保护分支/标签,`protected_file_patterns`/`required_approvals` 形同虚设。
157
158### H7 — SSH 通道缓冲无上限 → 内存耗尽 DoS(CWE-770)
159
160- `dvcs/gitssh/handler/channel.rs:8-12` — 每个 `data()` 回调 `state.buffer.extend_from_slice(data)` 无上限。
161- git 子进程直到 EOF 才 spawn(`channel.rs:18-51`),整包先驻留内存再整份拷给 git stdin。HTTP 侧对应防护 `MAX_RPC_BODY = 1<<30` 仅存在于 `gitsmart/handler/smart.rs:13`。
162- **补充(交叉验证)**:`max_sessions=32` 信号量虽在 `server.rs:22-26` 创建,但其 `OwnedSemaphorePermit` 标 `#[allow(dead_code)]` **从未用于拒绝超额连接** → 并发会话实际无上限;缓冲发生在 exec 之前(首个 `data` 即建 channel 条目),故**只需注册 SSH key**、无需任何仓库权限即可触发;`inactivity_timeout` 在持续流式期间不会触发。
163- **影响**:任意认证 SSH 用户单条/少数持续大流即可耗尽共享 git 进程内存(OOM)。
164
165### H8 — 聊天消息幂等键全局无主键 → 跨用户去重抑制 + 消息 ID 泄漏(CWE-639 / CWE-345)
166
167- `chat/chatlayer/storage_bridge.rs:52-85` — key = `chat:msg:{client_msg_id}`,**无 actor 作用域**。
168- `chat/chatlayer/service.rs:193-201` 与 439-447;`chat/glayer/handler/lookups.rs:15-25` — `GET /api/v1/chat/messages/by-client-msg-id`。
169- **攻击**:(a) 攻击者抢注受害者 `client_msg_id` → 受害者消息命中 `AlreadyResolved` 被静默丢弃,还被"告知"已用攻击者消息 UUID 接受;(b) 任意认证用户可确认他人 `client_msg_id` 存在性并拿到解析后的 `server_msg_id`(可枚举 UUID,跨会话存在性 oracle)。
170- **修复**:键加 actor 作用域 `chat:msg:{user_id}:{client_msg_id}`;`AlreadyResolved` 时校验已解析消息属于调用方;lookup 要求所有权。
171- **严重度(交叉验证)**:HIGH 成立——(b) lookup oracle 对任意认证用户无条件成立;(a) 的野外可利用性取决于客户端 `client_msg_id` 是否可预测(客户端生成随机 UUID 时可下调至 Medium–High)。存储层 `require_member` 只作用于 DB 消息持久化,Redis 幂等键与 lookup 路径不走 storagelayer。
172
173### H9 — Live 会议销毁副作用先于鉴权(CWE-285)
174
175- `chat/live/service.rs:211-242` — `end_room` 在**鉴权前**(第 224 行)调用 `self.client.delete_room(...)`(LiveKit admin `DeleteRoom`,踢掉全部参与者);第 225 行 `mark_ended()` 才执行真正的权限检查(`created_by == caller || require_channel_admin`)。
176- 任何该频道活跃空间成员都可销毁会议并断开所有参与者,即使 DB 更新随后因 `PermissionDenied` 失败。
177- **严重度(交叉验证)**:HIGH 处上沿——属瞬时可用性 DoS(可重开会话、无数据损坏),可下调至 Medium–High;对直播型产品仍是实质拒绝服务。
178- **修复**:先读 meet、先执行 `mark_ended`/专用 `require creator-or-admin`,鉴权成功后再调 `client.delete_room`。
179
180### H10 — 所有 HTTP 服务无 TLS,git Basic 认证明文传输密码
181
182- `config/default.yaml:385-386, 399-400` — `git_smart.bind_addr: 0.0.0.0:8080`、`git_ssh.bind_addr: 0.0.0.0:2222`;`app/apihost`/`app/livechat`/`app/gitpro` 均明文 HTTP 绑 0.0.0.0。
183- `dvcs/gitsmart/handler/mod.rs:167-183` — 每个 clone/fetch/push 用 `Authorization: Basic <base64(user:password)>` 认证。仓库内**无任何 TLS**(无 rustls/axum-server/tls-listener,无 TLS 终止部署清单)。
184- 旁路/邻近攻击者可解码每次 git 事务的 base64,批量收割全站密码;Basic 可重放。`auth.cookie_secure: true` 在明文 HTTP 上根本不发送,认证流程也静默失效(除非外部 TLS 代理存在)。
185- **修复**:`git_smart`/`apihost`/`livechat` 前置 TLS(入口或进程内 rustls),生产收紧 bind 到回环/内网,明文 HTTP 拒绝 Basic 认证。
186
187---
188
189## 5. MEDIUM 漏洞详情
190
191| # | 标题 | 文件 | 说明 |
192|---|---|---|---|
193| M1 | 签名密钥明文落库且列名误导 `*_encrypted`,无预置/无轮换 | `lib/jwt/keys.rs:89-93`、`lib/jwt/handlers.rs:111-116` | `private_key_encrypted` 直接喂给 `EncodingKey::from_ed_der`,无解密/KMS;`rotate_keys_handler` 是 TODO;新库无活动密钥 → `NoActiveKey` |
194| M2 | Redis 未编译 TLS、默认 `password: null` | `Cargo.toml:102`、`lib/cache/redis/mod.rs`、`config/default.yaml:53-60` | Redis 承载 presence、access-token blacklist、限流身份、缓存;无认证无 TLS → 可达端口即可读私有状态、清除 `auth:deny:*`(使登出失效)、投毒限流 |
195| M3 | 出口集成默认明文:Kafka/Meilisearch/OpenTelemetry | `lib/queue/{producer,consumer}.rs`、`lib/search/client.rs`、`lib/config/obs.rs`、`lib/obs/otlp.rs` | rdkafka 无 `security.protocol`/`sasl.*`;Meilisearch `http://127.0.0.1:7700` 明文;OTLP 明文带 `Authorization: Bearer`/`Basic` 逐字透传 token |
196| M4 | 默认 `auto_migrate: true` 每次启动跑 DDL | `config/default.yaml:46`、`lib/db/conn.rs:64-65`、`lib/db/schema.rs:14-16` | 违反最小权限;任何被攻破/异常的代码版本可自动改写 schema |
197| M5 | Webhook 分发无 SSRF 防护,secret/payload 明文落库 | `lib/webhook/dispatcher.rs:106-128,314-323`、`config/webhook.rs:33`、`lib/db/webhook/repo_webhooks.rs:14` | 跟随最多 5 个重定向,POST 到攻击者配置的 URL,无私有 IP/元数据 IP 过滤 → 内网扫描;secret 明文;deliveries 持久化完整 payload/响应体 |
198| M6 | API 面缺少进程内请求上限 | `lib/webapi/router.rs`、`gitsmart/handler/{smart,lfs}.rs` | 无 body 上限/超时/并发上限;gitsmart 的 1 GiB / LFS 的 8 GiB 上限本身即内存 DoS |
199| M7 | 登出仅当客户端发送 `Authorization` 头时撤销 access token | `lib/auth/features/logout/usecase.rs:143-160`、`handler/logout.rs:109-116` | 无头(cookie 客户端)只撤销 refresh 会话,无状态 access token 在 TTL 内继续有效 |
200| M8 | 刷新令牌重放窗口(grace_window 默认 5 分钟) | `lib/jwt/rotation.rs:148-156` | 被替换的旧 token 在 grace 窗口内仍可兑换新对;重用检测仅在 grace 之后 |
201| M9 | 陈旧授权声明:`is_admin`/`is_restricted` 烧进令牌且从不复查 | `lib/jwt/extractor.rs:96-108`、`rotation.rs:220-242` | 被降权的 admin 的旧令牌(含副本)最多 15 分钟仍过 `AuthAdmin` |
202| M10 | 签名密钥缓存从未在 web/chat 进程填充;永不重载/轮换;已撤销密钥仍受信任 | `lib/webapi/services.rs:94`(空缓存)、唯一 `load_from_db` 在 `dvcs/gitauth/service.rs:134` | apihost 的 `AuthUser`/`issue_login_pair` 全部 `UnknownKid`/`NoActiveKey` → 身份平面故障;gitpro 一次加载后永不过期校验,`grace.rs` 零调用方 |
203| M11 | PoW HMAC secret 在 API host 硬编码为空,且配置被忽略 | `lib/webapi/services.rs:97`(`PowService::new(PowSettings::default())`,`app.pow` 从不读取)、`config/default.yaml:196-204`、`lib/auth/pow/mod.rs:188-194` | 空 secret → 所有 `verify_solution_once` 失败 → login/register/forgot/reclaim/resend 永不成功(fail-closed 但不可用) |
204| M12 | 聊天 REST 无限流;actor 邮箱与 presence/mention 结构无界 | `chat/glayer/router.rs`、`chat/chatlayer/actor/runtime.rs`(`unbounded_channel`)、`projection/user.rs:124-141` | 合法令牌可无限刷消息/heartbeat(随机 `device_id`)/mentions;Redis zset/list 无界增长 |
205| M13 | 上传 content-type 是黑名单且未校验实际对象 | `chat/chatlayer/media.rs:25-57,225-272`、`chat/storagelayer/.../s3.rs:105-121` | presign 声称 `image/png`,随后 PUT 实际 `text/html`+HTML/JS → 存储源上存储型 XSS 可能(若 S3 域共享 cookie 或加同源 serving 路由则升级) |
206| M14 | Live room/presence 授权按 **space** 粒度而非发起频道 | `chat/storagelayer/mapper/meet.rs`、`chat/storagelayer/auth.rs:72-83,117-145`、`chat/chatlive/service.rs:81-174` | 1:1 私有会话频道所在空间的其他成员可 join/end/presence 获取 LiveKit 令牌,吞噬私有会话边界 |
207| M15 | git 子进程缺少 repo/global 配置隔离(预期防护是死代码) | `dvcs/gitssh/git_service.rs:92-103`(`build_git_command` 从未被调用)、实际 spawn 在 `channel.rs:130-139`/`command.rs:128-135`/`gitsmart/protocol/rpc.rs:33-45` | 若 gitpro 账号 `~/.gitconfig`/`/etc/gitconfig`/仓库 `.git/config` 出现 `core.fsmonitor`/`core.sshCommand`/credential helper,将在 upload-pack/receive-pack 上执行(纵深防御) |
208| M16 | LFS `batch` 下载要求 `Write`(授权映射错配) | `dvcs/gitsmart/handler/lfs.rs:31-35`(batch 对所有操作含 Download 都要求 Write);verify 仅 Read | 只读协作者对 LFS 对象永远 403,无法下载(功能/可用性缺陷,fail-closed) |
209| M17 | OTLP 遥测导出 PII(邮箱/IP) | `lib/auth/features/register/usecase.rs`(`email = %cmd.email`)、`dvcs/gitssh/handler/command.rs`(`client_addr`)、`lib/obs/otlp.rs` | `obs.enabled=true` + `enable_logs: true` 默认开启,`traces_sample_ratio` 默认 1.0,无脱敏层 → 明文邮箱/IP 送达第三方 |
210| M18 | 邮箱在 git commit 中作为数据暴露 | `lib/db/git/git_commits.rs`、`lib/export/repo/git_ref.rs`、`lib/repo/handler/branch/list.rs` | REST 视图把 `author_email`/`committer_email` 返回给有仓库访问的任何用户;配合 H2 可从私有仓库枚举真实邮箱 |
211
212---
213
214## 6. LOW 与潜在问题
215
216| # | 标题 | 文件 | 说明 |
217|---|---|---|---|
218| L1 | git HTTP Basic 无限流/无锁定 | `app/gitpro/src/main.rs`、`dvcs/gitauth/service.rs:112-128` | 每次尝试一次 Argon2 校验是唯一节流;弱密码可暴力 |
219| L2 | Apple OAuth 从 id_token **payload** 读 `kid` 而非 header | `lib/auth/features/oauth/oauth_apple/impls.rs:71-88` | Apple 登录恒失败(功能故障,签名仍校验,不可利用) |
220| L3 | Argon2 用 `Argon2::default()`(OWASP 最低参数) | `lib/auth/features/password_hash/usecase.rs:19,33` | 建议可配置更高 `m_cost`/`t_cost` |
221| L4 | 限流器是客户端 cookie 键控,处处可绕过 | `lib/rate_limit/middleware.rs:31-40,64-70,117-124` | 无 IP 分量;清/轮换 cookie 每请求新配额 → login/forgot/reset/register 无限尝试 |
222| L5 | SSH 认证忽略 `prohibit_login` | `dvcs/gitssh/handler/auth.rs:32` | 被禁止登录用户仍可走 SSH push/pull |
223| L6 | LFS 上传不校验 batch 声明的 size 与实际字节 | `server/lfs.rs:83-106,204-242` | 已由 `enforce_limit` + SHA-256 封顶,声明 size 欺骗配额记账 |
224| L7 | 服务器侧 mirror/import SSRF(潜在)+ askpass helper 卫生 | `dvcs/git/remote/usecase.rs`、`dvcs/git/validate.rs:53-66`、`gitcli.rs:76-105` | 允许 http/https/ssh 无 host/IP 限制(目前未接 HTTP 路由,属潜伏);askpass 写 `/tmp/git-askpass-{pid}-{nonce}.sh` 0600,路径部分可预测 → 本地 TOCTOU |
225| L8 | 默认弱 Postgres 凭据 `untitled/untitled` | `config/default.yaml:35` | 文档化 dev 默认;生产若带上该文件 DB 可猜 |
226| L9 | `pow.hmac_secret: ""` 默认关闭匿名反滥用 | `config/default.yaml:177-200`、`lib/auth/pow/mod.rs:189-201` | 应加配置 `validate()` 空 secret 即大声失败 |
227| L10 | 弱默认凭据占位(LiveKit/payment/search master key) | `config/default.yaml:373-377` | 目前 fail-closed(好);启用 LiveKit 后明文 http base_url 路径重现 |
228| L11 | SSH host key 文件 operator 自备、不强制权限 | `dvcs/gitssh/server.rs:61-70`、`config/git.rs:56` | 缺失即 fail-closed;应 bootstrap 生成 0600 |
229| L12 | `mark_read` 不校验成员 | `chat/chatlayer/service.rs:376-393`、`projection/user.rs:104-123` | 仅影响自身,但可任意设置读状态/删除 unread 计数 |
230| L13 | `.env` 自动加载可静默覆盖生产配置 | `lib/config/loader.rs:76-77` | 生产 CWD 残留 `.env` 即注入覆盖;建议 `--no-dotenv` 开关 |
231| L14 | TOTP secret 字段名 `*_encrypted` 但无加密代码 | `lib/db/user/users.rs` | 未来启用 2FA 若不加真实加密则明文落库 |
232| L15 | 登录/OAuth 账户状态可枚举 | `login/usecase.rs:82-113` | distinct 错误揭示账户存在/状态(PoW 廉价,消息应统一) |
233
234---
235
236## 7. 供应链与依赖审计
237
238### 7.1 cargo audit / cargo deny 结果
239
240| # | CVE/ID | Crate | 版本 → 修复 | 严重度/CVSS | 可达性 | 建议 |
241|---|---|---|---|---|---|---|
242| 1 | RUSTSEC-2026-0235 | **rkyv** | 0.7.46 → **`>=0.8.17`**(需 major 升级,0.7 线无 backport) | OOB 读 | 仅传递性(由 `rust_decimal 1.42.1` 引入,decimal 运算),不反序列化用户输入 | 升级至 0.8 并做兼容回归;勿让不可信二进制进入任何 rkyv 反序列化路径 |
243| 2 | RUSTSEC-2023-0071 | **rsa** | 0.9.10 → **无修复版** | 5.9 Medium · AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:N/A:N | 经 **`jsonwebtoken 11.0.0`**(`features=["rust_crypto"]`;`lib/jwt/signer.rs:12`、`lib/auth/features/oauth/oauth_apple/usecase.rs:62` 走 RS256 签名/验签)→ **JWT 认证令牌路径**;实际利用需大量时序样本 | 上游无补丁(正在迁移常量时间);位处平台认证面、比 SSH host-key 更敏感,高威胁部署应 wrapper 密钥轮换 / 优先 EdDSA |
244| 3 | RUSTSEC-2023-0071 | **rsa** | 0.10.0-rc.18 → 无修复 | 同上 | 经 `ssh-key 0.7.0-rc.11`(host key 签名)→ **SSH 认证**;实际利用需大量时序样本 | 同 #2(Marvin 时序缓解) |
245| 4 | RUSTSEC-2024-0436(`unmaintained`) | **paste** | 1.0.15 | informational | 仅内省宏,无运行时代码 | 可选换 `pastey` 兼容分支 |
246
247`cargo deny check advisories` 输出:**advisories FAILED**(RUSTSEC-2024-0436 unmaintained + RUSTSEC-2023-0071 ×2)。
248
249> **交叉验证修正(§7.1)**:#2/#3 的 rsa 可达性初版对调——rsa 0.9.10 实际由 `jsonwebtoken 11.0.0`(`features=["rust_crypto"]`)引入,位于 **JWT 签名/Apple OAuth 验签的认证令牌路径**(`lib/jwt/signer.rs:12`、`lib/jwt/keys.rs:77`、`lib/auth/features/oauth/oauth_apple/usecase.rs:62,148`),比 SSH host-key 路径更敏感;rsa 0.10.0-rc.18 才属于 ssh-key → SSH 认证。rkyv「无修复版」措辞修正:`>=0.8.17` 有修复但需 major 升级(0.7 线无 backport)。
250
251### 7.2 版本固定 / 供应链卫生
252
253- **`ssh-key = "=0.7.0-rc.11"`**、**`russh = "=0.62.2"`**:精确固定,已对照 russh 0.62.2 自身 manifest 验证——russh 上游本就依赖 `ssh-key 0.7.0-rc.x`(`ssh-key/rsa`、`ssh-key/dsa` 特性)。**合法生态约束**,非走私向量。
254- OTLP 组 `=0.31.0` / `=0.32.0` / `=0.2.1` 精确固定 — 有代码注释说明兼容集。**合法**。
255- **`reqwest` 双版本**:workspace 0.13.4 + `lib/obs` 0.12(阻塞客户端给 OTLP batch 线程)— 有注释。可维护性瑕疵,非后门。
256- `dotenvy`、`meilisearch-sdk`、`rdkafka` 均为知名 crate。
257- **零** git 源依赖、**零** `[patch]`/`[source]`/`replace-with` 重定向;两个 registry 缓存(crates.io + rsproxy.cn)字节级一致、校验和匹配。
258- **无 build.rs**(全 workspace);`features = "0.10.0"` 声明未使用(Cargo.toml:117)。
259- `cargo machete`:80 处未用依赖(29 个 crate,多为 central-config 迁移遗留),建议清理以缩小攻击面。
260
261---
262
263## 8. 后门与 C2 扫描结论
264
265**整体结论:无后门、无 C2 信标、无混淆、无供应链篡改。**
266
267- **`unsafe`:全库 0 处**(1367 个 `.rs` 文件,`grep -rl` 与 `-rc` 双重验证)。无 `transmute`、`#[no_mangle]`、`extern "C"`、`link_arg`、隐藏 `cfg_attr`、`std::ptr` 转换、长 0x-hex blob、XOR/Blowfish 字节处理。
268- base64/hex 仅用于:JWT 签名+claims、OAuth PKCE/摘要、argon2 密码哈希、OTLP Basic auth、Git LFS、altcha PoW、git SHA 的 `ObjectId::from_hex` — 全部合法。
269- **可执行载荷 / 写后执行**仅 4 处,全部合法:
270 - `dvcs/gitsync/post_receive.rs:30-36` — 写固定常量 `#!/bin/sh` capture hook,唯一插值是常量 `{CAPTURE_FILE}`,**无用户输入插值**。
271 - `dvcs/git/hook/usecase.rs:52` — 执行 hooks 脚本但拒绝绝对路径与 `ParentDir`/`RootDir` 组件。
272 - `dvcs/git/gitcli.rs:86-94` — 写 0600 askpass helper(token 走 env 不经 argv),Drop 删除;标准 git credential-helper 模式。
273 - `gitsmart/protocol/rpc.rs` / `gitssh` — spawn 真实 `git`/`gpg` 静态参数。
274- **网络回调**:所有出站 HTTP 均配置驱动或文档化(Apple/Google/GitHub OAuth、LiveKit RoomService、Airwallex、OpenObserve OTLP、Webhook dispatcher HMAC-SHA256 签名、Meilisearch)。硬编码域名仅 `git-lfs.github.com`、`appleid.apple.com` 等引用。**无硬编码 beacon IP/域名、无 DNS-TXT/lookup 外渗**。
275- **dev/test 混入生产**:`grep dev-dependencies` → 无;`dvcs/codesearch` 是空 stub 占位。
276- **Cargo.lock 完整性**:成员与 manifest 精确匹配(`1.94.0`、resolver "3");lock 历史新增仅为 otel/prost/tonic 栈(commit `d192872`),与 `lib/obs`/`lib/webhook` 接线一致。
277
278---
279
280## 9. 合规与隐私(GDPR/CCPA/ISO 27001)
281
282| 严重度 | 问题 | 条款 | 状态 |
283|---|---|---|---|
284| CRITICAL | 仓库 REST 授权绕过 → 元数据外泄 + 破坏性写 | ISO 27001 A.9.2、GDPR 机密性 | **未修复** |
285| HIGH | `/user/users/{uid}/profile-detail` 暴露真名/出生日期 | GDPR Art. 5(1)(c) 最小化、Art. 6 | **未修复** |
286| HIGH | 无"被遗忘权"实现;"删除"只撤销会话 | GDPR Art. 17(擦除)、Art. 15/20(访问/可携带) | **未实现**(无导出端点、无级联删除、无 `scheduled_for` 处理器) |
287| HIGH | 同意接受记录可伪造(IDOR) | GDPR Art. 7(可归属同意) | **未修复** |
288| MEDIUM | 邮箱/IP 导出到 OpenObserve 遥测 | GDPR Art. 4(1)、跨数据转移无法律基础/DPIA | **未修复** |
289| MEDIUM | git commit 邮箱可枚举 | 隐私泄露 | **未修复** |
290| MEDIUM/LOW | Webhook secret 明文、deliveries 无限期保留 | ISO 27001 A.8.24 / A.10.1.1 | **未修复** |
291
292**已确认的正向控制(无问题)**:
293- 密码:argon2 + 每用户随机盐;密码策略 + `pwd_version` 轮换使 JWT 失效。
294- access token / recovery code:加盐 SHA-256 落库;recovery code 哈希;仅存后 8 位前缀。
295- 会话行不存原始 IP/UA;`last_login_ip_hash` 未设置 — 良好的最小化。
296- 邮箱验证码哈希落库、限流(5 次尝试、60s 发码冷却)、常量时间比对。
297- 聊天存储层对每条消息/会话操作执行 `require_member`。
298- Avatar/attachment/LFS 对象走带签名/授权路径(presigned URL + TTL)。
299- Cookie:`HttpOnly`、`Secure`、`SameSite=Strict`,域名来自配置。
300- OAuth provider 行只存哈希 token + provider 身份。
301- 支付 crate 大部分 stub;Airwallex 客户端在本代码中不存卡号 PAN(DB 模型仅 last4/exp)。
302
303---
304
305## 10. 完整攻击链
306
307### 链 A — 任意仓库接管(远程 → 全写)— **CRITICAL,live**
3081. 任意注册用户 JWT 认证。
3092. `PUT /repo/{channel}/{repo}/collaborators/{self}` → handler(`lib/repo/handler/collab/add.rs:26-33`)→ `add_collaborator` 中 `require_admin(actor)` 仅 nil 检查,任意认证用户通过(`lib/repo/features/collab/usecase.rs:33-36`,调用于 105 行)。
3103. 攻击者设 `mode=admin` → git `receive-pack` 经 write-collab 升级放行(`lib/repo/permission.rs:36-42`)→ 部署完整代码、转移/删除仓库、翻转分支保护、投毒下游消费者。
311
312### 链 B — 私有仓库内容经 SSH 读取 — **HIGH,live**
3131. 任意注册用户添加 SSH key;exec 时 actor 由 `HyperContext::for_user` 构建(`dvcs/gitssh/handler/command.rs:47-50`)。
3142. `for_user` 授予 `RepoRead` → `effective_access` 不经任何成员/可见性检查返回 `Read`(`lib/repo/permission.rs:35-43`)。
3153. `git clone` 任意私有仓库成功;提交与 push 历史(含内嵌 secret)外泄。(HTTP smart 路径因 gitauth 传空 scopes 不受影响。)
316
317### 链 C — 私有仓库 PR/评论/审阅经 REST 读取 — **HIGH,live**
3181. 处理器认证后把 `actor` 直传无视权限的 usecase:`list_pull_requests`(`lib/repo/features/pull_request/usecase.rs:330`)、`get_pull_request`(383)、`list_pull_request_comments`(840)、`list_pull_request_reviews`(913)、`list_pull_request_events`(939)。`let _ = actor;` — 无成员检查。
3192. 任意认证攻击者可枚举任意仓库(含私有)的 PR body/评论/审阅/事件。
320
321### 附赠链 — 私有仓库元数据预认证可枚举
322`get_repo` 仅对私有仓库挡 nil actor,且成员检查缺失(`lib/repo/features/lifecycle/usecase/get.rs:15-20`);`list_collaborators` 不按 actor 收窄 — 为链 A 供料。
323
324---
325
326## 11. 确认已有的防护措施
327
328- **智能 HTTP**:`MAX_RPC_BODY = 1<<30`;gzip 解压炸弹上限 `GzDecoder::take(cap)` + 长度复查;LFS 流式 `enforce_limit`。
329- **LFS**:OID 固定 64 位 hex 校验;存储路径由已校验 OID + UUID 前缀派生 — 无遍历;`put_object`/`put_object_stream` 都强制 SHA-256 vs OID。
330- **SSH**:exec 命令白名单(upload-pack/receive-pack/upload-archive)+ 仓库路径严格字符集(`[a-z0-9._/-]`、≤512、拒绝 `..`/控制字节);`max_auth_attempts=10`、`auth_rejection_time`、`inactivity_timeout`、`keepalive`;`sanitize_for_log` 应用于 SSH exec 命令日志。
331- **HTTP dumb**:`resolve_within_root` 拒绝 `ParentDir` + `canonicalize` 前缀检查。
332- **JWT**:算法白名单仅 EdDSA/ES256/RS256(无 `none`/HS256 混淆);`kid` → 服务器侧 DB key;`exp/iss/aud` 强制;leeway 受配置封顶。
333- **OAuth**:state 32 字节随机绑定 SameSite=Strict cookie + PKCE S256;callback 验证并一次性消费 state;账户绑定要求 provider **已验证**邮箱;JSON 响应 — 无开放重定向。
334- **密码重置**:32 字节 CSPRNG token;重置 bump `pwd_version`、删除会话、deny-list 用户;forgot 流程响应形状恒定 — 无账户枚举。
335- **邮箱验证码**:5 次尝试上限 + 按邮箱发码限流;1M 空间使暴力不可行。
336- **Refresh token**:DB 会话行 + `pwd_version` 检查;撤销/替换会话(grace 外)拒绝。
337- **对象存储**:avatar/attachment/LFS 全部走带签名授权路径(presigned TTL)。
338- **聊天存储层**:`require_member`/`require_admin` 在消息/会话/pin/comment 仓库层强制执行。
339- **智能 HTTP 端点映射正确**:`info/refs` upload-pack→Read、receive-pack→Write、`None`→Read(dumb 也要求认证);`rpc` 要求 `service.required_access()`。
340
341---
342
343## 12. 修复优先级路线图
344
345**跨层根因**:`lib/repo` Web API 不把任何读写决策送过 `effective_access`,用 nil-actor-only 桩检查(`collab`、`contents` `require_write`),且 git 授权助手有优先级缺陷。**先做以下 5 项**:
346
347### P0 — 立即(本周)
3481. **`effective_access` 优先级修复** — 令牌作用域(及 `collaborator_mode` 升级)授予读权限必须要求 `is_public` 或 actor 为空间成员/列出的协作者。一处改动但杠杆极高:直接消除链 B,约束链 A/C。(对应 C3)
3492. **仓库 REST API 鉴权** — 用 `repo::permission::require(actor, ...)` 替换所有 `require_admin`/`require_write` 桩。关闭链 A/C,消除元数据枚举。(对应 C1/H1/H2)
3503. **修复 gitauth 身份混合** — actor 完全从验证后的身份构建;拒绝 `claims.sub ≠ username`。(对应 C2)
3514. **执行推送路径分支/标签保护** — 安装真实 `pre-receive` hook 拦截受保护 ref 的强制更新/删除。(对应 H6)
3525. **补齐 deny-list 覆盖** — 让 `access_blacklist()` 成为必覆写而非默认 `None`,并给 git/SSH/chat 的 verify 路径加 blacklist + `pwd_version` 检查。(对应 H5)
353
354### P1 — 短期(1-2 周)
3556. SSH 通道缓冲设上限 / 流式喂 git stdin;`max_sessions` 信号量真正拒绝超额连接。(CWE-770,对应 H7)
3567. 全服务 TLS 终止 + 生产 bind 收紧到回环/内网;明文 HTTP 拒绝 Basic。(对应 H10)
3578. Issue 层补 channel/space 成员校验;profile 强制 `can_view` + `show_*` 标志。(对应 H3/H4)
3589. 聊天幂等键加 actor 作用域;Live `end_room` 先鉴权后副作用。(对应 H8/H9)
35910. 签名密钥:启动即加载、周期重载、强制 `expires_at`/`grace`/撤销、按算法选 Key 构造器、实现 `rotate_keys_handler`、给 `private_key_encrypted` 做真实信封加密。(对应 M1/M10)
360
361### P2 — 中期(1 个月)
36211. 限流器改 IP 键控(处理受信代理 `X-Forwarded-For`)+ 每账户指数退避;聊天路由加限流层。(对应 L4/M12)
36312. Webhook SSRF 防护(私有 IP 黑名单 + 禁重定向)+ 加密 secret 列 + delivery 保留策略。(对应 M5)
36413. Redis/Kafka/OTLP TLS + 认证;obs 端点 `http://` 带 token 时启动即拒绝。(对应 M2/M3/M17)
36514. 上传改严格白名单 + finalize 时 magic-bytes 嗅探 + 下载强制 `Content-Disposition: attachment` + `nosniff`。(对应 M13)
36615. 生产 `auto_migrate: false` + 迁移门禁;清理 `cargo machete` 未用依赖;加 `deny.toml` + CI audit 门。(对应 M4)
367
368### P3 — 规划中
36916. 实现 GDPR 擦除(用户 + 级联自有数据)+ `scheduled_for` 处理器 + 数据访问/可携带端点;同意绑定 `claims.sub`。(对应合规 §9)
37017. git 远程 mirror/import SSRF 白名单 + known_hosts/StrictHostKeyChecking + `tempfile` O_EXCL askpass;SSH `prohibit_login` 对齐。(对应 L5/L7)
37118. 修复 Apple OAuth `kid` 读取位置;Argon2 参数化增强;`pow` 配置接线 + 空 secret 启动即失败。(对应 L2/L3/M11)
372
373---
374
375## 13. 交叉验证修订记录
376
377> 2026-08-08 修订:独立多 agent 交叉验证——7 个 review agent 逐条核验所有 HIGH/CRITICAL 发现的 `file:line` 引用与攻击路径(每个均主动搜索报告可能遗漏的缓解控制),3 个对抗性反驳 agent 攻击 C1/C2/C3(持怀疑立场、找不到有效防御即默认反驳),外加独立供应链复核(在线 `cargo audit`/`cargo deny` + Cargo.lock/manifest 逐项比对 + registry 缓存 sha256 抽样)。
378
379### 13.1 逐项验证结论
380
381| # | 严重程度 | 交叉验证结论 | 应用修订 |
382|---|---|---|---|
383| C1 | CRITICAL | **CONFIRMED**(对抗反驳未推翻,high 置信) | — |
384| C2 | CRITICAL | **CONFIRMED**(对抗反驳未推翻,high 置信) | 提权点行号 130-132 → **25-27**(§3) |
385| C3 | CRITICAL | **CONFIRMED**(对抗反驳未推翻,high 置信) | — |
386| H1 | HIGH | CONFIRMED | — |
387| H2 | HIGH | CONFIRMED | — |
388| H3 | HIGH | CONFIRMED | — |
389| H4 | HIGH | CONFIRMED | 行号 20-36 → **23-30**;fetch_profile 110-123 → **124-137**(§4) |
390| H5 | HIGH | **PARTIALLY CONFIRMED** | `lib/project` 已正确接线、从受害面移除;缺口=repo/issues(HTTP)+git HTTP/SSH+chat WS;严重度建议 MEDIUM–HIGH(§2/§4) |
391| H6 | HIGH | CONFIRMED | — |
392| H7 | HIGH | CONFIRMED | 补充 `max_sessions` 信号量为死代码、缓冲在 exec 前、仅需注册 key 无需仓库权限(§4) |
393| H8 | HIGH | CONFIRMED((a) 视 `client_msg_id` 可预测性可至 Medium–High;(b) 无条件) | 补严重度说明(§4) |
394| H9 | HIGH | CONFIRMED(瞬时可用性 DoS,可至 Medium–High) | 补严重度说明(§4) |
395| H10 | HIGH | CONFIRMED | — |
396| 供应链 | — | 总体 CONFIRMED | rsa 可达性 #2/#3 对调(rsa 0.9.10 在 JWT 认证路径);rkyv 修复版 ≥0.8.17(需 major);machete 80 处/29 crate(§1/§7) |
397
398### 13.2 未被推翻的关键结论
399
400- **CRITICAL ×3 全部经独立对抗性复核成立**:`permission::require` 在 `lib/repo/features` 零调用(唯一调用点 `dvcs/gitauth/permission.rs:98`)、SSH 作用域短路(`for_user` 无条件授予 `RepoRead`)、gitauth JWT-as-password 身份混合(actor 以攻击者 `user_id` 配他人 `is_site_admin`)均获源码级确认;3 条攻击链(任意仓库删除/接管、SSH 读全库私有仓库、git Basic 提权为站点管理员)的枚举前提(`GET /repos` 泄漏 `channel_id`+`id`)亦复现。
401- **供应链卫生结论成立**:`unsafe` 全库 0 处(1367 个 `.rs` 文件)、零 git 源依赖、零 `[patch]`/`[source]`/`replace-with` 重定向、无 `build.rs`;精确固定(`russh=0.62.2`/`ssh-key=0.7.0-rc.11`/OTLP)经 russh 上游 manifest 核实为生态约束;无后门/C2/混淆结论成立。
402- **CVE 清单在线复核一致**:RUSTSEC-2026-0235(rkyv 0.7.46)、RUSTSEC-2023-0071(rsa 0.9.10 + 0.10.0-rc.18)、RUSTSEC-2024-0436(paste 1.0.15,informational);`cargo deny check advisories` FAILED 复现。
403- **未发现报告漏报的有效缓解**:13 项发现均未在调用链/中间件/配置中找到能阻断攻击的防护;H8 声称的「存储层 `require_member` 严格」属实但只作用于 DB 持久化,不覆盖 Redis 幂等键与 lookup 路径。
404
405---
406
407*报告由 8 个专职安全 agent 并行审计 + 主会话源码复核 + `cargo audit`/`cargo deny` 依赖审计综合生成,并经独立多 agent 交叉验证修订(2026-08-08)。所有 `file:line` 引用均指向本仓库当前源码。*