x-api-rs 全项目问题审计(真实查验版)¶
| 项 | 内容 |
|---|---|
| 查验日期 | 2026-07-09 |
| 工作区 | /Users/zhangpengqiang/code/Rust/x-api-rs,分支 master |
| 方法 | 每条结论都执行过下面「查验命令 / 读库源码」;未跑通的写「未实测」 |
| workspace 版本 | Cargo.toml [workspace.package] version = "3.0.10" |
| 锁定 HTTP 栈 | Cargo.lock:wreq 6.0.0-rc.29、wreq-util 3.0.0-rc.13 |
| 默认 Chrome 基线 | src/core/src/common/fingerprint/data/chrome.rs:29 → CHROME_MAJOR = 149 |
证据等级
| 标记 | 含义 |
|---|---|
CODE |
仓库源码 / 配置文件可直接读到 |
LIB |
本地 ~/.cargo/registry 中 已锁定版本 的依赖源码 |
CMD |
本机执行命令的输出 |
UNIT |
cargo test 实际通过 |
未实测 |
未做外网 / MITM / 真实账号验证;不得写成已证实行为 |
0. 本次实际执行的查验(可复现)¶
以下命令均在 2026-07-09 于本机仓库根目录执行过:
# 基线版本
rg -n "pub const CHROME_MAJOR" src/core/src/common/fingerprint/data/chrome.rs
# → 29:pub const CHROME_MAJOR: u32 = 149;
# TLS 调用点(src + examples)
rg -n "tls_cert_verification" src examples --type rust
# → src 4 处均为 false;examples/rust/debug/inspect_headers.rs 1 处 false
# wreq 库语义(锁定版本源码,非臆测)
WREQ=~/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/wreq-6.0.0-rc.29
sed -n '300,310p' "$WREQ/src/client.rs" # 默认 tls_cert_verification: true
sed -n '1405,1419p' "$WREQ/src/client.rs" # 文档:Defaults to true;false 信任无效证书
# GraphQL
rg -n 'https://[^"]+/graphql/[A-Za-z0-9_-]+/' src/core/src --type rust | wc -l # 35
# 其中不同 queryId:32
# ignore
rg -n "^\s*#\[ignore\]" tests/rust --type rust | wc -l # 113 条属性
# CI / 分支
rg -n "branches:" -A2 .github/workflows/workspace-check.yml # - main
git remote show origin | head -5 # HEAD branch: master
git branch --show-current # master
# as_any 调用方
rg -n "\.as_any\(" src tests --type rust # 无匹配(exit 1)
# 派生确定性单测
cargo test -p x-api-core --lib test_derive_chrome_for_account -- --nocapture
# → 3 passed
# seed 算法复现(auth_token vs user_id 是否可能映射不同 OS)
# Python 复现 profile.rs 的 FNV-1a + ACCOUNT_PROFILE_WEIGHTS
# → 随机 10000 对 token/uid 中约 45.64% OS 不同
明确未做
- 未对生产 TLS 做 MITM 抓包证明「可劫持」
- 未在本机无凭据环境实际跑
pytest tests/python/看 CI 绿/红 - 未对 32 个 GraphQL queryId 逐个打真实 X 接口验证是否仍有效
1. 上版错误结论(已用代码否定)¶
| 旧说法 | 查验结果 |
|---|---|
user 仍用 as_any downcast |
rg '\.as_any\(' src tests 无调用方;仅 trait 定义 + RquestClient 实现(client.rs:112、:666) |
| feature「几乎无门控」 | web/api/mod.rs 各模块均有 #[cfg(feature = "...")];空 feature dm = [] 仍用于 cfg |
| 默认 Chrome 仍是 136 | 运行时 ClientProfile::default → CHROME_MAJOR = 149(profile.rs:109-116) |
2. TLS 证书校验(LIB + CODE,语义已按库源码纠正)¶
2.1 wreq API 真实含义(LIB)¶
锁定源码:wreq-6.0.0-rc.29/src/client.rs
| 证据点 | 内容 |
|---|---|
| 默认值 | ClientBuilder 初始化 tls_cert_verification: **true**(约 306 行) |
| 方法文档 | Controls the use of certificate validation. **Defaults totrue.**(约 1405–1413 行) |
| 关闭校验的警告 | 若信任无效证书,则 any certificate for any site 可用,含过期证书(同段 Warning) |
| 写入路径 | tls_cert_verification(bool) → config.tls_cert_verification → 连接器 .cert_verification(...)(约 523、1416–1418 行) |
语义(必须按参数名读,不能按中文注释反推)
| 调用 | 含义 |
|---|---|
| 不调用 / 库默认 | 开启 证书校验 |
.tls_cert_verification(true) |
开启 证书校验 |
.tls_cert_verification(false) |
关闭 证书校验(接受无效 / 过期 / 任意站点证书) |
纠正:此前若把「
false默认」理解成「库默认关闭」是错的。
库默认是true(校验);本项目在构建客户端时显式传入false(关闭校验)。
项目注释// 接受无效证书(client.rs:396)描述的是false的效果,与 wreq Warning 一致,不是说「默认值是 false」。
2.2 本仓库调用点(CODE + CMD)¶
rg tls_cert_verification src --type rust:
| 文件 | 行 | 代码 |
|---|---|---|
src/core/src/common/client.rs |
396 | .tls_cert_verification(false); // 接受无效证书(RquestClient::build_client) |
src/core/src/common/auth.rs |
279 | .tls_cert_verification(false);(build_auth_client) |
src/core/src/common/transaction/client.rs |
722 | .tls_cert_verification(false)(transaction 引导 HTTP) |
src/core/src/web/xchat/juicebox.rs |
162 | .tls_cert_verification(false) |
另:examples/rust/debug/inspect_headers.rs:26 也是 false(示例,非库默认路径)。
2.3 是否有运行时开关(CODE)¶
RquestClientBuilder 字段(client.rs:162-181)仅有:
auth/transaction/proxy_url/profile/http2_max_retry_count
无 tls_cert_verification 或类似字段;build_client 写死 false。
事实(CODE+LIB):主 API 客户端、auth 引导、transaction 引导、Juicebox 客户端均在代码层关闭证书校验。
影响(由 LIB Warning 直接推出,非运营推断):关闭校验后,无效证书会被信任 → 存在 MITM 面。
未实测:未用自签证书对真实 x.com 连接做握手验证。
3. 环境快照与敏感材料(CODE)¶
3.1 结构含明文凭据¶
src/core/src/web/environment.rs:
- 文件头 1–5 行:写明「明文敏感数据,调用方必须自行加密存储」
WebEnvironmentAuthSnapshot(约 42–47 行):cookies/csrf_token/auth_token/twid_user_idWebEnvironmentSnapshot::new(约 88–91 行):从AuthProvider填入上述字段
3.2 导出路径¶
- Rust:
WebClient::export_environment(web/client.rs:293-304)→ 含base_headers: twitter_headers_snapshot() - Python:
export_environment/export_environment_json(src/py/src/web/client.rs:114+)无明文门闩参数 - CLI:
environment export要求--allow-plaintext-secrets(src/cli/src/cli/commands/environment.rs:32-34)
事实:序列化 JSON 含完整 cookies/token;SDK 层不加密;仅 CLI 有显式确认标志。
3.3 restore_environment 不消费 headers.*¶
WebClient::restore_environment(web/client.rs:245-290)实际使用:
snapshot.auth.*与 cookies 一致性校验WebProfile::from_canonical_spec(&snapshot.profile.selector)ClientTransaction::from_snapshot(snapshot.transaction)http2_max_retry_count、proxy_url- (xchat feature)
attach_xchat_snapshot
对 snapshot.headers 的全文检索:
headers.base/post_user_agent_override仅在 export 写入(environment.rs:104-105)- restore 零读取
WEB_POST_USER_AGENT 注释(common/client.rs:59)写明仅为 schema 兼容保留。
事实:导出的 headers.base 与 post_user_agent_override 对还原路径无功能作用;还原后请求头由 profile + 当前 build_twitter_headers 生成。
4. 仓库元数据与文件入仓(CMD)¶
4.1 remote / repository 字段不一致¶
$ git remote -v
origin https://github.com/open-luban/x-api-rs.git (fetch)
origin ssh://[email protected]:2224/hgghghg097/x-api-rs.git (push)
origin https://github.com/open-luban/x-api-rs.git (push)
$ git remote show origin | rg "HEAD branch"
HEAD branch: master
$ rg repository Cargo.toml
repository = "https://github.com/Robin528919/x-api-rs"
$ rg "Repository|repo_url" pyproject.toml mkdocs.yml
pyproject.toml: Repository = "https://github.com/Robin528919/x-api-rs.git"
mkdocs.yml: repo_url: https://github.com/Robin528919/x-api-rs
事实:fetch/push 主仓为 open-luban;Cargo/pyproject/mkdocs 仍写 Robin528919。
4.2 根目录 C# 文件被 git 追踪¶
4.3 fixtures¶
$ git check-ignore -v tests/fixtures/accounts.txt
tests/fixtures/.gitignore:2:accounts.txt
$ git ls-files tests/fixtures/
# 含 example、媒体、以及已追踪的 dm_disabled_xchat_users.txt(用户 ID 列表,非 cookies)
事实:accounts.txt 等设计为本地 ignore;仓库内无 accounts 真凭据文件。
5. 指纹与客户端创建(CODE + UNIT)¶
5.1 默认 profile¶
// profile.rs:109-116
ClientProfile::Chrome {
os: OsType::Windows,
arch: Architecture::X64,
version: chrome::CHROME_MAJOR, // 149
}
与 wreq-util 3.0.0-rc.13:Emulation::Chrome149 存在(emulate.rs);项目 chrome_emulation_profile(149) => Emulation::Chrome149(profile.rs:224)。
5.2 WebClient::create 与 Transaction 共用 profile¶
web/client.rs:214-238:
profile = 显式 | derive_chrome_for_account(user_id 或 cookies)ClientTransaction::create(..., Some(profile))RquestClientBuilder::profile(profile)
事实:create 路径上 transaction 与主客户端同一 WebProfile。
5.3 测试 helper 与 create 不一致¶
tests/rust/common/mod.rs:222-234:
ClientTransaction::create(Some(&cookies_map), proxy_url.as_deref(), None)
// builder 未 .profile(...)
WebClient::with_client(...)
ClientTransaction::create 在 profile: None 时:unwrap_or_default()(transaction/client.rs:121)→ 固定 Windows x64 149,不调用 derive_chrome_for_account。
事实:测试 helper ≠ WebClient::create(None profile) 的生产派生路径。
5.4 auth_token seed vs create seed¶
| 路径 | seed | 证据 |
|---|---|---|
auth_token_to_cookies |
auth_token 字符串 |
auth.rs:233-241 + 注释 233–235 行已写明 |
WebClient::create 默认 |
有 twid 时用 user_id,否则 cookies |
web/client.rs:218-225 |
derive_chrome_for_account:FNV-1a + 权重表只改 OS/arch,版本恒为 CHROME_MAJOR(profile.rs:148-165)。
UNIT:test_derive_chrome_for_account_* 3 个测试通过。
算法复现(CMD):同一「账号」若一边用 token、一边用 user_id 作 seed,约 45% 映射到不同 OS(10000 对随机样本)。
未实测:未用真实 auth_token + 同一账号 cookies 做端到端指纹对比抓包。
5.5 Referer 写入点¶
| 位置 | 值 |
|---|---|
common/client.rs:455 |
https://x.com/home |
web/api/upload/client.rs:235 |
https://x.com/ |
web/api/user/client.rs:1283 |
https://x.com/ |
事实:无按业务路径动态切换 Referer 的参数。
5.6 GraphQL 端点硬编码¶
src/core/src 内 https://.../graphql/<id>/...:35 行,32 个不同 queryId(CMD)。
分散于 posts/user/search/settings/dm/xchat/inbox/communities 等。
事实:仓库内无「从 main.js 运行时拉取 queryId」的代码路径。
未实测:各 queryId 是否仍被 X 接受。
6. 文档 / 源码注释与代码不一致(CODE)¶
6.1 用户文档 Chrome 版本¶
| 来源 | 写的是 | 代码 |
|---|---|---|
docs/api/configuration.md:92,97,105,123,147 |
Chrome 136 | CHROME_MAJOR=149 |
docs/api/configuration.md:167,191 |
Chrome 149 | 一致 |
docs/api/README.md:109 |
默认 Chrome 136 | 不一致 |
事实:configuration.md 内 136 与 149 并存。
6.2 源码注释仍写 Chrome 136(本次查验新发现)¶
src/core/src/common/client.rs:
| 行 | 内容 |
|---|---|
| 154 | 示例注释「默认 Chrome 136」 |
| 172 | 字段注释「默认 Chrome 136」 |
| 217, 222 | profile() 文档「Chrome 136 TLS / default 136」 |
| 525 | 注释「guest token 请求使用默认 Chrome 136」 |
事实:注释/文档字符串与 ClientProfile::default()(149)冲突;运行时以代码常量为准。
6.3 仓库 URL¶
与 §4.1 相同:mkdocs.yml / pyproject.toml / Cargo.toml 写 Robin528919。
7. 测试与 CI(CMD + CODE)¶
7.1 workspace-check.yml¶
远端 HEAD branch: master(git remote show origin)。
事实:对 master 的 push 不会触发该 workflow 的 push 事件;PR(paths 匹配时)与 workflow_dispatch 仍可触发。
同文件:
cargo test -p x-api-core --all-features --no-run # 只编译
cargo test -p x-api-cli --all-features # 默认不含 ignored
pytest tests/python/ -v
7.2 Python 集成测试收集行为¶
pyproject.toml:testpaths = ["tests/python"];无addopts = -m "not integration"tests/python/conftest.py:cookiesfixture 无凭据时pytest.skip(约 71 行)@pytest.mark.integration在tests/python中约 141 处(CMD 计数)
事实(CODE):CI 会收集 integration 测试;无凭据时应 skip 而非必然 fail(取决于是否依赖 cookies fixture)。
未实测:本审计未在干净 CI 环境实际跑 pytest 统计 skip 数。
7.3 Rust ignored 集成¶
tests/rust 中 ^\s*#\[ignore\]:113 条(另有 1 处注释里的 /// #[ignore],合计 rg 全文 114 行)。
事实:默认 cargo test / CI cli 测试 不执行 这些 ignored 用例。
7.4 python_tests/¶
有效测试在 tests/python/。
8. Feature 与依赖(CODE)¶
8.1 Feature¶
src/core/Cargo.toml:
default = ["dm", "upload", "inbox", "user", "posts", "communities", "settings", "search", "xchat"]- 多数业务 feature 为
[];xchat拉p256/aes-gcm/juicebox_sdk等
模块级 #[cfg] 见 web/api/mod.rs。
事实:空 feature 仍门控编译;默认构建几乎全开。
8.2 预发布 / git 依赖¶
wreq = { version = "6.0.0-rc", ... } # lock: 6.0.0-rc.29
wreq-util = "3.0.0-rc" # lock: 3.0.0-rc.13
juicebox_sdk = { git = "https://github.com/open-luban/juicebox-sdk", tag = "0.3.4-luban", optional = true }
9. 明确不列为代码缺陷¶
| 项 | 原因 |
|---|---|
| 账号被平台封禁 | 非仓库可证伪的代码 bug |
| HTTP/2 头字节序与 Chrome 不完全相同 | 协议层 HPACK;代码只控集合与语义;未做字节级抓包对比 |
| CHANGELOG 历史路径 / 历史默认 136 | 历史记录;当前运行时以源码为准 |
| fixtures 示例图 / 已追踪 user id 列表 | 非 cookies/token |
10. 按优先级的问题清单(仅保留有证据项)¶
P0¶
- TLS 校验在 4 处业务路径显式关闭(§2;LIB 默认 true,项目传 false)
- 环境快照明文导出(§3.1–3.2;CLI 有门闩,Rust/Python 库导出无加密)
- repository / remote 不一致(§4.1)
- 根目录
TwitterMessageApi.cs入仓(§4.2)
P1¶
- 文档 +
client.rs注释仍写默认 Chrome 136,代码为 149(§6.1–6.2) - CI push 监听
main,远端默认分支master(§7.1) - GraphQL queryId 硬编码 35 行 / 32 id(§5.6)
- 测试 helper 不按账号派生 profile(§5.3)
- auth_token seed 与 create seed 不同,OS 可能分叉(§5.4;算法约 45% 不同)
- restore 忽略
headers.base/post_user_agent_override(§3.3)
P2¶
- Referer 固定策略(§5.5)
python_tests/仅残留 pyc(§7.4)- ignored 集成不进默认 CI(§7.3)
- wreq rc + juicebox git 依赖(§8.2)
11. 建议动作(仅对应已证实项)¶
| 动作 | 对应证据 |
|---|---|
TLS:改为默认 开启 校验(对齐 wreq 默认 true),另加显式 opt-out |
§2 LIB+CODE |
| 统一 Cargo/pyproject/mkdocs 与真实 remote 的仓库 URL | §4.1 |
全库替换文档与 源码注释 中「默认 Chrome 136」→ 当前 CHROME_MAJOR |
§6.1–6.2 |
workspace-check.yml push.branches 加入 master(或改默认分支) |
§7.1 |
测试 helper 对齐 WebClient::create 的 profile 解析 |
§5.3 |
文档写明 headers.* 不参与 restore(或 restore 时消费/去掉字段) |
§3.3 |
| queryId 集中常量 + 标注抓包日期 | §5.6 |
12. 修订记录¶
| 日期 | 说明 |
|---|---|
| 2026-07-09 | 初版(含部分未逐行核验的推断) |
| 2026-07-09 | 实证修订:纠正 as_any / feature 误判;补文件行号 |
| 2026-07-09 | 真实查验修订:对照 wreq-6.0.0-rc.29 源码写清 TLS 参数语义(库默认 true、项目显式 false);补执行命令清单与证据等级;修正 ignore 计数(113 属性);新增 client.rs 注释仍写 136、headers.post_user_agent_override 不消费、seed 分叉约 45% 算法复现;标明未做 MITM/pytest CI 实测项 |
13. 修复状态(2026-07-09 实施)¶
| 项 | 状态 |
|---|---|
| TLS 默认开启 + builder opt-out + 快照字段 | ✅ |
| repository URL 统一 open-luban | ✅ |
| 移除 TwitterMessageApi.cs | ✅ |
| 文档/注释 Chrome 136→149 | ✅ |
| CI push 监听 master | ✅ |
| 测试 helper → WebClient::create | ✅ |
| auth_token 引导用 default profile | ✅ |
| headers.* 不参与 restore 文档化 | ✅ |
| GraphQL 常量集中 graphql_endpoints | ✅ |
| 删除 python_tests 残留 | ✅ |
| Referer 固定策略 | 未改(产品策略,非缺陷必修) |
| ignored 集成进默认 CI | 未改(需真实凭据,保持 ignore) |
| wreq rc / juicebox git | 未改(依赖策略) |
| 环境快照库层加密 | 未改(仍由调用方加密;CLI 有明文门闩) |