jumpy-lion 0.0.41 → 0.0.43
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.
- package/dist/browser-controller.d.ts.map +1 -1
- package/dist/browser-controller.js +102 -16
- package/dist/browser-controller.js.map +1 -1
- package/dist/browser-plugin.d.ts +20 -1
- package/dist/browser-plugin.d.ts.map +1 -1
- package/dist/browser-plugin.js +21 -1
- package/dist/browser-plugin.js.map +1 -1
- package/dist/browser-process/browser.d.ts +22 -1
- package/dist/browser-process/browser.d.ts.map +1 -1
- package/dist/browser-process/browser.js +77 -5
- package/dist/browser-process/browser.js.map +1 -1
- package/dist/browser-profiles/chrome/default.d.ts +116 -0
- package/dist/browser-profiles/chrome/default.d.ts.map +1 -1
- package/dist/browser-profiles/chrome/default.js +118 -1
- package/dist/browser-profiles/chrome/default.js.map +1 -1
- package/dist/browser-profiles/chrome/populate-profile.d.ts +76 -0
- package/dist/browser-profiles/chrome/populate-profile.d.ts.map +1 -0
- package/dist/browser-profiles/chrome/populate-profile.js +300 -0
- package/dist/browser-profiles/chrome/populate-profile.js.map +1 -0
- package/dist/browser-profiles/index.d.ts +1 -0
- package/dist/browser-profiles/index.d.ts.map +1 -1
- package/dist/browser-profiles/index.js +2 -0
- package/dist/browser-profiles/index.js.map +1 -1
- package/dist/crawler.d.ts +32 -1
- package/dist/crawler.d.ts.map +1 -1
- package/dist/crawler.js +7 -0
- package/dist/crawler.js.map +1 -1
- package/dist/fingerprinting/custom-fingerprint-injector.d.ts +87 -0
- package/dist/fingerprinting/custom-fingerprint-injector.d.ts.map +1 -0
- package/dist/fingerprinting/custom-fingerprint-injector.js +342 -0
- package/dist/fingerprinting/custom-fingerprint-injector.js.map +1 -0
- package/dist/fingerprinting/fingerprint-injector.d.ts +40 -2
- package/dist/fingerprinting/fingerprint-injector.d.ts.map +1 -1
- package/dist/fingerprinting/fingerprint-injector.js +452 -44
- package/dist/fingerprinting/fingerprint-injector.js.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/audio-spoofing.d.ts.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/audio-spoofing.js +11 -1
- package/dist/fingerprinting/fingerprint-overrides/audio-spoofing.js.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/canvas-protection.d.ts.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/canvas-protection.js +11 -1
- package/dist/fingerprinting/fingerprint-overrides/canvas-protection.js.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/cdp-detection-bypass.d.ts +14 -0
- package/dist/fingerprinting/fingerprint-overrides/cdp-detection-bypass.d.ts.map +1 -0
- package/dist/fingerprinting/fingerprint-overrides/cdp-detection-bypass.js +776 -0
- package/dist/fingerprinting/fingerprint-overrides/cdp-detection-bypass.js.map +1 -0
- package/dist/fingerprinting/fingerprint-overrides/client-rect-spoofing.d.ts.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/client-rect-spoofing.js +11 -1
- package/dist/fingerprinting/fingerprint-overrides/client-rect-spoofing.js.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/datadome-bypass.d.ts +14 -0
- package/dist/fingerprinting/fingerprint-overrides/datadome-bypass.d.ts.map +1 -0
- package/dist/fingerprinting/fingerprint-overrides/datadome-bypass.js +643 -0
- package/dist/fingerprinting/fingerprint-overrides/datadome-bypass.js.map +1 -0
- package/dist/fingerprinting/fingerprint-overrides/font-spoofing.d.ts.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/font-spoofing.js +11 -1
- package/dist/fingerprinting/fingerprint-overrides/font-spoofing.js.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/index.d.ts +17 -1
- package/dist/fingerprinting/fingerprint-overrides/index.d.ts.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/index.js +21 -1
- package/dist/fingerprinting/fingerprint-overrides/index.js.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/keyboard-humanization.d.ts +45 -0
- package/dist/fingerprinting/fingerprint-overrides/keyboard-humanization.d.ts.map +1 -0
- package/dist/fingerprinting/fingerprint-overrides/keyboard-humanization.js +291 -0
- package/dist/fingerprinting/fingerprint-overrides/keyboard-humanization.js.map +1 -0
- package/dist/fingerprinting/fingerprint-overrides/locale-spoofing.d.ts.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/locale-spoofing.js +153 -90
- package/dist/fingerprinting/fingerprint-overrides/locale-spoofing.js.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/mouse-humanization.d.ts.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/mouse-humanization.js +4 -5
- package/dist/fingerprinting/fingerprint-overrides/mouse-humanization.js.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/performance-spoofing.d.ts.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/performance-spoofing.js +11 -1
- package/dist/fingerprinting/fingerprint-overrides/performance-spoofing.js.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/platform-consistency.d.ts +13 -0
- package/dist/fingerprinting/fingerprint-overrides/platform-consistency.d.ts.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/platform-consistency.js +413 -70
- package/dist/fingerprinting/fingerprint-overrides/platform-consistency.js.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/prototype-integrity.d.ts +13 -0
- package/dist/fingerprinting/fingerprint-overrides/prototype-integrity.d.ts.map +1 -0
- package/dist/fingerprinting/fingerprint-overrides/prototype-integrity.js +355 -0
- package/dist/fingerprinting/fingerprint-overrides/prototype-integrity.js.map +1 -0
- package/dist/fingerprinting/fingerprint-overrides/runtime-enable-bypass.d.ts +12 -3
- package/dist/fingerprinting/fingerprint-overrides/runtime-enable-bypass.d.ts.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/runtime-enable-bypass.js +261 -71
- package/dist/fingerprinting/fingerprint-overrides/runtime-enable-bypass.js.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/scroll-humanization.d.ts +55 -0
- package/dist/fingerprinting/fingerprint-overrides/scroll-humanization.d.ts.map +1 -0
- package/dist/fingerprinting/fingerprint-overrides/scroll-humanization.js +380 -0
- package/dist/fingerprinting/fingerprint-overrides/scroll-humanization.js.map +1 -0
- package/dist/fingerprinting/fingerprint-overrides/stealth-script.d.ts +8 -0
- package/dist/fingerprinting/fingerprint-overrides/stealth-script.d.ts.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/stealth-script.js +756 -71
- package/dist/fingerprinting/fingerprint-overrides/stealth-script.js.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/storage-consistency.d.ts +13 -0
- package/dist/fingerprinting/fingerprint-overrides/storage-consistency.d.ts.map +1 -0
- package/dist/fingerprinting/fingerprint-overrides/storage-consistency.js +368 -0
- package/dist/fingerprinting/fingerprint-overrides/storage-consistency.js.map +1 -0
- package/dist/fingerprinting/fingerprint-overrides/timing-consistency.d.ts +13 -0
- package/dist/fingerprinting/fingerprint-overrides/timing-consistency.d.ts.map +1 -0
- package/dist/fingerprinting/fingerprint-overrides/timing-consistency.js +438 -0
- package/dist/fingerprinting/fingerprint-overrides/timing-consistency.js.map +1 -0
- package/dist/fingerprinting/fingerprint-overrides/ua-ch.d.ts +7 -1
- package/dist/fingerprinting/fingerprint-overrides/ua-ch.d.ts.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/ua-ch.js +158 -58
- package/dist/fingerprinting/fingerprint-overrides/ua-ch.js.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/utils.d.ts +12 -0
- package/dist/fingerprinting/fingerprint-overrides/utils.d.ts.map +1 -0
- package/dist/fingerprinting/fingerprint-overrides/utils.js +517 -0
- package/dist/fingerprinting/fingerprint-overrides/utils.js.map +1 -0
- package/dist/fingerprinting/fingerprint-overrides/webgl-spoofing.d.ts.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/webgl-spoofing.js +51 -10
- package/dist/fingerprinting/fingerprint-overrides/webgl-spoofing.js.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/webgpu-spoofing.d.ts.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/webgpu-spoofing.js +11 -1
- package/dist/fingerprinting/fingerprint-overrides/webgpu-spoofing.js.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/webrtc-spoofing.d.ts.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/webrtc-spoofing.js +11 -1
- package/dist/fingerprinting/fingerprint-overrides/webrtc-spoofing.js.map +1 -1
- package/dist/page.d.ts +12 -0
- package/dist/page.d.ts.map +1 -1
- package/dist/page.js +35 -3
- package/dist/page.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +4 -4
- package/dist/fingerprinting/canvas-fingerprint.d.ts +0 -4
- package/dist/fingerprinting/canvas-fingerprint.d.ts.map +0 -1
- package/dist/fingerprinting/canvas-fingerprint.js +0 -60
- package/dist/fingerprinting/canvas-fingerprint.js.map +0 -1
|
@@ -1,13 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Base stealth script for hiding automation indicators
|
|
3
3
|
* Includes error stack hiding, console patching, and automation flags removal
|
|
4
|
+
*
|
|
5
|
+
* 2024-2025 Enhancements:
|
|
6
|
+
* - deviceMemory spoofing
|
|
7
|
+
* - pdfViewerEnabled consistency
|
|
8
|
+
* - Notification.permission handling
|
|
9
|
+
* - Bluetooth/USB/Serial API masking
|
|
10
|
+
* - Speech synthesis voice spoofing
|
|
11
|
+
* - Credentials API consistency
|
|
4
12
|
*/
|
|
5
13
|
export const createStealthScript = () => {
|
|
6
14
|
return `
|
|
7
15
|
(() => {
|
|
8
16
|
'use strict';
|
|
9
17
|
|
|
10
|
-
// Enhanced stealth script for 2025 -
|
|
18
|
+
// Enhanced stealth script for 2025 - December
|
|
11
19
|
|
|
12
20
|
// Helper functions
|
|
13
21
|
const wrapNative = (fnName) => {
|
|
@@ -24,6 +32,42 @@ export const createStealthScript = () => {
|
|
|
24
32
|
}
|
|
25
33
|
});
|
|
26
34
|
};
|
|
35
|
+
|
|
36
|
+
// Generate consistent session seed for stable values
|
|
37
|
+
const generateSessionSeed = () => {
|
|
38
|
+
const ua = navigator.userAgent || '';
|
|
39
|
+
const screen = window.screen;
|
|
40
|
+
const seed = ua.length + (screen.width || 0) + (screen.height || 0);
|
|
41
|
+
return seed;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const sessionSeed = generateSessionSeed();
|
|
45
|
+
|
|
46
|
+
// Helper to safely define property with guards
|
|
47
|
+
const safeDefineProperty = (obj, prop, descriptor) => {
|
|
48
|
+
try {
|
|
49
|
+
const existing = Object.getOwnPropertyDescriptor(obj, prop);
|
|
50
|
+
if (existing && existing.configurable === false) {
|
|
51
|
+
return false; // Cannot redefine
|
|
52
|
+
}
|
|
53
|
+
// Always use configurable: true to allow other scripts to modify
|
|
54
|
+
Object.defineProperty(obj, prop, {
|
|
55
|
+
...descriptor,
|
|
56
|
+
configurable: true
|
|
57
|
+
});
|
|
58
|
+
return true;
|
|
59
|
+
} catch (e) {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
// Helper to safely define getter property
|
|
65
|
+
const safeDefineGetter = (obj, prop, getter, enumerable = true) => {
|
|
66
|
+
return safeDefineProperty(obj, prop, {
|
|
67
|
+
get: getter,
|
|
68
|
+
enumerable: enumerable
|
|
69
|
+
});
|
|
70
|
+
};
|
|
27
71
|
|
|
28
72
|
// 1. Enhanced Error Stack Hiding (2025)
|
|
29
73
|
const hideErrorStack = () => {
|
|
@@ -98,9 +142,8 @@ export const createStealthScript = () => {
|
|
|
98
142
|
});
|
|
99
143
|
|
|
100
144
|
try {
|
|
101
|
-
|
|
145
|
+
safeDefineProperty(window, 'console', {
|
|
102
146
|
value: consoleProxy,
|
|
103
|
-
configurable: false,
|
|
104
147
|
writable: false,
|
|
105
148
|
enumerable: true
|
|
106
149
|
});
|
|
@@ -112,12 +155,9 @@ export const createStealthScript = () => {
|
|
|
112
155
|
|
|
113
156
|
// 3. WebDriver and Automation Flags Removal (2025)
|
|
114
157
|
const removeAutomationFlags = () => {
|
|
115
|
-
// Remove webdriver
|
|
158
|
+
// Remove webdriver (with guard)
|
|
116
159
|
if (navigator.webdriver) {
|
|
117
|
-
|
|
118
|
-
get: () => undefined,
|
|
119
|
-
configurable: false
|
|
120
|
-
});
|
|
160
|
+
safeDefineGetter(navigator, 'webdriver', () => undefined, false);
|
|
121
161
|
}
|
|
122
162
|
|
|
123
163
|
// Remove automation indicators
|
|
@@ -143,17 +183,16 @@ export const createStealthScript = () => {
|
|
|
143
183
|
} catch (e) {}
|
|
144
184
|
});
|
|
145
185
|
|
|
146
|
-
// Remove CDP specific properties
|
|
186
|
+
// Remove CDP specific properties (with guard)
|
|
147
187
|
if (window.chrome) {
|
|
148
188
|
const originalChrome = window.chrome;
|
|
149
|
-
|
|
150
|
-
|
|
189
|
+
safeDefineGetter(window, 'chrome', () => {
|
|
190
|
+
try {
|
|
151
191
|
if (new Error().stack.includes('evaluateOnNewDocument')) {
|
|
152
192
|
return undefined;
|
|
153
193
|
}
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
configurable: false
|
|
194
|
+
} catch (e) {}
|
|
195
|
+
return originalChrome;
|
|
157
196
|
});
|
|
158
197
|
}
|
|
159
198
|
};
|
|
@@ -178,7 +217,7 @@ export const createStealthScript = () => {
|
|
|
178
217
|
|
|
179
218
|
// Patch Error constructor to prevent stack trace manipulation
|
|
180
219
|
const OriginalError = Error;
|
|
181
|
-
|
|
220
|
+
const ErrorProxy = new Proxy(OriginalError, {
|
|
182
221
|
construct(target, args) {
|
|
183
222
|
const error = Reflect.construct(target, args);
|
|
184
223
|
|
|
@@ -194,9 +233,35 @@ export const createStealthScript = () => {
|
|
|
194
233
|
});
|
|
195
234
|
|
|
196
235
|
return error;
|
|
236
|
+
},
|
|
237
|
+
get(target, prop) {
|
|
238
|
+
// Return the original prototype when accessing .prototype
|
|
239
|
+
if (prop === 'prototype') {
|
|
240
|
+
return OriginalError.prototype;
|
|
241
|
+
}
|
|
242
|
+
return Reflect.get(target, prop);
|
|
243
|
+
},
|
|
244
|
+
set(target, prop, value) {
|
|
245
|
+
// Prevent setting prototype or other frozen properties
|
|
246
|
+
if (prop === 'prototype') {
|
|
247
|
+
return true; // Silently ignore
|
|
248
|
+
}
|
|
249
|
+
return Reflect.set(target, prop, value);
|
|
197
250
|
}
|
|
198
251
|
});
|
|
199
|
-
|
|
252
|
+
|
|
253
|
+
// Use defineProperty to replace Error without modifying frozen prototype
|
|
254
|
+
try {
|
|
255
|
+
Object.defineProperty(window, 'Error', {
|
|
256
|
+
value: ErrorProxy,
|
|
257
|
+
writable: true,
|
|
258
|
+
enumerable: false,
|
|
259
|
+
configurable: true
|
|
260
|
+
});
|
|
261
|
+
} catch (e) {
|
|
262
|
+
// If we can't redefine, just assign
|
|
263
|
+
window.Error = ErrorProxy;
|
|
264
|
+
}
|
|
200
265
|
};
|
|
201
266
|
|
|
202
267
|
// 5. Plugin and MimeType Spoofing (2025)
|
|
@@ -237,62 +302,58 @@ export const createStealthScript = () => {
|
|
|
237
302
|
}
|
|
238
303
|
];
|
|
239
304
|
|
|
240
|
-
// Create fake plugins
|
|
305
|
+
// Create fake plugins using Object.defineProperty for read-only properties
|
|
241
306
|
const fakePlugins = pluginData.map((p, index) => {
|
|
242
307
|
const plugin = Object.create(Plugin.prototype);
|
|
243
|
-
plugin
|
|
244
|
-
plugin
|
|
245
|
-
plugin
|
|
246
|
-
plugin
|
|
308
|
+
Object.defineProperty(plugin, 'name', { value: p.name, writable: false, enumerable: true });
|
|
309
|
+
Object.defineProperty(plugin, 'filename', { value: p.filename, writable: false, enumerable: true });
|
|
310
|
+
Object.defineProperty(plugin, 'description', { value: p.description, writable: false, enumerable: true });
|
|
311
|
+
Object.defineProperty(plugin, 'length', { value: 1, writable: false, enumerable: true });
|
|
247
312
|
plugin[0] = mimeData[index] || mimeData[0];
|
|
248
313
|
return plugin;
|
|
249
314
|
});
|
|
250
315
|
|
|
251
|
-
// Create fake mimeTypes
|
|
316
|
+
// Create fake mimeTypes using Object.defineProperty for read-only properties
|
|
252
317
|
const fakeMimeTypes = mimeData.map((m, index) => {
|
|
253
318
|
const mimeType = Object.create(MimeType.prototype);
|
|
254
|
-
mimeType
|
|
255
|
-
mimeType
|
|
256
|
-
mimeType
|
|
257
|
-
mimeType
|
|
319
|
+
Object.defineProperty(mimeType, 'type', { value: m.type, writable: false, enumerable: true });
|
|
320
|
+
Object.defineProperty(mimeType, 'suffixes', { value: m.suffixes, writable: false, enumerable: true });
|
|
321
|
+
Object.defineProperty(mimeType, 'description', { value: m.description, writable: false, enumerable: true });
|
|
322
|
+
Object.defineProperty(mimeType, 'enabledPlugin', { value: fakePlugins[0], writable: false, enumerable: true });
|
|
258
323
|
return mimeType;
|
|
259
324
|
});
|
|
260
325
|
|
|
261
|
-
// Override navigator.plugins
|
|
326
|
+
// Override navigator.plugins (with guard)
|
|
262
327
|
try {
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
enumerable: true
|
|
278
|
-
});
|
|
328
|
+
const pluginsGetter = () => {
|
|
329
|
+
const arr = Object.create(PluginArray.prototype);
|
|
330
|
+
fakePlugins.forEach((p, i) => {
|
|
331
|
+
arr[i] = p;
|
|
332
|
+
arr[p.name] = p;
|
|
333
|
+
});
|
|
334
|
+
// Use Object.defineProperty for read-only properties
|
|
335
|
+
Object.defineProperty(arr, 'length', { value: fakePlugins.length, writable: false, enumerable: true });
|
|
336
|
+
Object.defineProperty(arr, 'item', { value: (index) => arr[index] || null, writable: false, enumerable: false });
|
|
337
|
+
Object.defineProperty(arr, 'namedItem', { value: (name) => arr[name] || null, writable: false, enumerable: false });
|
|
338
|
+
Object.defineProperty(arr, 'refresh', { value: () => {}, writable: false, enumerable: false });
|
|
339
|
+
return arr;
|
|
340
|
+
};
|
|
341
|
+
safeDefineGetter(navigator, 'plugins', pluginsGetter);
|
|
279
342
|
|
|
280
|
-
// Override navigator.mimeTypes
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
enumerable: true
|
|
295
|
-
});
|
|
343
|
+
// Override navigator.mimeTypes (with guard)
|
|
344
|
+
const mimeTypesGetter = () => {
|
|
345
|
+
const arr = Object.create(MimeTypeArray.prototype);
|
|
346
|
+
fakeMimeTypes.forEach((m, i) => {
|
|
347
|
+
arr[i] = m;
|
|
348
|
+
arr[m.type] = m;
|
|
349
|
+
});
|
|
350
|
+
// Use Object.defineProperty for read-only properties
|
|
351
|
+
Object.defineProperty(arr, 'length', { value: fakeMimeTypes.length, writable: false, enumerable: true });
|
|
352
|
+
Object.defineProperty(arr, 'item', { value: (index) => arr[index] || null, writable: false, enumerable: false });
|
|
353
|
+
Object.defineProperty(arr, 'namedItem', { value: (name) => arr[name] || null, writable: false, enumerable: false });
|
|
354
|
+
return arr;
|
|
355
|
+
};
|
|
356
|
+
safeDefineGetter(navigator, 'mimeTypes', mimeTypesGetter);
|
|
296
357
|
} catch (e) {}
|
|
297
358
|
};
|
|
298
359
|
|
|
@@ -412,28 +473,652 @@ export const createStealthScript = () => {
|
|
|
412
473
|
};
|
|
413
474
|
|
|
414
475
|
if (!window.chrome) window.chrome = {};
|
|
415
|
-
|
|
476
|
+
safeDefineProperty(window.chrome, 'runtime', {
|
|
416
477
|
value: runtime,
|
|
417
|
-
configurable: false,
|
|
418
478
|
enumerable: true,
|
|
419
479
|
writable: false
|
|
420
480
|
});
|
|
421
481
|
}
|
|
422
482
|
};
|
|
423
483
|
|
|
484
|
+
// 10. Device Memory Spoofing (2024-2025)
|
|
485
|
+
const spoofDeviceMemory = () => {
|
|
486
|
+
const platform = navigator.platform || 'Win32';
|
|
487
|
+
|
|
488
|
+
// Platform-appropriate device memory values
|
|
489
|
+
const memoryConfig = {
|
|
490
|
+
'Win32': [4, 8, 16], // Most Windows PCs have 4-16GB
|
|
491
|
+
'MacIntel': [8, 16], // Macs typically have 8-16GB
|
|
492
|
+
'Linux x86_64': [4, 8, 16]
|
|
493
|
+
};
|
|
494
|
+
|
|
495
|
+
const memories = memoryConfig[platform] || memoryConfig['Win32'];
|
|
496
|
+
// Use session seed for consistency
|
|
497
|
+
const selectedMemory = memories[sessionSeed % memories.length];
|
|
498
|
+
|
|
499
|
+
// Use guard to avoid conflicts with other scripts
|
|
500
|
+
if ('deviceMemory' in navigator) {
|
|
501
|
+
safeDefineGetter(navigator, 'deviceMemory', () => selectedMemory);
|
|
502
|
+
}
|
|
503
|
+
};
|
|
504
|
+
|
|
505
|
+
// 11. PDF Viewer Enabled Spoofing (2024-2025)
|
|
506
|
+
const spoofPdfViewerEnabled = () => {
|
|
507
|
+
// Chrome always has PDF viewer enabled (use guard)
|
|
508
|
+
safeDefineGetter(navigator, 'pdfViewerEnabled', () => true);
|
|
509
|
+
};
|
|
510
|
+
|
|
511
|
+
// 12. Notification Permission Spoofing (2024-2025)
|
|
512
|
+
const spoofNotificationPermission = () => {
|
|
513
|
+
if ('Notification' in window) {
|
|
514
|
+
// Override the permission property to return 'default' (user hasn't been asked) - with guard
|
|
515
|
+
safeDefineGetter(Notification, 'permission', () => 'default');
|
|
516
|
+
|
|
517
|
+
// Override requestPermission to behave normally
|
|
518
|
+
const originalRequestPermission = Notification.requestPermission;
|
|
519
|
+
Notification.requestPermission = function(callback) {
|
|
520
|
+
// Return a promise that resolves to 'default' or 'denied'
|
|
521
|
+
const result = Promise.resolve('default');
|
|
522
|
+
if (callback && typeof callback === 'function') {
|
|
523
|
+
result.then(callback);
|
|
524
|
+
}
|
|
525
|
+
return result;
|
|
526
|
+
};
|
|
527
|
+
}
|
|
528
|
+
};
|
|
529
|
+
|
|
530
|
+
// 13. Bluetooth API Spoofing (2024-2025)
|
|
531
|
+
const spoofBluetoothAPI = () => {
|
|
532
|
+
if ('bluetooth' in navigator) {
|
|
533
|
+
// Make bluetooth appear available but return empty results
|
|
534
|
+
const fakeBluetooth = {
|
|
535
|
+
getAvailability: () => Promise.resolve(true),
|
|
536
|
+
requestDevice: () => Promise.reject(new DOMException('User cancelled the requestDevice() chooser.', 'NotFoundError')),
|
|
537
|
+
getDevices: () => Promise.resolve([]),
|
|
538
|
+
addEventListener: () => {},
|
|
539
|
+
removeEventListener: () => {},
|
|
540
|
+
dispatchEvent: () => true
|
|
541
|
+
};
|
|
542
|
+
|
|
543
|
+
safeDefineGetter(navigator, 'bluetooth', () => fakeBluetooth);
|
|
544
|
+
}
|
|
545
|
+
};
|
|
546
|
+
|
|
547
|
+
// 14. USB API Spoofing (2024-2025)
|
|
548
|
+
const spoofUSBAPI = () => {
|
|
549
|
+
if ('usb' in navigator) {
|
|
550
|
+
const fakeUSB = {
|
|
551
|
+
getDevices: () => Promise.resolve([]),
|
|
552
|
+
requestDevice: () => Promise.reject(new DOMException('No device selected.', 'NotFoundError')),
|
|
553
|
+
addEventListener: () => {},
|
|
554
|
+
removeEventListener: () => {},
|
|
555
|
+
dispatchEvent: () => true,
|
|
556
|
+
onconnect: null,
|
|
557
|
+
ondisconnect: null
|
|
558
|
+
};
|
|
559
|
+
|
|
560
|
+
safeDefineGetter(navigator, 'usb', () => fakeUSB);
|
|
561
|
+
}
|
|
562
|
+
};
|
|
563
|
+
|
|
564
|
+
// 15. Serial API Spoofing (2024-2025)
|
|
565
|
+
const spoofSerialAPI = () => {
|
|
566
|
+
if ('serial' in navigator) {
|
|
567
|
+
const fakeSerial = {
|
|
568
|
+
getPorts: () => Promise.resolve([]),
|
|
569
|
+
requestPort: () => Promise.reject(new DOMException('No port selected.', 'NotFoundError')),
|
|
570
|
+
addEventListener: () => {},
|
|
571
|
+
removeEventListener: () => {},
|
|
572
|
+
dispatchEvent: () => true,
|
|
573
|
+
onconnect: null,
|
|
574
|
+
ondisconnect: null
|
|
575
|
+
};
|
|
576
|
+
|
|
577
|
+
safeDefineGetter(navigator, 'serial', () => fakeSerial);
|
|
578
|
+
}
|
|
579
|
+
};
|
|
580
|
+
|
|
581
|
+
// 16. Speech Synthesis Voices Spoofing (2024-2025)
|
|
582
|
+
const spoofSpeechSynthesis = () => {
|
|
583
|
+
if ('speechSynthesis' in window) {
|
|
584
|
+
const platform = navigator.platform || 'Win32';
|
|
585
|
+
|
|
586
|
+
// Platform-specific voices
|
|
587
|
+
const voiceConfigs = {
|
|
588
|
+
'Win32': [
|
|
589
|
+
{ name: 'Microsoft David - English (United States)', lang: 'en-US', localService: true, default: true },
|
|
590
|
+
{ name: 'Microsoft Zira - English (United States)', lang: 'en-US', localService: true, default: false },
|
|
591
|
+
{ name: 'Microsoft Mark - English (United States)', lang: 'en-US', localService: true, default: false },
|
|
592
|
+
{ name: 'Google US English', lang: 'en-US', localService: false, default: false },
|
|
593
|
+
{ name: 'Google UK English Female', lang: 'en-GB', localService: false, default: false },
|
|
594
|
+
{ name: 'Google UK English Male', lang: 'en-GB', localService: false, default: false }
|
|
595
|
+
],
|
|
596
|
+
'MacIntel': [
|
|
597
|
+
{ name: 'Alex', lang: 'en-US', localService: true, default: true },
|
|
598
|
+
{ name: 'Samantha', lang: 'en-US', localService: true, default: false },
|
|
599
|
+
{ name: 'Daniel', lang: 'en-GB', localService: true, default: false },
|
|
600
|
+
{ name: 'Google US English', lang: 'en-US', localService: false, default: false }
|
|
601
|
+
],
|
|
602
|
+
'Linux x86_64': [
|
|
603
|
+
{ name: 'Google US English', lang: 'en-US', localService: false, default: true },
|
|
604
|
+
{ name: 'Google UK English Female', lang: 'en-GB', localService: false, default: false }
|
|
605
|
+
]
|
|
606
|
+
};
|
|
607
|
+
|
|
608
|
+
const voices = voiceConfigs[platform] || voiceConfigs['Win32'];
|
|
609
|
+
|
|
610
|
+
// Create SpeechSynthesisVoice-like objects
|
|
611
|
+
const fakeVoices = voices.map(v => ({
|
|
612
|
+
voiceURI: v.name,
|
|
613
|
+
name: v.name,
|
|
614
|
+
lang: v.lang,
|
|
615
|
+
localService: v.localService,
|
|
616
|
+
default: v.default
|
|
617
|
+
}));
|
|
618
|
+
|
|
619
|
+
// Override getVoices
|
|
620
|
+
const originalGetVoices = speechSynthesis.getVoices;
|
|
621
|
+
speechSynthesis.getVoices = function() {
|
|
622
|
+
// Return our platform-appropriate voices
|
|
623
|
+
return fakeVoices;
|
|
624
|
+
};
|
|
625
|
+
|
|
626
|
+
// Trigger voiceschanged event after a delay to simulate loading
|
|
627
|
+
setTimeout(() => {
|
|
628
|
+
try {
|
|
629
|
+
const event = new Event('voiceschanged');
|
|
630
|
+
speechSynthesis.dispatchEvent(event);
|
|
631
|
+
} catch (e) {}
|
|
632
|
+
}, 100);
|
|
633
|
+
}
|
|
634
|
+
};
|
|
635
|
+
|
|
636
|
+
// 17. Credentials API Spoofing (2024-2025)
|
|
637
|
+
const spoofCredentialsAPI = () => {
|
|
638
|
+
if ('credentials' in navigator) {
|
|
639
|
+
const originalGet = navigator.credentials.get;
|
|
640
|
+
const originalCreate = navigator.credentials.create;
|
|
641
|
+
const originalStore = navigator.credentials.store;
|
|
642
|
+
|
|
643
|
+
// Make credentials API behave like a normal browser
|
|
644
|
+
navigator.credentials.get = function(options) {
|
|
645
|
+
// For publicKey (WebAuthn), reject like a normal user cancellation
|
|
646
|
+
if (options && options.publicKey) {
|
|
647
|
+
return Promise.reject(new DOMException(
|
|
648
|
+
'The operation either timed out or was not allowed.',
|
|
649
|
+
'NotAllowedError'
|
|
650
|
+
));
|
|
651
|
+
}
|
|
652
|
+
// For password credentials, return null (no saved credentials)
|
|
653
|
+
return Promise.resolve(null);
|
|
654
|
+
};
|
|
655
|
+
|
|
656
|
+
navigator.credentials.create = function(options) {
|
|
657
|
+
if (options && options.publicKey) {
|
|
658
|
+
return Promise.reject(new DOMException(
|
|
659
|
+
'The operation either timed out or was not allowed.',
|
|
660
|
+
'NotAllowedError'
|
|
661
|
+
));
|
|
662
|
+
}
|
|
663
|
+
return originalCreate.apply(this, arguments);
|
|
664
|
+
};
|
|
665
|
+
|
|
666
|
+
navigator.credentials.store = function(credential) {
|
|
667
|
+
// Accept but don't actually store
|
|
668
|
+
return Promise.resolve(credential);
|
|
669
|
+
};
|
|
670
|
+
}
|
|
671
|
+
};
|
|
672
|
+
|
|
673
|
+
// 18. HID API Spoofing (2024-2025)
|
|
674
|
+
const spoofHIDAPI = () => {
|
|
675
|
+
if ('hid' in navigator) {
|
|
676
|
+
const fakeHID = {
|
|
677
|
+
getDevices: () => Promise.resolve([]),
|
|
678
|
+
requestDevice: () => Promise.reject(new DOMException('No device selected.', 'NotFoundError')),
|
|
679
|
+
addEventListener: () => {},
|
|
680
|
+
removeEventListener: () => {},
|
|
681
|
+
dispatchEvent: () => true,
|
|
682
|
+
onconnect: null,
|
|
683
|
+
ondisconnect: null
|
|
684
|
+
};
|
|
685
|
+
|
|
686
|
+
safeDefineGetter(navigator, 'hid', () => fakeHID);
|
|
687
|
+
}
|
|
688
|
+
};
|
|
689
|
+
|
|
690
|
+
// 19. Gamepad API Spoofing (2024-2025)
|
|
691
|
+
const spoofGamepadAPI = () => {
|
|
692
|
+
// Return empty array for getGamepads
|
|
693
|
+
const originalGetGamepads = navigator.getGamepads;
|
|
694
|
+
if (originalGetGamepads) {
|
|
695
|
+
navigator.getGamepads = function() {
|
|
696
|
+
// Return empty array like no gamepads connected
|
|
697
|
+
return [];
|
|
698
|
+
};
|
|
699
|
+
}
|
|
700
|
+
};
|
|
701
|
+
|
|
702
|
+
// 20. Clipboard API Protection (2024-2025)
|
|
703
|
+
const spoofClipboardAPI = () => {
|
|
704
|
+
if (navigator.clipboard) {
|
|
705
|
+
const originalReadText = navigator.clipboard.readText;
|
|
706
|
+
const originalRead = navigator.clipboard.read;
|
|
707
|
+
|
|
708
|
+
// Make clipboard read behave like permission not granted
|
|
709
|
+
navigator.clipboard.readText = function() {
|
|
710
|
+
return Promise.reject(new DOMException(
|
|
711
|
+
'Read permission denied.',
|
|
712
|
+
'NotAllowedError'
|
|
713
|
+
));
|
|
714
|
+
};
|
|
715
|
+
|
|
716
|
+
navigator.clipboard.read = function() {
|
|
717
|
+
return Promise.reject(new DOMException(
|
|
718
|
+
'Read permission denied.',
|
|
719
|
+
'NotAllowedError'
|
|
720
|
+
));
|
|
721
|
+
};
|
|
722
|
+
}
|
|
723
|
+
};
|
|
724
|
+
|
|
725
|
+
// 21. Do Not Track Spoofing (2024-2025)
|
|
726
|
+
const spoofDoNotTrack = () => {
|
|
727
|
+
// Most real users have DNT disabled or not set
|
|
728
|
+
safeDefineGetter(navigator, 'doNotTrack', () => null);
|
|
729
|
+
};
|
|
730
|
+
|
|
731
|
+
// 22. Global Privacy Control Spoofing (2024-2025)
|
|
732
|
+
const spoofGlobalPrivacyControl = () => {
|
|
733
|
+
// Most users don't have GPC enabled
|
|
734
|
+
safeDefineGetter(navigator, 'globalPrivacyControl', () => false);
|
|
735
|
+
};
|
|
736
|
+
|
|
737
|
+
// 23. Keyboard API Spoofing (2024-2025) - DataDome checks getLayoutMap()
|
|
738
|
+
const spoofKeyboardAPI = () => {
|
|
739
|
+
// Full US QWERTY keyboard layout map
|
|
740
|
+
const qwertyLayoutMap = new Map([
|
|
741
|
+
['Backquote', '\`'], ['Digit1', '1'], ['Digit2', '2'], ['Digit3', '3'],
|
|
742
|
+
['Digit4', '4'], ['Digit5', '5'], ['Digit6', '6'], ['Digit7', '7'],
|
|
743
|
+
['Digit8', '8'], ['Digit9', '9'], ['Digit0', '0'], ['Minus', '-'],
|
|
744
|
+
['Equal', '='], ['Backspace', ''], ['Tab', ''], ['KeyQ', 'q'],
|
|
745
|
+
['KeyW', 'w'], ['KeyE', 'e'], ['KeyR', 'r'], ['KeyT', 't'],
|
|
746
|
+
['KeyY', 'y'], ['KeyU', 'u'], ['KeyI', 'i'], ['KeyO', 'o'],
|
|
747
|
+
['KeyP', 'p'], ['BracketLeft', '['], ['BracketRight', ']'],
|
|
748
|
+
['Backslash', '\\\\'], ['CapsLock', ''], ['KeyA', 'a'], ['KeyS', 's'],
|
|
749
|
+
['KeyD', 'd'], ['KeyF', 'f'], ['KeyG', 'g'], ['KeyH', 'h'],
|
|
750
|
+
['KeyJ', 'j'], ['KeyK', 'k'], ['KeyL', 'l'], ['Semicolon', ';'],
|
|
751
|
+
['Quote', "'"], ['Enter', ''], ['ShiftLeft', ''], ['KeyZ', 'z'],
|
|
752
|
+
['KeyX', 'x'], ['KeyC', 'c'], ['KeyV', 'v'], ['KeyB', 'b'],
|
|
753
|
+
['KeyN', 'n'], ['KeyM', 'm'], ['Comma', ','], ['Period', '.'],
|
|
754
|
+
['Slash', '/'], ['ShiftRight', ''], ['ControlLeft', ''],
|
|
755
|
+
['AltLeft', ''], ['Space', ' '], ['AltRight', ''], ['ControlRight', ''],
|
|
756
|
+
['ArrowLeft', ''], ['ArrowUp', ''], ['ArrowDown', ''], ['ArrowRight', ''],
|
|
757
|
+
['NumpadDivide', '/'], ['NumpadMultiply', '*'], ['NumpadSubtract', '-'],
|
|
758
|
+
['Numpad7', '7'], ['Numpad8', '8'], ['Numpad9', '9'], ['NumpadAdd', '+'],
|
|
759
|
+
['Numpad4', '4'], ['Numpad5', '5'], ['Numpad6', '6'], ['Numpad1', '1'],
|
|
760
|
+
['Numpad2', '2'], ['Numpad3', '3'], ['NumpadEnter', ''], ['Numpad0', '0'],
|
|
761
|
+
['NumpadDecimal', '.']
|
|
762
|
+
]);
|
|
763
|
+
|
|
764
|
+
// Create a KeyboardLayoutMap-like object
|
|
765
|
+
const createLayoutMap = () => {
|
|
766
|
+
const map = {
|
|
767
|
+
entries: function* () { yield* qwertyLayoutMap.entries(); },
|
|
768
|
+
keys: function* () { yield* qwertyLayoutMap.keys(); },
|
|
769
|
+
values: function* () { yield* qwertyLayoutMap.values(); },
|
|
770
|
+
forEach: (callback) => qwertyLayoutMap.forEach(callback),
|
|
771
|
+
get: (key) => qwertyLayoutMap.get(key),
|
|
772
|
+
has: (key) => qwertyLayoutMap.has(key),
|
|
773
|
+
size: qwertyLayoutMap.size,
|
|
774
|
+
[Symbol.iterator]: function* () { yield* qwertyLayoutMap.entries(); }
|
|
775
|
+
};
|
|
776
|
+
|
|
777
|
+
// Make it look like a KeyboardLayoutMap
|
|
778
|
+
Object.defineProperty(map, Symbol.toStringTag, {
|
|
779
|
+
value: 'KeyboardLayoutMap',
|
|
780
|
+
configurable: true
|
|
781
|
+
});
|
|
782
|
+
|
|
783
|
+
return map;
|
|
784
|
+
};
|
|
785
|
+
|
|
786
|
+
// Cache the layout map promise for consistency (DataDome may check promise identity)
|
|
787
|
+
let cachedLayoutMapPromise = null;
|
|
788
|
+
|
|
789
|
+
const fakeKeyboard = {
|
|
790
|
+
getLayoutMap: function() {
|
|
791
|
+
if (!cachedLayoutMapPromise) {
|
|
792
|
+
cachedLayoutMapPromise = Promise.resolve(createLayoutMap());
|
|
793
|
+
}
|
|
794
|
+
return cachedLayoutMapPromise;
|
|
795
|
+
},
|
|
796
|
+
lock: function() { return Promise.resolve(); },
|
|
797
|
+
unlock: function() {}
|
|
798
|
+
};
|
|
799
|
+
|
|
800
|
+
// Make methods look native
|
|
801
|
+
Object.defineProperty(fakeKeyboard.getLayoutMap, 'toString', {
|
|
802
|
+
value: () => 'function getLayoutMap() { [native code] }',
|
|
803
|
+
configurable: false
|
|
804
|
+
});
|
|
805
|
+
|
|
806
|
+
safeDefineGetter(navigator, 'keyboard', () => fakeKeyboard);
|
|
807
|
+
};
|
|
808
|
+
|
|
809
|
+
// 24. MediaDevices API Spoofing (2024-2025) - DataDome checks enumerateDevices()
|
|
810
|
+
const spoofMediaDevicesAPI = () => {
|
|
811
|
+
if (!navigator.mediaDevices) return;
|
|
812
|
+
|
|
813
|
+
const originalEnumerateDevices = navigator.mediaDevices.enumerateDevices;
|
|
814
|
+
|
|
815
|
+
// Cache the promise for consistency
|
|
816
|
+
let cachedDevicesPromise = null;
|
|
817
|
+
|
|
818
|
+
// Create realistic but empty device list (user hasn't granted permissions)
|
|
819
|
+
const createDeviceList = () => {
|
|
820
|
+
const devices = [
|
|
821
|
+
{
|
|
822
|
+
deviceId: '',
|
|
823
|
+
groupId: '',
|
|
824
|
+
kind: 'audioinput',
|
|
825
|
+
label: '',
|
|
826
|
+
toJSON: function() { return { deviceId: '', groupId: '', kind: 'audioinput', label: '' }; }
|
|
827
|
+
},
|
|
828
|
+
{
|
|
829
|
+
deviceId: '',
|
|
830
|
+
groupId: '',
|
|
831
|
+
kind: 'videoinput',
|
|
832
|
+
label: '',
|
|
833
|
+
toJSON: function() { return { deviceId: '', groupId: '', kind: 'videoinput', label: '' }; }
|
|
834
|
+
},
|
|
835
|
+
{
|
|
836
|
+
deviceId: '',
|
|
837
|
+
groupId: '',
|
|
838
|
+
kind: 'audiooutput',
|
|
839
|
+
label: '',
|
|
840
|
+
toJSON: function() { return { deviceId: '', groupId: '', kind: 'audiooutput', label: '' }; }
|
|
841
|
+
}
|
|
842
|
+
];
|
|
843
|
+
|
|
844
|
+
// Set prototypes to MediaDeviceInfo
|
|
845
|
+
devices.forEach(device => {
|
|
846
|
+
if (typeof MediaDeviceInfo !== 'undefined') {
|
|
847
|
+
try {
|
|
848
|
+
Object.setPrototypeOf(device, MediaDeviceInfo.prototype);
|
|
849
|
+
} catch (e) {}
|
|
850
|
+
}
|
|
851
|
+
});
|
|
852
|
+
|
|
853
|
+
return devices;
|
|
854
|
+
};
|
|
855
|
+
|
|
856
|
+
navigator.mediaDevices.enumerateDevices = function() {
|
|
857
|
+
if (!cachedDevicesPromise) {
|
|
858
|
+
cachedDevicesPromise = Promise.resolve(createDeviceList());
|
|
859
|
+
}
|
|
860
|
+
return cachedDevicesPromise;
|
|
861
|
+
};
|
|
862
|
+
|
|
863
|
+
// Make it look native
|
|
864
|
+
Object.defineProperty(navigator.mediaDevices.enumerateDevices, 'toString', {
|
|
865
|
+
value: () => 'function enumerateDevices() { [native code] }',
|
|
866
|
+
configurable: false
|
|
867
|
+
});
|
|
868
|
+
};
|
|
869
|
+
|
|
870
|
+
// 25. ServiceWorker API Protection (2024-2025) - DataDome checks ready/register
|
|
871
|
+
const spoofServiceWorkerAPI = () => {
|
|
872
|
+
if (!navigator.serviceWorker) return;
|
|
873
|
+
|
|
874
|
+
const sw = navigator.serviceWorker;
|
|
875
|
+
|
|
876
|
+
// Wrap register to work normally but look native
|
|
877
|
+
const originalRegister = sw.register;
|
|
878
|
+
if (originalRegister) {
|
|
879
|
+
sw.register = function(scriptURL, options) {
|
|
880
|
+
return originalRegister.apply(this, arguments);
|
|
881
|
+
};
|
|
882
|
+
|
|
883
|
+
Object.defineProperty(sw.register, 'toString', {
|
|
884
|
+
value: () => 'function register() { [native code] }',
|
|
885
|
+
configurable: false
|
|
886
|
+
});
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
// Ensure ready returns a proper promise
|
|
890
|
+
// Note: ready is a getter, we shouldn't override it as it's already a promise
|
|
891
|
+
};
|
|
892
|
+
|
|
893
|
+
// 26. Undefined Navigator Properties (2024-2025) - DataDome checks these
|
|
894
|
+
const spoofUndefinedNavigatorProps = () => {
|
|
895
|
+
// navigator.brave - Only present in Brave browser, must be undefined for Chrome
|
|
896
|
+
safeDefineGetter(navigator, 'brave', () => undefined);
|
|
897
|
+
|
|
898
|
+
// navigator.buildID - Firefox-specific, undefined for Chrome
|
|
899
|
+
safeDefineGetter(navigator, 'buildID', () => undefined);
|
|
900
|
+
|
|
901
|
+
// navigator.contacts - Not widely supported, should be undefined
|
|
902
|
+
safeDefineGetter(navigator, 'contacts', () => undefined);
|
|
903
|
+
|
|
904
|
+
// navigator.cookieDeprecationLabel - Chrome 120+ API, undefined in most cases
|
|
905
|
+
safeDefineGetter(navigator, 'cookieDeprecationLabel', () => undefined);
|
|
906
|
+
|
|
907
|
+
// navigator.loadPurpose - Should be undefined
|
|
908
|
+
safeDefineGetter(navigator, 'loadPurpose', () => undefined);
|
|
909
|
+
};
|
|
910
|
+
|
|
911
|
+
// 27. Worker Constructor Protection - Prevent fingerprint leakage in worker contexts
|
|
912
|
+
const protectWorkerConstructor = () => {
|
|
913
|
+
if (typeof Worker === 'undefined') return;
|
|
914
|
+
|
|
915
|
+
// Cache fingerprint values to inject into workers
|
|
916
|
+
const fingerprintValues = {
|
|
917
|
+
userAgent: navigator.userAgent,
|
|
918
|
+
platform: navigator.platform,
|
|
919
|
+
vendor: navigator.vendor || '',
|
|
920
|
+
language: navigator.language,
|
|
921
|
+
languages: JSON.stringify(navigator.languages || ['en-US']),
|
|
922
|
+
hardwareConcurrency: navigator.hardwareConcurrency || 8,
|
|
923
|
+
deviceMemory: navigator.deviceMemory || 8,
|
|
924
|
+
};
|
|
925
|
+
|
|
926
|
+
// Create the worker override script
|
|
927
|
+
const workerOverrideScript = \`
|
|
928
|
+
// Worker fingerprint consistency overrides
|
|
929
|
+
(function() {
|
|
930
|
+
const fingerprintValues = \${JSON.stringify(fingerprintValues)};
|
|
931
|
+
|
|
932
|
+
// Override navigator properties in worker context
|
|
933
|
+
if (typeof WorkerNavigator !== 'undefined' && self.navigator) {
|
|
934
|
+
try {
|
|
935
|
+
Object.defineProperty(self.navigator, 'userAgent', { get: () => fingerprintValues.userAgent });
|
|
936
|
+
Object.defineProperty(self.navigator, 'platform', { get: () => fingerprintValues.platform });
|
|
937
|
+
Object.defineProperty(self.navigator, 'vendor', { get: () => fingerprintValues.vendor });
|
|
938
|
+
Object.defineProperty(self.navigator, 'language', { get: () => fingerprintValues.language });
|
|
939
|
+
Object.defineProperty(self.navigator, 'languages', { get: () => JSON.parse(fingerprintValues.languages) });
|
|
940
|
+
Object.defineProperty(self.navigator, 'hardwareConcurrency', { get: () => fingerprintValues.hardwareConcurrency });
|
|
941
|
+
Object.defineProperty(self.navigator, 'deviceMemory', { get: () => fingerprintValues.deviceMemory });
|
|
942
|
+
} catch (e) {}
|
|
943
|
+
}
|
|
944
|
+
})();
|
|
945
|
+
\`;
|
|
946
|
+
|
|
947
|
+
const OriginalWorker = Worker;
|
|
948
|
+
|
|
949
|
+
window.Worker = function(scriptURL, options) {
|
|
950
|
+
// For blob URLs, we can try to prepend our overrides
|
|
951
|
+
if (typeof scriptURL === 'string' && scriptURL.startsWith('blob:')) {
|
|
952
|
+
// Can't modify blob URLs easily, but the worker will still get our overrides
|
|
953
|
+
// if it was created from same-origin code
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
// Create the worker normally
|
|
957
|
+
const worker = new OriginalWorker(scriptURL, options);
|
|
958
|
+
|
|
959
|
+
// Intercept postMessage to detect fingerprint requests
|
|
960
|
+
const originalPostMessage = worker.postMessage.bind(worker);
|
|
961
|
+
worker.postMessage = function(message, transfer) {
|
|
962
|
+
return originalPostMessage(message, transfer);
|
|
963
|
+
};
|
|
964
|
+
|
|
965
|
+
return worker;
|
|
966
|
+
};
|
|
967
|
+
|
|
968
|
+
// Copy static properties
|
|
969
|
+
window.Worker.prototype = OriginalWorker.prototype;
|
|
970
|
+
|
|
971
|
+
// Make Worker constructor look native
|
|
972
|
+
Object.defineProperty(window.Worker, 'toString', {
|
|
973
|
+
value: () => 'function Worker() { [native code] }',
|
|
974
|
+
configurable: false
|
|
975
|
+
});
|
|
976
|
+
|
|
977
|
+
Object.defineProperty(window.Worker, 'name', {
|
|
978
|
+
value: 'Worker',
|
|
979
|
+
configurable: true
|
|
980
|
+
});
|
|
981
|
+
};
|
|
982
|
+
|
|
983
|
+
// 28. SharedWorker Protection - Similar to Worker
|
|
984
|
+
const protectSharedWorkerConstructor = () => {
|
|
985
|
+
if (typeof SharedWorker === 'undefined') return;
|
|
986
|
+
|
|
987
|
+
const OriginalSharedWorker = SharedWorker;
|
|
988
|
+
|
|
989
|
+
window.SharedWorker = function(scriptURL, options) {
|
|
990
|
+
const worker = new OriginalSharedWorker(scriptURL, options);
|
|
991
|
+
return worker;
|
|
992
|
+
};
|
|
993
|
+
|
|
994
|
+
// Copy prototype
|
|
995
|
+
window.SharedWorker.prototype = OriginalSharedWorker.prototype;
|
|
996
|
+
|
|
997
|
+
// Make SharedWorker constructor look native
|
|
998
|
+
Object.defineProperty(window.SharedWorker, 'toString', {
|
|
999
|
+
value: () => 'function SharedWorker() { [native code] }',
|
|
1000
|
+
configurable: false
|
|
1001
|
+
});
|
|
1002
|
+
|
|
1003
|
+
Object.defineProperty(window.SharedWorker, 'name', {
|
|
1004
|
+
value: 'SharedWorker',
|
|
1005
|
+
configurable: true
|
|
1006
|
+
});
|
|
1007
|
+
};
|
|
1008
|
+
|
|
1009
|
+
// 29. PostMessage Fingerprint Protection - Prevent cross-context fingerprint detection
|
|
1010
|
+
const protectPostMessage = () => {
|
|
1011
|
+
// Cache consistent fingerprint values
|
|
1012
|
+
const consistentFingerprint = {
|
|
1013
|
+
navigator: {
|
|
1014
|
+
userAgent: navigator.userAgent,
|
|
1015
|
+
platform: navigator.platform,
|
|
1016
|
+
vendor: navigator.vendor,
|
|
1017
|
+
language: navigator.language,
|
|
1018
|
+
languages: navigator.languages ? [...navigator.languages] : ['en-US'],
|
|
1019
|
+
hardwareConcurrency: navigator.hardwareConcurrency,
|
|
1020
|
+
deviceMemory: navigator.deviceMemory,
|
|
1021
|
+
maxTouchPoints: navigator.maxTouchPoints,
|
|
1022
|
+
webdriver: undefined,
|
|
1023
|
+
},
|
|
1024
|
+
screen: {
|
|
1025
|
+
width: screen.width,
|
|
1026
|
+
height: screen.height,
|
|
1027
|
+
availWidth: screen.availWidth,
|
|
1028
|
+
availHeight: screen.availHeight,
|
|
1029
|
+
colorDepth: screen.colorDepth,
|
|
1030
|
+
pixelDepth: screen.pixelDepth,
|
|
1031
|
+
}
|
|
1032
|
+
};
|
|
1033
|
+
|
|
1034
|
+
// Listen for fingerprint requests from iframes/workers
|
|
1035
|
+
window.addEventListener('message', function(event) {
|
|
1036
|
+
// Detect common fingerprint request patterns
|
|
1037
|
+
if (event.data && typeof event.data === 'object') {
|
|
1038
|
+
// If message looks like a fingerprint request, respond with consistent values
|
|
1039
|
+
if (event.data.type === 'fingerprintRequest' ||
|
|
1040
|
+
event.data.action === 'getFingerprint' ||
|
|
1041
|
+
event.data.cmd === 'getNavigator') {
|
|
1042
|
+
|
|
1043
|
+
if (event.source) {
|
|
1044
|
+
try {
|
|
1045
|
+
event.source.postMessage({
|
|
1046
|
+
type: 'fingerprintResponse',
|
|
1047
|
+
data: consistentFingerprint
|
|
1048
|
+
}, event.origin || '*');
|
|
1049
|
+
} catch (e) {}
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
}, false);
|
|
1054
|
+
|
|
1055
|
+
// Wrap postMessage to ensure consistent data
|
|
1056
|
+
const originalPostMessage = window.postMessage.bind(window);
|
|
1057
|
+
window.postMessage = function(message, targetOrigin, transfer) {
|
|
1058
|
+
return originalPostMessage(message, targetOrigin, transfer);
|
|
1059
|
+
};
|
|
1060
|
+
|
|
1061
|
+
Object.defineProperty(window.postMessage, 'toString', {
|
|
1062
|
+
value: () => 'function postMessage() { [native code] }',
|
|
1063
|
+
configurable: false
|
|
1064
|
+
});
|
|
1065
|
+
};
|
|
1066
|
+
|
|
424
1067
|
// Initialize all protections
|
|
1068
|
+
const DEBUG_PREFIX = '[CDP-FP-DEBUG]';
|
|
1069
|
+
const ERROR_PREFIX = '[CDP-FP-ERROR]';
|
|
1070
|
+
|
|
1071
|
+
console.log(DEBUG_PREFIX, 'Starting stealth protection injection...');
|
|
1072
|
+
|
|
1073
|
+
const applyProtection = (name, fn) => {
|
|
1074
|
+
try {
|
|
1075
|
+
fn();
|
|
1076
|
+
console.log(DEBUG_PREFIX, \`✓ Applied \${name}\`);
|
|
1077
|
+
return true;
|
|
1078
|
+
} catch (e) {
|
|
1079
|
+
console.error(ERROR_PREFIX, \`✗ Failed to apply \${name}:\`, e);
|
|
1080
|
+
throw e; // Rethrow to make errors visible
|
|
1081
|
+
}
|
|
1082
|
+
};
|
|
1083
|
+
|
|
425
1084
|
try {
|
|
426
|
-
hideErrorStack
|
|
427
|
-
patchConsole
|
|
428
|
-
removeAutomationFlags
|
|
429
|
-
preventRuntimeEnableDetection
|
|
430
|
-
spoofPlugins
|
|
431
|
-
spoofPermissions
|
|
432
|
-
spoofBatteryAPI
|
|
433
|
-
spoofConnectionAPI
|
|
434
|
-
spoofChromeRuntime
|
|
1085
|
+
applyProtection('Error Stack Hiding', hideErrorStack);
|
|
1086
|
+
applyProtection('Console Patching', patchConsole);
|
|
1087
|
+
applyProtection('Automation Flags Removal', removeAutomationFlags);
|
|
1088
|
+
applyProtection('Runtime.enable Detection Prevention', preventRuntimeEnableDetection);
|
|
1089
|
+
applyProtection('Plugin Spoofing', spoofPlugins);
|
|
1090
|
+
applyProtection('Permissions API Spoofing', spoofPermissions);
|
|
1091
|
+
applyProtection('Battery API Spoofing', spoofBatteryAPI);
|
|
1092
|
+
applyProtection('Connection API Spoofing', spoofConnectionAPI);
|
|
1093
|
+
applyProtection('Chrome Runtime API Spoofing', spoofChromeRuntime);
|
|
1094
|
+
// New 2024-2025 protections
|
|
1095
|
+
applyProtection('Device Memory Spoofing', spoofDeviceMemory);
|
|
1096
|
+
applyProtection('PDF Viewer Enabled Spoofing', spoofPdfViewerEnabled);
|
|
1097
|
+
applyProtection('Notification Permission Spoofing', spoofNotificationPermission);
|
|
1098
|
+
applyProtection('Bluetooth API Spoofing', spoofBluetoothAPI);
|
|
1099
|
+
applyProtection('USB API Spoofing', spoofUSBAPI);
|
|
1100
|
+
applyProtection('Serial API Spoofing', spoofSerialAPI);
|
|
1101
|
+
applyProtection('Speech Synthesis Spoofing', spoofSpeechSynthesis);
|
|
1102
|
+
applyProtection('Credentials API Spoofing', spoofCredentialsAPI);
|
|
1103
|
+
applyProtection('HID API Spoofing', spoofHIDAPI);
|
|
1104
|
+
applyProtection('Gamepad API Spoofing', spoofGamepadAPI);
|
|
1105
|
+
applyProtection('Clipboard API Protection', spoofClipboardAPI);
|
|
1106
|
+
applyProtection('Do Not Track Spoofing', spoofDoNotTrack);
|
|
1107
|
+
applyProtection('Global Privacy Control Spoofing', spoofGlobalPrivacyControl);
|
|
1108
|
+
// DataDome-specific protections
|
|
1109
|
+
applyProtection('Keyboard API Spoofing', spoofKeyboardAPI);
|
|
1110
|
+
applyProtection('MediaDevices API Spoofing', spoofMediaDevicesAPI);
|
|
1111
|
+
applyProtection('ServiceWorker API Protection', spoofServiceWorkerAPI);
|
|
1112
|
+
applyProtection('Undefined Navigator Props', spoofUndefinedNavigatorProps);
|
|
1113
|
+
// Cross-context consistency protections
|
|
1114
|
+
applyProtection('Worker Constructor Protection', protectWorkerConstructor);
|
|
1115
|
+
applyProtection('SharedWorker Protection', protectSharedWorkerConstructor);
|
|
1116
|
+
applyProtection('PostMessage Protection', protectPostMessage);
|
|
1117
|
+
|
|
1118
|
+
console.log(DEBUG_PREFIX, '✓ All stealth protections applied successfully');
|
|
435
1119
|
} catch (e) {
|
|
436
|
-
|
|
1120
|
+
console.error(ERROR_PREFIX, '✗ Failed to apply stealth protections:', e);
|
|
1121
|
+
throw e; // Rethrow to make errors visible
|
|
437
1122
|
}
|
|
438
1123
|
})();`;
|
|
439
1124
|
};
|