File size: 4,552 Bytes
bc0be9c |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 |
import { JSDOM } from "npm:jsdom";
import { userAgent } from "./utils.ts";
import crypto from "node:crypto";
// 更详细的类型定义
interface MockMeta {
readonly origin: string;
readonly stack: string;
readonly duration: number;
}
interface HashResults {
server_hashes: string[];
client_hashes: string[];
signals: Record<string, unknown>;
meta: Record<string, unknown>;
}
interface JSExecutionResult {
server_hashes?: string[];
client_hashes?: string[];
signals?: Record<string, unknown>;
meta?: Record<string, unknown>;
}
const mockMeta: MockMeta = {
origin: "https://duckduckgo.com",
stack: "",
duration: Math.floor(Math.random() * 100) + 1,
} as const;
class VqdHashGenerator {
private static extractIIFEJSDOM(jsCode: string): HashResults {
const results: HashResults = {
server_hashes: [],
client_hashes: [],
signals: {},
meta: {},
};
let dom: JSDOM | null = null;
try {
dom = new JSDOM(
`<!DOCTYPE html><html><head></head><body></body></html>`,
{
url: "https://duckduckgo.com",
referrer: "https://duckduckgo.com",
pretendToBeVisual: true,
resources: "usable",
}
);
const { window } = dom;
// 设置全局变量
globalThis.window = window;
globalThis.document = window.document;
// 创建自定义 navigator 对象
const customNavigator = {
userAgent: userAgent,
platform: "Win32",
language: "en-US",
languages: ["en-US", "en"],
cookieEnabled: true,
onLine: true,
hardwareConcurrency: 4,
maxTouchPoints: 0,
vendor: "Google Inc.",
vendorSub: "",
productSub: "20030107",
appName: "Netscape",
appVersion: userAgent,
product: "Gecko",
};
// 重新定义 window.navigator
Object.defineProperty(window, "navigator", {
value: customNavigator,
writable: true,
configurable: true,
enumerable: true,
});
// 设置 globalThis.navigator
Object.defineProperty(globalThis, "navigator", {
value: customNavigator,
writable: true,
configurable: true,
enumerable: true,
});
// 删除 webdriver 属性
delete (window.navigator as any).webdriver;
// 执行 JavaScript 代码
const result = window.eval(jsCode) as JSExecutionResult;
if (result && typeof result === "object") {
results.server_hashes = result.server_hashes || [];
results.client_hashes = result.client_hashes || [];
results.signals = result.signals || {};
results.meta = result.meta || {};
}
} catch (error) {
console.error("JSDOM execution failed:", error);
throw new Error(
`JSDOM执行失败: ${
error instanceof Error ? error.message : String(error)
}`
);
} finally {
// 清理资源
if (dom) {
dom.window.close();
}
delete globalThis.window;
delete globalThis.document;
delete globalThis.navigator;
}
return results;
}
private static hashClientValues(mockClientHashes: string[]): string[] {
return mockClientHashes.map((value: string): string => {
const hash = crypto.createHash("sha256");
hash.update(value, "utf8");
return hash.digest("base64");
});
}
static generate(vqdHashRequest: string): string {
if (!vqdHashRequest || typeof vqdHashRequest !== "string") {
throw new Error("VQD Hash 请求参数无效");
}
try {
// 解码 base64
const jsCode = atob(vqdHashRequest);
// 提取和处理 hash
const hash = this.extractIIFEJSDOM(jsCode);
// 处理客户端 hash
hash.client_hashes = this.hashClientValues(hash.client_hashes);
// 合并 meta 数据
hash.meta = {
...hash.meta,
...mockMeta,
};
// 返回编码后的结果
return btoa(JSON.stringify(hash));
} catch (error) {
console.error("generateVqdHash 执行失败:", error);
throw new Error(
`VQD Hash 生成失败: ${
error instanceof Error ? error.message : String(error)
}`
);
}
}
}
// 导出函数版本以保持向后兼容
export function generateVqdHash(vqdHashRequest: string): string {
return VqdHashGenerator.generate(vqdHashRequest);
}
// 导出类版本
export { VqdHashGenerator };
export type { HashResults, MockMeta, JSExecutionResult };
|