http_mimic 0.5.3 → 0.5.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +30 -0
- data/http_mimic.gemspec +1 -1
- data/lib/http_mimic/command_builder.rb +5 -3
- data/lib/http_mimic/configuration.rb +1 -1
- data/lib/http_mimic/request.rb +94 -41
- data/lib/http_mimic/response.rb +22 -3
- data/lib/http_mimic/response_parser.rb +6 -0
- data/lib/http_mimic/spa_detector.rb +1 -1
- data/lib/http_mimic/version.rb +2 -1
- data/lib/http_mimic/waf/akamai_solver.rb +172 -45
- data/lib/http_mimic/waf/browser_context.js +1297 -0
- metadata +2 -1
|
@@ -0,0 +1,1297 @@
|
|
|
1
|
+
// Virtual Browser Environment Polyfill for QuickJS
|
|
2
|
+
(function(global) {
|
|
3
|
+
"use strict";
|
|
4
|
+
|
|
5
|
+
// 1. Establish window hierarchy
|
|
6
|
+
global.window = global;
|
|
7
|
+
global.self = global;
|
|
8
|
+
global.top = global;
|
|
9
|
+
global.parent = global;
|
|
10
|
+
global.name = "";
|
|
11
|
+
global.innerWidth = 1920;
|
|
12
|
+
global.innerHeight = 960;
|
|
13
|
+
global.outerWidth = 1920;
|
|
14
|
+
global.outerHeight = 1080;
|
|
15
|
+
global.screenX = 0;
|
|
16
|
+
global.screenY = 25;
|
|
17
|
+
global.screenLeft = 0;
|
|
18
|
+
global.screenTop = 25;
|
|
19
|
+
global.crossOriginIsolated = false;
|
|
20
|
+
global.isSecureContext = true;
|
|
21
|
+
global.devicePixelRatio = 2;
|
|
22
|
+
global.chrome = {
|
|
23
|
+
app: { isInstalled: false, InstallState: { DISABLED: "disabled", INSTALLED: "installed", NOT_INSTALLED: "not_installed" }, RunningState: { CANNOT_RUN: "cannot_run", READY_TO_RUN: "ready_to_run", RUNNING: "running" } },
|
|
24
|
+
runtime: { id: undefined }
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
// Helper to make functions look like native functions & shield from prototype inspection
|
|
28
|
+
const nativeFnMap = new WeakMap();
|
|
29
|
+
const oldFunctionToString = Function.prototype.toString;
|
|
30
|
+
|
|
31
|
+
function patchNative(fn, name) {
|
|
32
|
+
if (!fn) return fn;
|
|
33
|
+
try {
|
|
34
|
+
Object.defineProperty(fn, "name", { value: name, configurable: true });
|
|
35
|
+
} catch(e) {}
|
|
36
|
+
const nativeStr = "function " + name + "() { [native code] }";
|
|
37
|
+
nativeFnMap.set(fn, nativeStr);
|
|
38
|
+
try {
|
|
39
|
+
fn.toString = function() { return nativeStr; };
|
|
40
|
+
nativeFnMap.set(fn.toString, "function toString() { [native code] }");
|
|
41
|
+
} catch(e) {}
|
|
42
|
+
return fn;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
Function.prototype.toString = function() {
|
|
46
|
+
if (nativeFnMap.has(this)) {
|
|
47
|
+
return nativeFnMap.get(this);
|
|
48
|
+
}
|
|
49
|
+
return oldFunctionToString.call(this);
|
|
50
|
+
};
|
|
51
|
+
patchNative(Function.prototype.toString, "toString");
|
|
52
|
+
|
|
53
|
+
// V8 Error Stack Trace Helpers
|
|
54
|
+
if (!Error.captureStackTrace) {
|
|
55
|
+
Error.captureStackTrace = patchNative(function(targetObj, constructorOpt) {
|
|
56
|
+
const err = new Error();
|
|
57
|
+
targetObj.stack = err.stack;
|
|
58
|
+
}, "captureStackTrace");
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Modern browser global APIs
|
|
62
|
+
function fetch(input, init) {
|
|
63
|
+
return Promise.resolve({
|
|
64
|
+
ok: true,
|
|
65
|
+
status: 200,
|
|
66
|
+
statusText: "OK",
|
|
67
|
+
headers: { get: function() { return null; } },
|
|
68
|
+
text: patchNative(function() { return Promise.resolve(""); }, "text"),
|
|
69
|
+
json: patchNative(function() { return Promise.resolve({}); }, "json"),
|
|
70
|
+
blob: patchNative(function() { return Promise.resolve({}); }, "blob")
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
patchNative(fetch, "fetch");
|
|
74
|
+
global.fetch = fetch;
|
|
75
|
+
|
|
76
|
+
function Notification(title, options) {
|
|
77
|
+
this.title = String(title || "");
|
|
78
|
+
}
|
|
79
|
+
Notification.permission = "default";
|
|
80
|
+
Notification.requestPermission = patchNative(function() {
|
|
81
|
+
return Promise.resolve("default");
|
|
82
|
+
}, "requestPermission");
|
|
83
|
+
patchNative(Notification, "Notification");
|
|
84
|
+
global.Notification = Notification;
|
|
85
|
+
|
|
86
|
+
const speechSynthesis = {
|
|
87
|
+
pending: false,
|
|
88
|
+
speaking: false,
|
|
89
|
+
paused: false,
|
|
90
|
+
onvoiceschanged: null,
|
|
91
|
+
getVoices: patchNative(function() { return []; }, "getVoices"),
|
|
92
|
+
speak: patchNative(function() {}, "speak"),
|
|
93
|
+
cancel: patchNative(function() {}, "cancel"),
|
|
94
|
+
pause: patchNative(function() {}, "pause"),
|
|
95
|
+
resume: patchNative(function() {}, "resume"),
|
|
96
|
+
addEventListener: patchNative(function() {}, "addEventListener"),
|
|
97
|
+
removeEventListener: patchNative(function() {}, "removeEventListener"),
|
|
98
|
+
dispatchEvent: patchNative(function() { return true; }, "dispatchEvent")
|
|
99
|
+
};
|
|
100
|
+
Object.defineProperty(global, "speechSynthesis", { value: speechSynthesis, writable: true, configurable: true });
|
|
101
|
+
|
|
102
|
+
// 2. Base Prototypes & WebIDL hierarchy
|
|
103
|
+
function EventTarget() {}
|
|
104
|
+
function Node() {}
|
|
105
|
+
function Element() {}
|
|
106
|
+
function HTMLElement() {}
|
|
107
|
+
function HTMLDocument() {}
|
|
108
|
+
function Document() {}
|
|
109
|
+
function Window() {}
|
|
110
|
+
|
|
111
|
+
Object.setPrototypeOf(Node.prototype, EventTarget.prototype);
|
|
112
|
+
Object.setPrototypeOf(Element.prototype, Node.prototype);
|
|
113
|
+
Object.setPrototypeOf(HTMLElement.prototype, Element.prototype);
|
|
114
|
+
Object.setPrototypeOf(Document.prototype, Node.prototype);
|
|
115
|
+
Object.setPrototypeOf(HTMLDocument.prototype, Document.prototype);
|
|
116
|
+
|
|
117
|
+
patchNative(EventTarget, "EventTarget");
|
|
118
|
+
patchNative(Node, "Node");
|
|
119
|
+
patchNative(Element, "Element");
|
|
120
|
+
patchNative(HTMLElement, "HTMLElement");
|
|
121
|
+
patchNative(Document, "Document");
|
|
122
|
+
patchNative(HTMLDocument, "HTMLDocument");
|
|
123
|
+
patchNative(Window, "Window");
|
|
124
|
+
|
|
125
|
+
global.EventTarget = EventTarget;
|
|
126
|
+
global.Node = Node;
|
|
127
|
+
global.Element = Element;
|
|
128
|
+
global.HTMLElement = HTMLElement;
|
|
129
|
+
global.Document = Document;
|
|
130
|
+
global.HTMLDocument = HTMLDocument;
|
|
131
|
+
global.Window = Window;
|
|
132
|
+
|
|
133
|
+
// Generic constructor generator
|
|
134
|
+
const genericConstructors = [
|
|
135
|
+
"HTMLDivElement", "HTMLSpanElement", "HTMLImageElement", "HTMLAnchorElement",
|
|
136
|
+
"HTMLHeadElement", "HTMLBodyElement", "HTMLScriptElement", "HTMLStyleElement",
|
|
137
|
+
"HTMLLinkElement", "HTMLMetaElement", "HTMLTitleElement", "HTMLParagraphElement",
|
|
138
|
+
"HTMLHeadingElement", "HTMLInputElement", "HTMLButtonElement", "HTMLFormElement",
|
|
139
|
+
"HTMLSelectElement", "HTMLOptionElement", "HTMLTextAreaElement", "HTMLTableElement",
|
|
140
|
+
"HTMLTableRowElement", "HTMLTableCellElement", "HTMLAudioElement", "HTMLVideoElement",
|
|
141
|
+
"HTMLIFrameElement", "HTMLCanvasElement", "CanvasRenderingContext2D", "WebGLRenderingContext",
|
|
142
|
+
"WebGL2RenderingContext", "AudioContext", "webkitAudioContext", "OfflineAudioContext",
|
|
143
|
+
"AudioBuffer", "AudioNode", "GainNode", "OscillatorNode", "DynamicsCompressorNode",
|
|
144
|
+
"AnalyserNode", "Event", "CustomEvent", "UIEvent", "MouseEvent", "KeyboardEvent",
|
|
145
|
+
"TouchEvent", "PointerEvent", "FocusEvent", "InputEvent", "WheelEvent",
|
|
146
|
+
"DeviceOrientationEvent", "DeviceMotionEvent", "Performance", "PerformanceEntry",
|
|
147
|
+
"PerformanceNavigationTiming", "PerformanceResourceTiming", "Storage", "File",
|
|
148
|
+
"FileList", "FileReader", "Blob", "FormData", "Headers", "Request", "Response",
|
|
149
|
+
"AbortController", "AbortSignal", "WebSocket", "Worker", "SharedWorker", "ServiceWorker",
|
|
150
|
+
"MutationObserver", "IntersectionObserver", "ResizeObserver", "PerformanceObserver",
|
|
151
|
+
"URLSearchParams", "DOMParser", "XMLSerializer", "XPathEvaluator", "XPathResult",
|
|
152
|
+
"MediaStream", "RTCPeerConnection", "IDBFactory", "IDBDatabase", "Cache", "CacheStorage",
|
|
153
|
+
"Credential", "CredentialsContainer", "Navigator", "Screen", "History", "Location",
|
|
154
|
+
"BarProp", "VisualViewport", "SpeechSynthesis", "SpeechSynthesisUtterance",
|
|
155
|
+
"PaymentRequest", "Bluetooth", "USB", "HID", "Serial", "XRSystem", "WakeLock",
|
|
156
|
+
"Geolocation", "BatteryManager", "NetworkInformation", "Permissions", "PermissionStatus"
|
|
157
|
+
];
|
|
158
|
+
|
|
159
|
+
genericConstructors.forEach(name => {
|
|
160
|
+
if (typeof global[name] === "undefined") {
|
|
161
|
+
function Ctor() {}
|
|
162
|
+
Ctor.prototype = Object.create(HTMLElement.prototype);
|
|
163
|
+
Ctor.prototype.constructor = Ctor;
|
|
164
|
+
patchNative(Ctor, name);
|
|
165
|
+
global[name] = Ctor;
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
function parseUrlComponents(urlString, baseUrl) {
|
|
170
|
+
let str = String(urlString || "");
|
|
171
|
+
if (!str.startsWith("http://") && !str.startsWith("https://")) {
|
|
172
|
+
const base = baseUrl || (typeof location !== "undefined" ? location.href : (global.__TARGET_URL__ || "http://localhost/"));
|
|
173
|
+
if (str.startsWith("//")) {
|
|
174
|
+
const proto = (typeof location !== "undefined" ? location.protocol : "https:");
|
|
175
|
+
str = proto + str;
|
|
176
|
+
} else if (str.startsWith("/")) {
|
|
177
|
+
const orig = (typeof location !== "undefined" ? location.origin : "http://localhost");
|
|
178
|
+
str = orig + str;
|
|
179
|
+
} else {
|
|
180
|
+
const orig = (typeof location !== "undefined" ? location.origin : "http://localhost");
|
|
181
|
+
str = orig + "/" + str;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
const match = str.match(/^(https?:)\/\/([^/:?#]+)(?::(\d+))?([^?#]*)(?:\?([^#]*))?(?:#(.*))?$/);
|
|
185
|
+
if (!match) return { href: str, protocol: "https:", host: "", hostname: "", port: "", pathname: "/", search: "", hash: "", origin: "" };
|
|
186
|
+
const proto = match[1];
|
|
187
|
+
const hostName = match[2];
|
|
188
|
+
const portNum = match[3] || "";
|
|
189
|
+
const fullHost = portNum ? `${hostName}:${portNum}` : hostName;
|
|
190
|
+
return {
|
|
191
|
+
href: str,
|
|
192
|
+
protocol: proto,
|
|
193
|
+
hostname: hostName,
|
|
194
|
+
port: portNum,
|
|
195
|
+
host: fullHost,
|
|
196
|
+
origin: `${proto}//${fullHost}`,
|
|
197
|
+
pathname: match[4] || "/",
|
|
198
|
+
search: match[5] ? `?${match[5]}` : "",
|
|
199
|
+
hash: match[6] ? `#${match[6]}` : ""
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function MockURL(url, base) {
|
|
204
|
+
const p = parseUrlComponents(url, base);
|
|
205
|
+
Object.assign(this, p);
|
|
206
|
+
this.toString = function() { return this.href; };
|
|
207
|
+
}
|
|
208
|
+
patchNative(MockURL, "URL");
|
|
209
|
+
global.URL = MockURL;
|
|
210
|
+
|
|
211
|
+
// 3. Location object
|
|
212
|
+
const currentUrl = global.__TARGET_URL__ || "http://localhost/";
|
|
213
|
+
const parsedUrl = parseUrlComponents(currentUrl);
|
|
214
|
+
|
|
215
|
+
const location = {
|
|
216
|
+
...parsedUrl,
|
|
217
|
+
assign: patchNative(function(url) {}, "assign"),
|
|
218
|
+
replace: patchNative(function(url) {}, "replace"),
|
|
219
|
+
reload: patchNative(function() {}, "reload"),
|
|
220
|
+
toString: patchNative(function() { return this.href; }, "toString")
|
|
221
|
+
};
|
|
222
|
+
Object.defineProperty(global, "location", { value: location, writable: true, configurable: true });
|
|
223
|
+
|
|
224
|
+
const history = {
|
|
225
|
+
length: 1,
|
|
226
|
+
scrollRestoration: "auto",
|
|
227
|
+
state: null,
|
|
228
|
+
back: patchNative(function() {}, "back"),
|
|
229
|
+
forward: patchNative(function() {}, "forward"),
|
|
230
|
+
go: patchNative(function(delta) {}, "go"),
|
|
231
|
+
pushState: patchNative(function(state, title, url) { this.state = state; }, "pushState"),
|
|
232
|
+
replaceState: patchNative(function(state, title, url) { this.state = state; }, "replaceState")
|
|
233
|
+
};
|
|
234
|
+
Object.defineProperty(global, "history", { value: history, writable: true, configurable: true });
|
|
235
|
+
|
|
236
|
+
// 4. Navigator object
|
|
237
|
+
const defaultUA = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36";
|
|
238
|
+
const userAgent = global.__USER_AGENT__ || defaultUA;
|
|
239
|
+
const isAndroid = userAgent.includes("Android");
|
|
240
|
+
const isIOS = userAgent.includes("iPhone") || userAgent.includes("iPad");
|
|
241
|
+
const isMobile = isAndroid || isIOS || userAgent.includes("Mobile");
|
|
242
|
+
|
|
243
|
+
const platform = global.__PLATFORM__ || (userAgent.includes("Win") ? "Win32" : (isAndroid ? "Linux armv8l" : (isIOS ? "iPhone" : "MacIntel")));
|
|
244
|
+
const language = global.__LANGUAGE__ || "en-US";
|
|
245
|
+
|
|
246
|
+
if (isMobile) {
|
|
247
|
+
global.innerWidth = 412;
|
|
248
|
+
global.innerHeight = 823;
|
|
249
|
+
global.outerWidth = 412;
|
|
250
|
+
global.outerHeight = 915;
|
|
251
|
+
global.screenX = 0;
|
|
252
|
+
global.screenY = 0;
|
|
253
|
+
global.screenLeft = 0;
|
|
254
|
+
global.screenTop = 0;
|
|
255
|
+
global.devicePixelRatio = 2.625;
|
|
256
|
+
global.ontouchstart = null;
|
|
257
|
+
global.ontouchend = null;
|
|
258
|
+
global.ontouchmove = null;
|
|
259
|
+
global.ontouchcancel = null;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
const navigator = {
|
|
263
|
+
userAgent: userAgent,
|
|
264
|
+
appVersion: userAgent.replace(/^Mozilla\//, ""),
|
|
265
|
+
appName: "Netscape",
|
|
266
|
+
appCodeName: "Mozilla",
|
|
267
|
+
platform: platform,
|
|
268
|
+
product: "Gecko",
|
|
269
|
+
productSub: "20030107",
|
|
270
|
+
vendor: userAgent.includes("Chrome") || userAgent.includes("Chromium") ? "Google Inc." : (userAgent.includes("Apple") ? "Apple Computer, Inc." : ""),
|
|
271
|
+
vendorSub: "",
|
|
272
|
+
language: language,
|
|
273
|
+
languages: Object.freeze([language, language.split("-")[0]].filter(Boolean)),
|
|
274
|
+
cookieEnabled: true,
|
|
275
|
+
hardwareConcurrency: 8,
|
|
276
|
+
deviceMemory: 8,
|
|
277
|
+
webdriver: false,
|
|
278
|
+
maxTouchPoints: isMobile ? 5 : 0,
|
|
279
|
+
onLine: true,
|
|
280
|
+
doNotTrack: null,
|
|
281
|
+
plugins: (function() {
|
|
282
|
+
const list = isMobile ? [] : [
|
|
283
|
+
{ name: "PDF Viewer", filename: "internal-pdf-viewer", description: "Portable Document Format" },
|
|
284
|
+
{ name: "Chrome PDF Viewer", filename: "internal-pdf-viewer", description: "Portable Document Format" },
|
|
285
|
+
{ name: "Chromium PDF Viewer", filename: "internal-pdf-viewer", description: "Portable Document Format" },
|
|
286
|
+
{ name: "Microsoft Edge PDF Viewer", filename: "internal-pdf-viewer", description: "Portable Document Format" },
|
|
287
|
+
{ name: "WebKit built-in PDF", filename: "internal-pdf-viewer", description: "Portable Document Format" }
|
|
288
|
+
];
|
|
289
|
+
list.item = patchNative(function(i) { return list[i] || null; }, "item");
|
|
290
|
+
list.namedItem = patchNative(function(name) { return list.find(p => p.name === name) || null; }, "namedItem");
|
|
291
|
+
list.refresh = patchNative(function() {}, "refresh");
|
|
292
|
+
return Object.freeze(list);
|
|
293
|
+
})(),
|
|
294
|
+
mimeTypes: (function() {
|
|
295
|
+
const list = isMobile ? [] : [
|
|
296
|
+
{ type: "application/pdf", suffixes: "pdf", description: "Portable Document Format" },
|
|
297
|
+
{ type: "text/pdf", suffixes: "pdf", description: "Portable Document Format" }
|
|
298
|
+
];
|
|
299
|
+
list.item = patchNative(function(i) { return list[i] || null; }, "item");
|
|
300
|
+
list.namedItem = patchNative(function(type) { return list.find(m => m.type === type) || null; }, "namedItem");
|
|
301
|
+
return Object.freeze(list);
|
|
302
|
+
})(),
|
|
303
|
+
connection: {
|
|
304
|
+
effectiveType: "4g",
|
|
305
|
+
rtt: 50,
|
|
306
|
+
downlink: 10,
|
|
307
|
+
saveData: false,
|
|
308
|
+
addEventListener: patchNative(function() {}, "addEventListener")
|
|
309
|
+
},
|
|
310
|
+
mediaDevices: {
|
|
311
|
+
enumerateDevices: patchNative(function() {
|
|
312
|
+
return Promise.resolve([
|
|
313
|
+
{ deviceId: "default", kind: "audioinput", label: "", groupId: "f7a39d8e" },
|
|
314
|
+
{ deviceId: "default", kind: "videoinput", label: "", groupId: "f7a39d8e" },
|
|
315
|
+
{ deviceId: "default", kind: "audiooutput", label: "", groupId: "f7a39d8e" }
|
|
316
|
+
]);
|
|
317
|
+
}, "enumerateDevices"),
|
|
318
|
+
addEventListener: patchNative(function() {}, "addEventListener"),
|
|
319
|
+
removeEventListener: patchNative(function() {}, "removeEventListener"),
|
|
320
|
+
dispatchEvent: patchNative(function() { return true; }, "dispatchEvent")
|
|
321
|
+
},
|
|
322
|
+
permissions: {
|
|
323
|
+
query: patchNative(function(desc) {
|
|
324
|
+
const name = (desc && desc.name) || "";
|
|
325
|
+
let state = "granted";
|
|
326
|
+
if (name === "notifications" || name === "geolocation" || name === "camera" || name === "microphone") {
|
|
327
|
+
state = "prompt";
|
|
328
|
+
}
|
|
329
|
+
return Promise.resolve({
|
|
330
|
+
state: state,
|
|
331
|
+
name: name,
|
|
332
|
+
onchange: null,
|
|
333
|
+
addEventListener: patchNative(function() {}, "addEventListener"),
|
|
334
|
+
removeEventListener: patchNative(function() {}, "removeEventListener"),
|
|
335
|
+
dispatchEvent: patchNative(function() { return true; }, "dispatchEvent")
|
|
336
|
+
});
|
|
337
|
+
}, "query")
|
|
338
|
+
},
|
|
339
|
+
userAgentData: {
|
|
340
|
+
brands: Object.freeze([
|
|
341
|
+
{ brand: "Chromium", version: (userAgent.match(/Chrome\/(\d+)/) || [null, "131"])[1] },
|
|
342
|
+
{ brand: "Not?A_Brand", version: "24" },
|
|
343
|
+
{ brand: "Google Chrome", version: (userAgent.match(/Chrome\/(\d+)/) || [null, "131"])[1] }
|
|
344
|
+
]),
|
|
345
|
+
mobile: isMobile,
|
|
346
|
+
platform: isAndroid ? "Android" : (isIOS ? "iOS" : (platform.includes("Win") ? "Windows" : (platform.includes("Linux") ? "Linux" : "macOS"))),
|
|
347
|
+
getHighEntropyValues: patchNative(function(hints) {
|
|
348
|
+
const ver = (userAgent.match(/Chrome\/(\d+)/) || [null, "131"])[1];
|
|
349
|
+
const fullVer = ver + ".0.6778.86";
|
|
350
|
+
return Promise.resolve({
|
|
351
|
+
architecture: isMobile ? "arm" : "x86",
|
|
352
|
+
bitness: "64",
|
|
353
|
+
brands: this.brands,
|
|
354
|
+
fullVersionList: [
|
|
355
|
+
{ brand: "Chromium", version: fullVer },
|
|
356
|
+
{ brand: "Not?A_Brand", version: "24.0.0.0" },
|
|
357
|
+
{ brand: "Google Chrome", version: fullVer }
|
|
358
|
+
],
|
|
359
|
+
mobile: this.mobile,
|
|
360
|
+
model: isAndroid ? "K" : "",
|
|
361
|
+
platform: this.platform,
|
|
362
|
+
platformVersion: isAndroid ? "10.0.0" : "13.6.9",
|
|
363
|
+
uaFullVersion: fullVer
|
|
364
|
+
});
|
|
365
|
+
}, "getHighEntropyValues"),
|
|
366
|
+
toJSON: patchNative(function() {
|
|
367
|
+
return { brands: this.brands, mobile: this.mobile, platform: this.platform };
|
|
368
|
+
}, "toJSON")
|
|
369
|
+
},
|
|
370
|
+
getBattery: patchNative(function() {
|
|
371
|
+
return Promise.resolve({
|
|
372
|
+
charging: true,
|
|
373
|
+
chargingTime: 0,
|
|
374
|
+
dischargingTime: Infinity,
|
|
375
|
+
level: 1,
|
|
376
|
+
addEventListener: patchNative(function() {}, "addEventListener")
|
|
377
|
+
});
|
|
378
|
+
}, "getBattery"),
|
|
379
|
+
javaEnabled: patchNative(function() { return false; }, "javaEnabled"),
|
|
380
|
+
vibrate: patchNative(function() { return true; }, "vibrate")
|
|
381
|
+
};
|
|
382
|
+
Object.defineProperty(global, "navigator", { value: navigator, writable: true, configurable: true });
|
|
383
|
+
|
|
384
|
+
// 5. Screen object
|
|
385
|
+
const screen = isMobile ? {
|
|
386
|
+
width: 412,
|
|
387
|
+
height: 915,
|
|
388
|
+
availWidth: 412,
|
|
389
|
+
availHeight: 915,
|
|
390
|
+
colorDepth: 24,
|
|
391
|
+
pixelDepth: 24,
|
|
392
|
+
availLeft: 0,
|
|
393
|
+
availTop: 0,
|
|
394
|
+
orientation: { type: "portrait-primary", angle: 0, addEventListener: function() {} }
|
|
395
|
+
} : {
|
|
396
|
+
width: 1920,
|
|
397
|
+
height: 1080,
|
|
398
|
+
availWidth: 1920,
|
|
399
|
+
availHeight: 1055,
|
|
400
|
+
colorDepth: 24,
|
|
401
|
+
pixelDepth: 24,
|
|
402
|
+
availLeft: 0,
|
|
403
|
+
availTop: 25,
|
|
404
|
+
orientation: { type: "landscape-primary", angle: 0, addEventListener: function() {} }
|
|
405
|
+
};
|
|
406
|
+
Object.defineProperty(global, "screen", { value: screen, writable: true, configurable: true });
|
|
407
|
+
|
|
408
|
+
// 6. DOM Element Factory & Authentic Font Metrics
|
|
409
|
+
const REAL_FONT_METRICS = {
|
|
410
|
+
"monospace": { "12px": [94, 14], "14px": [110, 16], "16px": [125, 19], "20px": [157, 24], "24px": [188, 28], "32px": [250, 38], "72px": [564, 84] },
|
|
411
|
+
"sans-serif": { "12px": [111, 17], "14px": [130, 20], "16px": [148, 22], "20px": [186, 28], "24px": [223, 33], "32px": [297, 45], "72px": [668, 100] },
|
|
412
|
+
"serif": { "12px": [104, 17], "14px": [121, 20], "16px": [139, 22], "20px": [173, 28], "24px": [208, 33], "32px": [277, 45], "72px": [624, 100] },
|
|
413
|
+
"Arial": { "12px": [108, 14], "14px": [126, 16], "16px": [144, 19], "20px": [180, 24], "24px": [216, 28], "32px": [288, 38], "72px": [648, 84] },
|
|
414
|
+
"Times New Roman": { "12px": [104, 14], "14px": [121, 16], "16px": [139, 19], "20px": [173, 24], "24px": [208, 28], "32px": [277, 38], "72px": [624, 84] },
|
|
415
|
+
"Courier New": { "12px": [101, 14], "14px": [118, 16], "16px": [135, 19], "20px": [168, 24], "24px": [202, 28], "32px": [269, 38], "72px": [605, 84] },
|
|
416
|
+
"Helvetica": { "12px": [108, 14], "14px": [126, 16], "16px": [144, 19], "20px": [180, 24], "24px": [216, 28], "32px": [288, 38], "72px": [648, 84] },
|
|
417
|
+
"Verdana": { "12px": [116, 15], "14px": [135, 17], "16px": [154, 20], "20px": [193, 25], "24px": [232, 29], "32px": [309, 39], "72px": [695, 87] },
|
|
418
|
+
"Georgia": { "12px": [109, 14], "14px": [127, 16], "16px": [145, 19], "20px": [182, 24], "24px": [218, 28], "32px": [291, 38], "72px": [655, 84] },
|
|
419
|
+
"Impact": { "12px": [82, 15], "14px": [96, 17], "16px": [109, 20], "20px": [137, 25], "24px": [164, 29], "32px": [219, 39], "72px": [492, 87] },
|
|
420
|
+
"Trebuchet MS": { "12px": [106, 14], "14px": [124, 16], "16px": [141, 19], "20px": [177, 24], "24px": [212, 28], "32px": [283, 38], "72px": [637, 84] },
|
|
421
|
+
"Tahoma": { "12px": [109, 15], "14px": [127, 17], "16px": [145, 20], "20px": [182, 25], "24px": [218, 29], "32px": [291, 39], "72px": [655, 87] }
|
|
422
|
+
};
|
|
423
|
+
|
|
424
|
+
const eventListeners = {};
|
|
425
|
+
const trackedScripts = [];
|
|
426
|
+
function addEventListener(type, listener) {
|
|
427
|
+
if (!eventListeners[type]) eventListeners[type] = [];
|
|
428
|
+
eventListeners[type].push(listener);
|
|
429
|
+
}
|
|
430
|
+
function removeEventListener(type, listener) {
|
|
431
|
+
if (!eventListeners[type]) return;
|
|
432
|
+
eventListeners[type] = eventListeners[type].filter(l => l !== listener);
|
|
433
|
+
}
|
|
434
|
+
function dispatchEvent(evt) {
|
|
435
|
+
const list = eventListeners[evt.type] || [];
|
|
436
|
+
list.forEach(fn => { try { fn(evt); } catch(e) {} });
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
const elementsById = {};
|
|
440
|
+
|
|
441
|
+
function createMockElement(tagName) {
|
|
442
|
+
const tag = tagName.toLowerCase();
|
|
443
|
+
const children = [];
|
|
444
|
+
const attributes = {};
|
|
445
|
+
const style = { display: "block", visibility: "visible" };
|
|
446
|
+
|
|
447
|
+
const el = {
|
|
448
|
+
tagName: tagName.toUpperCase(),
|
|
449
|
+
nodeName: tagName.toUpperCase(),
|
|
450
|
+
nodeType: 1,
|
|
451
|
+
style: style,
|
|
452
|
+
children: children,
|
|
453
|
+
childNodes: children,
|
|
454
|
+
parentNode: null,
|
|
455
|
+
clientWidth: tag === "body" || tag === "html" ? 1920 : 300,
|
|
456
|
+
clientHeight: tag === "body" || tag === "html" ? 1080 : 150,
|
|
457
|
+
offsetWidth: tag === "body" || tag === "html" ? 1920 : 300,
|
|
458
|
+
offsetHeight: tag === "body" || tag === "html" ? 1080 : 150,
|
|
459
|
+
offsetLeft: 0,
|
|
460
|
+
offsetTop: 0,
|
|
461
|
+
id: "",
|
|
462
|
+
className: "",
|
|
463
|
+
innerHTML: "",
|
|
464
|
+
innerText: "",
|
|
465
|
+
textContent: "",
|
|
466
|
+
setAttribute: patchNative(function(k, v) {
|
|
467
|
+
attributes[k] = String(v);
|
|
468
|
+
if (k === "id") {
|
|
469
|
+
el.id = String(v);
|
|
470
|
+
elementsById[el.id] = el;
|
|
471
|
+
}
|
|
472
|
+
}, "setAttribute"),
|
|
473
|
+
getAttribute: patchNative(function(k) { return attributes[k] || null; }, "getAttribute"),
|
|
474
|
+
hasAttribute: patchNative(function(k) { return k in attributes; }, "hasAttribute"),
|
|
475
|
+
removeAttribute: patchNative(function(k) {
|
|
476
|
+
delete attributes[k];
|
|
477
|
+
if (k === "id") { delete elementsById[el.id]; el.id = ""; }
|
|
478
|
+
}, "removeAttribute"),
|
|
479
|
+
appendChild: patchNative(function(child) {
|
|
480
|
+
if (child) {
|
|
481
|
+
child.parentNode = el;
|
|
482
|
+
children.push(child);
|
|
483
|
+
}
|
|
484
|
+
return child;
|
|
485
|
+
}, "appendChild"),
|
|
486
|
+
removeChild: patchNative(function(child) {
|
|
487
|
+
const idx = children.indexOf(child);
|
|
488
|
+
if (idx !== -1) {
|
|
489
|
+
children.splice(idx, 1);
|
|
490
|
+
if (child) child.parentNode = null;
|
|
491
|
+
}
|
|
492
|
+
return child;
|
|
493
|
+
}, "removeChild"),
|
|
494
|
+
remove: patchNative(function() {
|
|
495
|
+
if (el.parentNode) {
|
|
496
|
+
el.parentNode.removeChild(el);
|
|
497
|
+
}
|
|
498
|
+
}, "remove"),
|
|
499
|
+
contains: patchNative(function(child) {
|
|
500
|
+
return children.includes(child);
|
|
501
|
+
}, "contains"),
|
|
502
|
+
insertBefore: patchNative(function(node) {
|
|
503
|
+
if (node) {
|
|
504
|
+
node.parentNode = el;
|
|
505
|
+
children.push(node);
|
|
506
|
+
}
|
|
507
|
+
return node;
|
|
508
|
+
}, "insertBefore"),
|
|
509
|
+
addEventListener: patchNative(addEventListener, "addEventListener"),
|
|
510
|
+
removeEventListener: patchNative(removeEventListener, "removeEventListener"),
|
|
511
|
+
dispatchEvent: patchNative(dispatchEvent, "dispatchEvent"),
|
|
512
|
+
getBoundingClientRect: patchNative(function() {
|
|
513
|
+
return { top: 0, left: 0, right: 300, bottom: 150, width: 300, height: 150, x: 0, y: 0 };
|
|
514
|
+
}, "getBoundingClientRect"),
|
|
515
|
+
getElementsByTagName: patchNative(function(t) {
|
|
516
|
+
const lower = (t || "").toLowerCase();
|
|
517
|
+
if (lower === "script") return trackedScripts.length > 0 ? trackedScripts : [createMockElement("script")];
|
|
518
|
+
return [];
|
|
519
|
+
}, "getElementsByTagName")
|
|
520
|
+
};
|
|
521
|
+
|
|
522
|
+
if (tag === "a") {
|
|
523
|
+
let _href = "";
|
|
524
|
+
Object.defineProperty(el, "href", {
|
|
525
|
+
get() { return _href; },
|
|
526
|
+
set(val) {
|
|
527
|
+
_href = String(val);
|
|
528
|
+
attributes["href"] = _href;
|
|
529
|
+
try {
|
|
530
|
+
const u = new URL(_href, location.href);
|
|
531
|
+
el.protocol = u.protocol;
|
|
532
|
+
el.host = u.host;
|
|
533
|
+
el.hostname = u.hostname;
|
|
534
|
+
el.port = u.port;
|
|
535
|
+
el.pathname = u.pathname;
|
|
536
|
+
el.search = u.search;
|
|
537
|
+
el.hash = u.hash;
|
|
538
|
+
el.origin = u.origin;
|
|
539
|
+
} catch(e) {}
|
|
540
|
+
}
|
|
541
|
+
});
|
|
542
|
+
el.protocol = location.protocol;
|
|
543
|
+
el.host = location.host;
|
|
544
|
+
el.hostname = location.hostname;
|
|
545
|
+
el.port = location.port;
|
|
546
|
+
el.pathname = "/";
|
|
547
|
+
el.search = "";
|
|
548
|
+
el.hash = "";
|
|
549
|
+
el.origin = location.origin;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
if (tag === "script") {
|
|
553
|
+
let _src = "";
|
|
554
|
+
Object.defineProperty(el, "src", {
|
|
555
|
+
get: function() { return _src; },
|
|
556
|
+
set: function(val) {
|
|
557
|
+
_src = String(val);
|
|
558
|
+
attributes["src"] = _src;
|
|
559
|
+
},
|
|
560
|
+
configurable: true
|
|
561
|
+
});
|
|
562
|
+
trackedScripts.push(el);
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
|
|
566
|
+
Object.defineProperty(el, "offsetWidth", {
|
|
567
|
+
get: function() {
|
|
568
|
+
const family = el.style.fontFamily || "sans-serif";
|
|
569
|
+
const size = el.style.fontSize || "16px";
|
|
570
|
+
for (const font in REAL_FONT_METRICS) {
|
|
571
|
+
if (family.includes(font)) {
|
|
572
|
+
const match = REAL_FONT_METRICS[font][size] || REAL_FONT_METRICS[font]["16px"];
|
|
573
|
+
return match ? match[0] : 148;
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
return 148;
|
|
577
|
+
},
|
|
578
|
+
configurable: true
|
|
579
|
+
});
|
|
580
|
+
|
|
581
|
+
Object.defineProperty(el, "offsetHeight", {
|
|
582
|
+
get: function() {
|
|
583
|
+
const family = el.style.fontFamily || "sans-serif";
|
|
584
|
+
const size = el.style.fontSize || "16px";
|
|
585
|
+
for (const font in REAL_FONT_METRICS) {
|
|
586
|
+
if (family.includes(font)) {
|
|
587
|
+
const match = REAL_FONT_METRICS[font][size] || REAL_FONT_METRICS[font]["16px"];
|
|
588
|
+
return match ? match[1] : 22;
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
return 22;
|
|
592
|
+
},
|
|
593
|
+
configurable: true
|
|
594
|
+
});
|
|
595
|
+
|
|
596
|
+
if (tag === "canvas") {
|
|
597
|
+
el.getContext = patchNative(function(type) {
|
|
598
|
+
if (type === "2d") {
|
|
599
|
+
return {
|
|
600
|
+
canvas: el,
|
|
601
|
+
fillStyle: "#000",
|
|
602
|
+
strokeStyle: "#000",
|
|
603
|
+
font: "10px sans-serif",
|
|
604
|
+
textBaseline: "alphabetic",
|
|
605
|
+
textAlign: "start",
|
|
606
|
+
fillText: patchNative(function() {}, "fillText"),
|
|
607
|
+
strokeText: patchNative(function() {}, "strokeText"),
|
|
608
|
+
fillRect: patchNative(function() {}, "fillRect"),
|
|
609
|
+
clearRect: patchNative(function() {}, "clearRect"),
|
|
610
|
+
beginPath: patchNative(function() {}, "beginPath"),
|
|
611
|
+
arc: patchNative(function() {}, "arc"),
|
|
612
|
+
closePath: patchNative(function() {}, "closePath"),
|
|
613
|
+
fill: patchNative(function() {}, "fill"),
|
|
614
|
+
stroke: patchNative(function() {}, "stroke"),
|
|
615
|
+
measureText: patchNative(function(txt) {
|
|
616
|
+
return { width: (txt || "").length * 8, actualBoundingBoxAscent: 10, actualBoundingBoxDescent: 2 };
|
|
617
|
+
}, "measureText"),
|
|
618
|
+
getImageData: patchNative(function(x, y, w, h) {
|
|
619
|
+
const width = w || 16;
|
|
620
|
+
const height = h || 16;
|
|
621
|
+
const len = width * height * 4;
|
|
622
|
+
const data = new Uint8ClampedArray(len);
|
|
623
|
+
for (let i = 0; i < len; i += 4) {
|
|
624
|
+
const pixelIndex = i / 4;
|
|
625
|
+
const px = pixelIndex % width;
|
|
626
|
+
const py = Math.floor(pixelIndex / width);
|
|
627
|
+
data[i] = (px > 2 && px < 14 && py > 2 && py < 14) ? 255 : 10;
|
|
628
|
+
data[i + 1] = (px > 4 && py > 4) ? 102 : 20;
|
|
629
|
+
data[i + 2] = (px * 13 + py * 17) % 256;
|
|
630
|
+
data[i + 3] = 255;
|
|
631
|
+
}
|
|
632
|
+
return { data: data, width: width, height: height };
|
|
633
|
+
}, "getImageData"),
|
|
634
|
+
save: patchNative(function() {}, "save"),
|
|
635
|
+
restore: patchNative(function() {}, "restore")
|
|
636
|
+
};
|
|
637
|
+
}
|
|
638
|
+
if (type && type.includes("webgl")) {
|
|
639
|
+
const gl = {
|
|
640
|
+
canvas: el,
|
|
641
|
+
_clearR: 0.8,
|
|
642
|
+
_clearG: 0.4,
|
|
643
|
+
_clearB: 0.2,
|
|
644
|
+
_clearA: 1.0,
|
|
645
|
+
VERTEX_SHADER: 35633,
|
|
646
|
+
FRAGMENT_SHADER: 35632,
|
|
647
|
+
COMPILE_STATUS: 35713,
|
|
648
|
+
LINK_STATUS: 35714,
|
|
649
|
+
COLOR_BUFFER_BIT: 16384,
|
|
650
|
+
DEPTH_BUFFER_BIT: 256,
|
|
651
|
+
TRIANGLES: 4,
|
|
652
|
+
FLOAT: 5126,
|
|
653
|
+
RGBA: 6408,
|
|
654
|
+
UNSIGNED_BYTE: 5121,
|
|
655
|
+
HIGH_FLOAT: 36338,
|
|
656
|
+
MEDIUM_FLOAT: 36337,
|
|
657
|
+
LOW_FLOAT: 36336,
|
|
658
|
+
HIGH_INT: 36341,
|
|
659
|
+
MEDIUM_INT: 36340,
|
|
660
|
+
LOW_INT: 36339,
|
|
661
|
+
ARRAY_BUFFER: 34962,
|
|
662
|
+
STATIC_DRAW: 35044,
|
|
663
|
+
UNPACK_FLIP_Y_WEBGL: 37440,
|
|
664
|
+
TEXTURE_2D: 3553,
|
|
665
|
+
MAX_TEXTURE_SIZE: 16384,
|
|
666
|
+
MAX_VIEWPORT_DIMS: 3386,
|
|
667
|
+
VENDOR: 7936,
|
|
668
|
+
RENDERER: 7937,
|
|
669
|
+
VERSION: 7938,
|
|
670
|
+
SHADING_LANGUAGE_VERSION: 35724,
|
|
671
|
+
|
|
672
|
+
getParameter: patchNative(function(param) {
|
|
673
|
+
if (param === 0x9245 || param === 37445) return isAndroid ? "Qualcomm" : "Google Inc. (Intel)";
|
|
674
|
+
if (param === 0x9246 || param === 37446) return isAndroid ? "Adreno (TM) 640" : "ANGLE (Intel, ANGLE Metal Renderer: Intel(R) UHD Graphics 630, Unspecified Version)";
|
|
675
|
+
if (param === 7936) return "WebKit";
|
|
676
|
+
if (param === 7937) return "WebKit WebGL";
|
|
677
|
+
if (param === 7938) return "WebGL 1.0 (OpenGL ES 2.0 Chromium)";
|
|
678
|
+
if (param === 35724) return "WebGL GLSL ES 1.0 (OpenGL ES GLSL ES 1.0 Chromium)";
|
|
679
|
+
if (param === 3379) return 16384; // MAX_TEXTURE_SIZE
|
|
680
|
+
if (param === 3386) return new Int32Array([16384, 16384]); // MAX_VIEWPORT_DIMS
|
|
681
|
+
if (param === 34076) return 16384; // MAX_CUBE_MAP_TEXTURE_SIZE
|
|
682
|
+
if (param === 34921) return 16; // MAX_VERTEX_ATTRIBS
|
|
683
|
+
if (param === 34930) return 16; // MAX_TEXTURE_IMAGE_UNITS
|
|
684
|
+
if (param === 35661) return 32; // MAX_COMBINED_TEXTURE_IMAGE_UNITS
|
|
685
|
+
if (param === 36347) return 1024; // MAX_FRAGMENT_UNIFORM_VECTORS
|
|
686
|
+
if (param === 36348) return 1024; // MAX_VERTEX_UNIFORM_VECTORS
|
|
687
|
+
if (param === 36349) return 30; // MAX_VARYING_VECTORS
|
|
688
|
+
if (param === 34024) return 16384; // MAX_RENDERBUFFER_SIZE
|
|
689
|
+
if (param === 35373) return 16; // MAX_VERTEX_TEXTURE_IMAGE_UNITS
|
|
690
|
+
if (param === 33901) return new Float32Array([1, 511]); // ALIASED_POINT_SIZE_RANGE
|
|
691
|
+
if (param === 33902) return new Float32Array([1, 1]); // ALIASED_LINE_WIDTH_RANGE
|
|
692
|
+
return 0;
|
|
693
|
+
}, "getParameter"),
|
|
694
|
+
getExtension: patchNative(function(ext) {
|
|
695
|
+
if (ext === "WEBGL_debug_renderer_info") {
|
|
696
|
+
return { UNMASKED_VENDOR_WEBGL: 37445, UNMASKED_RENDERER_WEBGL: 37446 };
|
|
697
|
+
}
|
|
698
|
+
if (ext === "EXT_texture_filter_anisotropic") {
|
|
699
|
+
return { MAX_TEXTURE_MAX_ANISOTROPY_EXT: 34047 };
|
|
700
|
+
}
|
|
701
|
+
return {};
|
|
702
|
+
}, "getExtension"),
|
|
703
|
+
getSupportedExtensions: patchNative(function() {
|
|
704
|
+
return [
|
|
705
|
+
"ANGLE_instanced_arrays",
|
|
706
|
+
"EXT_blend_minmax",
|
|
707
|
+
"EXT_clip_control",
|
|
708
|
+
"EXT_color_buffer_half_float",
|
|
709
|
+
"EXT_depth_clamp",
|
|
710
|
+
"EXT_disjoint_timer_query",
|
|
711
|
+
"EXT_float_blend",
|
|
712
|
+
"EXT_frag_depth",
|
|
713
|
+
"EXT_polygon_offset_clamp",
|
|
714
|
+
"EXT_shader_texture_lod",
|
|
715
|
+
"EXT_texture_compression_bptc",
|
|
716
|
+
"EXT_texture_compression_rgtc",
|
|
717
|
+
"EXT_texture_filter_anisotropic",
|
|
718
|
+
"EXT_texture_mirror_clamp_to_edge",
|
|
719
|
+
"EXT_sRGB",
|
|
720
|
+
"KHR_parallel_shader_compile",
|
|
721
|
+
"OES_element_index_uint",
|
|
722
|
+
"OES_fbo_render_mipmap",
|
|
723
|
+
"OES_standard_derivatives",
|
|
724
|
+
"OES_texture_float",
|
|
725
|
+
"OES_texture_float_linear",
|
|
726
|
+
"OES_texture_half_float",
|
|
727
|
+
"OES_texture_half_float_linear",
|
|
728
|
+
"OES_vertex_array_object",
|
|
729
|
+
"WEBGL_blend_func_extended",
|
|
730
|
+
"WEBGL_color_buffer_float",
|
|
731
|
+
"WEBGL_compressed_texture_s3tc",
|
|
732
|
+
"WEBGL_compressed_texture_s3tc_srgb",
|
|
733
|
+
"WEBGL_debug_renderer_info",
|
|
734
|
+
"WEBGL_debug_shaders",
|
|
735
|
+
"WEBGL_depth_texture",
|
|
736
|
+
"WEBGL_draw_buffers",
|
|
737
|
+
"WEBGL_lose_context",
|
|
738
|
+
"WEBGL_multi_draw",
|
|
739
|
+
"WEBGL_polygon_mode"
|
|
740
|
+
];
|
|
741
|
+
}, "getSupportedExtensions"),
|
|
742
|
+
getShaderPrecisionFormat: patchNative(function(shaderType, precisionType) {
|
|
743
|
+
return { precision: 23, rangeMin: 127, rangeMax: 127 };
|
|
744
|
+
}, "getShaderPrecisionFormat"),
|
|
745
|
+
createShader: patchNative(function(type) {
|
|
746
|
+
return { type: type, source: "", compiled: true };
|
|
747
|
+
}, "createShader"),
|
|
748
|
+
shaderSource: patchNative(function(shader, src) {
|
|
749
|
+
if (shader) shader.source = src;
|
|
750
|
+
}, "shaderSource"),
|
|
751
|
+
compileShader: patchNative(function(shader) {
|
|
752
|
+
if (shader) shader.compiled = true;
|
|
753
|
+
}, "compileShader"),
|
|
754
|
+
getShaderParameter: patchNative(function(shader, param) {
|
|
755
|
+
return true;
|
|
756
|
+
}, "getShaderParameter"),
|
|
757
|
+
getShaderInfoLog: patchNative(function() { return ""; }, "getShaderInfoLog"),
|
|
758
|
+
createProgram: patchNative(function() {
|
|
759
|
+
return { shaders: [], linked: true };
|
|
760
|
+
}, "createProgram"),
|
|
761
|
+
attachShader: patchNative(function(prog, shader) {
|
|
762
|
+
if (prog && shader) prog.shaders.push(shader);
|
|
763
|
+
}, "attachShader"),
|
|
764
|
+
linkProgram: patchNative(function(prog) {
|
|
765
|
+
if (prog) prog.linked = true;
|
|
766
|
+
}, "linkProgram"),
|
|
767
|
+
getProgramParameter: patchNative(function(prog, param) {
|
|
768
|
+
return true;
|
|
769
|
+
}, "getProgramParameter"),
|
|
770
|
+
getProgramInfoLog: patchNative(function() { return ""; }, "getProgramInfoLog"),
|
|
771
|
+
useProgram: patchNative(function() {}, "useProgram"),
|
|
772
|
+
getAttribLocation: patchNative(function(prog, name) { return 0; }, "getAttribLocation"),
|
|
773
|
+
getUniformLocation: patchNative(function(prog, name) { return {}; }, "getUniformLocation"),
|
|
774
|
+
enableVertexAttribArray: patchNative(function(idx) {}, "enableVertexAttribArray"),
|
|
775
|
+
vertexAttribPointer: patchNative(function() {}, "vertexAttribPointer"),
|
|
776
|
+
createBuffer: patchNative(function() { return {}; }, "createBuffer"),
|
|
777
|
+
bindBuffer: patchNative(function() {}, "bindBuffer"),
|
|
778
|
+
bufferData: patchNative(function() {}, "bufferData"),
|
|
779
|
+
viewport: patchNative(function(x, y, w, h) {}, "viewport"),
|
|
780
|
+
clearColor: patchNative(function(r, g, b, a) {
|
|
781
|
+
gl._clearR = r;
|
|
782
|
+
gl._clearG = g;
|
|
783
|
+
gl._clearB = b;
|
|
784
|
+
gl._clearA = a;
|
|
785
|
+
}, "clearColor"),
|
|
786
|
+
clear: patchNative(function(mask) {}, "clear"),
|
|
787
|
+
drawArrays: patchNative(function(mode, first, count) {}, "drawArrays"),
|
|
788
|
+
drawElements: patchNative(function() {}, "drawElements"),
|
|
789
|
+
createTexture: patchNative(function() { return {}; }, "createTexture"),
|
|
790
|
+
bindTexture: patchNative(function() {}, "bindTexture"),
|
|
791
|
+
texParameteri: patchNative(function() {}, "texParameteri"),
|
|
792
|
+
texImage2D: patchNative(function() {}, "texImage2D"),
|
|
793
|
+
readPixels: patchNative(function(x, y, w, h, format, type, pixels) {
|
|
794
|
+
if (pixels && pixels.length) {
|
|
795
|
+
const r = gl._clearR !== undefined ? Math.round(gl._clearR * 255) : 204;
|
|
796
|
+
const g = gl._clearG !== undefined ? Math.round(gl._clearG * 255) : 102;
|
|
797
|
+
const b = gl._clearB !== undefined ? Math.round(gl._clearB * 255) : 51;
|
|
798
|
+
const a = gl._clearA !== undefined ? Math.round(gl._clearA * 255) : 255;
|
|
799
|
+
for (let i = 0; i < pixels.length; i += 4) {
|
|
800
|
+
pixels[i] = r;
|
|
801
|
+
pixels[i + 1] = g;
|
|
802
|
+
pixels[i + 2] = b;
|
|
803
|
+
pixels[i + 3] = a;
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
}, "readPixels")
|
|
807
|
+
};
|
|
808
|
+
return gl;
|
|
809
|
+
}
|
|
810
|
+
return null;
|
|
811
|
+
}, "getContext");
|
|
812
|
+
|
|
813
|
+
el.toDataURL = patchNative(function() {
|
|
814
|
+
return "data:image/png;base64,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";
|
|
815
|
+
}, "toDataURL");
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
return el;
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
// 7. Document object
|
|
822
|
+
const document = {
|
|
823
|
+
readyState: "complete",
|
|
824
|
+
compatMode: "CSS1Compat",
|
|
825
|
+
characterSet: "UTF-8",
|
|
826
|
+
contentType: "text/html",
|
|
827
|
+
doctype: { name: "html" },
|
|
828
|
+
location: location,
|
|
829
|
+
URL: location.href,
|
|
830
|
+
documentURI: location.href,
|
|
831
|
+
referrer: global.__REFERRER__ || "",
|
|
832
|
+
currentScript: null,
|
|
833
|
+
title: global.__DOCUMENT_TITLE__ || "",
|
|
834
|
+
hidden: false,
|
|
835
|
+
visibilityState: "visible",
|
|
836
|
+
hasFocus: patchNative(function() { return true; }, "hasFocus"),
|
|
837
|
+
documentElement: createMockElement("html"),
|
|
838
|
+
head: createMockElement("head"),
|
|
839
|
+
body: createMockElement("body"),
|
|
840
|
+
createElement: patchNative(function(tag) { return createMockElement(tag); }, "createElement"),
|
|
841
|
+
createElementNS: patchNative(function(ns, tag) { return createMockElement(tag); }, "createElementNS"),
|
|
842
|
+
createTextNode: patchNative(function(txt) { return { nodeType: 3, nodeValue: String(txt) }; }, "createTextNode"),
|
|
843
|
+
getElementById: patchNative(function(id) {
|
|
844
|
+
if (!id) return null;
|
|
845
|
+
if (!elementsById[id]) {
|
|
846
|
+
const isBtn = String(id).toLowerCase().includes("button") || String(id).toLowerCase().includes("btn");
|
|
847
|
+
const el = createMockElement(isBtn ? "button" : "div");
|
|
848
|
+
el.id = id;
|
|
849
|
+
elementsById[id] = el;
|
|
850
|
+
document.body.appendChild(el);
|
|
851
|
+
}
|
|
852
|
+
return elementsById[id];
|
|
853
|
+
}, "getElementById"),
|
|
854
|
+
getElementsByTagName: patchNative(function(tag) {
|
|
855
|
+
const t = (tag || "").toLowerCase();
|
|
856
|
+
if (t === "script") return trackedScripts.length > 0 ? trackedScripts : [createMockElement("script")];
|
|
857
|
+
if (t === "head") return [document.head];
|
|
858
|
+
if (t === "body") return [document.body];
|
|
859
|
+
if (t === "html") return [document.documentElement];
|
|
860
|
+
return [];
|
|
861
|
+
}, "getElementsByTagName"),
|
|
862
|
+
getElementsByClassName: patchNative(function(cls) {
|
|
863
|
+
const el = createMockElement("div");
|
|
864
|
+
el.className = cls;
|
|
865
|
+
document.body.appendChild(el);
|
|
866
|
+
return [el];
|
|
867
|
+
}, "getElementsByClassName"),
|
|
868
|
+
getElementsByName: patchNative(function() { return []; }, "getElementsByName"),
|
|
869
|
+
querySelector: patchNative(function(sel) {
|
|
870
|
+
if (!sel) return null;
|
|
871
|
+
if (sel.startsWith("#")) {
|
|
872
|
+
return document.getElementById(sel.slice(1));
|
|
873
|
+
}
|
|
874
|
+
const el = createMockElement("div");
|
|
875
|
+
document.body.appendChild(el);
|
|
876
|
+
return el;
|
|
877
|
+
}, "querySelector"),
|
|
878
|
+
querySelectorAll: patchNative(function(sel) {
|
|
879
|
+
const el = document.querySelector(sel);
|
|
880
|
+
return el ? [el] : [];
|
|
881
|
+
}, "querySelectorAll"),
|
|
882
|
+
addEventListener: patchNative(addEventListener, "addEventListener"),
|
|
883
|
+
removeEventListener: patchNative(removeEventListener, "removeEventListener"),
|
|
884
|
+
dispatchEvent: patchNative(dispatchEvent, "dispatchEvent")
|
|
885
|
+
};
|
|
886
|
+
document.documentElement.appendChild(document.head);
|
|
887
|
+
document.documentElement.appendChild(document.body);
|
|
888
|
+
|
|
889
|
+
const CPT_ELEMENT_IDS = [
|
|
890
|
+
"sec-if-cpt-container",
|
|
891
|
+
"sec-bc-text-container",
|
|
892
|
+
"sec-bc-tile-parent",
|
|
893
|
+
"sec-bc-tile-container",
|
|
894
|
+
"progress-button",
|
|
895
|
+
"sec-cpr-overlay"
|
|
896
|
+
];
|
|
897
|
+
CPT_ELEMENT_IDS.forEach(id => {
|
|
898
|
+
document.getElementById(id);
|
|
899
|
+
});
|
|
900
|
+
|
|
901
|
+
const cookieJar = new Map();
|
|
902
|
+
function initCookieJar(str) {
|
|
903
|
+
if (!str) return;
|
|
904
|
+
String(str).split(";").forEach(pair => {
|
|
905
|
+
const idx = pair.indexOf("=");
|
|
906
|
+
if (idx > -1) {
|
|
907
|
+
const k = pair.slice(0, idx).trim();
|
|
908
|
+
const v = pair.slice(idx + 1).trim();
|
|
909
|
+
if (k) cookieJar.set(k, v);
|
|
910
|
+
}
|
|
911
|
+
});
|
|
912
|
+
}
|
|
913
|
+
initCookieJar(global.__INITIAL_COOKIES__ || "");
|
|
914
|
+
|
|
915
|
+
Object.defineProperty(document, "cookie", {
|
|
916
|
+
get: patchNative(function() {
|
|
917
|
+
const pairs = [];
|
|
918
|
+
for (const [k, v] of cookieJar.entries()) {
|
|
919
|
+
pairs.push(k + "=" + v);
|
|
920
|
+
}
|
|
921
|
+
return pairs.join("; ");
|
|
922
|
+
}, "get_cookie"),
|
|
923
|
+
set: patchNative(function(v) {
|
|
924
|
+
if (!v) return;
|
|
925
|
+
const parts = String(v).split(";");
|
|
926
|
+
const first = parts[0];
|
|
927
|
+
const idx = first.indexOf("=");
|
|
928
|
+
if (idx > -1) {
|
|
929
|
+
const k = first.slice(0, idx).trim();
|
|
930
|
+
const val = first.slice(idx + 1).trim();
|
|
931
|
+
const isExpired = parts.some(p => {
|
|
932
|
+
const lower = p.trim().toLowerCase();
|
|
933
|
+
return lower.startsWith("max-age=0") || lower.includes("expires=thu, 01 jan 1970");
|
|
934
|
+
});
|
|
935
|
+
if (isExpired) {
|
|
936
|
+
cookieJar.delete(k);
|
|
937
|
+
} else {
|
|
938
|
+
cookieJar.set(k, val);
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
}, "set_cookie"),
|
|
942
|
+
configurable: true
|
|
943
|
+
});
|
|
944
|
+
|
|
945
|
+
Object.defineProperty(global, "document", { value: document, writable: true, configurable: true });
|
|
946
|
+
global.window.document = document;
|
|
947
|
+
|
|
948
|
+
|
|
949
|
+
// 8. Crypto and Audio Mocks
|
|
950
|
+
const crypto = {
|
|
951
|
+
getRandomValues: patchNative(function(arr) {
|
|
952
|
+
for (let i = 0; i < arr.length; i++) arr[i] = Math.floor(Math.random() * 256);
|
|
953
|
+
return arr;
|
|
954
|
+
}, "getRandomValues"),
|
|
955
|
+
subtle: {
|
|
956
|
+
digest: patchNative(function(algo, data) {
|
|
957
|
+
const buf = new ArrayBuffer(32);
|
|
958
|
+
const view = new Uint8Array(buf);
|
|
959
|
+
for (let i = 0; i < 32; i++) view[i] = (i * 17 + 5) % 256;
|
|
960
|
+
return Promise.resolve(buf);
|
|
961
|
+
}, "digest")
|
|
962
|
+
}
|
|
963
|
+
};
|
|
964
|
+
Object.defineProperty(global, "crypto", { value: crypto, writable: true, configurable: true });
|
|
965
|
+
|
|
966
|
+
function MockAudioBuffer() {
|
|
967
|
+
this.length = 44100;
|
|
968
|
+
this.sampleRate = 44100;
|
|
969
|
+
this.numberOfChannels = 1;
|
|
970
|
+
this.duration = 1.0;
|
|
971
|
+
this.getChannelData = patchNative(function(c) {
|
|
972
|
+
const data = new Float32Array(44100);
|
|
973
|
+
const realSlice = [
|
|
974
|
+
-0.10808049887418747, -0.39091169834136963, -0.005692684557288885, 0.3892313241958618,
|
|
975
|
+
0.1189708486199379, -0.3545846939086914, -0.22215832769870758, 0.28990939259529114,
|
|
976
|
+
0.30651888251304626, -0.20068730413913727, -0.3649080991744995, 0.09447670727968216,
|
|
977
|
+
0.39241594076156616, 0.01972721517086029, -0.38671594858169556, -0.13228479027748108,
|
|
978
|
+
0.34825482964515686, 0.2336597442626953, -0.28028473258018494, -0.3152626156806946
|
|
979
|
+
];
|
|
980
|
+
for (let i = 0; i < data.length; i++) {
|
|
981
|
+
data[i] = realSlice[i % realSlice.length];
|
|
982
|
+
}
|
|
983
|
+
return data;
|
|
984
|
+
}, "getChannelData");
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
function MockAudioNode() {
|
|
988
|
+
this.connect = patchNative(function(target) { return target; }, "connect");
|
|
989
|
+
this.disconnect = patchNative(function() {}, "disconnect");
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
function MockOscillatorNode() {
|
|
993
|
+
MockAudioNode.call(this);
|
|
994
|
+
this.type = "sine";
|
|
995
|
+
this.frequency = { value: 440, setValueAtTime: function() {} };
|
|
996
|
+
this.start = patchNative(function() {}, "start");
|
|
997
|
+
this.stop = patchNative(function() {}, "stop");
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
function MockDynamicsCompressorNode() {
|
|
1001
|
+
MockAudioNode.call(this);
|
|
1002
|
+
this.threshold = { value: -24 };
|
|
1003
|
+
this.knee = { value: 30 };
|
|
1004
|
+
this.ratio = { value: 12 };
|
|
1005
|
+
this.reduction = { value: 0 };
|
|
1006
|
+
this.attack = { value: 0.003 };
|
|
1007
|
+
this.release = { value: 0.25 };
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
function MockAudioContext() {
|
|
1011
|
+
this.destination = new MockAudioNode();
|
|
1012
|
+
this.createOscillator = patchNative(function() { return new MockOscillatorNode(); }, "createOscillator");
|
|
1013
|
+
this.createDynamicsCompressor = patchNative(function() { return new MockDynamicsCompressorNode(); }, "createDynamicsCompressor");
|
|
1014
|
+
this.createGain = patchNative(function() { return new MockAudioNode(); }, "createGain");
|
|
1015
|
+
this.createAnalyser = patchNative(function() {
|
|
1016
|
+
const node = new MockAudioNode();
|
|
1017
|
+
node.getFloatFrequencyData = patchNative(function(arr) {}, "getFloatFrequencyData");
|
|
1018
|
+
node.getByteFrequencyData = patchNative(function(arr) {}, "getByteFrequencyData");
|
|
1019
|
+
return node;
|
|
1020
|
+
}, "createAnalyser");
|
|
1021
|
+
this.createBufferSource = patchNative(function() {
|
|
1022
|
+
const node = new MockAudioNode();
|
|
1023
|
+
node.buffer = null;
|
|
1024
|
+
node.start = patchNative(function() {}, "start");
|
|
1025
|
+
return node;
|
|
1026
|
+
}, "createBufferSource");
|
|
1027
|
+
this.startRendering = patchNative(function() {
|
|
1028
|
+
return Promise.resolve(new MockAudioBuffer());
|
|
1029
|
+
}, "startRendering");
|
|
1030
|
+
this.resume = patchNative(function() { return Promise.resolve(); }, "resume");
|
|
1031
|
+
this.close = patchNative(function() { return Promise.resolve(); }, "close");
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
patchNative(MockAudioContext, "AudioContext");
|
|
1035
|
+
patchNative(MockAudioContext, "webkitAudioContext");
|
|
1036
|
+
patchNative(MockAudioContext, "OfflineAudioContext");
|
|
1037
|
+
|
|
1038
|
+
global.AudioContext = MockAudioContext;
|
|
1039
|
+
global.webkitAudioContext = MockAudioContext;
|
|
1040
|
+
global.OfflineAudioContext = MockAudioContext;
|
|
1041
|
+
|
|
1042
|
+
// 9. Performance & V8 Virtual JIT Clock Algorithm
|
|
1043
|
+
const startTime = Date.now() - 1500;
|
|
1044
|
+
let virtualNow = 118.4;
|
|
1045
|
+
let callCount = 0;
|
|
1046
|
+
let lastRealTime = Date.now();
|
|
1047
|
+
|
|
1048
|
+
function virtualJITNow() {
|
|
1049
|
+
callCount++;
|
|
1050
|
+
const currentRealTime = Date.now();
|
|
1051
|
+
const realDelta = currentRealTime - lastRealTime;
|
|
1052
|
+
|
|
1053
|
+
// V8 TurboFan warmup & micro-timing model:
|
|
1054
|
+
let microIncrement = 0.05 + (callCount % 5) * 0.02;
|
|
1055
|
+
if (callCount <= 12) {
|
|
1056
|
+
microIncrement += (12 - callCount) * 0.06;
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
if (realDelta > 0) {
|
|
1060
|
+
const scaledDelta = realDelta * 0.35;
|
|
1061
|
+
virtualNow += Math.max(microIncrement, scaledDelta);
|
|
1062
|
+
lastRealTime = currentRealTime;
|
|
1063
|
+
} else {
|
|
1064
|
+
virtualNow += microIncrement;
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
// Chrome quantizes performance.now to 100 microseconds (0.1ms)
|
|
1068
|
+
return Math.round(virtualNow * 10) / 10;
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
const performance = {
|
|
1072
|
+
timeOrigin: startTime,
|
|
1073
|
+
now: patchNative(virtualJITNow, "now"),
|
|
1074
|
+
timing: {
|
|
1075
|
+
navigationStart: startTime,
|
|
1076
|
+
unloadEventStart: 0,
|
|
1077
|
+
unloadEventEnd: 0,
|
|
1078
|
+
redirectStart: 0,
|
|
1079
|
+
redirectEnd: 0,
|
|
1080
|
+
fetchStart: startTime + 5,
|
|
1081
|
+
domainLookupStart: startTime + 10,
|
|
1082
|
+
domainLookupEnd: startTime + 20,
|
|
1083
|
+
connectStart: startTime + 20,
|
|
1084
|
+
connectEnd: startTime + 45,
|
|
1085
|
+
secureConnectionStart: startTime + 25,
|
|
1086
|
+
requestStart: startTime + 46,
|
|
1087
|
+
responseStart: startTime + 90,
|
|
1088
|
+
responseEnd: startTime + 110,
|
|
1089
|
+
domLoading: startTime + 115,
|
|
1090
|
+
domInteractive: startTime + 250,
|
|
1091
|
+
domContentLoadedEventStart: startTime + 260,
|
|
1092
|
+
domContentLoadedEventEnd: startTime + 270,
|
|
1093
|
+
domComplete: startTime + 400,
|
|
1094
|
+
loadEventStart: startTime + 410,
|
|
1095
|
+
loadEventEnd: startTime + 420
|
|
1096
|
+
},
|
|
1097
|
+
getEntriesByType: patchNative(function() { return []; }, "getEntriesByType"),
|
|
1098
|
+
getEntriesByName: patchNative(function() { return []; }, "getEntriesByName"),
|
|
1099
|
+
getEntries: patchNative(function() { return []; }, "getEntries"),
|
|
1100
|
+
mark: patchNative(function() {}, "mark"),
|
|
1101
|
+
measure: patchNative(function() {}, "measure"),
|
|
1102
|
+
clearMarks: patchNative(function() {}, "clearMarks"),
|
|
1103
|
+
clearMeasures: patchNative(function() {}, "clearMeasures")
|
|
1104
|
+
};
|
|
1105
|
+
Object.defineProperty(global, "performance", { value: performance, writable: true, configurable: true });
|
|
1106
|
+
|
|
1107
|
+
// 10. Storage
|
|
1108
|
+
const storageMap = {};
|
|
1109
|
+
const storage = {
|
|
1110
|
+
getItem: patchNative(function(k) { return storageMap[k] || null; }, "getItem"),
|
|
1111
|
+
setItem: patchNative(function(k, v) { storageMap[k] = String(v); }, "setItem"),
|
|
1112
|
+
removeItem: patchNative(function(k) { delete storageMap[k]; }, "removeItem"),
|
|
1113
|
+
clear: patchNative(function() { Object.keys(storageMap).forEach(k => delete storageMap[k]); }, "clear"),
|
|
1114
|
+
key: patchNative(function(i) { return Object.keys(storageMap)[i] || null; }, "key"),
|
|
1115
|
+
get length() { return Object.keys(storageMap).length; }
|
|
1116
|
+
};
|
|
1117
|
+
Object.defineProperty(global, "localStorage", { value: storage, writable: true, configurable: true });
|
|
1118
|
+
Object.defineProperty(global, "sessionStorage", { value: storage, writable: true, configurable: true });
|
|
1119
|
+
|
|
1120
|
+
// 11. Timer and Event Loop Helpers
|
|
1121
|
+
const timers = [];
|
|
1122
|
+
global.setTimeout = patchNative(function(fn, delay, ...args) {
|
|
1123
|
+
const id = timers.length + 1;
|
|
1124
|
+
timers.push({ id, fn, args, delay: delay || 0, type: "timeout" });
|
|
1125
|
+
return id;
|
|
1126
|
+
}, "setTimeout");
|
|
1127
|
+
|
|
1128
|
+
global.setInterval = patchNative(function(fn, delay, ...args) {
|
|
1129
|
+
const id = timers.length + 1;
|
|
1130
|
+
timers.push({ id, fn, args, delay: delay || 0, type: "interval" });
|
|
1131
|
+
return id;
|
|
1132
|
+
}, "setInterval");
|
|
1133
|
+
|
|
1134
|
+
global.clearTimeout = patchNative(function(id) {}, "clearTimeout");
|
|
1135
|
+
global.clearInterval = patchNative(function(id) {}, "clearInterval");
|
|
1136
|
+
global.requestAnimationFrame = patchNative(function(fn) { return global.setTimeout(fn, 16); }, "requestAnimationFrame");
|
|
1137
|
+
global.cancelAnimationFrame = patchNative(function(id) { global.clearTimeout(id); }, "cancelAnimationFrame");
|
|
1138
|
+
|
|
1139
|
+
global.addEventListener = patchNative(addEventListener, "addEventListener");
|
|
1140
|
+
global.removeEventListener = patchNative(removeEventListener, "removeEventListener");
|
|
1141
|
+
global.dispatchEvent = patchNative(dispatchEvent, "dispatchEvent");
|
|
1142
|
+
|
|
1143
|
+
// 12. Captured XHR & Fetch Interceptor
|
|
1144
|
+
global.__captured_sensor_data = null;
|
|
1145
|
+
global.__sensor_posts = [];
|
|
1146
|
+
|
|
1147
|
+
function XMLHttpRequest() {
|
|
1148
|
+
this.method = "GET";
|
|
1149
|
+
this.url = "";
|
|
1150
|
+
this.responseURL = "";
|
|
1151
|
+
this.headers = {};
|
|
1152
|
+
this.readyState = 0;
|
|
1153
|
+
this.status = 200;
|
|
1154
|
+
this.statusText = "OK";
|
|
1155
|
+
this.responseText = "{}";
|
|
1156
|
+
this.response = "{}";
|
|
1157
|
+
this.onreadystatechange = null;
|
|
1158
|
+
this.onload = null;
|
|
1159
|
+
this.onerror = null;
|
|
1160
|
+
|
|
1161
|
+
const self = this;
|
|
1162
|
+
this.open = patchNative(function(method, url, async) {
|
|
1163
|
+
self.method = String(method || "GET").toUpperCase();
|
|
1164
|
+
self.url = String(url || "");
|
|
1165
|
+
try {
|
|
1166
|
+
self.responseURL = new URL(self.url, location.href).href;
|
|
1167
|
+
} catch(e) {
|
|
1168
|
+
self.responseURL = self.url;
|
|
1169
|
+
}
|
|
1170
|
+
self.readyState = 1;
|
|
1171
|
+
}, "open");
|
|
1172
|
+
|
|
1173
|
+
this.setRequestHeader = patchNative(function(header, value) {
|
|
1174
|
+
self.headers[header] = value;
|
|
1175
|
+
}, "setRequestHeader");
|
|
1176
|
+
|
|
1177
|
+
this.send = patchNative(function(body) {
|
|
1178
|
+
self.readyState = 4;
|
|
1179
|
+
self.status = 200;
|
|
1180
|
+
self.statusText = "OK";
|
|
1181
|
+
self.responseText = "{}";
|
|
1182
|
+
self.response = "{}";
|
|
1183
|
+
global.__captured_sensor_data = body;
|
|
1184
|
+
global.__sensor_posts.push({ url: self.url, method: self.method, headers: self.headers, body: body });
|
|
1185
|
+
if (typeof self.onreadystatechange === "function") {
|
|
1186
|
+
try { self.onreadystatechange(); } catch(e) {}
|
|
1187
|
+
}
|
|
1188
|
+
if (typeof self.onload === "function") {
|
|
1189
|
+
try { self.onload(); } catch(e) {}
|
|
1190
|
+
}
|
|
1191
|
+
}, "send");
|
|
1192
|
+
}
|
|
1193
|
+
|
|
1194
|
+
XMLHttpRequest.prototype.open = patchNative(function(method, url, async) {
|
|
1195
|
+
if (this.open) return this.open.apply(this, arguments);
|
|
1196
|
+
}, "open");
|
|
1197
|
+
XMLHttpRequest.prototype.setRequestHeader = patchNative(function(header, value) {
|
|
1198
|
+
if (this.setRequestHeader) return this.setRequestHeader.apply(this, arguments);
|
|
1199
|
+
}, "setRequestHeader");
|
|
1200
|
+
XMLHttpRequest.prototype.send = patchNative(function(body) {
|
|
1201
|
+
if (this.send) return this.send.apply(this, arguments);
|
|
1202
|
+
}, "send");
|
|
1203
|
+
|
|
1204
|
+
patchNative(XMLHttpRequest, "XMLHttpRequest");
|
|
1205
|
+
global.XMLHttpRequest = XMLHttpRequest;
|
|
1206
|
+
|
|
1207
|
+
|
|
1208
|
+
|
|
1209
|
+
// 13. Synthetic Human Interaction Simulation (Bézier Spline Physics)
|
|
1210
|
+
global.__simulateHumanInteractions = function() {
|
|
1211
|
+
// Generate natural human cursor trajectory between (120, 80) and (840, 520)
|
|
1212
|
+
const p0 = { x: 120, y: 80 };
|
|
1213
|
+
const p1 = { x: 310, y: 190 };
|
|
1214
|
+
const p2 = { x: 580, y: 440 };
|
|
1215
|
+
const p3 = { x: 840, y: 520 };
|
|
1216
|
+
|
|
1217
|
+
const totalSteps = 24;
|
|
1218
|
+
let prevX = p0.x, prevY = p0.y;
|
|
1219
|
+
|
|
1220
|
+
for (let i = 0; i <= totalSteps; i++) {
|
|
1221
|
+
const t = i / totalSteps;
|
|
1222
|
+
const u = 1 - t;
|
|
1223
|
+
// Cubic Bézier formula with micro-jitter
|
|
1224
|
+
const rawX = u*u*u*p0.x + 3*u*u*t*p1.x + 3*u*t*t*p2.x + t*t*t*p3.x;
|
|
1225
|
+
const rawY = u*u*u*p0.y + 3*u*u*t*p1.y + 3*u*t*t*p2.y + t*t*t*p3.y;
|
|
1226
|
+
const jitter = (i % 3 === 0) ? (i % 2 === 0 ? 1 : -1) : 0;
|
|
1227
|
+
|
|
1228
|
+
const x = Math.round(rawX + jitter);
|
|
1229
|
+
const y = Math.round(rawY + jitter);
|
|
1230
|
+
const timeOffset = Math.round(50 + t * 480 + Math.sin(t * Math.PI) * 20);
|
|
1231
|
+
|
|
1232
|
+
const evt = {
|
|
1233
|
+
type: "mousemove",
|
|
1234
|
+
clientX: x,
|
|
1235
|
+
clientY: y,
|
|
1236
|
+
pageX: x,
|
|
1237
|
+
pageY: y,
|
|
1238
|
+
screenX: x + 20,
|
|
1239
|
+
screenY: y + 85,
|
|
1240
|
+
movementX: x - prevX,
|
|
1241
|
+
movementY: y - prevY,
|
|
1242
|
+
buttons: 0,
|
|
1243
|
+
button: 0,
|
|
1244
|
+
timeStamp: startTime + timeOffset,
|
|
1245
|
+
target: document.body,
|
|
1246
|
+
isTrusted: true
|
|
1247
|
+
};
|
|
1248
|
+
prevX = x;
|
|
1249
|
+
prevY = y;
|
|
1250
|
+
dispatchEvent(evt);
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1253
|
+
// Natural click at target coordinate
|
|
1254
|
+
const clickTime = startTime + 560;
|
|
1255
|
+
dispatchEvent({ type: "mousedown", clientX: prevX, clientY: prevY, screenX: prevX + 20, screenY: prevY + 85, button: 0, buttons: 1, timeStamp: clickTime, target: document.body, isTrusted: true });
|
|
1256
|
+
dispatchEvent({ type: "mouseup", clientX: prevX, clientY: prevY, screenX: prevX + 20, screenY: prevY + 85, button: 0, buttons: 0, timeStamp: clickTime + 85, target: document.body, isTrusted: true });
|
|
1257
|
+
dispatchEvent({ type: "click", clientX: prevX, clientY: prevY, screenX: prevX + 20, screenY: prevY + 85, button: 0, buttons: 0, timeStamp: clickTime + 90, target: document.body, isTrusted: true });
|
|
1258
|
+
|
|
1259
|
+
dispatchEvent({ type: "focus", target: window, isTrusted: true });
|
|
1260
|
+
dispatchEvent({ type: "scroll", target: window, isTrusted: true });
|
|
1261
|
+
};
|
|
1262
|
+
|
|
1263
|
+
// 14. Step-by-step Event Loop Runner
|
|
1264
|
+
global.__drainEventLoop = function(maxSteps = 50) {
|
|
1265
|
+
let steps = 0;
|
|
1266
|
+
while (timers.length > 0 && steps < maxSteps) {
|
|
1267
|
+
steps++;
|
|
1268
|
+
const timer = timers.shift();
|
|
1269
|
+
try {
|
|
1270
|
+
if (typeof timer.fn === "function") {
|
|
1271
|
+
timer.fn.apply(global, timer.args || []);
|
|
1272
|
+
} else if (typeof timer.fn === "string") {
|
|
1273
|
+
eval(timer.fn);
|
|
1274
|
+
}
|
|
1275
|
+
} catch(e) {}
|
|
1276
|
+
}
|
|
1277
|
+
};
|
|
1278
|
+
|
|
1279
|
+
// 15. Trusted Types Polyfill
|
|
1280
|
+
const trustedTypes = {
|
|
1281
|
+
createPolicy: patchNative(function(name, rules) {
|
|
1282
|
+
return {
|
|
1283
|
+
name: name,
|
|
1284
|
+
createScript: patchNative(function(s) { return rules && rules.createScript ? rules.createScript(s) : s; }, "createScript"),
|
|
1285
|
+
createScriptURL: patchNative(function(u) { return rules && rules.createScriptURL ? rules.createScriptURL(u) : u; }, "createScriptURL"),
|
|
1286
|
+
createHTML: patchNative(function(h) { return rules && rules.createHTML ? rules.createHTML(h) : h; }, "createHTML")
|
|
1287
|
+
};
|
|
1288
|
+
}, "createPolicy"),
|
|
1289
|
+
isScript: patchNative(function() { return true; }, "isScript"),
|
|
1290
|
+
isScriptURL: patchNative(function() { return true; }, "isScriptURL"),
|
|
1291
|
+
isHTML: patchNative(function() { return true; }, "isHTML"),
|
|
1292
|
+
emptyScript: "",
|
|
1293
|
+
emptyHTML: ""
|
|
1294
|
+
};
|
|
1295
|
+
Object.defineProperty(global, "trustedTypes", { value: trustedTypes, writable: true, configurable: true });
|
|
1296
|
+
|
|
1297
|
+
})(globalThis);
|