| name | browser-env-patch |
| description | 在当前补环境框架中修复浏览器环境缺失并跑通目标 JS / 加密入口,最终按网站 domain 收敛为可直接运行的 run/<domain>/_code.js,并用同目录测试脚本消费 _code.js 输出完成真实请求/验签。Use when the user asks to 补环境、修复 ENV_MISSING / Illegal invocation、补 DOM/BOM、运行加密脚本、修复 Node/vm2 与浏览器行为差异、修改 env/config/tools/run、或要求单脚本成品。For read-only proxy_log diagnosis, prefer proxy-log-analyzer. |
浏览器补环境
目标
在当前仓库中补齐 Node.js + vm2 沙盒缺失的浏览器环境,让目标脚本能按项目现有 dispatch / memory / 原型链模式稳定运行。
基本流程
- 先读入口:检查
main.js 实际拼接了哪些文件。本仓库当前执行 run/sign.js;不要假设存在 run/.js。
- 运行
node main.js,同时关注终端错误和 run/<domain>/proxy_log.json。
- 从日志中整理一批缺失项:
ENV_MISSING、Illegal invocation、关键 对象为undefined、明显错误的类型/结构/描述符。
- 先分类,再修改:区分框架代码问题、浏览器值问题、结构/原型链/描述符问题、自动化痕迹检测。
- 按仓库结构最小修改:优先改
tools/envFunc.js、config/config.js、env/、tools/globalThis.js、tools/proxyObj.js、run/sign.js。
- 每轮修改后必须重新运行
node main.js,再看 run/<domain>/proxy_log.json,确认本轮目标是否消失或对齐。
- 目标跑通后,把可复现成品收敛到
run/<domain>/_code.js:单文件、单入口、单命令可运行;临时 probe 和插桩脚本不能作为最终交付。
- 交付前判断
run/<domain>/_code.js 的生成模式:普通目标优先使用框架原生 main.js 生成;复杂目标需要生成自包含 _code.js,不能继续依赖项目内分片文件。
- 在同一站点目录生成测试脚本,消费
run/<domain>/_code.js 的输出完成真实请求或验签请求。只运行 main.js、output.js 或 _code.js 出字符串不算交付完成。
入口规则
- 先确认
main.js 读取的是哪个运行文件;当前仓库是 run/sign.js。
- 如果目标函数没有明确调用入口,先让用户提供,例如
window.xxx.sign(payload)。
- 尽量把用户给的原始加密代码和调用逻辑分开;若当前工程已经以
run/sign.js 作为入口,就沿用它。
- 推荐把最终结果写入
ffglobal.config.logList,例如 ["SIGNATURE", value],便于从 run/<domain>/proxy_log.json 直接定位。
- 调试阶段可以使用
run/sign.js、probe 文件、MCP 保存脚本;验签成功后必须整理到 run/<domain>/_code.js,让用户只需要运行一个脚本。
站点产物目录规则
- 最终产物必须按网站 domain 隔离,优先使用目录:
run/<domain>/。
<domain> 来自目标 URL 或 config/config.js 中的 ffglobal.memory.location.hostname / host / document.domain;例如 live.douyin.com、www.xiaohongshu.com。
- domain 只保留小写字母、数字、点和短横线;不要包含协议、路径、query、端口、空格或中文。
- 普通产物命名:
- 成品脚本:
run/<domain>/_code.js
- 调试启动器:
run/<domain>/output.js
- 日志:
run/<domain>/proxy_log.json
- 请求验证脚本:优先
run/<domain>/request.py 或 run/<domain>/demo.py;不要使用 test / tset 作为文件名前缀。
- Python 请求验证脚本默认使用
loguru 原生配置打印流程日志,按“第一次请求首页 / 匹配并请求 JS / JS 加密得到 sensor_data 或签名 / 提交验签请求 / 打印响应状态和关键 cookie”输出关键步骤。
- 验收不能只看服务端
success 或状态码;生成的 sensor_data、签名、header 中不得出现本地路径、工作目录名、node_modules、vm2、/Users/、C:\\、项目目录名等 Node/补环境痕迹。若出现,优先检查 Error.stack、Error.prepareStackTrace、process / Buffer / __dirname / __filename、document.currentScript.src、函数 toString()。
- 禁止把不同网站的最终成品都写到
run/_code.js 或同一个 run/request.py,避免后续任务互相覆盖。
- 如果当前
main.js 仍固定生成 run/_code.js,必须先改生成逻辑或手动搬运到 run/<domain>/;交付时说明实际目录。
成品脚本规则
run/<domain>/_code.js 是当前补环境框架的最终成品文件。
- 成品脚本必须能独立表达完整流程:环境初始化、必要 JS 加载、目标入口调用、签名/header 输出。
- 成品脚本应提供一个清晰主入口,不要要求用户再从 DevTools 复制变量、手动调用多个 probe、手动拼接 header。
- 输入应来自 curl 文件、请求对象或顶部配置块;不要把一次请求的 body/cookie/timestamp 写死成唯一可用路径。
- 输出应清晰可消费:打印 JSON 或写入
ffglobal.config.logList,包含目标签名值、替换 header、必要的调试摘要。
- 跑通标准不是“临时 probe 能出值”,也不是
_code.js 只打印签名字符串;必须有测试脚本调用 _code.js,读取其输出,再完成目标请求/验签。
交付验收闭环
每个加密/签名任务必须形成三段闭环:
- 生成阶段:运行项目约定命令,例如
node main.js,生成或更新 run/<domain>/_code.js。
- 产物阶段:独立运行
node run/<domain>/_code.js 或 node run/<domain>/output.js,确认它返回结构化结果,至少包含签名值、headers 或业务调用所需字段。输出应尽量是 JSON,便于测试脚本解析。
- 请求阶段:新增或更新同目录请求验证脚本,例如
run/<domain>/request.py、run/<domain>/demo.py 或用户指定文件。不要用 test / tset 作为文件名前缀。验证脚本必须调用 node run/<domain>/_code.js 或 node run/<domain>/output.js,解析输出,并把这些结果用于真实 API 请求或验签请求。
只有请求阶段成功才算交付完成。成功标准应来自真实业务响应,例如:
- HTTP 状态码符合预期。
- 响应 JSON 中
code/success/msg 符合预期。
- 关键业务数据非空或满足用户指定条件。
- 若接口只是验签接口,应明确验证通过字段。
测试脚本禁止重新实现一份签名逻辑来绕过 _code.js。它只能消费 _code.js 输出,把输出填入请求 header/body/query。
成品模式判定
优先使用轻量的框架生成模式,必要时升级为自包含打包模式:
- 框架生成模式:适用于目标代码可以放进
run/sign.js 或框架约定入口,并且 main.js 能自动生成可运行的 run/<domain>/_code.js。交付前必须同时验证生成命令和 node run/<domain>/_code.js 或 node run/<domain>/output.js。
- 自包含打包模式:适用于目标像 XHS 这类依赖多段 live JS、webpack runtime、动态安全脚本、特殊脚本顺序,或
main.js 生成的 _code.js 仍不能独立运行。此时必须把必要的环境代码、目标 JS、签名逻辑和入口调用内嵌到 run/<domain>/_code.js。
- 自包含模式下,
run/<domain>/_code.js 不能再引用项目内相对模块,不能运行时读取 config/、tools/、env/、run/*.live.js、临时 probe 等文件;只允许使用 Node 内置模块和已安装 npm 包。
- 无论哪种模式,最终验收都必须包括
node run/<domain>/_code.js 或 node run/<domain>/output.js 直接输出结果,以及测试脚本消费该输出完成请求。若还需要先打开 DevTools、先跑某个 probe、手动复制变量,说明没有收敛完成。
问题分类
- 框架代码问题:
ffglobal.toolsFunc.xxx is not a function、全局对象未挂载、代理顺序错误、dispatch 包装错误、原型链挂错。直接读仓库并修复,不需要浏览器采样。
- 浏览器值问题:
location.href、document.referrer、navigator.userAgent、screen.width 等。需要真实浏览器采样后再落值。
- 结构检测:
navigator.plugins、navigator.mimeTypes、screen.orientation、Storage、History 等。不要只补一个空对象,要采样 toStringTag、length、索引、item() / namedItem()。
- 原型链/描述符检测:
instanceof、constructor、Object.getOwnPropertyDescriptor、函数 toString()、可枚举/可配置/可写。采样描述符和 getter 所在层级。
- 自动化痕迹检测:
navigator.webdriver、__webdriver_*、__selenium_*、callSelenium、_selenium。多数真实浏览器为不存在或 undefined,但如果本轮要改或影响分支,仍需确认。
浏览器对比范围
不要笼统要求“所有日志都对比”。按下面三档判定:
必须对比
- 本轮准备修改或已经修改的属性、方法、构造函数、原型链、描述符。
- 会导致异常、短路、分支变化、签名变化的日志项。
- 出现在
ENV_MISSING 或 Illegal invocation 中的浏览器 API。
- 强检测字段:
location.href/origin/host/pathname/search/protocol、document.URL/referrer/cookie/domain/readyState/visibilityState、navigator.userAgent/platform/language/languages/webdriver/plugins/mimeTypes/hardwareConcurrency/deviceMemory/userAgentData、screen.width/height/availWidth/availHeight/colorDepth/pixelDepth/orientation、localStorage/sessionStorage。
- 结构、原型链、描述符、原生函数检测项:
Object.prototype.toString.call(x)、instanceof、constructor、__proto__、Object.getOwnPropertyDescriptor、函数 toString()。
建议对比
- 日志中高频出现的
对象为undefined。
ffglobal.memory 已有值但看起来可能与当前域名、UA、屏幕、cookie 不一致。
- 会进入更深检测路径的对象,例如
document.createElement("canvas")、crypto.subtle、fetch、WebSocket。
可暂缓
- 未修改、未阻塞、未影响分支的普通探测项。
- 自动化痕迹字段只是被读取且当前返回
undefined,并且本轮没有改它。
- 暂缓项必须在回复中标注“未采样,不能判定真实一致”,不要把“能出签名”当作完全正确。
采样方式
- 优先使用可用的真实浏览器工具或
js-reverse-mcp。
- 如果没有
js-reverse-mcp,使用 in-app browser、Playwright、浏览器控制台或用户提供的真实浏览器结果。
- 如果无法采样,明确写“未采样”,并只做不依赖真实值的框架修复。
常用采样脚本:
() => ({
href: location.href,
referrer: document.referrer,
url: document.URL,
ua: navigator.userAgent,
platform: navigator.platform,
webdriverDesc: Object.getOwnPropertyDescriptor(Navigator.prototype, "webdriver"),
pluginsTag: Object.prototype.toString.call(navigator.plugins),
pluginsLength: navigator.plugins && navigator.plugins.length,
mimeTypesTag: Object.prototype.toString.call(navigator.mimeTypes),
screen: {
width: screen.width,
height: screen.height,
availWidth: screen.availWidth,
availHeight: screen.availHeight,
colorDepth: screen.colorDepth,
pixelDepth: screen.pixelDepth
}
})
修改规则
- 所有浏览器方法优先走项目现有
dispatch。
- getter / setter 命名遵守
_get / _set。
- 构造函数、原型链、
Symbol.toStringTag、函数 toString 保护遵守现有模式。
- 静态常量需要时在构造函数和原型上各定义一遍。
- 不要为了跑通一次,把大量无关环境一起补进去。
- 不要凭记忆伪造浏览器值;真实值不可得时,标注未采样。
交付要求
每轮结束时简要说明:
- 本轮修了什么,改了哪些文件。
node main.js 是否通过,返回值或错误是什么。
run/<domain>/_code.js 使用的是框架生成模式还是自包含打包模式,是否已经更新为最终成品,能否单独运行。
- 测试脚本文件名是什么,是否通过调用
node run/<domain>/_code.js 获取结果,真实请求/验签响应是什么。
- 本轮目标日志是否消失。
- 对比结果只覆盖“必须对比”和本轮实际修改目标;其他项标注未采样即可。