jumpy-lion 0.0.42 → 0.0.44
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/fingerprinting/fingerprint-injector.d.ts.map +1 -1
- package/dist/fingerprinting/fingerprint-injector.js +4 -1
- package/dist/fingerprinting/fingerprint-injector.js.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/cdp-detection-bypass.d.ts.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/cdp-detection-bypass.js +283 -4
- package/dist/fingerprinting/fingerprint-overrides/cdp-detection-bypass.js.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/datadome-bypass.d.ts.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/datadome-bypass.js +107 -0
- package/dist/fingerprinting/fingerprint-overrides/datadome-bypass.js.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/index.d.ts +1 -0
- package/dist/fingerprinting/fingerprint-overrides/index.d.ts.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/index.js +2 -0
- package/dist/fingerprinting/fingerprint-overrides/index.js.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/locale-spoofing.d.ts.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/locale-spoofing.js +99 -68
- package/dist/fingerprinting/fingerprint-overrides/locale-spoofing.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 +2 -0
- package/dist/fingerprinting/fingerprint-overrides/runtime-enable-bypass.d.ts.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/runtime-enable-bypass.js +155 -24
- package/dist/fingerprinting/fingerprint-overrides/runtime-enable-bypass.js.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/stealth-script.d.ts.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/stealth-script.js +339 -0
- package/dist/fingerprinting/fingerprint-overrides/stealth-script.js.map +1 -1
- 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.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/utils.js +214 -12
- package/dist/fingerprinting/fingerprint-overrides/utils.js.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/webgl-spoofing.d.ts.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/webgl-spoofing.js +39 -10
- package/dist/fingerprinting/fingerprint-overrides/webgl-spoofing.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +1 -1
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
*
|
|
5
5
|
* IMPORTANT: This is now a SCRIPT to be injected via addScriptToEvaluateOnNewDocument,
|
|
6
6
|
* NOT via Runtime.evaluate (which would defeat the purpose since Runtime.evaluate IS detectable!)
|
|
7
|
+
*
|
|
8
|
+
* Enhanced 2024-2025: Comprehensive stack trace sanitization that removes ALL CDP markers
|
|
7
9
|
*/
|
|
8
10
|
/**
|
|
9
11
|
* Creates the runtime enable bypass script that should be bundled with other stealth scripts.
|
|
@@ -16,7 +18,7 @@ export const createRuntimeEnableBypassScript = () => `
|
|
|
16
18
|
|
|
17
19
|
try {
|
|
18
20
|
// =============================================================================
|
|
19
|
-
// PART 1: Error stack trace sanitization
|
|
21
|
+
// PART 1: Comprehensive Error stack trace sanitization
|
|
20
22
|
// CDP detection often works by examining Error.stack for automation markers
|
|
21
23
|
// =============================================================================
|
|
22
24
|
|
|
@@ -24,56 +26,185 @@ export const createRuntimeEnableBypassScript = () => `
|
|
|
24
26
|
const originalErrorToString = Error.prototype.toString;
|
|
25
27
|
const originalErrorStackDesc = Object.getOwnPropertyDescriptor(Error.prototype, 'stack');
|
|
26
28
|
|
|
27
|
-
//
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
29
|
+
// Comprehensive patterns that reveal CDP/automation
|
|
30
|
+
const CDP_MARKER_PATTERNS = [
|
|
31
|
+
// Puppeteer/Playwright markers
|
|
32
|
+
/__puppeteer_evaluation_script__/g,
|
|
33
|
+
/__playwright_evaluation_script__/g,
|
|
34
|
+
/__driver_evaluate/g,
|
|
35
|
+
/__webdriver_evaluate/g,
|
|
36
|
+
/__selenium_evaluate/g,
|
|
37
|
+
/__fxdriver_evaluate/g,
|
|
38
|
+
|
|
39
|
+
// CDP protocol markers
|
|
40
|
+
/CDP_[A-Z_]+/g,
|
|
41
|
+
/Runtime\\.evaluate/g,
|
|
42
|
+
/Runtime\\.callFunctionOn/g,
|
|
43
|
+
/Page\\.addScriptToEvaluateOnNewDocument/g,
|
|
44
|
+
|
|
45
|
+
// DevTools markers
|
|
46
|
+
/devtools:\\/\\/[^\\s)]+/g,
|
|
47
|
+
/chrome-extension:\\/\\/[a-z]+\\/[^\\s)]+/g,
|
|
48
|
+
|
|
49
|
+
// Puppeteer-specific patterns
|
|
50
|
+
/pptr:[^\\s)]+/g,
|
|
51
|
+
/\\(pptr:[^)]+\\)/g,
|
|
52
|
+
|
|
53
|
+
// Anonymous script patterns that reveal CDP injection
|
|
54
|
+
/at <anonymous>:\\d+:\\d+/g,
|
|
55
|
+
/at Object\\.<anonymous> \\(<anonymous>:\\d+:\\d+\\)/g,
|
|
56
|
+
/at Object\\.construct \\(<anonymous>:\\d+:\\d+\\)/g,
|
|
57
|
+
/at Object\\.apply \\(<anonymous>:\\d+:\\d+\\)/g,
|
|
58
|
+
/at Object\\.get \\(<anonymous>:\\d+:\\d+\\)/g,
|
|
59
|
+
/at newHandler\\.<computed>/g,
|
|
60
|
+
/\\(<anonymous>:\\d+:\\d+\\)/g,
|
|
61
|
+
|
|
62
|
+
// Source URL comments
|
|
63
|
+
/\\/\\/# sourceURL=[^\\n]+/g,
|
|
64
|
+
/\\/\\/# sourceMappingURL=[^\\n]+/g,
|
|
65
|
+
/\\/\\/@sourceURL=[^\\n]+/g,
|
|
66
|
+
];
|
|
67
|
+
|
|
68
|
+
// Realistic replacement frames for different contexts
|
|
69
|
+
const REALISTIC_FRAMES = [
|
|
70
|
+
'at https://www.google.com/pagead/managed/js/gpt/m202412091001/pubads_impl_page_level_ads.js:1:100',
|
|
71
|
+
'at https://www.googletagmanager.com/gtm.js:1:234',
|
|
72
|
+
'at https://www.google-analytics.com/analytics.js:1:456',
|
|
36
73
|
];
|
|
37
74
|
|
|
75
|
+
/**
|
|
76
|
+
* Sanitize a single stack frame line
|
|
77
|
+
*/
|
|
78
|
+
const sanitizeStackLine = (line) => {
|
|
79
|
+
if (!line || typeof line !== 'string') return line;
|
|
80
|
+
|
|
81
|
+
// Check if this line contains CDP markers
|
|
82
|
+
let hasCDPMarker = false;
|
|
83
|
+
for (const pattern of CDP_MARKER_PATTERNS) {
|
|
84
|
+
pattern.lastIndex = 0; // Reset regex state
|
|
85
|
+
if (pattern.test(line)) {
|
|
86
|
+
hasCDPMarker = true;
|
|
87
|
+
break;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// If line has CDP markers, replace the entire line with a realistic one
|
|
92
|
+
// or just the marker portion
|
|
93
|
+
if (hasCDPMarker) {
|
|
94
|
+
// If the line is mostly anonymous/CDP content, skip it entirely
|
|
95
|
+
if (line.includes('<anonymous>:') && !line.includes('http')) {
|
|
96
|
+
return null; // Mark for removal
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Otherwise, sanitize the markers
|
|
100
|
+
let sanitized = line;
|
|
101
|
+
for (const pattern of CDP_MARKER_PATTERNS) {
|
|
102
|
+
pattern.lastIndex = 0;
|
|
103
|
+
sanitized = sanitized.replace(pattern, '');
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Clean up any double spaces or empty parentheses
|
|
107
|
+
sanitized = sanitized
|
|
108
|
+
.replace(/\\(\\s*\\)/g, '')
|
|
109
|
+
.replace(/\\s+/g, ' ')
|
|
110
|
+
.trim();
|
|
111
|
+
|
|
112
|
+
// If line is now too short to be meaningful, skip it
|
|
113
|
+
if (sanitized.length < 10 || sanitized === 'at') {
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return sanitized;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return line;
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Comprehensively sanitize a stack trace
|
|
125
|
+
*/
|
|
38
126
|
const sanitizeStack = (stack) => {
|
|
39
127
|
if (!stack || typeof stack !== 'string') return stack;
|
|
40
128
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
129
|
+
const lines = stack.split('\\n');
|
|
130
|
+
const sanitizedLines = [];
|
|
131
|
+
let removedCount = 0;
|
|
132
|
+
|
|
133
|
+
for (let i = 0; i < lines.length; i++) {
|
|
134
|
+
const line = lines[i];
|
|
135
|
+
const sanitized = sanitizeStackLine(line);
|
|
136
|
+
|
|
137
|
+
if (sanitized === null) {
|
|
138
|
+
removedCount++;
|
|
139
|
+
// Don't add too many removed lines in a row
|
|
140
|
+
if (removedCount > 3) continue;
|
|
141
|
+
} else {
|
|
142
|
+
removedCount = 0;
|
|
143
|
+
sanitizedLines.push(sanitized);
|
|
49
144
|
}
|
|
50
145
|
}
|
|
51
|
-
|
|
146
|
+
|
|
147
|
+
// Ensure stack looks natural - should have at least error message + a few frames
|
|
148
|
+
if (sanitizedLines.length < 2) {
|
|
149
|
+
// Add some realistic frames if stack is too short
|
|
150
|
+
sanitizedLines.push(' at HTMLDocument.<anonymous> (https://www.example.com/:1:100)');
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return sanitizedLines.join('\\n');
|
|
52
154
|
};
|
|
53
155
|
|
|
54
156
|
// Override Error.prepareStackTrace if it exists (V8 specific)
|
|
55
|
-
|
|
157
|
+
// This is called when stack property is first accessed
|
|
158
|
+
if (Error.prepareStackTrace !== undefined) {
|
|
56
159
|
const originalPrepareStackTrace = Error.prepareStackTrace;
|
|
57
160
|
Error.prepareStackTrace = function(error, structuredStackTrace) {
|
|
58
|
-
|
|
161
|
+
// If there's an original prepareStackTrace, use it first
|
|
162
|
+
let stack;
|
|
163
|
+
if (typeof originalPrepareStackTrace === 'function') {
|
|
164
|
+
stack = originalPrepareStackTrace.call(this, error, structuredStackTrace);
|
|
165
|
+
} else {
|
|
166
|
+
// Default V8 formatting
|
|
167
|
+
stack = error.toString() + '\\n' +
|
|
168
|
+
structuredStackTrace.map(frame => {
|
|
169
|
+
const fnName = frame.getFunctionName() || '<anonymous>';
|
|
170
|
+
const fileName = frame.getFileName() || '<anonymous>';
|
|
171
|
+
const lineNumber = frame.getLineNumber() || 0;
|
|
172
|
+
const columnNumber = frame.getColumnNumber() || 0;
|
|
173
|
+
return \` at \${fnName} (\${fileName}:\${lineNumber}:\${columnNumber})\`;
|
|
174
|
+
}).join('\\n');
|
|
175
|
+
}
|
|
59
176
|
return sanitizeStack(stack);
|
|
60
177
|
};
|
|
61
178
|
}
|
|
62
179
|
|
|
63
|
-
// Override Error.prototype.stack getter
|
|
180
|
+
// Override Error.prototype.stack getter with comprehensive sanitization
|
|
64
181
|
if (originalErrorStackDesc && originalErrorStackDesc.get) {
|
|
65
182
|
Object.defineProperty(Error.prototype, 'stack', {
|
|
66
183
|
get: function() {
|
|
67
184
|
const stack = originalErrorStackDesc.get.call(this);
|
|
68
185
|
return sanitizeStack(stack);
|
|
69
186
|
},
|
|
70
|
-
set:
|
|
187
|
+
set: function(value) {
|
|
188
|
+
if (originalErrorStackDesc.set) {
|
|
189
|
+
// Sanitize before setting too
|
|
190
|
+
originalErrorStackDesc.set.call(this, sanitizeStack(value));
|
|
191
|
+
}
|
|
192
|
+
},
|
|
71
193
|
configurable: true,
|
|
72
194
|
enumerable: false
|
|
73
195
|
});
|
|
74
196
|
}
|
|
75
197
|
|
|
76
|
-
|
|
198
|
+
// Also patch Error.captureStackTrace for V8
|
|
199
|
+
if (typeof Error.captureStackTrace === 'function') {
|
|
200
|
+
const originalCaptureStackTrace = Error.captureStackTrace;
|
|
201
|
+
Error.captureStackTrace = function(targetObject, constructorOpt) {
|
|
202
|
+
originalCaptureStackTrace.call(this, targetObject, constructorOpt);
|
|
203
|
+
// Stack will be sanitized when accessed via our getter
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
console.log(DEBUG_PREFIX, '✓ Comprehensive Error stack trace sanitization applied');
|
|
77
208
|
|
|
78
209
|
// =============================================================================
|
|
79
210
|
// PART 2: Console protection
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime-enable-bypass.js","sourceRoot":"","sources":["../../../src/fingerprinting/fingerprint-overrides/runtime-enable-bypass.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"runtime-enable-bypass.js","sourceRoot":"","sources":["../../../src/fingerprinting/fingerprint-overrides/runtime-enable-bypass.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH;;;GAGG;AACH,MAAM,CAAC,MAAM,+BAA+B,GAAG,GAAW,EAAE,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmQ5D,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stealth-script.d.ts","sourceRoot":"","sources":["../../../src/fingerprinting/fingerprint-overrides/stealth-script.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,eAAO,MAAM,mBAAmB,QAAO,
|
|
1
|
+
{"version":3,"file":"stealth-script.d.ts","sourceRoot":"","sources":["../../../src/fingerprinting/fingerprint-overrides/stealth-script.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,eAAO,MAAM,mBAAmB,QAAO,MAulCtC,CAAC"}
|
|
@@ -734,6 +734,336 @@ export const createStealthScript = () => {
|
|
|
734
734
|
safeDefineGetter(navigator, 'globalPrivacyControl', () => false);
|
|
735
735
|
};
|
|
736
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
|
+
|
|
737
1067
|
// Initialize all protections
|
|
738
1068
|
const DEBUG_PREFIX = '[CDP-FP-DEBUG]';
|
|
739
1069
|
const ERROR_PREFIX = '[CDP-FP-ERROR]';
|
|
@@ -775,6 +1105,15 @@ export const createStealthScript = () => {
|
|
|
775
1105
|
applyProtection('Clipboard API Protection', spoofClipboardAPI);
|
|
776
1106
|
applyProtection('Do Not Track Spoofing', spoofDoNotTrack);
|
|
777
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);
|
|
778
1117
|
|
|
779
1118
|
console.log(DEBUG_PREFIX, '✓ All stealth protections applied successfully');
|
|
780
1119
|
} catch (e) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stealth-script.js","sourceRoot":"","sources":["../../../src/fingerprinting/fingerprint-overrides/stealth-script.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,MAAM,CAAC,MAAM,mBAAmB,GAAG,GAAW,EAAE;IAC5C,OAAO
|
|
1
|
+
{"version":3,"file":"stealth-script.js","sourceRoot":"","sources":["../../../src/fingerprinting/fingerprint-overrides/stealth-script.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,MAAM,CAAC,MAAM,mBAAmB,GAAG,GAAW,EAAE;IAC5C,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;MAqlCL,CAAC;AACP,CAAC,CAAC"}
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* navigator.userAgentData spoofing
|
|
2
|
+
* navigator.userAgentData spoofing (UA-CH Client Hints)
|
|
3
3
|
* Generates brand list & bitness matching the chosen platform.
|
|
4
|
+
*
|
|
5
|
+
* 2024-2025 Enhanced:
|
|
6
|
+
* - Consistent platform returns "Windows" for Win32
|
|
7
|
+
* - Proper getHighEntropyValues() with all Windows-specific fields
|
|
8
|
+
* - Realistic Chrome version numbers matching userAgent
|
|
9
|
+
* - Proper prototype chain and native-like appearance
|
|
4
10
|
*/
|
|
5
11
|
export declare const createUAClientHintsSpoofingScript: (platform?: string) => string;
|
|
6
12
|
//# sourceMappingURL=ua-ch.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ua-ch.d.ts","sourceRoot":"","sources":["../../../src/fingerprinting/fingerprint-overrides/ua-ch.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"ua-ch.d.ts","sourceRoot":"","sources":["../../../src/fingerprinting/fingerprint-overrides/ua-ch.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,eAAO,MAAM,iCAAiC,yBAAyB,MAwLtE,CAAC"}
|