587 lines
18 KiB
Markdown
587 lines
18 KiB
Markdown
# 最终规范项目交付
|
||
|
||
|
||
|
||
本文件只在 fixtures 多样本通过,并准备最终交付时读取。这里的“一体化”不是指整个项目只能有一个文件,而是指:**最终项目只有一个可直接执行的入口文件;执行该入口后,自动完成生成加密参数、发送 Node.js / Python 模拟请求,并输出请求成功/失败结果**。
|
||
|
||
|
||
|
||
## 硬性要求
|
||
|
||
|
||
|
||
1. **最终项目必须干净**
|
||
|
||
最终交付目录不得包含临时文件、测试文件、trace、HAR、hook、指纹采样 Hook、截图、缓存、浏览器 Profile、失败响应、调试日志或空目录。可以保留必要源码模块、配置模板和依赖清单。
|
||
|
||
|
||
|
||
2. **最终项目必须是规范目录结构**
|
||
|
||
允许把补环境、目标入口、请求客户端、配置、工具函数拆成模块,避免把所有代码硬塞进一个超大文件。推荐目录见下文。
|
||
|
||
|
||
|
||
3. **最终项目只能有一个执行入口**
|
||
|
||
默认入口为:
|
||
|
||
|
||
|
||
```text
|
||
|
||
case/result/final.js
|
||
|
||
```
|
||
|
||
|
||
|
||
如果用户明确选择 Python 请求客户端,可以使用:
|
||
|
||
|
||
|
||
```text
|
||
|
||
case/result/final.py
|
||
|
||
```
|
||
|
||
|
||
|
||
入口执行后必须完成:
|
||
|
||
|
||
|
||
```text
|
||
|
||
安装/加载补环境 → 调用目标 JS 入口生成加密参数 → 组装请求 → 用 Node.js / Python TLS 指纹兼容 Session 客户端发送模拟请求 → 输出验证结果 → 销毁 session
|
||
|
||
```
|
||
|
||
|
||
|
||
其他文件只能作为被入口调用的模块,不得再提供 `server.js`、`bridge.py`、`runner.js`、`sign.js`、`test.js` 等第二入口或测试入口。
|
||
|
||
|
||
|
||
4. **最终项目不能有自动化操作代码**
|
||
|
||
最终项目内任何源码文件不得出现:
|
||
|
||
|
||
|
||
- ruyiPage / RuyiTrace 启动或控制代码。
|
||
|
||
- Playwright / Puppeteer / Selenium / CloakBrowser / browser-use。
|
||
|
||
- CDP / WebDriver / Marionette 控制代码。
|
||
|
||
- `page.goto`、`browser.launch`、`chromium.launch`、`FirefoxPage`、`page.capture` 等自动化取证调用。
|
||
|
||
|
||
|
||
ruyiPage / RuyiTrace / CloakBrowser / Playwright / Puppeteer 只能用于前置取证、环境日志采集和指纹采样;不能进入最终项目代码。Canvas / WebGL / WebGPU / Audio / 字体 / DOM 几何指纹应由 Node.js 终端 API 值回放实现,不得通过自动化浏览器实时生成。
|
||
|
||
|
||
|
||
5. **最终加密参数必须由补环境生成,禁止复用样本值**
|
||
|
||
cURL / HAR / fixture 中已有的 sign、token、a_bogus、h5st、x-s、x-t、mtgsig、w_rid 等值只能作为浏览器真实样本和 expected fixture。最终项目不得把这些值硬编码到 `final.js`、请求模块、配置文件或 signer 模块中。入口必须调用补环境后的目标 JS 入口 / signer 重新生成加密参数,再组装请求。
|
||
|
||
|
||
|
||
禁止:
|
||
|
||
|
||
|
||
```javascript
|
||
|
||
const sign = 'curl 里复制出来的样本值';
|
||
|
||
headers['x-s'] = '样本 x-s';
|
||
|
||
url.searchParams.set('a_bogus', '样本 a_bogus');
|
||
|
||
```
|
||
|
||
|
||
|
||
推荐:
|
||
|
||
|
||
|
||
```javascript
|
||
|
||
const params = await signer.generate(requestInput);
|
||
|
||
headers['x-s'] = params['x-s'];
|
||
|
||
url.searchParams.set('a_bogus', params.a_bogus);
|
||
|
||
```
|
||
|
||
|
||
|
||
6. **最终请求必须由前置阶段已确认的 Node.js 或 Python Session 客户端完成**
|
||
|
||
最终验证流程必须是:
|
||
|
||
|
||
|
||
```text
|
||
|
||
创建 session → 生成加密参数 → 用已确认的 TLS 指纹兼容客户端在同一 session 中组装请求 → 发起少量授权验证请求 → 销毁 session
|
||
|
||
```
|
||
|
||
|
||
|
||
可选客户端为 Node.js CycleTLS / impers / curl-cffi-node,或 Python curl_cffi / cffi_curl / cyCronet;如果用户选择“不发真实请求”,入口只输出本地 sign / 参数和组装后的脱敏请求信息。即使只有一个目标 API,也必须使用 Session 模式,动态资源刷新、Cookie / challenge 生成链路和目标 API 复用同一 Cookie jar / Header / UA / Client Hints / TLS 指纹 / fingerprint baseline。
|
||
|
||
|
||
|
||
禁止:
|
||
|
||
|
||
|
||
```text
|
||
|
||
生成加密参数 → 再通过浏览器自动化打开页面 / 点击 / 抓包验证
|
||
|
||
```
|
||
|
||
|
||
|
||
|
||
|
||
7. **项目完成后默认生成最终总结**
|
||
|
||
最终交付前必须生成:
|
||
|
||
|
||
|
||
```text
|
||
|
||
case/result/最终项目总结.md
|
||
|
||
```
|
||
|
||
|
||
|
||
总结必须读取 `references/final-project-summary.md` 的模板要求,使用 `scripts/write_markdown_utf8.js` 以 UTF-8 写入,并包含 native addon / NativeProtect 使用情况、指纹基线一致性、最终请求 Session 请求链、加密参数生成与样本复用检查、最终交付结构、测试结果和清理结果。只有用户明确要求“不生成最终总结”时才可跳过;跳过时要在阶段输出中记录原因,并在 `check_final_artifact.js` 中显式使用 `--no-require-final-summary`。
|
||
|
||
|
||
|
||
|
||
8. **最终代码必须简洁可读并带中文注释**
|
||
最终补环境代码必须按职责拆分模块,禁止压缩、堆叠、过长函数、过深嵌套和无意义命名。所有手写源码必须有文件头中文职责注释,关键 WebAPI、getter / setter、addon-first、fallback、指纹回放和加密入口必须有中文说明。中文注释必须 UTF-8 正常显示,不得包含问号、连续问号或乱码。原始目标 bundle 如必须保留,应放在 `src/target/original/` 等独立目录,不得和手写补环境代码混在一起。
|
||
|
||
9. **动态 HTML / JS 必须运行时刷新**
|
||
|
||
如果 `case/notes/resource-manifest.json` 中存在 `dynamic: true` 且 `requiredForFinal: true` 的资源,最终项目必须包含运行时刷新模块,例如 `src/resources/fetch-runtime-resources.js`。`final.js` / `final.py` 执行顺序必须是:创建请求 session → 刷新当前 HTML / JS / challenge / seed → 更新同一 session 的 Cookie / Storage / runtime context → 加载当前资源运行 signer → 使用已确认 TLS 指纹兼容客户端在同一 session 发送最终请求 → 销毁 session。
|
||
|
||
不得把 `case/js/snapshots/`、403 / challenge 页面、动态 HTML、动态 chunk 或旧 seed 固定复制到 `result/` 作为 signer 主路径;这些快照只能用于分析、fixture 对比和历史证据。交付前必须运行:
|
||
|
||
```bash
|
||
node scripts/check_dynamic_resources.js --case-dir case --require-runtime-refresh --markdown
|
||
```
|
||
|
||
|
||
10. **验证码接口的轨迹入口必须可替换**
|
||
|
||
如果目标是验证码 / 风控验证接口,并且加密参数依赖点击、鼠标移动、拖动或触摸事件,最终项目允许保留旧轨迹 fixture 作为参数生成输入,但必须暴露可替换入口,例如 `motionTrack`、`eventFixture`、`verifyContext`、`clickPoints` 或 `dragPath`,并使用 UTF-8 中文注释说明“当前旧轨迹只用于补环境生成加密参数,不保证最终验证通过,后续识别和真实轨迹生成交给 web-verify-patcher”。
|
||
|
||
不得把旧轨迹硬编码在 signer 内部,不得把验证码最终通过率写成 web-js-env-patcher 的交付结论。调用 `web-verify-patcher` 前必须先运行 `check_web_verify_patcher.js --require`。
|
||
|
||
|
||
11. **必须交付前检查**
|
||
|
||
交付前运行:
|
||
|
||
|
||
|
||
```bash
|
||
|
||
node scripts/write_markdown_utf8.js --input case/tmp/最终项目总结草稿.md --out case/result/最终项目总结.md --require-chinese-name --markdown
|
||
|
||
node scripts/check_final_artifact.js --case-dir case --markdown
|
||
|
||
node scripts/clean_case.js --case-dir case --dry-run --json
|
||
|
||
```
|
||
|
||
|
||
|
||
若检查失败,先修复和清理,再交付。
|
||
|
||
|
||
|
||
## 推荐 Node.js 最终目录
|
||
|
||
|
||
|
||
```text
|
||
|
||
case/result/
|
||
|
||
├── final.js # 唯一执行入口:node final.js
|
||
|
||
├── 最终项目总结.md # 项目完成后默认生成的 UTF-8 中文命名最终总结
|
||
|
||
├── package.json # 可选,仅列运行依赖和 start 脚本
|
||
|
||
├── config.example.json # 可选,脱敏配置模板;真实敏感值由用户本地填写
|
||
|
||
└── src/
|
||
|
||
├── env/
|
||
|
||
│ ├── install-env.js # 安装补环境
|
||
|
||
│ ├── native-protect.js
|
||
|
||
│ └── fingerprint-env.js # 可选:指纹终端 API 值回放
|
||
|
||
├── target/
|
||
|
||
│ ├── target.js # 目标 JS 片段或入口包装
|
||
|
||
│ └── entry.js # 暴露 makeEncryptedParams
|
||
|
||
├── request/
|
||
|
||
│ └── client.js # Node.js CycleTLS / impers 请求,或用户确认不发真实请求
|
||
|
||
├── resources/
|
||
|
||
│ └── fetch-runtime-resources.js # 可选:动态 HTML / JS / challenge 运行时刷新
|
||
|
||
└── utils/
|
||
|
||
└── normalize.js
|
||
|
||
```
|
||
|
||
|
||
|
||
要求:
|
||
|
||
|
||
|
||
- `final.js` 是唯一可以直接运行的入口。
|
||
|
||
- `package.json` 的 `scripts.start` 只能指向 `node final.js`。
|
||
|
||
- `src/` 中模块不能直接启动浏览器、启动服务或发起额外批量请求。
|
||
|
||
- 不交付 `test/`、`tests/`、`__tests__/`、`tmp/`、`logs/`、`hooks/`、`screenshots/`、`ruyi-trace/`、`browser-profile/`。
|
||
|
||
|
||
|
||
## 推荐 Python 最终目录
|
||
|
||
|
||
|
||
仅当用户明确选择 Python 请求客户端时使用:
|
||
|
||
|
||
|
||
```text
|
||
|
||
case/result/
|
||
|
||
├── final.py # 唯一执行入口:python final.py
|
||
|
||
├── 最终项目总结.md # 项目完成后默认生成的 UTF-8 中文命名最终总结
|
||
|
||
├── requirements.txt # 可选,仅列运行依赖
|
||
|
||
├── config.example.json # 可选,脱敏配置模板
|
||
|
||
└── src/
|
||
|
||
├── request_client.py # curl_cffi / cffi_curl / cyCronet,或用户确认不发真实请求
|
||
|
||
├── signer.py # 参数生成入口;仅在逻辑已可靠迁移时使用
|
||
|
||
└── normalize.py
|
||
|
||
```
|
||
|
||
|
||
|
||
如果目标 JS 必须在 Node.js 补环境里执行,优先交付 Node.js 项目,不要用 Python 调浏览器自动化来完成签名。
|
||
|
||
|
||
|
||
## `final.js` 入口职责
|
||
|
||
|
||
|
||
`final.js` 不需要包含全部源码,但必须串联完整流程:
|
||
|
||
|
||
|
||
```javascript
|
||
|
||
// 唯一入口:生成加密参数并使用已确认的请求客户端验证结果
|
||
'use strict';
|
||
|
||
|
||
|
||
let fetchRuntimeResources = null;
|
||
try {
|
||
({ fetchRuntimeResources } = require('./src/resources/fetch-runtime-resources'));
|
||
} catch (_) {
|
||
// 无动态资源时可以不提供刷新模块;存在动态资源时必须提供并通过检查。
|
||
}
|
||
|
||
const { makeEncryptedParams } = require('./src/target/entry');
|
||
|
||
const { createRequestSession } = require('./src/request/client');
|
||
|
||
|
||
|
||
// 请求配置来自脱敏后的浏览器成功样本,敏感值由用户本地补充
|
||
const CONFIG = {
|
||
|
||
api: 'https://example.com/api',
|
||
|
||
method: 'GET',
|
||
|
||
headers: {
|
||
|
||
'user-agent': '<从样本脱敏迁移>',
|
||
|
||
},
|
||
|
||
query: {},
|
||
|
||
body: null,
|
||
|
||
};
|
||
|
||
|
||
|
||
async function main() {
|
||
|
||
const session = await createRequestSession(CONFIG);
|
||
try {
|
||
// 如果存在动态 HTML / JS / challenge,必须在同一 session 中刷新当前有效资源,旧快照不能作为最终主输入。
|
||
const runtimeResources = typeof fetchRuntimeResources === 'function'
|
||
? await fetchRuntimeResources(CONFIG, session)
|
||
: null;
|
||
|
||
// 加密参数必须由补环境后的目标入口动态生成,不复用 cURL 样本值。
|
||
const params = await makeEncryptedParams({ request: CONFIG, runtimeResources, session });
|
||
|
||
const response = await session.request({ config: CONFIG, params });
|
||
|
||
const ok = response.status >= 200 && response.status < 300;
|
||
|
||
console.log(JSON.stringify({ ok, params, response }, null, 2));
|
||
|
||
if (!ok) process.exitCode = 2;
|
||
} finally {
|
||
// 中文说明:无论请求成功还是失败,都销毁 session,清理 Cookie jar 和敏感运行态。
|
||
await session.close();
|
||
}
|
||
|
||
}
|
||
|
||
|
||
|
||
if (require.main === module) {
|
||
|
||
main().catch(err => {
|
||
|
||
console.error(err);
|
||
|
||
process.exit(1);
|
||
|
||
});
|
||
|
||
}
|
||
|
||
```
|
||
|
||
|
||
|
||
`src/request/client.js` 应使用前置阶段已确认的 Node.js CycleTLS / impers / curl-cffi-node 创建 Session 客户端,导出 `createRequestSession()`,并维护 Cookie jar、Header 状态和 `close()`;或在用户明确选择“不发真实请求”时只输出本地参数和脱敏请求。不要临时退回普通 `fetch` / `https.request` 发真实请求,也不要调用任何浏览器自动化。
|
||
|
||
|
||
|
||
## `final.py` 入口职责
|
||
|
||
|
||
|
||
```python
|
||
|
||
# 唯一入口:生成加密参数并使用已确认的请求客户端验证结果
|
||
from src.signer import make_encrypted_params
|
||
|
||
from src.request_client import create_request_session, close_request_session
|
||
|
||
|
||
|
||
# 请求配置来自脱敏后的浏览器成功样本,敏感值由用户本地补充
|
||
CONFIG = {
|
||
|
||
"api": "https://example.com/api",
|
||
|
||
"method": "GET",
|
||
|
||
"headers": {
|
||
|
||
"user-agent": "<从样本脱敏迁移>",
|
||
|
||
},
|
||
|
||
"query": {},
|
||
|
||
"body": None,
|
||
|
||
}
|
||
|
||
|
||
|
||
def main():
|
||
|
||
session = create_request_session(CONFIG)
|
||
try:
|
||
|
||
# 加密参数必须由补环境后的目标入口动态生成,不复用 cURL 样本值
|
||
params = make_encrypted_params({"request": CONFIG, "session": session})
|
||
|
||
response = session.request(CONFIG, params)
|
||
|
||
ok = 200 <= response["status"] < 300
|
||
|
||
print({"ok": ok, "params": params, "response": response})
|
||
|
||
if not ok:
|
||
|
||
raise SystemExit(2)
|
||
finally:
|
||
|
||
# 中文说明:请求结束后销毁 Session,清理 Cookie jar 和敏感运行态。
|
||
close_request_session(session)
|
||
|
||
|
||
|
||
if __name__ == "__main__":
|
||
|
||
main()
|
||
|
||
```
|
||
|
||
|
||
|
||
不得在 `final.py` 或 `src/` 中引入 Selenium、Playwright、pyppeteer、ruyiPage 或其他浏览器自动化。
|
||
|
||
|
||
|
||
## 交付前检查清单
|
||
|
||
|
||
|
||
- [ ] `case/result/` 是规范项目目录,而不是临时文件堆。
|
||
|
||
- [ ] 只有一个执行入口:`final.js` 或 `final.py`。
|
||
|
||
- [ ] 执行入口可直接运行,并会生成加密参数、使用 Session 发送模拟请求、输出请求结果并销毁 session。
|
||
|
||
- [ ] 模块拆分合理,必要源码位于 `src/`。
|
||
- [ ] 补环境代码已运行 `check_code_quality.js`,中文注释 UTF-8 正常、无问号、无连续问号、无乱码。
|
||
|
||
- [ ] 项目内任何源码都不包含 ruyiPage / RuyiTrace / Playwright / Puppeteer / Selenium / CloakBrowser / CDP / WebDriver 自动化代码。
|
||
|
||
- [ ] 如涉及 Canvas / WebGL / WebGPU / Audio / 字体 / DOM 几何指纹,最终项目使用真实浏览器采样 fixture + 终端 API 值回放,不依赖 node-canvas / headless-gl / 自动化浏览器。
|
||
|
||
- [ ] 最终请求由前置阶段已确认的 Node.js / Python TLS 指纹兼容 Session 客户端发起,或用户明确选择不发真实请求;同一请求链复用 session,结束后销毁,不通过浏览器自动化验证。
|
||
|
||
- [ ] fixtures 已通过;动态参数建议三组以上。
|
||
|
||
- [ ] 如存在动态 HTML / JS / challenge,已生成 `case/notes/resource-manifest.json`,并运行 `check_dynamic_resources.js --require-runtime-refresh` 通过。
|
||
|
||
- [ ] 动态快照未复制进 `result/`;最终入口会运行时刷新当前资源。
|
||
|
||
- [ ] Cookie、token、Authorization、localStorage 等敏感值已脱敏或仅由用户本地配置,不明文写入报告。
|
||
|
||
- [ ] 临时 trace、hook、日志、HAR、截图、Profile、缓存和测试文件已清理。
|
||
|
||
- [ ] 已运行 `check_code_quality.js`、`check_env_realism.js`(addon-first 默认强制)、涉及指纹时运行 `check_fingerprint_fixture.js`,已生成 UTF-8 `result/最终项目总结.md`,并运行 `check_final_artifact.js` 和 `clean_case.js --dry-run`。
|
||
|
||
|
||
|
||
## 输出模板
|
||
|
||
|
||
|
||
```markdown
|
||
|
||
## 最终交付
|
||
|
||
|
||
|
||
- 项目目录:
|
||
|
||
- 唯一执行入口:
|
||
|
||
- 运行方式:
|
||
|
||
- 入口能力:生成加密参数 + 使用 Node.js / Python Session 模拟请求 + 输出请求结果 + 销毁 session
|
||
|
||
- 请求实现:Node.js CycleTLS / Node.js impers / Node.js curl-cffi / curl-cffi-node / Python curl_cffi / Python cffi_curl / Python cyCronet / 不发真实请求
|
||
|
||
- 是否包含浏览器自动化代码:否
|
||
|
||
- 是否包含多余测试/临时文件:否
|
||
|
||
- fixtures 验证:通过 / 未执行,原因
|
||
|
||
- 动态资源保鲜检查:无动态资源 / 已在同一 session 运行时刷新 / 未通过,原因
|
||
|
||
- Session 模式:已创建并复用 / 不发真实请求;销毁方式:close / exit / dispose / Cookie jar 清理
|
||
|
||
- resource manifest:`case/notes/resource-manifest.json` / 未涉及
|
||
|
||
- 运行时刷新模块:`result/src/resources/fetch-runtime-resources.js` / 未涉及
|
||
|
||
- 动态快照是否进入 result:否
|
||
|
||
- 最终真实请求验证:已执行 / 未执行,原因
|
||
|
||
- 敏感材料处理:
|
||
|
||
- 清理检查:
|
||
|
||
- 最终总结:已生成 `result/最终项目总结.md` / 用户明确要求不生成
|
||
|
||
```
|
||
|
||
## 补环境真实性交付检查
|
||
|
||
|
||
|
||
交付前执行:
|
||
|
||
|
||
|
||
```bash
|
||
|
||
node scripts/check_env_realism.js --case-dir case --markdown
|
||
|
||
node scripts/check_env_realism.js --case-dir case --require-document-all --require-ruyitrace --require-fingerprint-fixture --markdown
|
||
|
||
node scripts/check_fingerprint_fixture.js --case-dir case --require canvas,webgl --markdown
|
||
|
||
```
|
||
|
||
|
||
|
||
要求最终环境代码体现:属性描述符、访问器、原型链、构造函数、函数 toString 保护、访问器 toString 保护、实例对象 toString 保护;创建函数、构造函数、getter、setter、`document.all`、addon 支持的原型链时从补环境初始化阶段就必须 addon-first,addon 不可用或用户明确豁免才 `NativeProtect` fallback 并记录原因;涉及浏览器指纹时必须保留真实采样 fixture 并做终端 API 值回放;使用 RuyiTrace 时必须保留 `notes/ruyitrace-summary.md` 与 `notes/missing-env-priority.md` 证据摘要。
|
||
|
||
|
||
|