跳转至

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.lockwreq 6.0.0-rc.29wreq-util 3.0.0-rc.13
默认 Chrome 基线 src/core/src/common/fingerprint/data/chrome.rs:29CHROME_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::defaultCHROME_MAJOR = 149profile.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_id
  • WebEnvironmentSnapshot::new(约 88–91 行):从 AuthProvider 填入上述字段

3.2 导出路径

  • Rust:WebClient::export_environmentweb/client.rs:293-304)→ 含 base_headers: twitter_headers_snapshot()
  • Python:export_environment / export_environment_jsonsrc/py/src/web/client.rs:114+明文门闩参数
  • CLI:environment export 要求 --allow-plaintext-secretssrc/cli/src/cli/commands/environment.rs:32-34

事实:序列化 JSON 含完整 cookies/token;SDK 层不加密;仅 CLI 有显式确认标志。

3.3 restore_environment 不消费 headers.*

WebClient::restore_environmentweb/client.rs:245-290)实际使用:

  • snapshot.auth.* 与 cookies 一致性校验
  • WebProfile::from_canonical_spec(&snapshot.profile.selector)
  • ClientTransaction::from_snapshot(snapshot.transaction)
  • http2_max_retry_countproxy_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.basepost_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://git@gitlab.aiaipool.com: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 追踪

$ git ls-files TwitterMessageApi.cs
TwitterMessageApi.cs

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.13Emulation::Chrome149 存在(emulate.rs);项目 chrome_emulation_profile(149) => Emulation::Chrome149profile.rs:224)。

5.2 WebClient::create 与 Transaction 共用 profile

web/client.rs:214-238

  1. profile = 显式 | derive_chrome_for_account(user_id 或 cookies)
  2. ClientTransaction::create(..., Some(profile))
  3. 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::createprofile: 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_MAJORprofile.rs:148-165)。

UNITtest_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/srchttps://.../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

on:
  pull_request: { paths: [...] }
  push:
    branches:
      - main
  workflow_dispatch:

远端 HEAD branch: mastergit remote show origin)。

事实:对 masterpush 不会触发该 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.tomltestpaths = ["tests/python"] addopts = -m "not integration"
  • tests/python/conftest.pycookies fixture 无凭据时 pytest.skip(约 71 行)
  • @pytest.mark.integrationtests/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/

find python_tests -type f
# 仅 __pycache__/*.pyc,无 .py 源

有效测试在 tests/python/


8. Feature 与依赖(CODE)

8.1 Feature

src/core/Cargo.toml

  • default = ["dm", "upload", "inbox", "user", "posts", "communities", "settings", "search", "xchat"]
  • 多数业务 feature 为 []xchatp256/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

  1. TLS 校验在 4 处业务路径显式关闭(§2;LIB 默认 true,项目传 false)
  2. 环境快照明文导出(§3.1–3.2;CLI 有门闩,Rust/Python 库导出无加密)
  3. repository / remote 不一致(§4.1)
  4. 根目录 TwitterMessageApi.cs 入仓(§4.2)

P1

  1. 文档 + client.rs 注释仍写默认 Chrome 136,代码为 149(§6.1–6.2)
  2. CI push 监听 main,远端默认分支 master(§7.1)
  3. GraphQL queryId 硬编码 35 行 / 32 id(§5.6)
  4. 测试 helper 不按账号派生 profile(§5.3)
  5. auth_token seed 与 create seed 不同,OS 可能分叉(§5.4;算法约 45% 不同)
  6. restore 忽略 headers.base / post_user_agent_override(§3.3)

P2

  1. Referer 固定策略(§5.5)
  2. python_tests/ 仅残留 pyc(§7.4)
  3. ignored 集成不进默认 CI(§7.3)
  4. 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 有明文门闩)