Files

231 lines
14 KiB
Markdown
Raw Permalink 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.

# 补环境代码可读性与中文注释规范
每次生成、重构或交付 `case/result/` 中的补环境代码前读取本文件。目标是让最终代码 **简洁、可读、可维护、中文注释正常显示**,并避免生成压缩、堆叠、无注释或乱码代码。
## 硬性原则
1. **先规划目录,再写代码**
写代码前先列出最终目录、每个文件职责和调用关系。不要边写边临时堆文件。
2. **按职责拆模块**
不要把所有补环境逻辑塞进一个 `env.js`。推荐结构:
```text
case/result/
├── final.js
├── 最终项目总结.md
└── src/
├── env/
│ ├── install-env.js
│ ├── native-api.js
│ ├── manifest.js
│ ├── core/
│ │ ├── descriptors.js
│ │ └── cookie-store.js
│ ├── browser-objects/
│ │ ├── window.js
│ │ ├── navigator.js
│ │ ├── document.js
│ │ ├── location.js
│ │ ├── screen.js
│ │ ├── xhr.js
│ │ └── storage.js
│ └── fingerprint/
│ ├── canvas.js
│ ├── webgl.js
│ └── dom-geometry.js
├── target/
│ └── entry.js
├── request/
│ └── client.js
└── utils/
└── normalize.js
```
如果需要 Node 诊断执行器、runtime probe 或 Akamai / Shape / F5 等高强度检测的本地执行器,应继续拆成下列职责,而不是把所有 WebAPI 放进 `src/signer/*probe*.js`:
```text
case/result/src/
├── signer/
│ ├── runtime_signer.py
│ └── akamai_runtime_probe.js # 只做入口和编排,建议少于 150 行
└── node-runtime/
├── bootstrap/
│ ├── read-stdin.js
│ ├── execute-script.js
│ └── result-recorder.js
├── utils/
│ ├── hash.js
│ ├── sanitize-url.js
│ └── body-summary.js
└── env/
├── install-env.js
├── native-api.js
├── node-leakage.js
├── browser-objects/
│ ├── window.js
│ ├── navigator.js
│ ├── document.js
│ ├── location.js
│ ├── history.js
│ ├── screen.js
│ ├── storage.js
│ ├── performance.js
│ └── events.js
├── fingerprint/
│ ├── canvas.js
│ ├── webgl.js
│ ├── audio.js
│ └── dom-geometry.js
└── network/
├── xhr.js
├── fetch.js
└── beacon.js
```
3. **signer / probe 不得承载补环境主体**
`src/signer/`、`src/request/`、`src/resources/`、`runtime_probe.js`、`probe.js`、`runner.js`、`diagnostic.js` 等文件只允许做编排、进程入口、请求封装、资源刷新或安全摘要输出;只要文件内实现了 `navigator`、`document`、`window`、`screen`、`Storage`、`XMLHttpRequest`、`fetch`、`Canvas`、`WebGL`、`Audio`、`performance`、DOM 构造链、事件系统等浏览器 WebAPI 主体,就必须拆入 `src/env/` 或 `src/node-runtime/env/` 下的真实模块。不得因为文件名叫 probe、runtime、signer 或 diagnostic 就豁免模块化要求。
4. **补环境代码必须可读**
禁止压缩代码、单行堆叠多个语句、过度匿名函数、无意义变量名、超长函数和超深嵌套。原始目标 bundle 如必须保留,应放到 `src/target/original/` 或等价目录,并与手写补环境代码分离。
5. **中文注释必须正常显示**
所有交付源码使用 UTF-8 无 BOM。不要使用未指定编码的 PowerShell / cmd 重定向写中文源码。中文注释中不得出现问号、连续问号或替换字符。
6. **WebAPI 实现必须可审计**
补环境源码不得把 WebAPI 写成无法审查的一行堆叠代码。禁止一行连续堆多个 `Object.defineProperty`、`Object.defineProperties`、`Object.assign`、函数定义或对象字面量方法。`install-env.js` 只负责装配,不应成为巨型文件;`navigator`、`screen`、`document`、`storage`、`indexedDB`、`canvas`、`webgl`、`events`、`worker`、`request` 等应按职责拆模块。
7. **普通 WebAPI 函数必须 addon-first**
不要在 WebAPI 主路径中直接写 `ctx.Blob = function(){}`、`ctx.indexedDB = { open() {} }`、`prototype = { getContext() {} }` 或 `ctx.URL.createObjectURL = function(){}`。普通函数也要提取为具名函数,并通过 addon-first helper 包装后再安装 descriptor。addon 不可用时才用 `NativeProtect` / JS fallback,并在注释与阶段报告中说明原因。
8. **浏览器对象私有状态不得泄露**
不要在浏览器可见对象上写 `this.__readyState`、`this._headers`、`__children`、`__parentNode`、`__responseHeaders`、`_store` 等内部字段。non-enumerable 也会被 `Object.getOwnPropertyNames`、`Reflect.ownKeys` 和 descriptor 检测到。内部状态优先使用 addon / xbs private API,其次使用模块级 `WeakMap`;XHR、fetch、Headers、Response、DOM 节点、EventTarget、Storage、Performance 等都适用。
9. **XHR/fetch 必须拆为网络环境模块并桥接 Session**
`XMLHttpRequest` / `fetch` / `Request` / `Response` / `Headers` / `sendBeacon` 的浏览器语义放在 `src/env/network/` 或 `src/node-runtime/env/network/`。需要真实请求时,网络模块只能调用 live session bridge;不得在 env 里直接调用 Node 宿主 `fetch`、`http`、`https`、`axios`、`undici`。fixture/default response 只能保留为 `offline-fixture` 诊断模式。
10. **构造函数错误必须可追溯**
构造函数的报错类型和 message 必须来自目标浏览器采样。不要写泛化 `throw new TypeError('Illegal constructor')`;需要在注释、fixture 或阶段报告中说明该错误来自哪个浏览器、哪个调用方式。
11. **addon 实例不再二次标记对象类型**
`markObjectType` 不是批准 API。addon 构造函数 / `createProtoChains` 实例工厂创建出的实例本身应已处理 `Object.prototype.toString`;只有 JS fallback 普通对象才允许 `markObjectToString` / `Symbol.toStringTag`,并必须写明 fallback 原因。
12. **注释说明“为什么”和“来源”**
注释不是逐行翻译代码,而是说明模块职责、浏览器样本来源、RuyiTrace 证据、fixture 匹配规则、addon-first 决策和 fallback 原因。
13. **isolated-vm 也必须文件化模块交付**
isolated-vm 底层确实需要把源码字符串交给 V8,但最终项目不得把补环境源码写成大段 `String.raw`、`CORE_SCRIPT`、`BROWSER_OBJECTS_SCRIPT`、`*_SCRIPT` 聚合字符串。正确做法是把 `navigator.js`、`document.js`、`window.js`、`canvas.js`、`webgl.js` 等作为真实源码文件保存,由宿主侧 runtime 使用 `fs.readFileSync` 读取,再通过 `compileScriptSync(source, { filename })` 或等价方法注入同一个 isolated-vm Context。只有少量 bootstrap 片段允许字符串形式,且必须少于 40 行、不能承载主要 WebAPI 实现,并写明原因。
## 中文注释要求
必须写中文注释的位置:
- 文件顶部:说明模块职责、输入来源和边界。
- 每个环境对象模块:说明模拟哪个浏览器对象。
- 关键 getter / setter:说明真实值来自 fixture、RuyiTrace、请求样本还是用户配置。
- native-like 函数和构造函数:说明挂载位置、浏览器行为和 addon-first 处理。
- fallback 分支:说明 addon 不可用、ABI 不兼容、API 调用失败或用户明确豁免的原因。
- 指纹值回放函数:说明匹配 key、采样来源和缺失样本时的处理。
- 加密参数生成入口:说明输入、输出和 fixtures 验证方式。
禁止:
```js
// 不合格:用问句解释实现
// 不合格:连续问号乱码示例
// 临时先这样
```
推荐:
```js
// 安装 Navigator 相关环境对象,字段值来自浏览器 fixture
function installNavigatorEnv(globalObject, fixture, nativeApi) {
// Navigator 构造函数错误信息来自 constructor-errors.fixture.json 中的浏览器采样
const Navigator = nativeApi.createNativeConstructor('Navigator', 0, function Navigator(isNew) {
if (isNew) {
return nativeApi.throwIllegalConstructor('Navigator');
}
return nativeApi.throwIllegalConstructor('Navigator');
});
// userAgent 使用真实浏览器采样值,不在 Node.js 中伪造默认值
nativeApi.defineNativeGetter(Navigator.prototype, 'userAgent', function getUserAgent() {
return fixture.browser.userAgent;
});
}
```
## 命名与函数拆分
- 函数名使用业务含义,例如 `installNavigatorEnv`、`createStorageArea`、`replayCanvasToDataURL`。
- 避免 `a`、`b`、`fn1`、`tmp`、`xxx` 作为长期变量名。
- 单函数建议不超过 90 行。
- 单文件建议不超过 500 行。
- 单行建议不超过 180 字符。
- 属性描述符、构造函数 callback、WebAPI 方法安装、`Object.assign`、`try/catch` 较长逻辑不得压成一行。
- 嵌套层级尽量不超过 6 层;复杂逻辑用提前返回或拆函数。
## isolated-vm 文件化加载示例
推荐由 `install-env.js` 只负责装配,由 runtime 顺序读取文件:
```js
const ENV_FILES = [
'src/env/core/descriptors.js',
'src/env/browser-objects/window.js',
'src/env/browser-objects/navigator.js',
'src/env/browser-objects/document.js',
'src/env/fingerprint/canvas.js',
'src/env/fingerprint/webgl.js',
];
function installEnv(runtime, config) {
runtime.setCopy('__ENV_CONFIG__', config);
runtime.runFiles(ENV_FILES);
runtime.runFile('src/env/install-all.js');
}
```
每个文件都应是正常 JavaScript 文件,文件顶部用中文说明模块职责。Context 内不需要也不应该直接 `require('./navigator.js')`;由宿主 runtime 读取文件内容后注入,既保持 isolated-vm 隔离,又保留源码文件结构、中文注释和 filename 栈定位。
## 最终交付前检查
交付前必须运行:
```bash
node scripts/check_code_quality.js --case-dir case --markdown
node scripts/check_object_shape_audit.js --case-dir case --markdown
node scripts/check_xhr_fetch_session_bridge.js --case-dir case --markdown
node scripts/check_trace_runtime_conformance.js --case-dir case --markdown
node scripts/check_xhr_fetch_semantics.js --case-dir case --require --require-no-send --out case/tmp/xhr-fetch-semantics-audit.json --markdown
node scripts/check_environment_closure.js --case-dir case --before-real-request --markdown
node scripts/check_webapi_addon_coverage.js --case-dir case --markdown
node scripts/check_env_realism.js --case-dir case --markdown
node scripts/check_final_artifact.js --case-dir case --markdown
```
如果 `check_code_quality.js` 或 `check_webapi_addon_coverage.js` 失败,必须立即停止后续补环境验证、真实请求验证和交付;先重构代码、拆分 signer/probe 中的 WebAPI 主体、修复中文注释、编码问题、普通 WebAPI 函数、普通对象和宿主透传,再重新运行检查。不得以“当前请求能跑通”“只是诊断脚本”“只是 probe”为理由跳过失败项。
## 常见修复方式
| 问题 | 修复方式 |
|---|---|
| 单个 `env.js` 太大 | 按 navigator、document、storage、fingerprint 拆模块 |
| isolated-vm 生成大段 `String.raw` | 改为真实 `.js` 文件,由 runtime.runFile / runtime.runFiles 读取后注入 Context |
| `script-browser-objects.js` / `script-core.js` 承载主要补环境 | 拆成 `browser-objects/navigator.js`、`document.js`、`window.js`、`fingerprint/canvas.js` 等模块 |
| 没有中文注释 | 增加文件头、对象说明、来源说明和 fallback 原因 |
| 中文注释有问号或乱码 | 用 UTF-8 重新写入,避免默认 shell 重定向 |
| 函数过长 | 拆成解析 fixture、创建构造函数、安装属性、安装实例四步 |
| 普通赋值太多 | 改为统一 helper,例如 `defineValue`、`defineNativeGetter` |
| 普通 WebAPI 函数 | 提取具名 callback,用 `addon.createNativeFunction` 或 addon-first helper 包装 |
| 普通 WebAPI 对象 | 用 `createProtoChains` 建立构造函数、prototype 和实例工厂,再安装 descriptor |
| 泛化 `Illegal constructor` | 先采样浏览器错误类型和 message,再用 `throwBrowserTypeError` / `throwIllegalConstructor` 等 helper 复现 |
| `markObjectType` | 删除该调用;addon 实例不需要二次标记,JS fallback 才用 `markObjectToString` 并记录原因 |
| `prototype = {}` | 改为 `createProtoChains` 或 `Object.defineProperties`,保留 `constructor` 和 `Symbol.toStringTag` |
| 直接复用宿主 Web API | 按浏览器样本和目标调用范围重建可控实现,不盲目透传 Node 宿主对象 |
| `this.__readyState` / `_headers` / `_store` | 改用 addon / xbs private API 或模块级 WeakMap |
| XHR/fetch 返回默认 200 / fixture | 标为 offline-fixture;真实请求时改为 live session bridge |
| Python curl_cffi 最终请求但 Node XHR 自己发请求 | 改为 final.py 持有同一 Session,Node 通过 IPC 请求 Python 发送 |
| target bundle 混入手写代码 | 原始 bundle 放 `src/target/original/`,入口包装放 `src/target/entry.js` |