Files
reverse-skills-max/vendor-new/xbsReverseSkill/web-js-env-patcher/references/webapi-env-detection-matrix.md

286 lines
19 KiB
Markdown
Raw 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.

# WebAPI 环境检测矩阵门禁
本文件用于解决另一类常见失败:Trace 已经列出了 API,补环境也实现了 API,但目标仍因 iframe、Worker、PerformanceTimeline、DOM/CSSOM、枚举、任务队列或 writer 分支行为不像真实浏览器而进入错误分支。Trace API 覆盖矩阵回答“访问了什么”,本矩阵回答“访问到的浏览器行为是否一致”。
本文件只用于授权范围内的网页端 Node.js 补环境、真实浏览器基线采样和 Node 行为 diff。不得把它解释为厂商专用绕过流程;遇到 JSVMP / opcode / 字节码解释器时仍只记录环境行为、请求链和 writer 分支,不主动分析虚拟机源码。
## 触发条件
出现以下任一信号时,在进入真实请求、长跑验证或 final writer 判定前必须建立 WebAPI 环境检测矩阵:
- Trace / 阶段报告中出现 `iframe`、`srcdoc`、`contentWindow`、`contentDocument`、`defaultView`、`document.write`、`document.close`。
- 出现 `Worker`、`SharedWorker`、`DedicatedWorkerGlobalScope`、`postMessage`、`MessageChannel`、`MessagePort`、`terminate`、Worker 内 timer 或 Worker 内 `performance`。
- 出现 `PerformanceObserver`、`PerformanceObserverEntryList.getEntries`、`PerformanceResourceTiming`、`PerformancePaintTiming`、`performance.mark`、`performance.getEntries*`、XHR / image / favicon resource timing。
- 出现 `XMLHttpRequest`、`fetch`、`Request`、`Response`、`Headers`、`navigator.sendBeacon`,且目标路径依赖动态资源、Cookie、challenge、telemetry、resource timing 或 writer 分支。
- 出现 `Object.keys`、`Object.getOwnPropertyNames`、`Reflect.ownKeys`、descriptor、getter / setter、prototype walk、brand check、`Function.prototype.toString` 多通道。
- 出现 `this.__xxx`、`this._xxx`、`__readyState`、`__headers`、`__children`、`Object.defineProperty(obj, "__xxx")`、`defineValue(obj, "_xxx")` 等浏览器对象私有状态泄露迹象。
- 出现 `structuredClone`、`DataCloneError`、`MessagePort.prototype.postMessage(fn)`、Error stack / message 差异。
- 出现 DOM / CSSOM 完整性检查:`DOMParser`、`innerHTML`、Comment/Text 原型、`HTMLUnknownElement`、`textarea`、`option`、`select`、`HTMLCollection`、`ShadowRoot`、`CSSStyleSheet.insertRule`、`CSSRuleList`、`getComputedStyle`、`document.links/images/forms/title/on*`。
- 出现 writer 分支差异,例如同一链路中 Node 停在 continuation / reload writer,而真实浏览器进入 form writer / final writer。
## 必须产物
触发本门禁后,进入真实请求验证前至少生成:
```text
case/fixtures/browser-env-detection-baseline.json
case/tmp/node-env-detection-audit.json
case/notes/webapi-env-detection-matrix.md
```
如果已经有高强度 diff 文件,也要在 `case/notes/high-intensity-env-diff.md` 中引用本矩阵结论。若暂时不能生成真实浏览器 baseline,必须在矩阵中标记为 `needs-browser-baseline` 并说明阻塞原因,不得把 Node probe 通过当作真实浏览器一致。
矩阵状态不能自行证明一致。浏览器 baseline 与 Node audit 的每个触发类别必须包含同名 probe 和实际观测值;`check_webapi_env_detection_matrix.js` 将深度比较观测结果。空类别数组、只有 `status: matched` 或只有证据路径而没有结果均视为未审计。两端必须记录相同 `probeSuiteVersion` 与 `probeSourceHash`,并用 case 内的 `probeSourceFile` 让门禁重算真实 SHA-256,证明运行的是同一份 probe 源码;Node audit 还必须记录 `runtimeSourceHash`、`probeVersion` 和可信 `generatedBy`,源码变化后旧 audit 失效。
`browser-env-detection-baseline.json` 至少记录:
```json
{
"schemaVersion": "webapi-env-detection-baseline/v2",
"baselineId": "fp-example-001",
"capturedBy": "ruyiPage+RuyiTrace",
"browser": "Firefox",
"probeSuiteVersion": "webapi-realm-lifecycle/v2",
"probeSourceFile": "fixtures/webapi-env-probe-suite.js",
"probeSourceHash": "<same-sha256-on-browser-and-node>",
"categories": {
"iframe-realm": [
{
"id": "global-relations",
"capability": "realm-global-relations",
"observation": {"selfIsWindow": true, "parentIsMain": true, "defaultViewIsFrame": true}
}
],
"worker-task": [
{
"id": "immediate-terminate",
"capability": "worker-terminate-immediate",
"observation": {"messagesAfterTerminate": []}
}
],
"message-lifecycle": [
{
"id": "sender-close",
"capability": "message-port-close-sender",
"observation": {"delivered": false}
}
],
"performance-timeline": [{"id": "resource-entry-order", "observation": {"entries": []}}],
"dom-cssom": [{"id": "current-path-involved", "observation": {"involved": false}}],
"dom-crud": [
{
"id": "valid-combinator-selector",
"capability": "dom-selector-validity",
"observation": {"selector": "div > span", "throws": false}
}
],
"event-clone-error": [{"id": "xhr-brand", "observation": {"brand": "[object XMLHttpRequest]"}}],
"xhr-fetch-session-bridge": [{"id": "network-mode", "observation": {"mode": "live-session-bridge"}}],
"object-shape": [{"id": "xhr-prototype-chain", "observation": {"chain": ["XMLHttpRequest", "XMLHttpRequestEventTarget", "EventTarget", "Object"]}}],
"private-state-leakage": [{"id": "xhr-private-own-keys", "observation": {"keys": []}}],
"clock-timer": [{"id": "current-path-involved", "observation": {"involved": false}}],
"writer-branch": [{"id": "final-writer-path", "observation": {"writer": "form-writer"}}]
}
}
```
`node-env-detection-audit.json` 至少记录同一组类别、同一 probe 名称、同一 `capability` 和 Node 实际观测值,并额外包含 `generatedBy`、`probeVersion`、`probeSuiteVersion`、`probeSourceFile`、`probeSourceHash`、`runtimeSourceHash`。`not-involved` 也必须以明确 probe 观测表达,不能使用空数组。
以下字段才算实际观测:`observation`、`observed`、`result`、`value`、`output` 或 `expected`。`evidence`、`notes`、`status`、文件路径和人工结论不属于实际观测。
## 状态枚举
矩阵中每个检测项必须选择一个状态:
| 状态 | 含义 | 是否允许真实请求 |
|---|---|---|
| `matched` | 浏览器 baseline 与 Node audit 一致 | 允许 |
| `accepted-diff` | 差异已评估为不影响当前路径,并有证据 | 允许,但必须写风险 |
| `not-involved` | 当前 Trace / writer 路径未涉及 | 允许 |
| `needs-browser-baseline` | 缺真实浏览器基线 | 不允许 |
| `needs-node-audit` | 缺 Node 同 probe audit | 不允许 |
| `mismatch` | 存在未修复行为差异 | 不允许 |
| `native-capability-gap` | 当前 JS / addon / xbs 均无法可靠表达 | 不允许,进入 native 能力缺口闭环 |
| `unknown` | 未判断 | 不允许 |
Realm identity、对象形状、私有状态、Worker/MessagePort 生命周期、DOM CRUD、当前网络 writer 和请求语义属于 P0/P1 时不得使用 `accepted-diff`。这些差异必须修复、补采真实 baseline,或进入明确的 native capability gap。
## 强制 capability
触发下列类别时,browser 与 Node 两端都必须包含这些 capability;probe 名称可以按项目细化,但 capability id 不得省略:
| 类别 | capability |
|---|---|
| `iframe-realm` | `realm-global-relations`、`realm-ecma-constructor-isolation`、`realm-webapi-constructor-isolation`、`realm-object-isolation`、`realm-document-relations`、`realm-navigation-lifecycle` |
| `worker-task` | `worker-global-surface`、`worker-constructor-isolation`、`worker-object-isolation`、`worker-message-order`、`worker-terminate-immediate`、`worker-terminate-deferred` |
| `message-lifecycle` | `message-order`、`message-port-start`、`message-port-close-sender`、`message-port-close-receiver`、`message-port-transfer` |
| `dom-crud` | `dom-tree-mutation`、`dom-live-collections`、`dom-selector-validity`、`dom-html-parsing`、`dom-document-relations`、`dom-mutation-observer` |
## 必查类别
### iframe / Window realm
检查:
- `iframe.contentWindow` / `contentDocument` / `defaultView` 关系。
- `srcdoc`、sandbox、`document.write()`、`document.close()`、load 事件顺序。
- 主窗口与 iframe Window 的 `eval`、`fetch`、`location`、`navigator` descriptor。
- 主窗口与 iframe 的 `Object/Function/Array/Promise`、`Event/EventTarget`、`URL/Blob`、`Headers/Request/Response/XHR` 构造器 identity 必须不同。
- iframe 的 `self/window/globalThis/frames` 指向自身,`parent/top` 指向正确上层;`document.defaultView/contentWindow/contentDocument/frameElement` 关系必须闭合。
- 允许共享同源 Cookie/Storage 后端和 TLS Session,但 navigator/performance/crypto/storage wrapper/fetch/timer 等公开对象不能直接复用主 Realm 实例。
- `Object.keys`、`Object.getOwnPropertyNames`、`Reflect.ownKeys` 的数量、顺序和过滤策略。
- `document.all`、`currentScript`、`readyState`、`defaultView` 是否按目标浏览器可见。
禁止为了推进链路全量放开 raw ownNames;只能基于浏览器 baseline 做窄范围 A/B,并记录为什么该项可见。
### Worker / Message task queue
检查:
- Worker scope 是否有独立 `performance`、`timeOrigin`、`performance.now()` 序列和 UA / baseline 选择。
- Worker 的 `self/globalThis`、构造器图和实例必须独立;Window-only API 必须缺失,主 Window 也不得错误暴露 WorkerGlobalScope-only API。
- Worker 内 `setTimeout` / `clearTimeout` 是否绑定 Worker 私有状态。
- `Worker.prototype.terminate()` 后 pending timer、延迟 `postMessage` 是否清理。
- 立即 terminate 与延迟 terminate 必须分开检测。
- `WorkerGlobalScope.self.postMessage()` 与 `MessagePort.prototype.postMessage()` 是否后续 task 派发,而不是同步栈内派发。
- `addEventListener` / `removeEventListener` / `dispatchEvent` 是否支持 listener object、capture、once、passive、stopImmediatePropagation。
Promise resolved、Worker 回包出现或 probe 不报错都不能单独视为完成;必须验证同步 / 异步顺序和 terminate 后行为。
### MessagePort / postMessage 生命周期
检查:
- `postMessage` 相对当前同步栈、Promise microtask、queueMicrotask、timer 的顺序。
- `start()` 与设置 `onmessage` 的隐式启动行为。
- sender close、receiver close、双方 close、消息入队后 close。
- transfer 后原端口是否失效、目标端口是否保持 entangled。
- `close()` 是否清除监听、待派发 task 和 peer 关系;不能只在发送时检查“对端是否 closed”。
### Performance timeline / resource timing
检查:
- `performance.getEntries()`、`getEntriesByType()`、`getEntriesByName()` 的排序。
- `PerformanceObserver.observe()` 对 `buffered` 与非 `buffered` 的差异。
- `PerformanceObserverEntryList.getEntries()` 回调内容和触发时机。
- XHR readyState 4 / load / loadend 前后 resource entry 是否可见。
- image、favicon、Turnstile / third-party script、flow、`/d/`、`/peek` 等资源是否进入正确 timeline。
- `PerformanceEntry.toJSON()`、paint、mark、resource entry 序列化是否一致。
禁止无条件 synthetic 初始 callback;禁止用插入顺序替代浏览器 `startTime` 排序,除非目标 baseline 证明如此。
### DOM / CSSOM 短值行为
检查:
- `document.createElement(tag)` 的 tag 到构造器映射,特别是未知 tag、`html`、`textarea`、`option`、`select`、`form`、`input`、`img`、`iframe`。
- `innerHTML` / `DOMParser.parseFromString()` 后 Text、CDATA、Comment、Element 原型与 `[object Xxx]`。
- `appendChild/removeChild/insertBefore/replaceChild/replaceChildren` 的移动、重复插入、DocumentFragment 展开、错误类型和 ownerDocument/isConnected 更新。
- `childNodes`、`children`、`getElementsBy*` 的 live 语义与 `querySelectorAll` 的 static 语义。
- 合法选择器如 `div > span` 不得错误抛出 `SyntaxError`;非法选择器必须按浏览器 baseline 抛错。
- HTML parser 不得丢弃 Comment/Text;`innerHTML`、clone/import/adopt 后 document/parent/sibling 关系必须一致。
- `MutationObserver` 的 record 内容、合并策略和 microtask 时序。
- `HTMLCollection`、`NodeList`、`HTMLOptionsCollection` 是否 native-like,是否实时或快照符合目标路径。
- `attachShadow()`、`ShadowRoot.host/mode`、`shadowRoot` descriptor。
- `CSSStyleSheet.insertRule()`、`CSSStyleRule.selectorText/style`、`CSSRuleList.item()`、`getComputedStyle()` 对目标选择器的最小一致性。
- `document.links/images/forms/title/on*`、`document.cookie` 可见性与 descriptor。
### Error / clone / native shape
检查:
- `structuredClone(fn)`、`MessagePort.prototype.postMessage(fn)` 的错误 name / message / stack。
- `Function.prototype.toString.call(fn)`、`fn + ""`、`String(fn)`、保存旧 FTS 后调用。
- 构造函数直接调用 / `new` 调用错误差异。
- `Object.getOwnPropertyDescriptor`、`__lookupGetter__`、`__lookupSetter__`、`hasOwnProperty`、`propertyIsEnumerable`、`for...in`。
- `Object.prototype.toString.call`、`Symbol.toStringTag`、`instanceof`、prototype walk。
### XHR / fetch session bridge
检查:
- `XMLHttpRequest.send()` / `fetch()` / `navigator.sendBeacon()` 是否为 `offline-fixture` 诊断模式,还是 `live-session-bridge` 真实请求模式。
- 真实请求模式是否通过同一 CycleTLS / impers / curl-cffi-node / curl_cffi / cffi_curl / cyCronet Session 发送。
- Python `curl_cffi` 场景是否由 `final.py` 持有唯一 session,并通过 IPC 服务 Node JS runtime 的 XHR/fetch 请求。
- Cookie jar、`document.cookie`、`Set-Cookie`、`getResponseHeader("set-cookie")`、Response headers、body、readyState / Promise / event 顺序是否与浏览器 baseline 一致。
- XHR / fetch 请求是否产生正确的 PerformanceResourceTiming 可见性。
禁止把 fixture / 默认 200 / mock response 写成真实请求成功;禁止在 XHR/fetch 内部直接调用 Node 宿主 `fetch`、`http`、`https`、`axios` 或 Python 普通 `requests`。
### Object shape
检查:
- `Object.keys`、`Object.getOwnPropertyNames`、`Object.getOwnPropertySymbols`、`Reflect.ownKeys` 的数量、顺序和字段名。
- `Object.getOwnPropertyDescriptor`、`hasOwnProperty`、`propertyIsEnumerable`、`for...in`、`in`、`__lookupGetter__`、`__lookupSetter__`。
- 构造函数、prototype、实例对象、集合对象、iframe realm、Worker realm 的 prototype walk。
- `Object.prototype.toString.call(obj)`、`Symbol.toStringTag`、`constructor.name`、`instanceof`。
如对象形状涉及内部状态,应同时生成 `object-shape-audit.md`、`browser-object-shape-baseline.json` 和 `node-object-shape-audit.json`。
### Private state leakage
检查:
- 浏览器可见对象是否存在自定义 `_` / `__` 自有属性,包括 non-enumerable 属性。
- 是否使用 `this.__state`、`this._headers`、`Object.defineProperty(obj, "__readyState")`、`defineValue(obj, "_store")` 或自定义 Symbol 保存内部状态。
- 内部状态是否迁移到 addon / xbs private API、原生内部槽或模块级 `WeakMap`。
发现 `_` / `__` 私有状态泄露时直接阻断;不能用 `enumerable:false` 作为豁免。
### Clock / timer
检查:
- `Date.now()`、`new Date()`、主线程 `performance.now()`、Worker `performance.now()` 是否来自同一 baseline 策略。
- 虚拟 timer driver 是否只运行到期 timer,不因 sleep cap 把 10s / 30s timer 一轮快进。
- `queueMicrotask`、Promise job、timer task、MessagePort task、PerformanceObserver callback 的相对顺序。
### Writer branch
检查:
- 请求链中每个关键响应对应的 writer 类型:continuation program、reload writer、form writer、final writer。
- Node 与真实浏览器在 final writer 前 300 到 1000 个环境事件的类别差异。
- 是否出现 `HTMLFormElement.submit(true)`、hidden input 创建、`cf_chl_rc_ni` 删除、`Location.reload` 等分支证据。
- 不得把拿到 Cookie、Set-Cookie 或 reload writer 响应写成最终成功;必须记录 writer 类型。
## 阻断规则
- 存在 `mismatch`、`needs-browser-baseline`、`needs-node-audit`、`native-capability-gap` 或 `unknown` 的 P0/P1 检测项时,不得执行真实请求验证、不得宣称 final writer 已闭环。
- 只有类别名称或手工 `matched`、没有逐 probe 浏览器值和 Node 值时,直接阻断。
- browser/Node 的 `probeSuiteVersion`、`probeSourceFile` 或 `probeSourceHash` 缺失,hash 不一致,源文件不在 case 内,或声明 hash 与实际文件不一致,直接阻断。
- iframe/Worker 构造器或公开对象复用主 Realm、MessagePort 只实现单侧 close、DOM probe 把合法 selector 当非法、HTML parser 丢 Comment,直接阻断。
- 存在 `xhr-fetch-session-bridge` 阻断项时,不得宣称 TLS 指纹已解决;fixture/mock 只能作为离线诊断结论。
- 存在 `private-state-leakage` 阻断项时,不得交付;必须迁移到 addon / xbs private API 或 WeakMap。
- 如果后续阶段新增 WebAPI 是因为本矩阵遗漏了已触发检测项,必须标为 `missed-from-webapi-env-matrix` 流程缺陷,并补写矩阵与代码变更记忆。
- 如果真实请求失败但矩阵未覆盖 Worker / performance / iframe / DOM/CSS / writer branch,而 Trace 或阶段报告已经出现这些信号,下一步只能补矩阵和离线 diff,不能继续盲目真实请求。
## 阶段报告记录模板
```markdown
## WebAPI 环境检测矩阵
- 矩阵文件:case/notes/webapi-env-detection-matrix.md
- 浏览器 baseline:case/fixtures/browser-env-detection-baseline.json
- Node audit:case/tmp/node-env-detection-audit.json
- baselineId:
- 触发类别:iframe-realm / worker-task / message-lifecycle / performance-timeline / dom-cssom / dom-crud / event-clone-error / xhr-fetch-session-bridge / object-shape / private-state-leakage / clock-timer / writer-branch
- 阻断项:无 / 列表
- writer 分支:真实浏览器 = form writer;Node = reload writer / continuation / form writer
| 类别 | 检测项 | 浏览器证据 | Node 证据 | 状态 | 处理 |
|---|---|---|---|---|---|
| worker-task | postMessage async task | fixtures/... | tmp/... | matched | 保持 |
```
## 检查命令
```bash
node scripts/check_webapi_env_detection_matrix.js --case-dir case --markdown
node scripts/check_webapi_env_detection_matrix.js --case-dir case --require --require-writer-branch --json
```
检查失败时,下一步只能补浏览器 baseline、Node audit、矩阵、native 能力缺口或离线 diff;不能继续真实请求验证。