@fanboynz/network-scanner 1.0.64 → 1.0.66

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.
@@ -1,198 +1,106 @@
1
1
  // === Enhanced Fingerprint Protection Module - Puppeteer 23.x Compatible ===
2
2
  // This module handles advanced browser fingerprint spoofing, user agent changes,
3
3
  // and comprehensive bot detection evasion techniques.
4
+ //const { applyErrorSuppression } = require('./error-suppression');
4
5
 
5
6
  // Default values for fingerprint spoofing if not set to 'random'
6
7
  const DEFAULT_PLATFORM = 'Win32';
7
8
  const DEFAULT_TIMEZONE = 'America/New_York';
8
9
 
9
- /**
10
- * Built-in properties that should not be modified to avoid browser detection
11
- */
12
- const BUILT_IN_PROPERTIES = [
10
+ // Built-in properties that should not be modified
11
+ const BUILT_IN_PROPERTIES = new Set([
13
12
  'href', 'origin', 'protocol', 'host', 'hostname', 'port', 'pathname', 'search', 'hash',
14
- 'constructor', 'prototype', '__proto__', 'toString', 'valueOf',
15
- 'assign', 'reload', 'replace' // Additional location object methods
16
- ];
13
+ 'constructor', 'prototype', '__proto__', 'toString', 'valueOf', 'assign', 'reload', 'replace'
14
+ ]);
15
+
16
+ // User agent collections with latest versions
17
+ const USER_AGENT_COLLECTIONS = {
18
+ chrome: [
19
+ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
20
+ "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",
21
+ "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36"
22
+ ],
23
+ firefox: [
24
+ "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:133.0) Gecko/20100101 Firefox/133.0",
25
+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:133.0) Gecko/20100101 Firefox/133.0",
26
+ "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:133.0) Gecko/20100101 Firefox/133.0"
27
+ ],
28
+ safari: [
29
+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.2 Safari/605.1.15",
30
+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.6 Safari/605.1.15"
31
+ ]
32
+ };
33
+
34
+ // Timezone configuration with offsets
35
+ const TIMEZONE_CONFIG = {
36
+ 'America/New_York': { offset: 300, abbr: 'EST' },
37
+ 'America/Los_Angeles': { offset: 480, abbr: 'PST' },
38
+ 'Europe/London': { offset: 0, abbr: 'GMT' },
39
+ 'America/Chicago': { offset: 360, abbr: 'CST' }
40
+ };
17
41
 
18
42
  /**
19
- * Checks if a property is a built-in that shouldn't be modified
43
+ * Safely defines a property with comprehensive error handling
20
44
  */
21
- function isBuiltInProperty(target, property) {
22
-
23
- // Special handling for Location object properties
24
- if (target === window.location || (target.constructor && target.constructor.name === 'Location')) {
25
- return ['href', 'origin', 'protocol', 'host', 'hostname', 'port', 'pathname', 'search', 'hash'].includes(property);
26
- }
27
-
28
- return BUILT_IN_PROPERTIES.includes(property);
29
- }
30
-
31
- /**
32
- * Creates a safe property descriptor for Puppeteer 23.x
33
- */
34
- function createSafeDescriptor(descriptor, existingDescriptor) {
35
- const safeDescriptor = { ...descriptor };
36
-
37
- // Always ensure configurable is true unless specifically set to false
38
- if (safeDescriptor.configurable !== false) {
39
- safeDescriptor.configurable = true;
40
- }
41
-
42
- // Preserve existing enumerable state if not specified
43
- if (safeDescriptor.enumerable === undefined && existingDescriptor) {
44
- safeDescriptor.enumerable = existingDescriptor.enumerable;
45
- }
46
-
47
- // Ensure writable is properly set for data descriptors
48
- if ('value' in safeDescriptor && safeDescriptor.writable === undefined) {
49
- safeDescriptor.writable = true;
45
+ function safeDefineProperty(target, property, descriptor, options = {}) {
46
+ if (BUILT_IN_PROPERTIES.has(property)) {
47
+ if (options.debug) console.log(`[fingerprint] Skipping built-in property: ${property}`);
48
+ return false;
50
49
  }
51
-
52
- return safeDescriptor;
53
- }
54
50
 
55
- /**
56
- * Safely defines or redefines a property with error handling for Puppeteer 23.x
57
- * @param {Object} target - Target object
58
- * @param {string} property - Property name
59
- * @param {Object} descriptor - Property descriptor
60
- * @param {boolean} forceDebug - Debug logging flag
61
- */
62
- function safeDefineProperty(target, property, descriptor, forceDebug = false) {
63
51
  try {
64
- // Enhanced validation before attempting to modify
65
- if (!canModifyProperty(target, property, forceDebug)) {
52
+ const existing = Object.getOwnPropertyDescriptor(target, property);
53
+ if (existing?.configurable === false) {
54
+ if (options.debug) console.log(`[fingerprint] Cannot modify non-configurable: ${property}`);
66
55
  return false;
67
56
  }
68
- const existingDescriptor = Object.getOwnPropertyDescriptor(target, property);
69
57
 
70
- // Enhanced validation for Puppeteer 23.x
71
- if (existingDescriptor) {
72
- // Check if it's a built-in property that shouldn't be modified
73
- if (isBuiltInProperty(target, property)) {
74
- if (forceDebug) {
75
- console.log(`[fingerprint] Skipping built-in property: ${property}`);
76
- }
77
- return false;
78
- }
79
-
80
- // Check if property is truly non-configurable
81
- if (existingDescriptor.configurable === false) {
82
- if (forceDebug) {
83
- console.log(`[fingerprint] Cannot redefine non-configurable property: ${property}`);
84
- }
85
- return false;
86
- }
87
- }
88
-
89
- // Enhanced descriptor validation
90
- const safeDescriptor = createSafeDescriptor(descriptor, existingDescriptor);
91
-
92
- Object.defineProperty(target, property, safeDescriptor);
58
+ Object.defineProperty(target, property, {
59
+ ...descriptor,
60
+ configurable: true
61
+ });
93
62
  return true;
94
- } catch (defineErr) {
95
- if (forceDebug) {
96
- console.log(`[fingerprint] Property definition failed for ${property}: ${defineErr.message}`);
97
- }
63
+ } catch (err) {
64
+ if (options.debug) console.log(`[fingerprint] Failed to define ${property}: ${err.message}`);
98
65
  return false;
99
66
  }
100
67
  }
101
68
 
102
69
  /**
103
- * Enhanced validation for property modification with location object handling
104
- * Consolidated from canSafelyModifyProperty to avoid duplication
105
- */
106
- function canModifyProperty(target, property, forceDebug = false) {
107
- try {
108
- // COMPREHENSIVE DEBUG: Log every canModifyProperty check
109
- if (forceDebug) {
110
- console.log(`[fingerprint] canModifyProperty called for: ${property} on target:`, target.constructor?.name || 'unknown');
111
- if (property === 'href') {
112
- console.log(`[fingerprint] CRITICAL: Checking if href can be modified!`);
113
- console.log(`[fingerprint] Target details:`, {
114
- isWindow: target === window,
115
- isLocation: target === window.location,
116
- constructorName: target.constructor?.name
117
- });
118
- }
119
- }
120
-
121
- // Check if it's a built-in property that shouldn't be modified
122
- if (isBuiltInProperty(target, property)) {
123
- if (forceDebug) {
124
- console.log(`[fingerprint] Skipping built-in property: ${property}`);
125
- }
126
- return false;
127
- }
128
-
129
- const descriptor = Object.getOwnPropertyDescriptor(target, property);
130
- if (!descriptor) return true; // Property doesn't exist, can be added
131
-
132
- return descriptor.configurable !== false;
133
- } catch (checkErr) {
134
- if (forceDebug) {
135
- console.log(`[fingerprint] Property check failed for ${property}: ${checkErr.message}`);
136
- }
137
- return false; // If we can't check, assume we can't modify
138
- }
139
- }
140
-
141
- /**
142
- * Safely executes fingerprint spoofing code with comprehensive error handling
143
- * @param {Function} spoofFunction - Function to execute
144
- * @param {string} description - Description of the spoofing operation
145
- * @param {boolean} forceDebug - Debug logging flag
70
+ * Safely executes spoofing operations with error handling
146
71
  */
147
- function safeExecuteSpoofing(spoofFunction, description, forceDebug = false) {
72
+ function safeSpoofingExecution(spoofFunction, description, options = {}) {
148
73
  try {
149
74
  spoofFunction();
150
75
  return true;
151
- } catch (spoofErr) {
152
- // Enhanced error categorization for Puppeteer 23.x
153
- const isCriticalError = spoofErr.message.includes('Cannot redefine property') ||
154
- spoofErr.message.includes('non-configurable') ||
155
- spoofErr.message.includes('Invalid property descriptor');
156
-
157
- if (forceDebug) {
158
- const errorLevel = isCriticalError ? 'CRITICAL' : 'WARNING';
159
- console.log(`[fingerprint] ${errorLevel} - ${description} failed: ${spoofErr.message}`);
160
- }
161
- // Continue execution - don't let spoofing failures break the scan
76
+ } catch (err) {
77
+ if (options.debug) console.log(`[fingerprint] ${description} failed: ${err.message}`);
162
78
  return false;
163
79
  }
164
80
  }
165
81
 
166
82
  /**
167
83
  * Generates realistic screen resolutions based on common monitor sizes
168
- * @returns {object} Screen resolution object with width and height
169
84
  */
170
85
  function getRealisticScreenResolution() {
171
86
  const commonResolutions = [
172
- { width: 1920, height: 1080 }, // Full HD - most common
173
- { width: 1366, height: 768 }, // Common laptop
174
- { width: 1440, height: 900 }, // MacBook Air
175
- { width: 1536, height: 864 }, // Scaled HD
176
- { width: 1600, height: 900 }, // 16:9 widescreen
177
- { width: 2560, height: 1440 }, // 1440p
178
- { width: 1280, height: 720 }, // 720p
179
- { width: 3440, height: 1440 } // Ultrawide
87
+ { width: 1920, height: 1080 },
88
+ { width: 1366, height: 768 },
89
+ { width: 1440, height: 900 },
90
+ { width: 1536, height: 864 },
91
+ { width: 1600, height: 900 },
92
+ { width: 2560, height: 1440 },
93
+ { width: 1280, height: 720 },
94
+ { width: 3440, height: 1440 }
180
95
  ];
181
-
182
96
  return commonResolutions[Math.floor(Math.random() * commonResolutions.length)];
183
97
  }
184
98
 
185
99
  /**
186
- * Generates an object with randomized but realistic browser fingerprint values.
187
- * This is used to spoof various navigator and screen properties to make
188
- * the headless browser instance appear more like a regular user's browser
189
- * and bypass fingerprint-based bot detection.
190
- *
191
- * @returns {object} An object containing the spoofed fingerprint properties
100
+ * Generates randomized but realistic browser fingerprint values
192
101
  */
193
102
  function getRandomFingerprint() {
194
103
  const resolution = getRealisticScreenResolution();
195
-
196
104
  return {
197
105
  deviceMemory: [4, 8, 16, 32][Math.floor(Math.random() * 4)],
198
106
  hardwareConcurrency: [2, 4, 6, 8, 12, 16][Math.floor(Math.random() * 6)],
@@ -200,7 +108,7 @@ function getRandomFingerprint() {
200
108
  width: resolution.width,
201
109
  height: resolution.height,
202
110
  availWidth: resolution.width,
203
- availHeight: resolution.height - 40, // Account for taskbar
111
+ availHeight: resolution.height - 40,
204
112
  colorDepth: 24,
205
113
  pixelDepth: 24
206
114
  },
@@ -213,1178 +121,432 @@ function getRandomFingerprint() {
213
121
  }
214
122
 
215
123
  /**
216
- * Enhanced user agent spoofing with latest browser versions and comprehensive stealth protection
217
- * Compatible with Puppeteer 23.x
218
- * @param {import('puppeteer').Page} page - The Puppeteer page instance
219
- * @param {object} siteConfig - The site configuration object
220
- * @param {boolean} forceDebug - Whether debug logging is enabled
221
- * @param {string} currentUrl - The current URL being processed (for logging)
222
- * @returns {Promise<void>}
124
+ * Creates mock Chrome runtime objects
223
125
  */
224
- async function applyUserAgentSpoofing(page, siteConfig, forceDebug, currentUrl) {
225
- if (!siteConfig.userAgent) return;
126
+ function createMockChromeRuntime() {
127
+ return {
128
+ onConnect: { addListener: () => {}, removeListener: () => {} },
129
+ onMessage: { addListener: () => {}, removeListener: () => {} },
130
+ sendMessage: () => {},
131
+ connect: () => ({
132
+ onMessage: { addListener: () => {}, removeListener: () => {} },
133
+ postMessage: () => {},
134
+ disconnect: () => {}
135
+ }),
136
+ getManifest: () => ({ name: "Chrome", version: "131.0.0.0" }),
137
+ getURL: (path) => `chrome-extension://invalid/${path}`,
138
+ id: undefined
139
+ };
140
+ }
226
141
 
227
- if (forceDebug) console.log(`[debug] Enhanced userAgent spoofing enabled for ${currentUrl}: ${siteConfig.userAgent}`);
142
+ /**
143
+ * Generates realistic Chrome loadTimes data
144
+ */
145
+ function generateRealisticLoadTimes() {
146
+ const now = performance.now();
147
+ return {
148
+ commitLoadTime: now - Math.random() * 1000,
149
+ connectionInfo: 'http/1.1',
150
+ finishDocumentLoadTime: now - Math.random() * 500,
151
+ finishLoadTime: now - Math.random() * 100,
152
+ firstPaintAfterLoadTime: now - Math.random() * 50,
153
+ firstPaintTime: now - Math.random() * 200,
154
+ navigationType: 'Navigation',
155
+ npnNegotiatedProtocol: 'unknown',
156
+ requestTime: now - Math.random() * 2000,
157
+ startLoadTime: now - Math.random() * 1500,
158
+ wasAlternateProtocolAvailable: false,
159
+ wasFetchedViaSpdy: false,
160
+ wasNpnNegotiated: false
161
+ };
162
+ }
163
+
164
+ /**
165
+ * Generates Chrome CSI data
166
+ */
167
+ function generateCSIData() {
168
+ return {
169
+ onloadT: Date.now(),
170
+ pageT: Math.random() * 1000,
171
+ startE: Date.now() - Math.random() * 2000,
172
+ tran: Math.floor(Math.random() * 20)
173
+ };
174
+ }
175
+
176
+ /**
177
+ * Creates mock fingerprinting objects
178
+ */
179
+ function createFingerprintMocks() {
180
+ const mockResult = {
181
+ visitorId: 'mock_visitor_' + Math.random().toString(36).substr(2, 9),
182
+ confidence: { score: 0.99 },
183
+ components: {
184
+ screen: { value: { width: 1920, height: 1080 } },
185
+ timezone: { value: DEFAULT_TIMEZONE },
186
+ language: { value: 'en-US' }
187
+ }
188
+ };
189
+
190
+ return {
191
+ fp: {
192
+ getResult: (callback) => callback ? setTimeout(() => callback(mockResult), 0) : mockResult,
193
+ get: (callback) => Promise.resolve(mockResult),
194
+ load: () => Promise.resolve(window.fp),
195
+ components: { screen: { value: { width: 1920, height: 1080 } } },
196
+ x64hash128: () => 'mock_hash',
197
+ tz: DEFAULT_TIMEZONE,
198
+ timezone: DEFAULT_TIMEZONE
199
+ },
200
+ FingerprintJS: {
201
+ load: () => Promise.resolve({
202
+ get: () => Promise.resolve(mockResult)
203
+ })
204
+ },
205
+ ClientJS: function() {
206
+ this.getFingerprint = () => 'mock_fingerprint_' + Math.random().toString(36).substr(2, 9);
207
+ this.getBrowser = () => 'Chrome';
208
+ this.getOS = () => 'Windows';
209
+ this.fp = {};
210
+ }
211
+ };
212
+ }
213
+
214
+ /**
215
+ * Applies timezone spoofing
216
+ */
217
+ function applyTimezoneSpoofing(timezone, options = {}) {
218
+ const tzConfig = TIMEZONE_CONFIG[timezone] || TIMEZONE_CONFIG[DEFAULT_TIMEZONE];
219
+
220
+ // Spoof Intl.DateTimeFormat
221
+ if (window.Intl?.DateTimeFormat) {
222
+ const OriginalDateTimeFormat = window.Intl.DateTimeFormat;
223
+ window.Intl.DateTimeFormat = function(...args) {
224
+ const instance = new OriginalDateTimeFormat(...args);
225
+ const originalResolvedOptions = instance.resolvedOptions;
226
+
227
+ instance.resolvedOptions = function() {
228
+ const opts = originalResolvedOptions.call(this);
229
+ opts.timeZone = timezone;
230
+ return opts;
231
+ };
232
+ return instance;
233
+ };
234
+ Object.setPrototypeOf(window.Intl.DateTimeFormat, OriginalDateTimeFormat);
235
+ }
228
236
 
229
- // Updated user agents with latest browser versions
230
- const userAgents = {
231
- chrome: [
232
- "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
233
- "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36",
234
- "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",
235
- "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36"
236
- ],
237
- firefox: [
238
- "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:133.0) Gecko/20100101 Firefox/133.0",
239
- "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:133.0) Gecko/20100101 Firefox/133.0",
240
- "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:133.0) Gecko/20100101 Firefox/133.0"
241
- ],
242
- safari: [
243
- "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.2 Safari/605.1.15",
244
- "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.6 Safari/605.1.15"
245
- ]
237
+ // Spoof Date.getTimezoneOffset
238
+ const originalGetTimezoneOffset = Date.prototype.getTimezoneOffset;
239
+ Date.prototype.getTimezoneOffset = function() {
240
+ return tzConfig.offset;
246
241
  };
247
242
 
248
- const selectedUserAgents = userAgents[siteConfig.userAgent.toLowerCase()];
243
+ return true;
244
+ }
245
+
246
+ /**
247
+ * Enhanced user agent spoofing with stealth protection
248
+ */
249
+ async function applyUserAgentSpoofing(page, siteConfig, forceDebug, currentUrl) {
250
+ if (!siteConfig.userAgent) return;
251
+
252
+ if (forceDebug) console.log(`[debug] User agent spoofing: ${siteConfig.userAgent}`);
253
+
254
+ const selectedUserAgents = USER_AGENT_COLLECTIONS[siteConfig.userAgent.toLowerCase()];
249
255
  const ua = selectedUserAgents ? selectedUserAgents[Math.floor(Math.random() * selectedUserAgents.length)] : null;
250
256
 
251
257
  if (ua) {
252
258
  await page.setUserAgent(ua);
253
259
 
254
- // Apply comprehensive stealth protection when userAgent is set
255
- if (forceDebug) console.log(`[debug] Applying enhanced stealth protection for ${currentUrl}`);
260
+ if (forceDebug) console.log(`[debug] Applying stealth protection for ${currentUrl}`);
256
261
 
257
262
  try {
258
263
  await page.evaluateOnNewDocument((userAgent, debugEnabled) => {
259
264
 
260
- // Helper function for safe property operations (local scope)
261
- function safeExecuteSpoofing(spoofFunction, description) {
262
- try {
263
- spoofFunction();
264
- return true;
265
- } catch (spoofErr) {
266
- if (debugEnabled) {
267
- console.log(`[fingerprint] ${description} failed: ${spoofErr.message}`);
268
- }
269
- return false;
270
- }
271
- }
272
-
273
- // Helper function for safe property definition (local scope)
274
- function safeDefineProperty(target, property, descriptor) {
275
- try {
276
- // COMPREHENSIVE DEBUG: Log every property definition attempt
277
- if (debugEnabled) {
278
- console.log(`[fingerprint] safeDefineProperty called for: ${property} on target:`, target.constructor?.name || 'unknown');
279
- if (property === 'href') {
280
- console.log(`[fingerprint] CRITICAL: Attempting to define href property!`);
281
- }
282
- }
283
-
284
- // Enhanced built-in property protection using global constant
285
- if (property === 'href' || ['href', 'origin', 'protocol', 'host', 'hostname', 'port', 'pathname', 'search', 'hash', 'constructor', 'prototype', '__proto__', 'toString', 'valueOf', 'assign', 'reload', 'replace'].includes(property)) {
286
- if (debugEnabled) {
287
- console.log(`[fingerprint] BLOCKED: Built-in property ${property} blocked in safeDefineProperty`);
288
- }
289
- return false;
290
- }
291
-
292
- const existingDescriptor = Object.getOwnPropertyDescriptor(target, property);
293
-
294
- if (existingDescriptor && existingDescriptor.configurable === false) {
295
- if (debugEnabled) {
296
- console.log(`[fingerprint] Cannot redefine non-configurable property: ${property}`);
297
- }
298
- return false;
299
- }
300
-
301
- const safeDescriptor = {
302
- ...descriptor,
303
- configurable: true // Always use configurable: true for Puppeteer 23.x compatibility
304
- };
305
-
306
- Object.defineProperty(target, property, safeDescriptor);
307
- return true;
308
- } catch (defineErr) {
309
- if (debugEnabled) {
310
- console.log(`[fingerprint] Property definition failed for ${property}: ${defineErr.message}`);
311
- }
312
- return false;
313
- }
314
- }
315
-
316
- // Validate properties before attempting to modify them (local scope)
317
- function canModifyProperty(target, property) {
318
- try {
319
- // COMPREHENSIVE DEBUG: Log every canModifyProperty check
320
- if (debugEnabled) {
321
- console.log(`[fingerprint] canModifyProperty called for: ${property} on target:`, target.constructor?.name || 'unknown');
322
- if (property === 'href') {
323
- console.log(`[fingerprint] CRITICAL: Checking if href can be modified!`);
324
- console.log(`[fingerprint] Target details:`, {
325
- isWindow: target === window,
326
- isLocation: target === window.location,
327
- constructorName: target.constructor?.name
328
- });
329
- }
330
- }
331
-
332
- // Enhanced built-in property check with location object handling
333
- const builtInProperties = ['href', 'origin', 'protocol', 'host', 'hostname', 'port', 'pathname', 'search', 'hash', 'constructor', 'prototype', '__proto__', 'toString', 'valueOf', 'assign', 'reload', 'replace'];
334
-
335
- // Special handling for Location object properties
336
- if (target === window.location || (target.constructor && target.constructor.name === 'Location')) {
337
- const locationProperties = ['href', 'origin', 'protocol', 'host', 'hostname', 'port', 'pathname', 'search', 'hash'];
338
- if (locationProperties.includes(property)) {
339
- if (debugEnabled) {
340
- console.log(`[fingerprint] Skipping location property: ${property}`);
341
- }
342
- return false;
343
- }
344
- }
345
-
346
- if (builtInProperties.includes(property)) {
347
- if (debugEnabled) {
348
- console.log(`[fingerprint] Skipping built-in property: ${property}`);
349
- }
350
- return false;
351
- }
352
-
353
- const descriptor = Object.getOwnPropertyDescriptor(target, property);
354
- if (!descriptor) return true; // Property doesn't exist, can be added
355
-
356
- return descriptor.configurable !== false;
357
- } catch (checkErr) {
358
- return false; // If we can't check, assume we can't modify
359
- }
360
- }
361
-
362
- // GLOBAL HREF PROTECTION: Override Object.defineProperty to block ALL href modifications
363
- const originalDefineProperty = Object.defineProperty;
364
- Object.defineProperty = function(target, property, descriptor) {
365
- // Block ALL attempts to redefine href anywhere
366
- if (property === 'href') {
367
- if (debugEnabled) {
368
- console.log(`[fingerprint] GLOBAL BLOCK: Prevented ${property} redefinition on:`, target.constructor?.name || 'unknown');
369
- console.trace('[fingerprint] Call stack for blocked href:');
370
- }
371
- // Return false to indicate failure, but don't throw
372
- return false;
373
- }
265
+ // Apply inline error suppression first
266
+ (function() {
267
+ const originalConsoleError = console.error;
268
+ const originalWindowError = window.onerror;
374
269
 
375
- // Block other location properties too
376
- const locationProps = ['origin', 'protocol', 'host', 'hostname', 'port', 'pathname', 'search', 'hash'];
377
- if (locationProps.includes(property)) {
378
- if (debugEnabled) {
379
- console.log(`[fingerprint] GLOBAL BLOCK: Prevented location property ${property} redefinition on:`, target.constructor?.name || 'unknown');
380
- }
381
- return false;
270
+ function shouldSuppressFingerprintError(message) {
271
+ const patterns = [
272
+ /\.closest is not a function/i,
273
+ /\.querySelector is not a function/i,
274
+ /\.addEventListener is not a function/i,
275
+ /Cannot read propert(y|ies) of null \\(reading 'fp'\\)/i,
276
+ /Cannot read propert(y|ies) of undefined \\(reading 'fp'\\)/i,
277
+ /Cannot redefine property: href/i,
278
+ /Cannot redefine property: __webdriver_script_func/i,
279
+ /Cannot redefine property: webdriver/i,
280
+ /Cannot read propert(y|ies) of undefined \\(reading 'toLowerCase'\\)/i,
281
+ /\\.toLowerCase is not a function/i,
282
+ /fp is not defined/i,
283
+ /fingerprint is not defined/i,
284
+ /FingerprintJS is not defined/i,
285
+ /\\$ is not defined/i,
286
+ /jQuery is not defined/i,
287
+ /_ is not defined/i,
288
+ /Failed to load resource.*server responded with a status of [45]\\d{2}/i,
289
+ /Failed to fetch/i,
290
+ /(webdriver|callPhantom|_phantom|__nightmare|_selenium) is not defined/i,
291
+ /Failed to execute 'observe' on 'IntersectionObserver'.*parameter 1 is not of type 'Element'/i,
292
+ /tz check/i,
293
+ /new window\\.Error.*<anonymous>/i,
294
+ /Failed to load resource.*server responded with a status of 40[34]/i,
295
+ /Blocked script execution in 'about:blank'.*sandboxed.*allow-scripts/i,
296
+ /Page JavaScript error:/i,
297
+ /^[a-zA-Z0-9_$]+\[.*\]\s+is not a function/i,
298
+ /^[a-zA-Z0-9_$]+\(.*\)\s+is not a function/i,
299
+ /^[a-zA-Z0-9_$]+\.[a-zA-Z0-9_$]+.*is not a function/i
300
+ ];
301
+ return patterns.some(pattern => pattern.test(String(message || '')));
382
302
  }
383
303
 
384
- // For all other properties, use the original function
385
- try {
386
- return originalDefineProperty.apply(this, arguments);
387
- } catch (err) {
388
- if (debugEnabled) {
389
- console.log(`[fingerprint] Original defineProperty failed for ${property}:`, err.message);
304
+ console.error = function(...args) {
305
+ const message = args.join(' ');
306
+ if (shouldSuppressFingerprintError(message)) {
307
+ if (debugEnabled) console.log("[fingerprint] Suppressed error:", message);
308
+ return;
390
309
  }
391
- return false;
392
- }
393
- };
394
-
395
- // GLOBAL NULL PROTECTION: Prevent "Cannot read properties of null" errors
396
- const originalGetOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
397
- Object.getOwnPropertyDescriptor = function(target, property) {
398
- if (!target) {
399
- if (debugEnabled) {
400
- console.log(`[fingerprint] NULL TARGET: Prevented property access on null target for property: ${property}`);
310
+ return originalConsoleError.apply(this, arguments);
311
+ };
312
+
313
+ window.onerror = function(message, source, lineno, colno, error) {
314
+ if (shouldSuppressFingerprintError(message)) {
315
+ if (debugEnabled) console.log("[fingerprint] Suppressed window error:", message);
316
+ return true;
401
317
  }
402
- return undefined;
403
- }
404
- return originalGetOwnPropertyDescriptor.apply(this, arguments);
405
- };
406
-
407
- // Add safe property access wrapper
408
- window.safePropertyAccess = function(obj, property, defaultValue = null) {
409
- try {
410
- return (obj && obj[property] !== undefined) ? obj[property] : defaultValue;
411
- } catch (err) {
412
- return defaultValue;
413
- }
414
- };
415
-
416
- // Enhanced null object protection for property access
417
- const originalHasOwnProperty = Object.prototype.hasOwnProperty;
418
- Object.prototype.hasOwnProperty = function(property) {
419
- if (this === null || this === undefined) {
420
- if (debugEnabled && property === 'fp') {
421
- console.log(`[fingerprint] NULL OBJECT: Prevented hasOwnProperty('${property}') on null/undefined`);
318
+ if (originalWindowError) {
319
+ return originalWindowError.apply(this, arguments);
422
320
  }
423
321
  return false;
424
- }
425
- return originalHasOwnProperty.call(this, property);
426
- };
427
-
428
- // Enhanced undefined object protection for method access
429
- const originalPropertyAccess = Object.getOwnPropertyDescriptor;
430
-
431
- // Global protection against undefined method access
432
- const createSafeMethodProxy = (methodName) => {
433
- return function(...args) {
434
- if (debugEnabled) {
435
- console.log(`[fingerprint] Safe ${methodName} proxy called with args:`, args);
436
- }
437
- // For alert specifically, show the message safely
438
- if (methodName === 'alert' && args.length > 0) {
439
- try {
440
- window.alert(args[0]);
441
- } catch (alertErr) {
442
- if (debugEnabled) {
443
- console.log(`[fingerprint] Safe alert failed, logging instead: ${args[0]}`);
444
- }
445
- console.log(`[Alert] ${args[0]}`);
446
- }
447
- }
448
- // For other methods, just return a safe empty function
449
- return undefined;
450
322
  };
451
- };
323
+ })();
452
324
 
453
- // Create safe global methods that might be accessed on undefined objects
454
- window.safeAlert = createSafeMethodProxy('alert');
455
- window.safeConfirm = createSafeMethodProxy('confirm');
456
- window.safePrompt = createSafeMethodProxy('prompt');
457
-
458
- // Global error handler for fingerprinting access errors
459
- const originalErrorHandler = window.onerror;
460
- window.onerror = function(message, source, lineno, colno, error) {
461
- // Handle fingerprinting-related null access errors
462
- if (typeof message === 'string' && (
463
- message.includes("Cannot read properties of null (reading 'fp')") ||
464
- message.includes("Cannot read property 'fp' of null") ||
465
- message.includes("Cannot read properties of undefined (reading 'alert')") ||
466
- message.includes("Cannot read property 'alert' of undefined") ||
467
- message.includes("Cannot read properties of undefined (reading 'confirm')") ||
468
- message.includes("Cannot read properties of undefined (reading 'prompt')") ||
469
- message.includes("fp is not defined") ||
470
- message.includes("alert is not defined") ||
471
- message.includes("is not a function") ||
472
- message.includes("Cannot read properties of undefined (reading") ||
473
- message.includes("Cannot read properties of null (reading") ||
474
- message.includes("fingerprint") && message.includes("null")
475
- )) {
476
- if (debugEnabled) {
477
- console.log(`[fingerprint] Suppressed fingerprinting null access error: ${message}`);
478
- }
479
- return true; // Prevent error from showing in console
480
- }
325
+ // Create safe property definition helper
326
+ function safeDefinePropertyLocal(target, property, descriptor) {
327
+ const builtInProps = new Set(['href', 'origin', 'protocol', 'host', 'hostname', 'port', 'pathname', 'search', 'hash', 'constructor', 'prototype', '__proto__', 'toString', 'valueOf', 'assign', 'reload', 'replace']);
481
328
 
482
- // Call original error handler for other errors
483
- if (originalErrorHandler) {
484
- return originalErrorHandler.apply(this, arguments);
329
+ if (builtInProps.has(property)) {
330
+ if (debugEnabled) console.log(`[fingerprint] Skipping built-in property: ${property}`);
331
+ return false;
485
332
  }
486
- return false;
487
- };
488
333
 
489
- // CRITICAL: Simplified CDP Detection Prevention
490
- // Prevents detection via Chrome DevTools Protocol Error.stack analysis
491
- safeExecuteSpoofing(() => {
492
- // Store original Error constructor
493
- const OriginalError = window.Error;
494
-
495
- // Override Error constructor to prevent CDP detection
496
- window.Error = function(...args) {
497
- const error = new OriginalError(...args);
498
-
499
- // Get original stack descriptor to preserve behavior
500
- const originalStackDescriptor = Object.getOwnPropertyDescriptor(error, 'stack') ||
501
- Object.getOwnPropertyDescriptor(OriginalError.prototype, 'stack');
502
-
503
- // Override stack property to prevent CDP detection via Error.stack getter
504
- Object.defineProperty(error, 'stack', {
505
- get: function() {
506
- // This is the critical part - prevent CDP detection flag from being set
507
- // Anti-bot systems set a flag when Error.stack getter is accessed
508
-
509
- let stack;
510
- if (originalStackDescriptor && originalStackDescriptor.get) {
511
- try {
512
- stack = originalStackDescriptor.get.call(this);
513
- } catch (stackErr) {
514
- stack = 'Error\n at unknown location';
515
- }
516
- } else if (originalStackDescriptor && originalStackDescriptor.value) {
517
- stack = originalStackDescriptor.value;
518
- } else {
519
- // Fallback stack trace
520
- stack = `${this.name || 'Error'}: ${this.message || ''}\n at unknown location`;
521
- }
522
-
523
- // Clean automation traces from stack if present
524
- if (typeof stack === 'string') {
525
- stack = stack
526
- .replace(/.*puppeteer.*\n?/gi, '')
527
- .replace(/.*chrome-devtools.*\n?/gi, '')
528
- .replace(/.*webdriver.*\n?/gi, '')
529
- .replace(/.*automation.*\n?/gi, '')
530
- .replace(/\n\s*\n/g, '\n')
531
- .trim();
532
-
533
- // Ensure we always have a valid stack
534
- if (!stack) {
535
- stack = `${this.name || 'Error'}: ${this.message || ''}\n at unknown location`;
536
- }
537
- }
538
-
539
- return stack;
540
- },
541
- set: function(value) {
542
- // Allow stack to be set normally
543
- if (originalStackDescriptor && originalStackDescriptor.set) {
544
- originalStackDescriptor.set.call(this, value);
545
- } else {
546
- // Create internal property if no setter exists
547
- Object.defineProperty(this, '_internalStack', {
548
- value: value,
549
- writable: true,
550
- configurable: true
551
- });
552
- }
553
- },
554
- configurable: true,
555
- enumerable: false
556
- });
557
-
558
- return error;
559
- };
560
-
561
- // Preserve Error prototype and constructor properties
562
- window.Error.prototype = OriginalError.prototype;
563
- Object.setPrototypeOf(window.Error, OriginalError);
564
-
565
- // Copy essential static properties
566
- ['captureStackTrace', 'stackTraceLimit', 'prepareStackTrace'].forEach(prop => {
567
- if (OriginalError[prop]) {
568
- try {
569
- window.Error[prop] = OriginalError[prop];
570
- } catch (propErr) {
571
- // Ignore if property can't be copied
572
- }
573
- }
574
- });
575
-
576
- // Enhanced Error.captureStackTrace protection
577
- if (OriginalError.captureStackTrace) {
578
- window.Error.captureStackTrace = function(targetObject, constructorOpt) {
579
- try {
580
- const result = OriginalError.captureStackTrace.call(this, targetObject, constructorOpt);
581
-
582
- // Clean captured stack trace
583
- if (targetObject && targetObject.stack && typeof targetObject.stack === 'string') {
584
- targetObject.stack = targetObject.stack
585
- .replace(/.*puppeteer.*\n?/gi, '')
586
- .replace(/.*chrome-devtools.*\n?/gi, '')
587
- .replace(/.*webdriver.*\n?/gi, '')
588
- .replace(/.*automation.*\n?/gi, '')
589
- .replace(/\n\s*\n/g, '\n')
590
- .trim();
591
- }
592
-
593
- return result;
594
- } catch (captureErr) {
595
- if (debugEnabled) {
596
- console.log('[fingerprint] captureStackTrace error handled:', captureErr.message);
597
- }
598
- return undefined;
599
- }
600
- };
601
- }
602
-
603
- // Prevent global CDP detection flag
604
- try {
605
- Object.defineProperty(window, 'cdpDetected', {
606
- get: () => false,
607
- set: () => false,
608
- configurable: false,
609
- enumerable: false
610
- });
611
- } catch (cdpPropErr) {
612
- // Ignore if property already exists
613
- }
614
-
615
- // Additional protection: prevent common CDP detection patterns
616
- const cdpDetectionStrings = ['cdpDetected', 'chromeDevtools', 'runtimeEvaluate'];
617
- cdpDetectionStrings.forEach(str => {
618
- try {
619
- if (!window[str]) {
620
- Object.defineProperty(window, str, {
621
- get: () => false,
622
- set: () => false,
623
- configurable: false,
624
- enumerable: false
625
- });
626
- }
627
- } catch (strErr) {
628
- // Ignore property definition errors
629
- }
630
- });
631
-
632
- if (debugEnabled) {
633
- console.log('[fingerprint] Simplified CDP detection prevention installed - Error constructor protected');
634
- }
635
-
636
- }, 'Simplified CDP Detection Prevention');
637
-
638
- // Test function for CDP detection (only in debug mode)
639
- if (debugEnabled) {
640
- try {
641
- let testCdpDetected = false;
642
- const testError = new Error('test');
643
-
644
- // Simulate anti-bot CDP detection attempt
645
- Object.defineProperty(testError, 'stack', {
646
- get() {
647
- testCdpDetected = true;
648
- return 'test stack trace';
649
- }
650
- });
651
-
652
- // Access stack - should NOT trigger detection with our patch
653
- const testStack = testError.stack;
654
-
655
- console.log(`[fingerprint] CDP protection test: ${testCdpDetected ? 'FAILED - Detection triggered!' : 'PASSED - Protection working'}`);
656
- } catch (testErr) {
657
- console.log('[fingerprint] CDP test error:', testErr.message);
658
- }
659
- }
660
-
661
- // COMPREHENSIVE FINGERPRINTING MOCK OBJECTS
662
- // Create enhanced mock fingerprinting objects that might be expected
663
- window.fp = window.fp || {
664
- getResult: (callback) => {
665
- const result = {
666
- visitorId: 'mock_visitor_id_' + Math.random().toString(36).substring(7),
667
- confidence: { score: 0.99 },
668
- components: {
669
- screen: { value: { width: 1920, height: 1080 } },
670
- timezone: { value: 'America/New_York' },
671
- language: { value: 'en-US' }
672
- }
673
- };
674
- if (typeof callback === 'function') {
675
- try {
676
- setTimeout(() => callback(result), 0);
677
- } catch (callbackErr) {
678
- if (debugEnabled) console.log(`[fingerprint] FP callback error: ${callbackErr.message}`);
679
- }
680
- }
681
- return result;
682
- },
683
- get: (callback) => {
684
- const result = {
685
- visitorId: 'mock_visitor_id_' + Math.random().toString(36).substring(7),
686
- confidence: { score: 0.99 },
687
- components: {}
688
- };
689
- if (typeof callback === 'function') {
690
- try {
691
- setTimeout(() => callback(result), 0);
692
- } catch (callbackErr) {
693
- if (debugEnabled) console.log(`[fingerprint] FP.get callback error: ${callbackErr.message}`);
694
- }
334
+ try {
335
+ const existing = Object.getOwnPropertyDescriptor(target, property);
336
+ if (existing?.configurable === false) {
337
+ if (debugEnabled) console.log(`[fingerprint] Cannot modify non-configurable: ${property}`);
338
+ return false;
695
339
  }
696
- return Promise.resolve(result);
697
- },
698
- load: () => Promise.resolve(window.fp),
699
- components: {
700
- screen: { value: { width: 1920, height: 1080 } }
701
- },
702
- x64hash128: () => 'mock_hash',
703
- tz: 'America/New_York', // Mock timezone
704
- timezone: 'America/New_York'
705
- };
706
-
707
- // Enhanced timezone protection - create comprehensive timezone objects
708
- window.timezone = window.timezone || 'America/New_York';
709
- // Create comprehensive FingerprintJS mock objects
710
- window.FingerprintJS = window.FingerprintJS || {
711
- load: (options) => Promise.resolve({
712
- get: (getOptions) => Promise.resolve({
713
- visitorId: 'mock_visitor_id_' + Math.random().toString(36).substring(7),
714
- confidence: { score: 0.99 },
715
- components: {}
716
- })
717
- })
718
- };
719
-
720
- // Mock other common fingerprinting libraries that might access .fp
721
- window.ClientJS = window.ClientJS || function() {
722
- this.getFingerprint = () => 'mock_fingerprint_' + Math.random().toString(36).substring(7);
723
- this.getBrowser = () => 'Chrome';
724
- this.getOS = () => 'Windows';
725
- this.fp = {}; // Prevent null access
726
- };
727
-
728
- // Create safe proxy wrapper for fingerprinting objects
729
- const createFingerprintProxy = (targetName) => {
730
- if (!window[targetName]) {
731
- window[targetName] = new Proxy({}, {
732
- get: (target, prop) => {
733
- if (debugEnabled && prop === 'fp') {
734
- console.log(`[fingerprint] Safe proxy accessed: ${targetName}.fp`);
735
- }
736
- if (prop === 'fp') {
737
- return {}; // Return safe empty object for .fp access
738
- }
739
- // Handle common method access on undefined objects
740
- if (prop === 'alert') {
741
- return window.safeAlert;
742
- }
743
- if (prop === 'confirm') {
744
- return window.safeConfirm;
745
- }
746
- if (prop === 'prompt') {
747
- return window.safePrompt;
748
- }
749
- // Return safe empty function for other method-like properties
750
- if (typeof target[prop] === 'undefined' && prop.endsWith && (prop.endsWith('alert') || prop.endsWith('confirm') || prop.endsWith('prompt'))) {
751
- return createSafeMethodProxy(prop);
752
- }
753
- return target[prop] || undefined;
754
- }
755
- });
756
- }
757
- };
758
-
759
- // Create safe proxies for common fingerprinting object names
760
- ['fpjs', 'fingerprint', 'deviceFingerprint', 'browserFingerprint', 'fpCollector'].forEach(createFingerprintProxy);
761
340
 
762
- // Enhanced protection for common undefined object method access patterns
763
- const commonObjectNames = ['popup', 'modal', 'dialog', 'notification', 'banner', 'overlay', 'widget'];
764
- commonObjectNames.forEach(objName => {
765
- if (!window[objName]) {
766
- window[objName] = new Proxy({}, {
767
- get: (target, prop) => {
768
- if (debugEnabled && ['alert', 'confirm', 'prompt'].includes(prop)) {
769
- console.log(`[fingerprint] Safe proxy method accessed: ${objName}.${prop}`);
770
- }
771
- if (prop === 'alert') {
772
- return window.safeAlert;
773
- }
774
- if (prop === 'confirm') {
775
- return window.safeConfirm;
776
- }
777
- if (prop === 'prompt') {
778
- return window.safePrompt;
779
- }
780
- // Return safe function for any other method access
781
- return () => undefined;
782
- }
341
+ Object.defineProperty(target, property, {
342
+ ...descriptor,
343
+ configurable: true
783
344
  });
784
- }
785
- });
786
-
787
- // Global protection for undefined objects accessing dialog methods
788
- const originalAlert = window.alert;
789
- const originalConfirm = window.confirm;
790
- const originalPrompt = window.prompt;
791
-
792
- // Ensure these methods are always available and safe
793
- window.alert = window.alert || function(message) {
794
- console.log(`[Alert] ${message}`);
795
- };
796
- window.confirm = window.confirm || function(message) {
797
- console.log(`[Confirm] ${message}`);
798
- return false;
799
- };
800
- window.prompt = window.prompt || function(message, defaultValue) {
801
- console.log(`[Prompt] ${message}`);
802
- return defaultValue || null;
803
- };
804
-
805
-
806
- window.timeZone = window.timeZone || 'America/New_York';
807
-
808
- // Comprehensive Date prototype protection
809
- const originalDateToString = Date.prototype.toString;
810
- Date.prototype.toString = function() {
811
- try {
812
- return originalDateToString.call(this);
345
+ return true;
813
346
  } catch (err) {
814
- return new Date().toISOString();
347
+ if (debugEnabled) console.log(`[fingerprint] Failed to define ${property}: ${err.message}`);
348
+ return false;
815
349
  }
816
- };
350
+ }
817
351
 
818
- const originalGetTimezoneOffset = Date.prototype.getTimezoneOffset;
819
- Date.prototype.getTimezoneOffset = function() {
352
+ // Safe execution wrapper
353
+ function safeExecute(fn, description) {
820
354
  try {
821
- return originalGetTimezoneOffset.call(this);
355
+ fn();
356
+ return true;
822
357
  } catch (err) {
823
- return 300; // EST offset as fallback
824
- }
825
- };
826
-
827
- // Override navigator.timezone related properties
828
- if (navigator && !navigator.timezone) {
829
- Object.defineProperty(navigator, 'timezone', {
830
- get: () => 'America/New_York',
831
- configurable: true
832
- });
833
- }
834
-
835
- // Protect common timezone detection methods
836
- window.getTimezone = window.getTimezone || (() => 'America/New_York');
837
- window.getTimezoneOffset = window.getTimezoneOffset || (() => 300);
838
-
839
- // Create jstz-like object (common timezone detection library)
840
- window.jstz = window.jstz || {
841
- determine: () => ({ name: () => 'America/New_York' }),
842
- olson: { timezones: { 'America/New_York': true } }
843
- };
844
-
845
- // Create mock timezone object
846
- window.tz = window.tz || {
847
- name: 'America/New_York',
848
- offset: -300,
849
- abbr: 'EST',
850
- dst: false,
851
- getTimezoneOffset: () => 300
852
- };
853
-
854
- // Ensure Intl.DateTimeFormat resolvedOptions returns safe values
855
- if (window.Intl && window.Intl.DateTimeFormat) {
856
- const OriginalDateTimeFormat = window.Intl.DateTimeFormat;
857
- window.Intl.DateTimeFormat = function(...args) {
858
- const instance = new OriginalDateTimeFormat(...args);
859
- const originalResolvedOptions = instance.resolvedOptions;
860
-
861
- instance.resolvedOptions = function() {
862
- try {
863
- const options = originalResolvedOptions.call(this);
864
- // Ensure timezone is always set to prevent null errors
865
- if (!options.timeZone) {
866
- options.timeZone = 'America/New_York';
867
- }
868
- return options;
869
- } catch (err) {
870
- // Return safe default options if resolvedOptions fails
871
- return {
872
- locale: 'en-US',
873
- timeZone: 'America/New_York',
874
- calendar: 'gregory',
875
- numberingSystem: 'latn'
876
- };
877
- }
878
- };
879
- return instance;
880
- };
881
-
882
- // Copy static methods
883
- Object.setPrototypeOf(window.Intl.DateTimeFormat, OriginalDateTimeFormat);
884
- };
885
-
886
-
887
- // JavaScript Library Protection - Create mock objects for common libraries
888
- // jQuery protection
889
- if (typeof window.$ === 'undefined') {
890
- window.$ = function(selector) {
891
- // Return a minimal jQuery-like object
892
- return {
893
- ready: function(fn) { if (typeof fn === 'function') setTimeout(fn, 0); return this; },
894
- on: function() { return this; },
895
- off: function() { return this; },
896
- click: function() { return this; },
897
- hide: function() { return this; },
898
- show: function() { return this; },
899
- css: function() { return this; },
900
- attr: function() { return this; },
901
- html: function() { return this; },
902
- text: function() { return this; },
903
- val: function() { return this; },
904
- addClass: function() { return this; },
905
- removeClass: function() { return this; },
906
- length: 0,
907
- each: function() { return this; }
908
- };
909
- };
910
- // Common jQuery aliases
911
- window.jQuery = window.$;
912
- }
913
-
914
- // Other common library protections
915
- window._ = window._ || { // Lodash/Underscore
916
- forEach: function() {},
917
- map: function() { return []; },
918
- filter: function() { return []; },
919
- find: function() { return undefined; }
920
- };
921
-
922
- window.moment = window.moment || function() { // Moment.js
923
- return {
924
- format: function() { return new Date().toISOString(); },
925
- valueOf: function() { return Date.now(); }
926
- };
927
- };
928
-
929
- // Enhanced console error handling for library errors
930
- const originalConsoleError = console.error;
931
- console.error = function(...args) {
932
- const message = args.join(' ');
933
- // Suppress common library error messages
934
- if (typeof message === 'string' && (
935
- message.includes('$ is not defined') ||
936
- message.includes('jQuery is not defined') ||
937
- message.includes('_ is not defined') ||
938
- message.includes('moment is not defined') ||
939
- message.includes('bootstrap is not defined') ||
940
- message.includes('is not a function') ||
941
- message.includes('Cannot read property') ||
942
- message.includes('Cannot read properties of undefined') ||
943
- message.includes('Cannot read properties of null') ||
944
- message.includes('.closest is not a function') ||
945
- message.includes('.toLowerCase') ||
946
- message.includes('is not valid JSON')
947
- )) {
948
- if (debugEnabled) {
949
- console.log(`[fingerprint] Suppressed library error: ${message}`);
950
- }
951
- return; // Don't log the error
952
- }
953
- // For all other errors, use original console.error
954
- return originalConsoleError.apply(this, arguments);
955
- };
956
-
957
- // Enhanced TZ Check Protection - Handle timezone validation functions
958
- window.tzCheck = window.tzCheck || function() {
959
- return 'America/New_York';
960
- };
961
-
962
- window.checkTimezone = window.checkTimezone || function() {
963
- return true;
964
- };
965
-
966
- window.validateTimezone = window.validateTimezone || function() {
967
- return { valid: true, timezone: 'America/New_York' };
968
- };
969
-
970
- // Mock common timezone libraries and their methods
971
- window.momentTimezone = window.momentTimezone || {
972
- tz: {
973
- guess: () => 'America/New_York',
974
- names: () => ['America/New_York'],
975
- zone: () => ({ name: 'America/New_York', abbr: 'EST' })
976
- }
977
- };
978
-
979
- // Enhanced Intl timezone protection
980
- if (window.Intl) {
981
- // Override supportedLocalesOf to always return safe values
982
- if (window.Intl.DateTimeFormat && window.Intl.DateTimeFormat.supportedLocalesOf) {
983
- const originalSupportedLocales = window.Intl.DateTimeFormat.supportedLocalesOf;
984
- window.Intl.DateTimeFormat.supportedLocalesOf = function(locales, options) {
985
- try {
986
- return originalSupportedLocales.call(this, locales, options);
987
- } catch (err) {
988
- return ['en-US'];
989
- }
990
- };
358
+ if (debugEnabled) console.log(`[fingerprint] ${description} failed: ${err.message}`);
359
+ return false;
991
360
  }
992
-
993
- // Add resolvedOptions protection to all Intl objects
994
- ['DateTimeFormat', 'NumberFormat', 'Collator'].forEach(intlType => {
995
- if (window.Intl[intlType]) {
996
- const OriginalIntl = window.Intl[intlType];
997
- window.Intl[intlType] = function(...args) {
998
- try {
999
- return new OriginalIntl(...args);
1000
- } catch (err) {
1001
- // Return basic mock object for any Intl constructor failures
1002
- return {
1003
- resolvedOptions: () => ({
1004
- locale: 'en-US',
1005
- timeZone: 'America/New_York',
1006
- calendar: 'gregory'
1007
- }),
1008
- format: () => new Date().toLocaleDateString()
1009
- };
1010
- }
1011
- };
1012
- Object.setPrototypeOf(window.Intl[intlType], OriginalIntl);
1013
- }
1014
- });
1015
361
  }
1016
-
1017
- // Global error handler enhancement for timezone-specific errors
1018
- const originalWindowError = window.onerror;
1019
- window.onerror = function(message, source, lineno, colno, error) {
1020
- // Handle timezone-specific errors
1021
- if (typeof message === 'string' && (
1022
- message.includes('tz check') ||
1023
- message.includes('timezone check') ||
1024
- message.includes('tz is not defined') ||
1025
- message.includes('timezone is not defined') ||
1026
- message.includes('Invalid timezone') ||
1027
- message.includes('TimeZone') ||
1028
- message.includes('getTimezoneOffset')
1029
- )) {
1030
- if (debugEnabled) {
1031
- console.log(`[fingerprint] Suppressed timezone error: ${message}`);
1032
- }
1033
- return true; // Prevent the error from showing
1034
- }
1035
-
1036
- // Call original error handler for non-timezone errors
1037
- if (originalWindowError) {
1038
- return originalWindowError.apply(this, arguments);
1039
- }
1040
- return false;
1041
- };
1042
-
1043
-
1044
-
1045
- // 1. Enhanced webdriver removal with safe descriptor manipulation
1046
- safeExecuteSpoofing(() => {
1047
- // Skip if navigator.webdriver is non-configurable
1048
- if (!canModifyProperty(navigator, 'webdriver', debugEnabled)) {
1049
- if (debugEnabled) {
1050
- console.log('[fingerprint] Skipping non-configurable navigator.webdriver');
1051
- }
1052
- return;
1053
- }
1054
362
 
363
+ // Remove webdriver properties
364
+ safeExecute(() => {
1055
365
  try {
1056
366
  delete navigator.webdriver;
1057
- } catch (delErr) {
1058
- // Deletion might fail, try to set to undefined
1059
- try {
1060
- navigator.webdriver = undefined;
1061
- } catch (assignErr) {
1062
- // Both deletion and assignment failed, skip
1063
- if (debugEnabled) {
1064
- console.log('[fingerprint] Cannot modify navigator.webdriver, skipping');
1065
- }
1066
- return;
1067
- }
1068
- }
1069
-
1070
- safeDefineProperty(navigator, 'webdriver', {
1071
- get: () => undefined,
1072
- enumerable: false,
1073
- configurable: true
1074
- }, debugEnabled);
367
+ } catch (e) {}
368
+ safeDefinePropertyLocal(navigator, 'webdriver', { get: () => undefined });
1075
369
  }, 'webdriver removal');
1076
-
1077
- // 2. Enhanced automation detection removal with safe handling
1078
- const automationProps = [
1079
- 'callPhantom', '_phantom', '__nightmare', '_selenium',
1080
- '__selenium_unwrapped', '__webdriver_evaluate', '__driver_evaluate',
1081
- '__webdriver_script_function', '__webdriver_script_func',
1082
- '__webdriver_script_fn', '__fxdriver_evaluate', '__driver_unwrapped',
1083
- '__webdriver_unwrapped', '__selenium_evaluate', '__fxdriver_unwrapped',
1084
- 'spawn', 'emit', 'Buffer', 'domAutomation',
1085
- 'domAutomationController', '__lastWatirAlert', '__lastWatirConfirm',
1086
- '__lastWatirPrompt', '_Selenium_IDE_Recorder', '_selenium', 'calledSelenium'
1087
- ];
1088
-
1089
- safeExecuteSpoofing(() => {
1090
- automationProps.forEach(prop => {
370
+
371
+ // Remove automation properties
372
+ safeExecute(() => {
373
+ const automationProps = [
374
+ 'callPhantom', '_phantom', '__nightmare', '_selenium', '__selenium_unwrapped',
375
+ '__webdriver_evaluate', '__driver_evaluate', '__webdriver_script_function',
376
+ 'spawn', 'emit', 'Buffer', 'domAutomation', 'domAutomationController'
377
+ ];
1091
378
 
1092
- // Debug: Log which property is being processed
1093
- if (debugEnabled && (prop === 'href' || prop.includes('href'))) {
1094
- console.log(`[fingerprint] WARNING: Processing href-related property: ${prop}`);
1095
- }
379
+ automationProps.forEach(prop => {
1096
380
  try {
1097
- // Skip built-in properties immediately
1098
- if (['href', 'origin', 'protocol', 'host', 'hostname', 'port', 'pathname', 'search', 'hash', 'constructor', 'prototype', '__proto__', 'toString', 'valueOf', 'assign', 'reload', 'replace'].includes(prop)) {
1099
- if (debugEnabled) {
1100
- console.log(`[fingerprint] Skipping built-in automation property: ${prop}`);
1101
- }
1102
- return;
1103
- }
1104
-
1105
- // Additional safety check specifically for href
1106
- if (prop === 'href') {
1107
- if (debugEnabled) console.log(`[fingerprint] BLOCKING href property modification attempt`);
1108
- return;
1109
- }
1110
-
1111
- if (!canModifyProperty(window, prop, debugEnabled) && !canModifyProperty(navigator, prop, debugEnabled)) {
1112
- if (debugEnabled) {
1113
- console.log(`[fingerprint] Skipping non-configurable automation property ${prop}`);
1114
- }
1115
- return;
1116
- }
1117
-
1118
- // Try to delete from both window and navigator
1119
- try { delete window[prop]; } catch(e) {}
1120
- try { delete navigator[prop]; } catch(e) {}
1121
-
1122
- // Only try to redefine if we can
1123
- if (canModifyProperty(window, prop, debugEnabled)) {
1124
- safeDefineProperty(window, prop, {
1125
- get: () => undefined,
1126
- enumerable: false
1127
- }, debugEnabled);
1128
- }
1129
-
1130
- if (canModifyProperty(navigator, prop, debugEnabled)) {
1131
- safeDefineProperty(navigator, prop, {
1132
- get: () => undefined,
1133
- enumerable: false
1134
- }, debugEnabled);
1135
- }
1136
- } catch (propErr) {
1137
- // Skip problematic properties
1138
- if (debugEnabled) {
1139
- console.log(`[fingerprint] Skipped automation property ${prop}: ${propErr.message}`);
1140
- }
1141
- }
381
+ delete window[prop];
382
+ delete navigator[prop];
383
+ safeDefinePropertyLocal(window, prop, { get: () => undefined });
384
+ safeDefinePropertyLocal(navigator, prop, { get: () => undefined });
385
+ } catch (e) {}
1142
386
  });
1143
387
  }, 'automation properties removal');
1144
388
 
1145
- // 3. Enhanced Chrome runtime simulation with safe property handling
1146
-
1147
- safeExecuteSpoofing(() => {
1148
- if (!window.chrome || !window.chrome.runtime) {
389
+ // Simulate Chrome runtime
390
+ safeExecute(() => {
391
+ if (!window.chrome?.runtime) {
1149
392
  window.chrome = {
1150
393
  runtime: {
1151
394
  onConnect: { addListener: () => {}, removeListener: () => {} },
1152
395
  onMessage: { addListener: () => {}, removeListener: () => {} },
1153
396
  sendMessage: () => {},
1154
- connect: () => ({
1155
- onMessage: { addListener: () => {}, removeListener: () => {} },
1156
- postMessage: () => {},
1157
- disconnect: () => {}
1158
- }),
1159
- getManifest: () => ({
1160
- name: "Chrome",
1161
- version: "131.0.0.0"
1162
- }),
397
+ connect: () => ({ onMessage: { addListener: () => {}, removeListener: () => {} }, postMessage: () => {}, disconnect: () => {} }),
398
+ getManifest: () => ({ name: "Chrome", version: "131.0.0.0" }),
1163
399
  getURL: (path) => `chrome-extension://invalid/${path}`,
1164
400
  id: undefined
1165
401
  },
1166
- loadTimes: () => ({
1167
- commitLoadTime: performance.now() - Math.random() * 1000,
1168
- connectionInfo: 'http/1.1',
1169
- finishDocumentLoadTime: performance.now() - Math.random() * 500,
1170
- finishLoadTime: performance.now() - Math.random() * 100,
1171
- firstPaintAfterLoadTime: performance.now() - Math.random() * 50,
1172
- firstPaintTime: performance.now() - Math.random() * 200,
1173
- navigationType: 'Navigation',
1174
- npnNegotiatedProtocol: 'unknown',
1175
- requestTime: performance.now() - Math.random() * 2000,
1176
- startLoadTime: performance.now() - Math.random() * 1500,
1177
- wasAlternateProtocolAvailable: false,
1178
- wasFetchedViaSpdy: false,
1179
- wasNpnNegotiated: false
1180
- }),
402
+ loadTimes: () => {
403
+ const now = performance.now();
404
+ return {
405
+ commitLoadTime: now - Math.random() * 1000,
406
+ connectionInfo: 'http/1.1',
407
+ finishDocumentLoadTime: now - Math.random() * 500,
408
+ finishLoadTime: now - Math.random() * 100,
409
+ navigationType: 'Navigation'
410
+ };
411
+ },
1181
412
  csi: () => ({
1182
413
  onloadT: Date.now(),
1183
414
  pageT: Math.random() * 1000,
1184
- startE: Date.now() - Math.random() * 2000,
1185
- tran: Math.floor(Math.random() * 20)
1186
- }),
1187
- app: {
1188
- isInstalled: false,
1189
- InstallState: { DISABLED: 'disabled', INSTALLED: 'installed', NOT_INSTALLED: 'not_installed' },
1190
- RunningState: { CANNOT_RUN: 'cannot_run', READY_TO_RUN: 'ready_to_run', RUNNING: 'running' }
1191
- }
415
+ startE: Date.now() - Math.random() * 2000
416
+ })
1192
417
  };
1193
418
  }
1194
419
  }, 'Chrome runtime simulation');
1195
-
1196
- // 4. Realistic plugins based on user agent with safe property handling
1197
- safeExecuteSpoofing(() => {
1198
- const isChrome = userAgent.includes('Chrome');
1199
- const isFirefox = userAgent.includes('Firefox');
1200
- const isSafari = userAgent.includes('Safari') && !userAgent.includes('Chrome');
1201
-
420
+
421
+ // Spoof plugins based on user agent
422
+ safeExecute(() => {
1202
423
  let plugins = [];
1203
- if (isChrome) {
424
+ if (userAgent.includes('Chrome')) {
1204
425
  plugins = [
1205
- { name: 'Chrome PDF Plugin', length: 1, description: 'Portable Document Format', filename: 'internal-pdf-viewer' },
1206
- { name: 'Chrome PDF Viewer', length: 1, description: 'PDF Viewer', filename: 'mhjfbmdgcfjbbpaeojofohoefgiehjai' },
1207
- { name: 'Native Client', length: 2, description: 'Native Client Executable', filename: 'internal-nacl-plugin' }
426
+ { name: 'Chrome PDF Plugin', description: 'Portable Document Format', filename: 'internal-pdf-viewer' },
427
+ { name: 'Chrome PDF Viewer', description: 'PDF Viewer', filename: 'mhjfbmdgcfjbbpaeojofohoefgiehjai' }
1208
428
  ];
1209
- } else if (isFirefox) {
429
+ } else if (userAgent.includes('Firefox')) {
1210
430
  plugins = [
1211
- { name: 'PDF.js', length: 2, description: 'Portable Document Format', filename: 'internal-pdf-js' }
1212
- ];
1213
- } else if (isSafari) {
1214
- plugins = [
1215
- { name: 'WebKit built-in PDF', length: 1, description: 'Portable Document Format', filename: 'internal-pdf-viewer' }
431
+ { name: 'PDF.js', description: 'Portable Document Format', filename: 'internal-pdf-js' }
1216
432
  ];
1217
433
  }
1218
-
1219
- safeDefineProperty(navigator, 'plugins', {
1220
- get: () => plugins
1221
- }, debugEnabled);
434
+ safeDefinePropertyLocal(navigator, 'plugins', { get: () => plugins });
1222
435
  }, 'plugins spoofing');
1223
-
1224
- // 5. Enhanced language spoofing with safe property handling
1225
- safeExecuteSpoofing(() => {
1226
- const languages = ['en-US', 'en'];
1227
-
1228
- safeDefineProperty(navigator, 'languages', {
1229
- get: () => languages
1230
- }, debugEnabled);
1231
-
1232
- safeDefineProperty(navigator, 'language', {
1233
- get: () => languages[0]
1234
- }, debugEnabled);
436
+
437
+ // Spoof languages
438
+ safeExecute(() => {
439
+ const languages = ['en-US', 'en'];
440
+ safeDefinePropertyLocal(navigator, 'languages', { get: () => languages });
441
+ safeDefinePropertyLocal(navigator, 'language', { get: () => languages[0] });
1235
442
  }, 'language spoofing');
1236
-
1237
- // 6. Vendor and product info based on user agent with safe handling
1238
- safeExecuteSpoofing(() => {
1239
- const isFirefox = userAgent.includes('Firefox');
1240
- const isSafari = userAgent.includes('Safari') && !userAgent.includes('Chrome');
1241
-
443
+
444
+ // Spoof vendor information
445
+ safeExecute(() => {
1242
446
  let vendor = 'Google Inc.';
1243
447
  let product = 'Gecko';
1244
448
 
1245
- if (isFirefox) {
449
+ if (userAgent.includes('Firefox')) {
1246
450
  vendor = '';
1247
- product = 'Gecko';
1248
- } else if (isSafari) {
451
+ } else if (userAgent.includes('Safari') && !userAgent.includes('Chrome')) {
1249
452
  vendor = 'Apple Computer, Inc.';
1250
- product = 'Gecko';
1251
453
  }
1252
454
 
1253
- safeDefineProperty(navigator, 'vendor', {
1254
- get: () => vendor
1255
- }, debugEnabled);
1256
-
1257
- safeDefineProperty(navigator, 'product', {
1258
- get: () => product
1259
- }, debugEnabled);
455
+ safeDefinePropertyLocal(navigator, 'vendor', { get: () => vendor });
456
+ safeDefinePropertyLocal(navigator, 'product', { get: () => product });
1260
457
  }, 'vendor/product spoofing');
1261
-
1262
- // 7. Add realistic mimeTypes with safe handling
1263
- safeExecuteSpoofing(() => {
1264
- const isChrome = userAgent.includes('Chrome');
1265
-
1266
- safeDefineProperty(navigator, 'mimeTypes', {
1267
- get: () => {
1268
- if (isChrome) {
1269
- return [
1270
- { type: 'application/pdf', description: 'Portable Document Format', suffixes: 'pdf', enabledPlugin: navigator.plugins[0] },
1271
- { type: 'application/x-google-chrome-pdf', description: 'Portable Document Format', suffixes: 'pdf', enabledPlugin: navigator.plugins[1] },
1272
- { type: 'application/x-nacl', description: 'Native Client Executable', suffixes: '', enabledPlugin: navigator.plugins[2] }
1273
- ];
1274
- }
1275
- return [];
1276
- }
1277
- }, debugEnabled);
1278
- }, 'mimeTypes spoofing');
1279
-
1280
- // 8. Enhanced permission API spoofing with safe handling
1281
- safeExecuteSpoofing(() => {
1282
- if (navigator.permissions && navigator.permissions.query) {
1283
- const originalQuery = navigator.permissions.query;
1284
- navigator.permissions.query = function(parameters) {
1285
- const granted = ['camera', 'microphone', 'notifications'];
1286
- const denied = ['midi', 'push', 'speaker'];
1287
- const prompt = ['geolocation'];
1288
-
1289
- if (granted.includes(parameters.name)) {
1290
- return Promise.resolve({ state: 'granted', onchange: null });
1291
- } else if (denied.includes(parameters.name)) {
1292
- return Promise.resolve({ state: 'denied', onchange: null });
1293
- } else if (prompt.includes(parameters.name)) {
1294
- return Promise.resolve({ state: 'prompt', onchange: null });
1295
- }
1296
- return originalQuery.apply(this, arguments);
1297
- };
458
+
459
+ // Spoof MIME types
460
+ safeExecute(() => {
461
+ let mimeTypes = [];
462
+ if (userAgent.includes('Chrome')) {
463
+ mimeTypes = [
464
+ { type: 'application/pdf', description: 'Portable Document Format', suffixes: 'pdf' },
465
+ { type: 'application/x-google-chrome-pdf', description: 'Portable Document Format', suffixes: 'pdf' }
466
+ ];
1298
467
  }
1299
- }, 'permissions API spoofing');
1300
-
1301
- // 9. Spoof iframe contentWindow access with safe handling
1302
- safeExecuteSpoofing(() => {
1303
- const originalContentWindow = Object.getOwnPropertyDescriptor(HTMLIFrameElement.prototype, 'contentWindow');
1304
- if (originalContentWindow && originalContentWindow.configurable !== false) {
1305
- safeDefineProperty(HTMLIFrameElement.prototype, 'contentWindow', {
468
+ safeDefinePropertyLocal(navigator, 'mimeTypes', { get: () => mimeTypes });
469
+ }, 'mimeTypes spoofing');
470
+
471
+ // Enhanced Error.stack protection for CDP detection
472
+ safeExecute(() => {
473
+ const OriginalError = window.Error;
474
+ window.Error = function(...args) {
475
+ const error = new OriginalError(...args);
476
+ const originalStack = error.stack;
477
+
478
+ Object.defineProperty(error, 'stack', {
1306
479
  get: function() {
1307
- const win = originalContentWindow.get.call(this);
1308
- if (win) {
1309
- // Remove automation properties from iframe windows too
1310
- const automationProps = [
1311
- 'callPhantom', '_phantom', '__nightmare', '_selenium',
1312
- '__selenium_unwrapped', '__webdriver_evaluate', '__driver_evaluate'
1313
- ];
1314
- automationProps.forEach(prop => {
1315
- try {
1316
- // Use the same built-in property check for iframe context
1317
- if (!canModifyProperty(win, prop, debugEnabled)) return;
1318
- delete win[prop];
1319
- safeDefineProperty(win, prop, {
1320
- get: () => undefined,
1321
- enumerable: false
1322
- }, debugEnabled);
1323
- } catch(e) {}
1324
- });
1325
- }
1326
- return win;
1327
- }
1328
- }, debugEnabled);
1329
- }
1330
- }, 'iframe contentWindow spoofing');
1331
-
1332
- // 10. Enhanced connection information spoofing with safe handling
1333
- safeExecuteSpoofing(() => {
1334
- if (navigator.connection) {
1335
- // Check if connection properties can be modified
1336
- try {
1337
- const connectionTest = Object.getOwnPropertyDescriptor(navigator.connection, 'rtt');
1338
- if (connectionTest && connectionTest.configurable === false) {
1339
- if (debugEnabled) {
1340
- console.log('[fingerprint] Connection properties are non-configurable, skipping');
480
+ if (typeof originalStack === 'string') {
481
+ return originalStack
482
+ .replace(/.*puppeteer.*\n?/gi, '')
483
+ .replace(/.*chrome-devtools.*\n?/gi, '')
484
+ .replace(/.*webdriver.*\n?/gi, '')
485
+ .replace(/.*automation.*\n?/gi, '')
486
+ .trim() || `${this.name || 'Error'}: ${this.message || ''}\n at unknown location`;
1341
487
  }
1342
- return;
1343
- }
1344
- } catch (connTestErr) {
1345
- if (debugEnabled) {
1346
- console.log('[fingerprint] Cannot test connection properties, skipping connection spoofing');
1347
- }
1348
- return;
488
+ return originalStack;
489
+ },
490
+ configurable: true
491
+ });
492
+ return error;
493
+ };
494
+
495
+ window.Error.prototype = OriginalError.prototype;
496
+ Object.setPrototypeOf(window.Error, OriginalError);
497
+
498
+ // Copy static properties
499
+ ['captureStackTrace', 'stackTraceLimit', 'prepareStackTrace'].forEach(prop => {
500
+ if (OriginalError[prop]) {
501
+ try { window.Error[prop] = OriginalError[prop]; } catch (e) {}
1349
502
  }
1350
- safeDefineProperty(navigator.connection, 'rtt', {
1351
- get: () => Math.floor(Math.random() * 100) + 50
1352
- }, debugEnabled);
1353
- safeDefineProperty(navigator.connection, 'downlink', {
1354
- get: () => Math.random() * 10 + 1
1355
- }, debugEnabled);
1356
- safeDefineProperty(navigator.connection, 'effectiveType', {
1357
- get: () => '4g'
1358
- }, debugEnabled);
1359
- safeDefineProperty(navigator.connection, 'saveData', {
1360
- get: () => false
1361
- }, debugEnabled);
1362
- }
1363
- }, 'connection info spoofing');
1364
-
1365
- // 11. Spoof WebGL fingerprinting with safe handling
1366
- safeExecuteSpoofing(() => {
503
+ });
504
+ }, 'Error stack protection');
505
+
506
+ // Create fingerprinting mock objects
507
+ safeExecute(() => {
508
+ const mockResult = {
509
+ visitorId: 'mock_visitor_' + Math.random().toString(36).substr(2, 9),
510
+ confidence: { score: 0.99 },
511
+ components: {
512
+ screen: { value: { width: 1920, height: 1080 } },
513
+ timezone: { value: 'America/New_York' }
514
+ }
515
+ };
516
+
517
+ window.fp = window.fp || {
518
+ getResult: (callback) => callback ? setTimeout(() => callback(mockResult), 0) : mockResult,
519
+ get: (callback) => Promise.resolve(mockResult),
520
+ load: () => Promise.resolve(window.fp)
521
+ };
522
+
523
+ window.FingerprintJS = window.FingerprintJS || {
524
+ load: () => Promise.resolve({ get: () => Promise.resolve(mockResult) })
525
+ };
526
+
527
+ window.ClientJS = window.ClientJS || function() {
528
+ this.getFingerprint = () => 'mock_fingerprint_' + Math.random().toString(36).substr(2, 9);
529
+ };
530
+ }, 'fingerprinting mocks');
531
+
532
+ // WebGL spoofing
533
+ safeExecute(() => {
1367
534
  if (window.WebGLRenderingContext) {
1368
535
  const getParameter = WebGLRenderingContext.prototype.getParameter;
1369
536
  WebGLRenderingContext.prototype.getParameter = function(parameter) {
1370
- if (parameter === 37445) { // UNMASKED_VENDOR_WEBGL
1371
- return 'Intel Inc.';
1372
- }
1373
- if (parameter === 37446) { // UNMASKED_RENDERER_WEBGL
1374
- return 'Intel Iris OpenGL Engine';
1375
- }
537
+ if (parameter === 37445) return 'Intel Inc.';
538
+ if (parameter === 37446) return 'Intel Iris OpenGL Engine';
1376
539
  return getParameter.call(this, parameter);
1377
540
  };
1378
541
  }
1379
- }, 'WebGL fingerprinting');
1380
-
1381
- // 12. Spoof canvas fingerprinting with subtle noise and safe handling
1382
- safeExecuteSpoofing(() => {
542
+ }, 'WebGL spoofing');
543
+
544
+ // Canvas fingerprinting protection
545
+ safeExecute(() => {
1383
546
  const originalToDataURL = HTMLCanvasElement.prototype.toDataURL;
1384
547
  HTMLCanvasElement.prototype.toDataURL = function(...args) {
1385
548
  const context = this.getContext('2d');
1386
549
  if (context) {
1387
- // Add subtle noise to canvas to prevent fingerprinting
1388
550
  const imageData = context.getImageData(0, 0, this.width, this.height);
1389
551
  for (let i = 0; i < imageData.data.length; i += 4) {
1390
552
  imageData.data[i] = imageData.data[i] + Math.floor(Math.random() * 3) - 1;
@@ -1393,361 +555,157 @@ async function applyUserAgentSpoofing(page, siteConfig, forceDebug, currentUrl)
1393
555
  }
1394
556
  return originalToDataURL.apply(this, args);
1395
557
  };
1396
- }, 'canvas fingerprinting');
1397
-
1398
- // 13. Enhanced Error.captureStackTrace with safe handling
1399
- safeExecuteSpoofing(() => {
1400
- if (Error.captureStackTrace) {
1401
- const originalCaptureStackTrace = Error.captureStackTrace;
1402
- Error.captureStackTrace = function(targetObject, constructorOpt) {
1403
- const result = originalCaptureStackTrace.call(this, targetObject, constructorOpt);
1404
- if (targetObject.stack) {
1405
- // Remove puppeteer-related stack traces
1406
- targetObject.stack = targetObject.stack
1407
- .split('\n')
1408
- .filter(line => !line.includes('puppeteer') && !line.includes('DevTools') && !line.includes('chrome-devtools'))
1409
- .join('\n');
1410
- }
1411
- return result;
1412
- };
1413
- }
1414
- }, 'stack trace cleaning');
1415
-
1416
- // 14. Patch toString methods with safe handling
1417
- safeExecuteSpoofing(() => {
1418
- Function.prototype.toString = new Proxy(Function.prototype.toString, {
1419
- apply: function(target, thisArg, argumentsList) {
1420
- const result = target.apply(thisArg, argumentsList);
1421
- return result.replace(/puppeteer/gi, 'browser').replace(/headless/gi, 'chrome');
1422
- }
1423
- });
1424
- }, 'toString method patching');
1425
-
1426
- // 15. Spoof battery API with safe handling
1427
- safeExecuteSpoofing(() => {
558
+ }, 'canvas fingerprinting protection');
559
+
560
+ // Battery API spoofing
561
+ safeExecute(() => {
1428
562
  if (navigator.getBattery) {
1429
- const originalGetBattery = navigator.getBattery;
1430
563
  navigator.getBattery = function() {
1431
564
  return Promise.resolve({
1432
565
  charging: Math.random() > 0.5,
1433
566
  chargingTime: Math.random() > 0.5 ? Infinity : Math.random() * 3600,
1434
567
  dischargingTime: Math.random() * 7200,
1435
- level: Math.random() * 0.99 + 0.01,
1436
- addEventListener: () => {},
1437
- removeEventListener: () => {},
1438
- dispatchEvent: () => true
568
+ level: Math.random() * 0.99 + 0.01
1439
569
  });
1440
570
  };
1441
571
  }
1442
572
  }, 'battery API spoofing');
1443
-
1444
- // 16. Add realistic timing to console methods with safe handling
1445
- safeExecuteSpoofing(() => {
1446
- ['debug', 'error', 'info', 'log', 'warn'].forEach(method => {
1447
- const original = console[method];
1448
- console[method] = function(...args) {
1449
- // Add tiny random delay to mimic human-like console timing
1450
- setTimeout(() => original.apply(console, args), Math.random() * 5);
1451
- };
1452
- });
1453
- }, 'console timing');
1454
573
 
1455
- // 18. Enhanced service worker handling to prevent registration errors
1456
- safeExecuteSpoofing(() => {
1457
- if ('serviceWorker' in navigator) {
1458
- const originalRegister = navigator.serviceWorker.register;
1459
- const originalGetRegistration = navigator.serviceWorker.getRegistration;
1460
- const originalGetRegistrations = navigator.serviceWorker.getRegistrations;
1461
-
1462
- // Wrap register method to handle errors gracefully
1463
- navigator.serviceWorker.register = function(scriptURL, options) {
1464
- try {
1465
- if (debugEnabled) {
1466
- console.log('[fingerprint] Service worker registration intercepted:', scriptURL);
1467
- }
1468
-
1469
- // Return a resolved promise to prevent registration errors
1470
- return Promise.resolve({
1471
- installing: null,
1472
- waiting: null,
1473
- active: {
1474
- scriptURL: scriptURL,
1475
- state: 'activated'
1476
- },
1477
- scope: options?.scope || '/',
1478
- update: () => Promise.resolve(),
1479
- unregister: () => Promise.resolve(true),
1480
- addEventListener: () => {},
1481
- removeEventListener: () => {},
1482
- dispatchEvent: () => true
1483
- });
1484
- } catch (registerErr) {
1485
- if (debugEnabled) {
1486
- console.log('[fingerprint] Service worker register error handled:', registerErr.message);
1487
- }
1488
- // Return rejected promise to maintain normal error flow for sites that expect it
1489
- return Promise.reject(new Error('ServiceWorker registration failed'));
1490
- }
1491
- };
1492
-
1493
- // Wrap getRegistration to return mock registration
1494
- navigator.serviceWorker.getRegistration = function(scope) {
1495
- try {
1496
- return Promise.resolve(null); // No existing registrations
1497
- } catch (getRegErr) {
1498
- return Promise.reject(getRegErr);
1499
- }
1500
- };
1501
-
1502
- // Wrap getRegistrations to return empty array
1503
- navigator.serviceWorker.getRegistrations = function() {
1504
- try {
1505
- return Promise.resolve([]); // No existing registrations
1506
- } catch (getRegsErr) {
1507
- return Promise.reject(getRegsErr);
1508
- }
1509
- };
1510
- }
1511
- }, 'service worker handling');
574
+ // Suppress common console errors
1512
575
 
1513
- // 17. Enhanced network error handling and fetch/XHR safety
1514
- safeExecuteSpoofing(() => {
1515
- // HTTP status code error suppression only
1516
- const originalConsoleError = console.error;
1517
- console.error = function(...args) {
1518
- const message = args.join(' ').toString();
1519
-
1520
- // Only suppress HTTP status code errors
1521
- const isHttpStatusError = typeof message === 'string' &&
1522
- /Failed to load resource.*server responded with a status of [45]\d{2}(\s*\(\))?/i.test(message);
1523
-
1524
- if (isHttpStatusError) {
1525
- if (debugEnabled) {
1526
- console.log(`[fingerprint] Suppressed HTTP status error: ${message}`);
1527
- }
1528
- return; // Suppress the error
576
+ // Enhanced Mouse/Pointer Spoofing
577
+ safeExecute(() => {
578
+ // Spoof pointer capabilities
579
+ if (navigator.maxTouchPoints !== undefined) {
580
+ safeDefinePropertyLocal(navigator, 'maxTouchPoints', {
581
+ get: () => Math.random() > 0.7 ? 0 : Math.floor(Math.random() * 5) + 1
582
+ });
583
+ }
584
+
585
+ // Spoof mouse timing patterns to prevent behavioral fingerprinting
586
+ const originalAddEventListener = EventTarget.prototype.addEventListener;
587
+ EventTarget.prototype.addEventListener = function(type, listener, options) {
588
+ if (type === 'mousemove' && typeof listener === 'function') {
589
+ const wrappedListener = function(event) {
590
+ // Add slight timing variation to prevent pattern detection
591
+ const delay = Math.random() * 2; // 0-2ms variation
592
+ setTimeout(() => listener.call(this, event), delay);
593
+ };
594
+ return originalAddEventListener.call(this, type, wrappedListener, options);
1529
595
  }
1530
- // For all other errors, use original console.error
1531
- return originalConsoleError.apply(this, arguments);
596
+ return originalAddEventListener.call(this, type, listener, options);
1532
597
  };
1533
-
1534
- // Safely wrap fetch to prevent network errors from propagating to page
1535
- if (window.fetch) {
1536
- const originalFetch = window.fetch;
1537
- window.fetch = function(...args) {
1538
- try {
1539
- const result = originalFetch.apply(this, args);
1540
-
1541
- // Handle fetch promise rejections to prevent uncaught network errors
1542
- if (result && typeof result.catch === 'function') {
1543
- return result.catch(fetchErr => {
1544
- // Log network errors silently without throwing to page
1545
- if (debugEnabled && fetchErr.name === 'TypeError' && fetchErr.message.includes('fetch')) {
1546
- console.log('[fingerprint] Fetch network error handled:', fetchErr.message);
1547
- }
1548
- // Re-throw the error so normal error handling still works
1549
- throw fetchErr;
1550
- });
1551
- }
1552
-
1553
- return result;
1554
- } catch (fetchWrapErr) {
1555
- if (debugEnabled) {
1556
- console.log('[fingerprint] Fetch wrapper error:', fetchWrapErr.message);
1557
- }
1558
- return originalFetch.apply(this, args);
1559
- }
598
+
599
+ // Spoof PointerEvent if available
600
+ if (window.PointerEvent) {
601
+ const OriginalPointerEvent = window.PointerEvent;
602
+ window.PointerEvent = function(type, eventInitDict = {}) {
603
+ // Add realistic pointer properties
604
+ const enhancedDict = {
605
+ ...eventInitDict,
606
+ pressure: eventInitDict.pressure || (Math.random() * 0.3 + 0.2), // 0.2-0.5
607
+ tangentialPressure: eventInitDict.tangentialPressure || 0,
608
+ tiltX: eventInitDict.tiltX || (Math.random() * 10 - 5), // -5 to 5
609
+ tiltY: eventInitDict.tiltY || (Math.random() * 10 - 5),
610
+ twist: eventInitDict.twist || Math.random() * 360,
611
+ pointerType: eventInitDict.pointerType || 'mouse'
612
+ };
613
+ return new OriginalPointerEvent(type, enhancedDict);
1560
614
  };
1561
-
1562
- // Preserve fetch properties
1563
- Object.setPrototypeOf(window.fetch, originalFetch);
615
+ Object.setPrototypeOf(window.PointerEvent, OriginalPointerEvent);
1564
616
  }
1565
617
 
1566
- // Safely wrap XMLHttpRequest to prevent network errors
1567
- if (window.XMLHttpRequest) {
1568
- const OriginalXHR = window.XMLHttpRequest;
1569
-
1570
- window.XMLHttpRequest = function() {
1571
- const xhr = new OriginalXHR();
1572
- const originalOpen = xhr.open;
1573
- const originalSend = xhr.send;
1574
-
1575
- // Wrap open method
1576
- xhr.open = function(...args) {
1577
- try {
1578
- return originalOpen.apply(this, args);
1579
- } catch (openErr) {
1580
- if (debugEnabled) {
1581
- console.log('[fingerprint] XHR open error handled:', openErr.message);
1582
- }
1583
- throw openErr;
1584
- }
1585
- };
1586
-
1587
- // Wrap send method
1588
- xhr.send = function(...args) {
1589
- try {
1590
- return originalSend.apply(this, args);
1591
- } catch (sendErr) {
1592
- if (debugEnabled) {
1593
- console.log('[fingerprint] XHR send error handled:', sendErr.message);
1594
- }
1595
- throw sendErr;
1596
- }
1597
- };
1598
-
1599
- // Add error event listener to prevent uncaught network errors
1600
- xhr.addEventListener('error', function(event) {
1601
- if (debugEnabled) {
1602
- console.log('[fingerprint] XHR network error event handled');
1603
- }
1604
- // Don't prevent default - let normal error handling work
1605
- });
1606
-
1607
- return xhr;
618
+ // Spoof touch capabilities for mobile detection evasion
619
+ if (!window.TouchEvent && Math.random() > 0.8) {
620
+ // 20% chance to add touch support to confuse fingerprinters
621
+ window.TouchEvent = function(type, eventInitDict = {}) {
622
+ return new MouseEvent(type, eventInitDict);
1608
623
  };
1609
624
 
1610
- // Preserve XMLHttpRequest properties
1611
- Object.setPrototypeOf(window.XMLHttpRequest, OriginalXHR);
1612
- window.XMLHttpRequest.prototype = OriginalXHR.prototype;
625
+ safeDefinePropertyLocal(navigator, 'maxTouchPoints', { get: () => 1 });
1613
626
  }
1614
627
 
1615
- // Global error handler for HTTP status code errors only
1616
- const originalErrorHandler = window.onerror;
1617
- window.onerror = function(message, source, lineno, colno, error) {
1618
- const messageStr = String(message || '');
1619
-
1620
- // Only handle HTTP status code errors
1621
- const isHttpStatusError = /Failed to load resource.*server responded with a status of [45]\d{2}(\s*\(\))?/i.test(messageStr);
1622
-
1623
- if (isHttpStatusError) {
1624
- if (debugEnabled) {
1625
- console.log(`[fingerprint] HTTP status error handled by global handler: ${message}`);
1626
- }
1627
- // Return true to suppress the error
1628
- return true;
1629
- }
1630
-
1631
- // Call original error handler for all other errors
1632
- if (originalErrorHandler) {
1633
- return originalErrorHandler.apply(this, arguments);
1634
- }
1635
-
1636
- return false;
1637
- };
628
+ // Spoof mouse wheel behavior
629
+ const originalWheelEvent = window.WheelEvent;
630
+ if (originalWheelEvent) {
631
+ window.WheelEvent = function(type, eventInitDict = {}) {
632
+ const enhancedDict = {
633
+ ...eventInitDict,
634
+ deltaMode: eventInitDict.deltaMode || 0,
635
+ deltaX: eventInitDict.deltaX || 0,
636
+ deltaY: eventInitDict.deltaY || (Math.random() * 100 - 50),
637
+ deltaZ: eventInitDict.deltaZ || 0
638
+ };
639
+ return new originalWheelEvent(type, enhancedDict);
640
+ };
641
+ }
1638
642
 
1639
- // Unhandled promise rejection handler for HTTP status errors only
1640
- const originalUnhandledRejection = window.onunhandledrejection;
1641
- window.onunhandledrejection = function(event) {
1642
- const reason = event.reason;
1643
- let shouldSuppress = false;
1644
-
1645
- if (reason) {
1646
- const reasonMessage = String(reason.message || reason || '');
1647
- // Only suppress HTTP status code related promise rejections
1648
- shouldSuppress = /Failed to load resource.*server responded with a status of [45]\d{2}(\s*\(\))?/i.test(reasonMessage);
1649
- }
1650
-
1651
- if (shouldSuppress) {
1652
- if (debugEnabled) {
1653
- console.log('[fingerprint] HTTP status promise rejection handled:', reason);
1654
- }
1655
- event.preventDefault();
643
+ }, 'enhanced mouse/pointer spoofing');
644
+
645
+ safeExecute(() => {
646
+ const originalConsoleError = console.error;
647
+ console.error = function(...args) {
648
+ const message = args.join(' ');
649
+ if (typeof message === 'string' && (
650
+ message.includes('Failed to load resource') ||
651
+ message.includes('is not defined') ||
652
+ message.includes('is not a function')
653
+ )) {
654
+ if (debugEnabled) console.log(`[fingerprint] Suppressed error: ${message}`);
1656
655
  return;
1657
656
  }
1658
-
1659
- // Call original handler for non-network rejections
1660
- if (originalUnhandledRejection) {
1661
- return originalUnhandledRejection.apply(this, arguments);
1662
- }
657
+ return originalConsoleError.apply(this, arguments);
1663
658
  };
1664
-
1665
- }, 'network error handling');
1666
-
659
+ }, 'console error suppression');
1667
660
 
1668
661
  }, ua, forceDebug);
1669
662
  } catch (stealthErr) {
1670
- console.warn(`[enhanced stealth protection failed] ${currentUrl}: ${stealthErr.message}`);
663
+ console.warn(`[stealth protection failed] ${currentUrl}: ${stealthErr.message}`);
1671
664
  }
1672
665
  }
1673
666
  }
1674
667
 
1675
668
  /**
1676
- * Enhanced Brave browser spoofing with more realistic implementation
1677
- * Compatible with Puppeteer 23.x
1678
- * @param {import('puppeteer').Page} page - The Puppeteer page instance
1679
- * @param {object} siteConfig - The site configuration object
1680
- * @param {boolean} forceDebug - Whether debug logging is enabled
1681
- * @param {string} currentUrl - The current URL being processed (for logging)
1682
- * @returns {Promise<void>}
669
+ * Enhanced Brave browser spoofing
1683
670
  */
1684
671
  async function applyBraveSpoofing(page, siteConfig, forceDebug, currentUrl) {
1685
672
  if (!siteConfig.isBrave) return;
1686
673
 
1687
- if (forceDebug) console.log(`[debug] Enhanced Brave spoofing enabled for ${currentUrl}`);
674
+ if (forceDebug) console.log(`[debug] Brave spoofing enabled for ${currentUrl}`);
1688
675
 
1689
676
  await page.evaluateOnNewDocument((debugEnabled) => {
1690
-
1691
- function safeDefinePropertyLocal(target, property, descriptor) {
1692
- try {
1693
- const existingDescriptor = Object.getOwnPropertyDescriptor(target, property);
1694
-
1695
- if (existingDescriptor && existingDescriptor.configurable === false) {
1696
- if (debugEnabled) {
1697
- console.log(`[fingerprint] Cannot redefine non-configurable property: ${property}`);
1698
- }
1699
- return false;
1700
- }
1701
-
1702
- const safeDescriptor = {
1703
- ...descriptor,
677
+ try {
678
+ Object.defineProperty(navigator, 'brave', {
679
+ get: () => ({
680
+ isBrave: () => Promise.resolve(true),
681
+ setBadge: () => {},
682
+ clearBadge: () => {},
683
+ getAdBlockEnabled: () => Promise.resolve(true),
684
+ getShieldsEnabled: () => Promise.resolve(true)
685
+ }),
686
+ configurable: true
687
+ });
688
+
689
+ if (navigator.userAgent && !navigator.userAgent.includes('Brave')) {
690
+ Object.defineProperty(navigator, 'userAgent', {
691
+ get: () => navigator.userAgent.replace('Chrome/', 'Brave/').replace('Safari/537.36', 'Safari/537.36 Brave/1.60'),
1704
692
  configurable: true
1705
- };
1706
-
1707
- Object.defineProperty(target, property, safeDescriptor);
1708
- return true;
1709
- } catch (defineErr) {
1710
- if (debugEnabled) {
1711
- console.log(`[fingerprint] Property definition failed for ${property}: ${defineErr.message}`);
1712
- }
1713
- return false;
693
+ });
1714
694
  }
1715
- }
1716
-
1717
- // More comprehensive Brave spoofing with safe property handling
1718
- safeDefinePropertyLocal(navigator, 'brave', {
1719
- get: () => ({
1720
- isBrave: () => Promise.resolve(true),
1721
- setBadge: () => {},
1722
- clearBadge: () => {},
1723
- getAdBlockEnabled: () => Promise.resolve(true),
1724
- getShieldsEnabled: () => Promise.resolve(true)
1725
- })
1726
- });
1727
-
1728
- // Brave-specific user agent adjustments with safe handling
1729
- if (navigator.userAgent && !navigator.userAgent.includes('Brave')) {
1730
- safeDefinePropertyLocal(navigator, 'userAgent', {
1731
- get: () => navigator.userAgent.replace('Chrome/', 'Brave/').replace('Safari/537.36', 'Safari/537.36 Brave/1.60')
1732
- });
695
+ } catch (err) {
696
+ if (debugEnabled) console.log(`[fingerprint] Brave spoofing error: ${err.message}`);
1733
697
  }
1734
698
  }, forceDebug);
1735
699
  }
1736
700
 
1737
701
  /**
1738
- * Enhanced fingerprint protection with more realistic and varied spoofing
1739
- * Compatible with Puppeteer 23.x
1740
- * @param {import('puppeteer').Page} page - The Puppeteer page instance
1741
- * @param {object} siteConfig - The site configuration object
1742
- * @param {boolean} forceDebug - Whether debug logging is enabled
1743
- * @param {string} currentUrl - The current URL being processed (for logging)
1744
- * @returns {Promise<void>}
702
+ * Enhanced fingerprint protection with realistic spoofing
1745
703
  */
1746
704
  async function applyFingerprintProtection(page, siteConfig, forceDebug, currentUrl) {
1747
705
  const fingerprintSetting = siteConfig.fingerprint_protection;
1748
706
  if (!fingerprintSetting) return;
1749
707
 
1750
- if (forceDebug) console.log(`[debug] Enhanced fingerprint_protection enabled for ${currentUrl}`);
708
+ if (forceDebug) console.log(`[debug] Fingerprint protection enabled for ${currentUrl}`);
1751
709
 
1752
710
  const spoof = fingerprintSetting === 'random' ? getRandomFingerprint() : {
1753
711
  deviceMemory: 8,
@@ -1763,338 +721,207 @@ async function applyFingerprintProtection(page, siteConfig, forceDebug, currentU
1763
721
  try {
1764
722
  await page.evaluateOnNewDocument(({ spoof, debugEnabled }) => {
1765
723
 
1766
- // Use local versions of helper functions for this context
1767
724
  function safeDefinePropertyLocal(target, property, descriptor) {
1768
725
  try {
1769
- const existingDescriptor = Object.getOwnPropertyDescriptor(target, property);
1770
-
1771
- if (existingDescriptor && existingDescriptor.configurable === false) {
1772
- if (debugEnabled) {
1773
- console.log(`[fingerprint] Cannot redefine non-configurable property: ${property}`);
1774
- }
1775
- return false;
1776
- }
726
+ const existing = Object.getOwnPropertyDescriptor(target, property);
727
+ if (existing?.configurable === false) return false;
1777
728
 
1778
- const safeDescriptor = {
729
+ Object.defineProperty(target, property, {
1779
730
  ...descriptor,
1780
- configurable: true,
1781
- enumerable: descriptor.enumerable !== false
1782
- };
1783
-
1784
- Object.defineProperty(target, property, safeDescriptor);
1785
- return true;
1786
- } catch (defineErr) {
1787
- if (debugEnabled) {
1788
- console.log(`[fingerprint] Property definition failed for ${property}: ${defineErr.message}`);
1789
- }
1790
- return false;
1791
- }
1792
- }
1793
-
1794
- function safeExecuteSpoofingLocal(spoofFunction, description) {
1795
- try {
1796
- spoofFunction();
731
+ configurable: true
732
+ });
1797
733
  return true;
1798
- } catch (spoofErr) {
1799
- if (debugEnabled) {
1800
- console.log(`[fingerprint] ${description} failed: ${spoofErr.message}`);
1801
- }
734
+ } catch (err) {
735
+ if (debugEnabled) console.log(`[fingerprint] Failed to define ${property}: ${err.message}`);
1802
736
  return false;
1803
737
  }
1804
738
  }
1805
-
1806
- // Enhanced property spoofing with more realistic values and safe handling
1807
- safeExecuteSpoofingLocal(() => {
1808
- safeDefinePropertyLocal(navigator, 'platform', {
1809
- get: () => spoof.platform
1810
- });
1811
- }, 'platform spoofing');
1812
-
1813
- safeExecuteSpoofingLocal(() => {
1814
- safeDefinePropertyLocal(navigator, 'deviceMemory', {
1815
- get: () => spoof.deviceMemory
1816
- });
1817
- }, 'deviceMemory spoofing');
1818
-
1819
- safeExecuteSpoofingLocal(() => {
1820
- safeDefinePropertyLocal(navigator, 'hardwareConcurrency', {
1821
- get: () => spoof.hardwareConcurrency
1822
- });
1823
- }, 'hardwareConcurrency spoofing');
1824
-
1825
- // Enhanced screen properties with safe handling
1826
- safeExecuteSpoofingLocal(() => {
1827
- ['width', 'height', 'availWidth', 'availHeight', 'colorDepth', 'pixelDepth'].forEach(prop => {
1828
- if (spoof.screen[prop] !== undefined) {
1829
- safeDefinePropertyLocal(window.screen, prop, {
1830
- get: () => spoof.screen[prop]
1831
- });
1832
- }
1833
- });
1834
- }, 'screen properties spoofing');
1835
-
1836
- // Enhanced language spoofing in fingerprint protection
1837
- safeExecuteSpoofingLocal(() => {
1838
- const languages = Array.isArray(spoof.language) ? spoof.language : [spoof.language, spoof.language.split('-')[0]];
1839
-
1840
- safeDefinePropertyLocal(navigator, 'languages', {
1841
- get: () => languages
1842
- });
1843
-
1844
- safeDefinePropertyLocal(navigator, 'language', {
1845
- get: () => languages[0]
1846
- });
1847
- }, 'language spoofing in fingerprint protection');
1848
739
 
740
+ // Platform spoofing
741
+ safeDefinePropertyLocal(navigator, 'platform', { get: () => spoof.platform });
1849
742
 
1850
- // Enhanced timezone spoofing with safe handling
1851
- safeExecuteSpoofingLocal(() => {
1852
- // Validate timezone value before proceeding
1853
- if (!spoof.timezone || typeof spoof.timezone !== 'string') {
1854
- if (debugEnabled) {
1855
- console.log('[fingerprint] Invalid timezone value, skipping timezone spoofing');
1856
- }
1857
- return;
1858
- }
1859
-
1860
- // Check if Intl.DateTimeFormat is available and configurable
1861
- try {
1862
- const intlDescriptor = Object.getOwnPropertyDescriptor(window, 'Intl');
1863
- if (intlDescriptor && intlDescriptor.configurable === false) {
1864
- if (debugEnabled) {
1865
- console.log('[fingerprint] Intl object is non-configurable, skipping timezone spoofing');
1866
- }
1867
- return;
1868
- }
1869
- } catch (intlCheckErr) {
1870
- if (debugEnabled) {
1871
- console.log('[fingerprint] Cannot check Intl configurability, skipping timezone spoofing');
1872
- }
1873
- return;
743
+ // Memory spoofing
744
+ safeDefinePropertyLocal(navigator, 'deviceMemory', { get: () => spoof.deviceMemory });
745
+ safeDefinePropertyLocal(navigator, 'hardwareConcurrency', { get: () => spoof.hardwareConcurrency });
746
+
747
+ // Screen properties spoofing
748
+ ['width', 'height', 'availWidth', 'availHeight', 'colorDepth', 'pixelDepth'].forEach(prop => {
749
+ if (spoof.screen[prop] !== undefined) {
750
+ safeDefinePropertyLocal(window.screen, prop, { get: () => spoof.screen[prop] });
1874
751
  }
1875
-
1876
- const originalDateTimeFormat = Intl.DateTimeFormat;
1877
-
1878
- // Safely override DateTimeFormat
1879
- try {
1880
- Intl.DateTimeFormat = function(...args) {
1881
- try {
1882
- const instance = new originalDateTimeFormat(...args);
1883
- const originalResolvedOptions = instance.resolvedOptions;
1884
-
1885
- instance.resolvedOptions = function() {
1886
- try {
1887
- const options = originalResolvedOptions.call(this);
1888
- // Validate timezone before setting
1889
- if (spoof.timezone && typeof spoof.timezone === 'string') {
1890
- options.timeZone = spoof.timezone;
1891
- }
1892
- return options;
1893
- } catch (optionsErr) {
1894
- if (debugEnabled) {
1895
- console.log('[fingerprint] resolvedOptions error, using original:', optionsErr.message);
1896
- }
1897
- return originalResolvedOptions.call(this);
1898
- }
1899
- };
1900
- return instance;
1901
- } catch (instanceErr) {
1902
- if (debugEnabled) {
1903
- console.log('[fingerprint] DateTimeFormat instance error, using original:', instanceErr.message);
1904
- }
1905
- return new originalDateTimeFormat(...args);
1906
- }
1907
- };
752
+ });
753
+
754
+ // Language spoofing
755
+ const languages = Array.isArray(spoof.language) ? spoof.language : [spoof.language, spoof.language.split('-')[0]];
756
+ safeDefinePropertyLocal(navigator, 'languages', { get: () => languages });
757
+ safeDefinePropertyLocal(navigator, 'language', { get: () => languages[0] });
758
+
759
+ // Timezone spoofing
760
+ if (spoof.timezone && window.Intl?.DateTimeFormat) {
761
+ const OriginalDateTimeFormat = window.Intl.DateTimeFormat;
762
+ window.Intl.DateTimeFormat = function(...args) {
763
+ const instance = new OriginalDateTimeFormat(...args);
764
+ const originalResolvedOptions = instance.resolvedOptions;
1908
765
 
1909
- // Copy static properties from original
1910
- Object.setPrototypeOf(Intl.DateTimeFormat, originalDateTimeFormat);
1911
- Object.getOwnPropertyNames(originalDateTimeFormat).forEach(prop => {
1912
- if (prop !== 'length' && prop !== 'name' && prop !== 'prototype') {
1913
- try {
1914
- Intl.DateTimeFormat[prop] = originalDateTimeFormat[prop];
1915
- } catch (propErr) {
1916
- // Ignore property copy errors
1917
- }
1918
- }
1919
- });
1920
- } catch (dateTimeFormatErr) {
1921
- if (debugEnabled) {
1922
- console.log('[fingerprint] DateTimeFormat override failed:', dateTimeFormatErr.message);
1923
- }
1924
- }
766
+ instance.resolvedOptions = function() {
767
+ const opts = originalResolvedOptions.call(this);
768
+ opts.timeZone = spoof.timezone;
769
+ return opts;
770
+ };
771
+ return instance;
772
+ };
773
+ Object.setPrototypeOf(window.Intl.DateTimeFormat, OriginalDateTimeFormat);
774
+
775
+ // Timezone offset spoofing
776
+ const timezoneOffsets = {
777
+ 'America/New_York': 300,
778
+ 'America/Los_Angeles': 480,
779
+ 'Europe/London': 0,
780
+ 'America/Chicago': 360
781
+ };
1925
782
 
1926
- // Spoof Date.getTimezoneOffset with safe handling
1927
- try {
1928
- const originalGetTimezoneOffset = Date.prototype.getTimezoneOffset;
1929
-
1930
- // Check if getTimezoneOffset can be overridden
1931
- const tzDescriptor = Object.getOwnPropertyDescriptor(Date.prototype, 'getTimezoneOffset');
1932
- if (tzDescriptor && tzDescriptor.configurable === false) {
1933
- if (debugEnabled) {
1934
- console.log('[fingerprint] getTimezoneOffset is non-configurable, skipping');
1935
- }
1936
- } else {
1937
- Date.prototype.getTimezoneOffset = function() {
1938
- try {
1939
- // Validate timezone and return appropriate offset
1940
- const timezoneOffsets = {
1941
- 'America/New_York': 300, // EST offset
1942
- 'America/Los_Angeles': 480, // PST offset
1943
- 'Europe/London': 0, // GMT offset
1944
- 'America/Chicago': 360 // CST offset
1945
- };
1946
-
1947
- if (spoof.timezone && timezoneOffsets.hasOwnProperty(spoof.timezone)) {
1948
- return timezoneOffsets[spoof.timezone];
1949
- }
1950
-
1951
- // Fallback to original if timezone not recognized
1952
- return originalGetTimezoneOffset.call(this);
1953
- } catch (tzOffsetErr) {
1954
- if (debugEnabled) {
1955
- console.log('[fingerprint] getTimezoneOffset error, using original:', tzOffsetErr.message);
1956
- }
1957
- return originalGetTimezoneOffset.call(this);
1958
- }
1959
- };
1960
- }
1961
- } catch (timezoneOffsetErr) {
1962
- if (debugEnabled) {
1963
- console.log('[fingerprint] Timezone offset spoofing failed:', timezoneOffsetErr.message);
1964
- }
1965
- }
1966
- }, 'timezone spoofing');
783
+ const originalGetTimezoneOffset = Date.prototype.getTimezoneOffset;
784
+ Date.prototype.getTimezoneOffset = function() {
785
+ return timezoneOffsets[spoof.timezone] || originalGetTimezoneOffset.call(this);
786
+ };
787
+ }
1967
788
 
1968
- // Enhanced cookie and DNT spoofing with safe handling
1969
- safeExecuteSpoofingLocal(() => {
1970
- if (spoof.cookieEnabled !== undefined) {
1971
- safeDefinePropertyLocal(navigator, 'cookieEnabled', {
1972
- get: () => spoof.cookieEnabled
1973
- });
1974
- }
1975
-
1976
- if (spoof.doNotTrack !== undefined) {
1977
- safeDefinePropertyLocal(navigator, 'doNotTrack', {
1978
- get: () => spoof.doNotTrack
1979
- });
1980
- }
1981
- }, 'cookie/DNT spoofing');
789
+ // Cookie and DNT spoofing
790
+ if (spoof.cookieEnabled !== undefined) {
791
+ safeDefinePropertyLocal(navigator, 'cookieEnabled', { get: () => spoof.cookieEnabled });
792
+ }
793
+ if (spoof.doNotTrack !== undefined) {
794
+ safeDefinePropertyLocal(navigator, 'doNotTrack', { get: () => spoof.doNotTrack });
795
+ }
1982
796
 
1983
797
  }, { spoof, debugEnabled: forceDebug });
1984
798
  } catch (err) {
1985
- console.warn(`[enhanced fingerprint spoof failed] ${currentUrl}: ${err.message}`);
799
+ console.warn(`[fingerprint protection failed] ${currentUrl}: ${err.message}`);
1986
800
  }
1987
801
  }
1988
802
 
1989
803
  /**
1990
- * Add mouse movement simulation to appear more human-like
1991
- * Compatible with Puppeteer 23.x
1992
- * @param {import('puppeteer').Page} page - The Puppeteer page instance
1993
- * @param {boolean} forceDebug - Whether debug logging is enabled
1994
- * @returns {Promise<void>}
804
+ * Simulate human-like behavior
805
+ * Enhanced with realistic mouse patterns and timing
1995
806
  */
1996
807
  async function simulateHumanBehavior(page, forceDebug) {
1997
808
  try {
1998
809
  await page.evaluateOnNewDocument((debugEnabled) => {
1999
810
 
2000
- function safeExecuteSpoofingLocal(spoofFunction, description) {
2001
- try {
2002
- spoofFunction();
2003
- return true;
2004
- } catch (spoofErr) {
2005
- if (debugEnabled) {
2006
- console.log(`[fingerprint] ${description} failed: ${spoofErr.message}`);
2007
- }
2008
- return false;
2009
- }
2010
- }
2011
-
2012
- // Simulate human-like mouse movements with safe handling
2013
- safeExecuteSpoofingLocal(() => {
2014
- let mouseX = Math.random() * window.innerWidth;
2015
- let mouseY = Math.random() * window.innerHeight;
811
+ try {
812
+ // Enhanced human-like mouse simulation with realistic patterns
813
+ let mouseX = Math.random() * (window.innerWidth - 100) + 50;
814
+ let mouseY = Math.random() * (window.innerHeight - 100) + 50;
815
+ let lastMoveTime = Date.now();
816
+ let moveCount = 0;
817
+
818
+ // Realistic mouse movement patterns
819
+ const movePatterns = {
820
+ linear: () => ({
821
+ deltaX: (Math.random() - 0.5) * 4,
822
+ deltaY: (Math.random() - 0.5) * 4
823
+ }),
824
+ curved: () => {
825
+ const angle = moveCount * 0.1;
826
+ return {
827
+ deltaX: Math.sin(angle) * 3 + (Math.random() - 0.5) * 2,
828
+ deltaY: Math.cos(angle) * 3 + (Math.random() - 0.5) * 2
829
+ };
830
+ },
831
+ jittery: () => ({
832
+ deltaX: (Math.random() - 0.5) * 8,
833
+ deltaY: (Math.random() - 0.5) * 8
834
+ })
835
+ };
836
+
837
+ const patterns = Object.keys(movePatterns);
838
+ let currentPattern = patterns[Math.floor(Math.random() * patterns.length)];
839
+ let patternChangeCounter = 0;
2016
840
 
2017
841
  const moveInterval = setInterval(() => {
2018
- mouseX += (Math.random() - 0.5) * 20;
2019
- mouseY += (Math.random() - 0.5) * 20;
842
+ const now = Date.now();
843
+ const timeDelta = now - lastMoveTime;
844
+
845
+ // Change pattern occasionally
846
+ if (patternChangeCounter++ > 20 + Math.random() * 30) {
847
+ currentPattern = patterns[Math.floor(Math.random() * patterns.length)];
848
+ patternChangeCounter = 0;
849
+ }
850
+
851
+ // Apply movement pattern
852
+ const movement = movePatterns[currentPattern]();
853
+ mouseX += movement.deltaX;
854
+ mouseY += movement.deltaY;
2020
855
 
2021
- mouseX = Math.max(0, Math.min(window.innerWidth, mouseX));
2022
- mouseY = Math.max(0, Math.min(window.innerHeight, mouseY));
856
+ // Keep within bounds with padding
857
+ mouseX = Math.max(10, Math.min(window.innerWidth - 10, mouseX));
858
+ mouseY = Math.max(10, Math.min(window.innerHeight - 10, mouseY));
2023
859
 
2024
860
  try {
861
+ // Dispatch realistic mouse events with varying timing
2025
862
  document.dispatchEvent(new MouseEvent('mousemove', {
2026
863
  clientX: mouseX,
2027
864
  clientY: mouseY,
2028
- bubbles: true
865
+ bubbles: true,
866
+ cancelable: true,
867
+ view: window,
868
+ detail: 0,
869
+ buttons: 0,
870
+ button: 0
2029
871
  }));
2030
- } catch (mouseErr) {
2031
- // Ignore mouse event errors
2032
- }
2033
- }, 1000 + Math.random() * 2000);
2034
-
2035
- // Simulate occasional clicks and scrolls with safe handling
2036
- setTimeout(() => {
2037
- try {
2038
- if (Math.random() > 0.7) {
872
+
873
+ // Occasionally simulate clicks for more realistic behavior
874
+ if (Math.random() > 0.995) { // Very rare clicks
2039
875
  document.dispatchEvent(new MouseEvent('click', {
2040
876
  clientX: mouseX,
2041
877
  clientY: mouseY,
2042
- bubbles: true
878
+ bubbles: true,
879
+ cancelable: true,
880
+ view: window
2043
881
  }));
2044
882
  }
2045
883
 
2046
- // Simulate scroll events
2047
- if (Math.random() > 0.8) {
2048
- window.scrollBy(0, Math.random() * 100 - 50);
2049
- }
2050
- } catch (interactionErr) {
2051
- // Ignore interaction errors
2052
- }
2053
- }, 5000 + Math.random() * 10000);
884
+ moveCount++;
885
+ lastMoveTime = now;
886
+
887
+ } catch (e) {}
888
+ }, 50 + Math.random() * 100); // More frequent, realistic timing (50-150ms)
2054
889
 
2055
- // Stop simulation after 30 seconds to avoid detection
890
+ // Stop after 45 seconds with gradual slowdown
2056
891
  setTimeout(() => {
2057
- try {
892
+ try {
2058
893
  clearInterval(moveInterval);
2059
- } catch (clearErr) {
2060
- // Ignore clear errors
2061
- }
2062
- }, 30000);
2063
- }, 'human behavior simulation');
894
+ if (debugEnabled) console.log('[fingerprint] Enhanced mouse simulation completed');
895
+ } catch (e) {}
896
+ }, 45000);
897
+
898
+ } catch (err) {
899
+ if (debugEnabled) console.log(`[fingerprint] Human behavior simulation failed: ${err.message}`);
900
+ }
2064
901
 
2065
902
  }, forceDebug);
2066
903
  } catch (err) {
2067
- if (forceDebug) console.log(`[debug] Human behavior simulation failed: ${err.message}`);
904
+ if (forceDebug) console.log(`[debug] Human behavior simulation setup failed: ${err.message}`);
2068
905
  }
2069
906
  }
2070
907
 
2071
908
  /**
2072
- * Enhanced main function that applies all fingerprint spoofing techniques
2073
- * Compatible with Puppeteer 23.x
2074
- * @param {import('puppeteer').Page} page - The Puppeteer page instance
2075
- * @param {object} siteConfig - The site configuration object
2076
- * @param {boolean} forceDebug - Whether debug logging is enabled
2077
- * @param {string} currentUrl - The current URL being processed (for logging)
2078
- * @returns {Promise<void>}
909
+ * Main function that applies all fingerprint spoofing techniques
2079
910
  */
2080
911
  async function applyAllFingerprintSpoofing(page, siteConfig, forceDebug, currentUrl) {
2081
- // Apply all spoofing techniques with enhanced error handling
2082
- try {
2083
- await applyUserAgentSpoofing(page, siteConfig, forceDebug, currentUrl);
2084
- } catch (uaErr) {
2085
- if (forceDebug) console.log(`[debug] User agent spoofing failed for ${currentUrl}: ${uaErr.message}`);
2086
- }
2087
-
2088
- try {
2089
- await applyBraveSpoofing(page, siteConfig, forceDebug, currentUrl);
2090
- } catch (braveErr) {
2091
- if (forceDebug) console.log(`[debug] Brave spoofing failed for ${currentUrl}: ${braveErr.message}`);
2092
- }
2093
-
2094
- try {
2095
- await applyFingerprintProtection(page, siteConfig, forceDebug, currentUrl);
2096
- } catch (fpErr) {
2097
- if (forceDebug) console.log(`[debug] Fingerprint protection failed for ${currentUrl}: ${fpErr.message}`);
912
+
913
+ const techniques = [
914
+ { fn: applyUserAgentSpoofing, name: 'User agent spoofing' },
915
+ { fn: applyBraveSpoofing, name: 'Brave spoofing' },
916
+ { fn: applyFingerprintProtection, name: 'Fingerprint protection' }
917
+ ];
918
+
919
+ for (const { fn, name } of techniques) {
920
+ try {
921
+ await fn(page, siteConfig, forceDebug, currentUrl);
922
+ } catch (err) {
923
+ if (forceDebug) console.log(`[debug] ${name} failed for ${currentUrl}: ${err.message}`);
924
+ }
2098
925
  }
2099
926
 
2100
927
  // Add human behavior simulation if user agent spoofing is enabled
@@ -2107,6 +934,12 @@ async function applyAllFingerprintSpoofing(page, siteConfig, forceDebug, current
2107
934
  }
2108
935
  }
2109
936
 
937
+ // Legacy compatibility function - maintained for backwards compatibility
938
+ function safeExecuteSpoofing(spoofFunction, description, forceDebug = false) {
939
+ return safeSpoofingExecution(spoofFunction, description, { debug: forceDebug });
940
+ }
941
+
942
+
2110
943
  module.exports = {
2111
944
  getRandomFingerprint,
2112
945
  getRealisticScreenResolution,
@@ -2116,7 +949,10 @@ module.exports = {
2116
949
  applyAllFingerprintSpoofing,
2117
950
  simulateHumanBehavior,
2118
951
  safeDefineProperty,
2119
- safeExecuteSpoofing,
952
+ safeExecuteSpoofing, // Legacy compatibility
953
+ safeSpoofingExecution,
954
+ createFingerprintMocks,
955
+ applyTimezoneSpoofing,
2120
956
  DEFAULT_PLATFORM,
2121
957
  DEFAULT_TIMEZONE
2122
958
  };