@fanboynz/network-scanner 1.0.64 → 1.0.65

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.
Files changed (3) hide show
  1. package/lib/fingerprint.js +524 -1825
  2. package/nwss.js +2 -2
  3. package/package.json +1 -1
@@ -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
70
+ * Safely executes spoofing operations with error handling
105
71
  */
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
146
- */
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,428 @@ 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
+ }
141
+
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
+ }
226
175
 
227
- if (forceDebug) console.log(`[debug] Enhanced userAgent spoofing enabled for ${currentUrl}: ${siteConfig.userAgent}`);
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
+ };
228
189
 
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
- ]
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
+ }
246
211
  };
212
+ }
213
+
214
+ /**
215
+ * Applies timezone spoofing
216
+ */
217
+ function applyTimezoneSpoofing(timezone, options = {}) {
218
+ const tzConfig = TIMEZONE_CONFIG[timezone] || TIMEZONE_CONFIG[DEFAULT_TIMEZONE];
247
219
 
248
- const selectedUserAgents = userAgents[siteConfig.userAgent.toLowerCase()];
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
+ }
236
+
237
+ // Spoof Date.getTimezoneOffset
238
+ const originalGetTimezoneOffset = Date.prototype.getTimezoneOffset;
239
+ Date.prototype.getTimezoneOffset = function() {
240
+ return tzConfig.offset;
241
+ };
242
+
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
+ ];
297
+ return patterns.some(pattern => pattern.test(String(message || '')));
382
298
  }
383
299
 
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);
300
+ console.error = function(...args) {
301
+ const message = args.join(' ');
302
+ if (shouldSuppressFingerprintError(message)) {
303
+ if (debugEnabled) console.log("[fingerprint] Suppressed error:", message);
304
+ return;
390
305
  }
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}`);
306
+ return originalConsoleError.apply(this, arguments);
307
+ };
308
+
309
+ window.onerror = function(message, source, lineno, colno, error) {
310
+ if (shouldSuppressFingerprintError(message)) {
311
+ if (debugEnabled) console.log("[fingerprint] Suppressed window error:", message);
312
+ return true;
401
313
  }
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`);
314
+ if (originalWindowError) {
315
+ return originalWindowError.apply(this, arguments);
422
316
  }
423
317
  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
318
  };
451
- };
319
+ })();
452
320
 
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
- }
321
+ // Create safe property definition helper
322
+ function safeDefinePropertyLocal(target, property, descriptor) {
323
+ const builtInProps = new Set(['href', 'origin', 'protocol', 'host', 'hostname', 'port', 'pathname', 'search', 'hash', 'constructor', 'prototype', '__proto__', 'toString', 'valueOf', 'assign', 'reload', 'replace']);
481
324
 
482
- // Call original error handler for other errors
483
- if (originalErrorHandler) {
484
- return originalErrorHandler.apply(this, arguments);
325
+ if (builtInProps.has(property)) {
326
+ if (debugEnabled) console.log(`[fingerprint] Skipping built-in property: ${property}`);
327
+ return false;
485
328
  }
486
- return false;
487
- };
488
-
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
329
 
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
- }
330
+ try {
331
+ const existing = Object.getOwnPropertyDescriptor(target, property);
332
+ if (existing?.configurable === false) {
333
+ if (debugEnabled) console.log(`[fingerprint] Cannot modify non-configurable: ${property}`);
334
+ return false;
695
335
  }
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
336
 
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
- }
337
+ Object.defineProperty(target, property, {
338
+ ...descriptor,
339
+ configurable: true
783
340
  });
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);
341
+ return true;
813
342
  } catch (err) {
814
- return new Date().toISOString();
343
+ if (debugEnabled) console.log(`[fingerprint] Failed to define ${property}: ${err.message}`);
344
+ return false;
815
345
  }
816
- };
346
+ }
817
347
 
818
- const originalGetTimezoneOffset = Date.prototype.getTimezoneOffset;
819
- Date.prototype.getTimezoneOffset = function() {
348
+ // Safe execution wrapper
349
+ function safeExecute(fn, description) {
820
350
  try {
821
- return originalGetTimezoneOffset.call(this);
351
+ fn();
352
+ return true;
822
353
  } 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
- };
354
+ if (debugEnabled) console.log(`[fingerprint] ${description} failed: ${err.message}`);
355
+ return false;
991
356
  }
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
357
  }
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
358
 
359
+ // Remove webdriver properties
360
+ safeExecute(() => {
1055
361
  try {
1056
362
  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);
363
+ } catch (e) {}
364
+ safeDefinePropertyLocal(navigator, 'webdriver', { get: () => undefined });
1075
365
  }, '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 => {
1091
-
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
- }
1096
- 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
366
 
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
- }
367
+ // Remove automation properties
368
+ safeExecute(() => {
369
+ const automationProps = [
370
+ 'callPhantom', '_phantom', '__nightmare', '_selenium', '__selenium_unwrapped',
371
+ '__webdriver_evaluate', '__driver_evaluate', '__webdriver_script_function',
372
+ 'spawn', 'emit', 'Buffer', 'domAutomation', 'domAutomationController'
373
+ ];
374
+
375
+ automationProps.forEach(prop => {
376
+ try {
377
+ delete window[prop];
378
+ delete navigator[prop];
379
+ safeDefinePropertyLocal(window, prop, { get: () => undefined });
380
+ safeDefinePropertyLocal(navigator, prop, { get: () => undefined });
381
+ } catch (e) {}
1142
382
  });
1143
383
  }, 'automation properties removal');
1144
384
 
1145
- // 3. Enhanced Chrome runtime simulation with safe property handling
1146
-
1147
- safeExecuteSpoofing(() => {
1148
- if (!window.chrome || !window.chrome.runtime) {
385
+ // Simulate Chrome runtime
386
+ safeExecute(() => {
387
+ if (!window.chrome?.runtime) {
1149
388
  window.chrome = {
1150
389
  runtime: {
1151
390
  onConnect: { addListener: () => {}, removeListener: () => {} },
1152
391
  onMessage: { addListener: () => {}, removeListener: () => {} },
1153
392
  sendMessage: () => {},
1154
- connect: () => ({
1155
- onMessage: { addListener: () => {}, removeListener: () => {} },
1156
- postMessage: () => {},
1157
- disconnect: () => {}
1158
- }),
1159
- getManifest: () => ({
1160
- name: "Chrome",
1161
- version: "131.0.0.0"
1162
- }),
393
+ connect: () => ({ onMessage: { addListener: () => {}, removeListener: () => {} }, postMessage: () => {}, disconnect: () => {} }),
394
+ getManifest: () => ({ name: "Chrome", version: "131.0.0.0" }),
1163
395
  getURL: (path) => `chrome-extension://invalid/${path}`,
1164
396
  id: undefined
1165
397
  },
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
- }),
398
+ loadTimes: () => {
399
+ const now = performance.now();
400
+ return {
401
+ commitLoadTime: now - Math.random() * 1000,
402
+ connectionInfo: 'http/1.1',
403
+ finishDocumentLoadTime: now - Math.random() * 500,
404
+ finishLoadTime: now - Math.random() * 100,
405
+ navigationType: 'Navigation'
406
+ };
407
+ },
1181
408
  csi: () => ({
1182
409
  onloadT: Date.now(),
1183
410
  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
- }
411
+ startE: Date.now() - Math.random() * 2000
412
+ })
1192
413
  };
1193
414
  }
1194
415
  }, '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
-
416
+
417
+ // Spoof plugins based on user agent
418
+ safeExecute(() => {
1202
419
  let plugins = [];
1203
- if (isChrome) {
1204
- 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' }
1208
- ];
1209
- } else if (isFirefox) {
420
+ if (userAgent.includes('Chrome')) {
1210
421
  plugins = [
1211
- { name: 'PDF.js', length: 2, description: 'Portable Document Format', filename: 'internal-pdf-js' }
422
+ { name: 'Chrome PDF Plugin', description: 'Portable Document Format', filename: 'internal-pdf-viewer' },
423
+ { name: 'Chrome PDF Viewer', description: 'PDF Viewer', filename: 'mhjfbmdgcfjbbpaeojofohoefgiehjai' }
1212
424
  ];
1213
- } else if (isSafari) {
425
+ } else if (userAgent.includes('Firefox')) {
1214
426
  plugins = [
1215
- { name: 'WebKit built-in PDF', length: 1, description: 'Portable Document Format', filename: 'internal-pdf-viewer' }
427
+ { name: 'PDF.js', description: 'Portable Document Format', filename: 'internal-pdf-js' }
1216
428
  ];
1217
429
  }
1218
-
1219
- safeDefineProperty(navigator, 'plugins', {
1220
- get: () => plugins
1221
- }, debugEnabled);
430
+ safeDefinePropertyLocal(navigator, 'plugins', { get: () => plugins });
1222
431
  }, 'plugins spoofing');
1223
-
1224
- // 5. Enhanced language spoofing with safe property handling
1225
- safeExecuteSpoofing(() => {
432
+
433
+ // Spoof languages
434
+ safeExecute(() => {
1226
435
  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
+ safeDefinePropertyLocal(navigator, 'languages', { get: () => languages });
437
+ safeDefinePropertyLocal(navigator, 'language', { get: () => languages[0] });
1235
438
  }, '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
-
439
+
440
+ // Spoof vendor information
441
+ safeExecute(() => {
1242
442
  let vendor = 'Google Inc.';
1243
443
  let product = 'Gecko';
1244
444
 
1245
- if (isFirefox) {
445
+ if (userAgent.includes('Firefox')) {
1246
446
  vendor = '';
1247
- product = 'Gecko';
1248
- } else if (isSafari) {
447
+ } else if (userAgent.includes('Safari') && !userAgent.includes('Chrome')) {
1249
448
  vendor = 'Apple Computer, Inc.';
1250
- product = 'Gecko';
1251
449
  }
1252
450
 
1253
- safeDefineProperty(navigator, 'vendor', {
1254
- get: () => vendor
1255
- }, debugEnabled);
1256
-
1257
- safeDefineProperty(navigator, 'product', {
1258
- get: () => product
1259
- }, debugEnabled);
451
+ safeDefinePropertyLocal(navigator, 'vendor', { get: () => vendor });
452
+ safeDefinePropertyLocal(navigator, 'product', { get: () => product });
1260
453
  }, '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
- };
454
+
455
+ // Spoof MIME types
456
+ safeExecute(() => {
457
+ let mimeTypes = [];
458
+ if (userAgent.includes('Chrome')) {
459
+ mimeTypes = [
460
+ { type: 'application/pdf', description: 'Portable Document Format', suffixes: 'pdf' },
461
+ { type: 'application/x-google-chrome-pdf', description: 'Portable Document Format', suffixes: 'pdf' }
462
+ ];
1298
463
  }
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', {
464
+ safeDefinePropertyLocal(navigator, 'mimeTypes', { get: () => mimeTypes });
465
+ }, 'mimeTypes spoofing');
466
+
467
+ // Enhanced Error.stack protection for CDP detection
468
+ safeExecute(() => {
469
+ const OriginalError = window.Error;
470
+ window.Error = function(...args) {
471
+ const error = new OriginalError(...args);
472
+ const originalStack = error.stack;
473
+
474
+ Object.defineProperty(error, 'stack', {
1306
475
  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');
476
+ if (typeof originalStack === 'string') {
477
+ return originalStack
478
+ .replace(/.*puppeteer.*\n?/gi, '')
479
+ .replace(/.*chrome-devtools.*\n?/gi, '')
480
+ .replace(/.*webdriver.*\n?/gi, '')
481
+ .replace(/.*automation.*\n?/gi, '')
482
+ .trim() || `${this.name || 'Error'}: ${this.message || ''}\n at unknown location`;
1341
483
  }
1342
- return;
1343
- }
1344
- } catch (connTestErr) {
1345
- if (debugEnabled) {
1346
- console.log('[fingerprint] Cannot test connection properties, skipping connection spoofing');
1347
- }
1348
- return;
484
+ return originalStack;
485
+ },
486
+ configurable: true
487
+ });
488
+ return error;
489
+ };
490
+
491
+ window.Error.prototype = OriginalError.prototype;
492
+ Object.setPrototypeOf(window.Error, OriginalError);
493
+
494
+ // Copy static properties
495
+ ['captureStackTrace', 'stackTraceLimit', 'prepareStackTrace'].forEach(prop => {
496
+ if (OriginalError[prop]) {
497
+ try { window.Error[prop] = OriginalError[prop]; } catch (e) {}
1349
498
  }
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(() => {
499
+ });
500
+ }, 'Error stack protection');
501
+
502
+ // Create fingerprinting mock objects
503
+ safeExecute(() => {
504
+ const mockResult = {
505
+ visitorId: 'mock_visitor_' + Math.random().toString(36).substr(2, 9),
506
+ confidence: { score: 0.99 },
507
+ components: {
508
+ screen: { value: { width: 1920, height: 1080 } },
509
+ timezone: { value: 'America/New_York' }
510
+ }
511
+ };
512
+
513
+ window.fp = window.fp || {
514
+ getResult: (callback) => callback ? setTimeout(() => callback(mockResult), 0) : mockResult,
515
+ get: (callback) => Promise.resolve(mockResult),
516
+ load: () => Promise.resolve(window.fp)
517
+ };
518
+
519
+ window.FingerprintJS = window.FingerprintJS || {
520
+ load: () => Promise.resolve({ get: () => Promise.resolve(mockResult) })
521
+ };
522
+
523
+ window.ClientJS = window.ClientJS || function() {
524
+ this.getFingerprint = () => 'mock_fingerprint_' + Math.random().toString(36).substr(2, 9);
525
+ };
526
+ }, 'fingerprinting mocks');
527
+
528
+ // WebGL spoofing
529
+ safeExecute(() => {
1367
530
  if (window.WebGLRenderingContext) {
1368
531
  const getParameter = WebGLRenderingContext.prototype.getParameter;
1369
532
  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
- }
533
+ if (parameter === 37445) return 'Intel Inc.';
534
+ if (parameter === 37446) return 'Intel Iris OpenGL Engine';
1376
535
  return getParameter.call(this, parameter);
1377
536
  };
1378
537
  }
1379
- }, 'WebGL fingerprinting');
1380
-
1381
- // 12. Spoof canvas fingerprinting with subtle noise and safe handling
1382
- safeExecuteSpoofing(() => {
538
+ }, 'WebGL spoofing');
539
+
540
+ // Canvas fingerprinting protection
541
+ safeExecute(() => {
1383
542
  const originalToDataURL = HTMLCanvasElement.prototype.toDataURL;
1384
543
  HTMLCanvasElement.prototype.toDataURL = function(...args) {
1385
544
  const context = this.getContext('2d');
1386
545
  if (context) {
1387
- // Add subtle noise to canvas to prevent fingerprinting
1388
546
  const imageData = context.getImageData(0, 0, this.width, this.height);
1389
547
  for (let i = 0; i < imageData.data.length; i += 4) {
1390
548
  imageData.data[i] = imageData.data[i] + Math.floor(Math.random() * 3) - 1;
@@ -1393,361 +551,87 @@ async function applyUserAgentSpoofing(page, siteConfig, forceDebug, currentUrl)
1393
551
  }
1394
552
  return originalToDataURL.apply(this, args);
1395
553
  };
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(() => {
554
+ }, 'canvas fingerprinting protection');
555
+
556
+ // Battery API spoofing
557
+ safeExecute(() => {
1428
558
  if (navigator.getBattery) {
1429
- const originalGetBattery = navigator.getBattery;
1430
559
  navigator.getBattery = function() {
1431
560
  return Promise.resolve({
1432
561
  charging: Math.random() > 0.5,
1433
562
  chargingTime: Math.random() > 0.5 ? Infinity : Math.random() * 3600,
1434
563
  dischargingTime: Math.random() * 7200,
1435
- level: Math.random() * 0.99 + 0.01,
1436
- addEventListener: () => {},
1437
- removeEventListener: () => {},
1438
- dispatchEvent: () => true
564
+ level: Math.random() * 0.99 + 0.01
1439
565
  });
1440
566
  };
1441
567
  }
1442
568
  }, '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
-
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');
1512
569
 
1513
- // 17. Enhanced network error handling and fetch/XHR safety
1514
- safeExecuteSpoofing(() => {
1515
- // HTTP status code error suppression only
570
+ // Suppress common console errors
571
+ safeExecute(() => {
1516
572
  const originalConsoleError = console.error;
1517
573
  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
1529
- }
1530
- // For all other errors, use original console.error
1531
- return originalConsoleError.apply(this, arguments);
1532
- };
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
- }
1560
- };
1561
-
1562
- // Preserve fetch properties
1563
- Object.setPrototypeOf(window.fetch, originalFetch);
1564
- }
1565
-
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;
1608
- };
1609
-
1610
- // Preserve XMLHttpRequest properties
1611
- Object.setPrototypeOf(window.XMLHttpRequest, OriginalXHR);
1612
- window.XMLHttpRequest.prototype = OriginalXHR.prototype;
1613
- }
1614
-
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
- };
1638
-
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();
574
+ const message = args.join(' ');
575
+ if (typeof message === 'string' && (
576
+ message.includes('Failed to load resource') ||
577
+ message.includes('is not defined') ||
578
+ message.includes('is not a function')
579
+ )) {
580
+ if (debugEnabled) console.log(`[fingerprint] Suppressed error: ${message}`);
1656
581
  return;
1657
582
  }
1658
-
1659
- // Call original handler for non-network rejections
1660
- if (originalUnhandledRejection) {
1661
- return originalUnhandledRejection.apply(this, arguments);
1662
- }
583
+ return originalConsoleError.apply(this, arguments);
1663
584
  };
1664
-
1665
- }, 'network error handling');
1666
-
585
+ }, 'console error suppression');
1667
586
 
1668
587
  }, ua, forceDebug);
1669
588
  } catch (stealthErr) {
1670
- console.warn(`[enhanced stealth protection failed] ${currentUrl}: ${stealthErr.message}`);
589
+ console.warn(`[stealth protection failed] ${currentUrl}: ${stealthErr.message}`);
1671
590
  }
1672
591
  }
1673
592
  }
1674
593
 
1675
594
  /**
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>}
595
+ * Enhanced Brave browser spoofing
1683
596
  */
1684
597
  async function applyBraveSpoofing(page, siteConfig, forceDebug, currentUrl) {
1685
598
  if (!siteConfig.isBrave) return;
1686
599
 
1687
- if (forceDebug) console.log(`[debug] Enhanced Brave spoofing enabled for ${currentUrl}`);
600
+ if (forceDebug) console.log(`[debug] Brave spoofing enabled for ${currentUrl}`);
1688
601
 
1689
602
  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,
603
+ try {
604
+ Object.defineProperty(navigator, 'brave', {
605
+ get: () => ({
606
+ isBrave: () => Promise.resolve(true),
607
+ setBadge: () => {},
608
+ clearBadge: () => {},
609
+ getAdBlockEnabled: () => Promise.resolve(true),
610
+ getShieldsEnabled: () => Promise.resolve(true)
611
+ }),
612
+ configurable: true
613
+ });
614
+
615
+ if (navigator.userAgent && !navigator.userAgent.includes('Brave')) {
616
+ Object.defineProperty(navigator, 'userAgent', {
617
+ get: () => navigator.userAgent.replace('Chrome/', 'Brave/').replace('Safari/537.36', 'Safari/537.36 Brave/1.60'),
1704
618
  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;
619
+ });
1714
620
  }
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
- });
621
+ } catch (err) {
622
+ if (debugEnabled) console.log(`[fingerprint] Brave spoofing error: ${err.message}`);
1733
623
  }
1734
624
  }, forceDebug);
1735
625
  }
1736
626
 
1737
627
  /**
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>}
628
+ * Enhanced fingerprint protection with realistic spoofing
1745
629
  */
1746
630
  async function applyFingerprintProtection(page, siteConfig, forceDebug, currentUrl) {
1747
631
  const fingerprintSetting = siteConfig.fingerprint_protection;
1748
632
  if (!fingerprintSetting) return;
1749
633
 
1750
- if (forceDebug) console.log(`[debug] Enhanced fingerprint_protection enabled for ${currentUrl}`);
634
+ if (forceDebug) console.log(`[debug] Fingerprint protection enabled for ${currentUrl}`);
1751
635
 
1752
636
  const spoof = fingerprintSetting === 'random' ? getRandomFingerprint() : {
1753
637
  deviceMemory: 8,
@@ -1763,254 +647,93 @@ async function applyFingerprintProtection(page, siteConfig, forceDebug, currentU
1763
647
  try {
1764
648
  await page.evaluateOnNewDocument(({ spoof, debugEnabled }) => {
1765
649
 
1766
- // Use local versions of helper functions for this context
1767
650
  function safeDefinePropertyLocal(target, property, descriptor) {
1768
651
  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
- }
652
+ const existing = Object.getOwnPropertyDescriptor(target, property);
653
+ if (existing?.configurable === false) return false;
1777
654
 
1778
- const safeDescriptor = {
655
+ Object.defineProperty(target, property, {
1779
656
  ...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();
657
+ configurable: true
658
+ });
1797
659
  return true;
1798
- } catch (spoofErr) {
1799
- if (debugEnabled) {
1800
- console.log(`[fingerprint] ${description} failed: ${spoofErr.message}`);
1801
- }
660
+ } catch (err) {
661
+ if (debugEnabled) console.log(`[fingerprint] Failed to define ${property}: ${err.message}`);
1802
662
  return false;
1803
663
  }
1804
664
  }
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
665
 
666
+ // Platform spoofing
667
+ safeDefinePropertyLocal(navigator, 'platform', { get: () => spoof.platform });
1849
668
 
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;
669
+ // Memory spoofing
670
+ safeDefinePropertyLocal(navigator, 'deviceMemory', { get: () => spoof.deviceMemory });
671
+ safeDefinePropertyLocal(navigator, 'hardwareConcurrency', { get: () => spoof.hardwareConcurrency });
672
+
673
+ // Screen properties spoofing
674
+ ['width', 'height', 'availWidth', 'availHeight', 'colorDepth', 'pixelDepth'].forEach(prop => {
675
+ if (spoof.screen[prop] !== undefined) {
676
+ safeDefinePropertyLocal(window.screen, prop, { get: () => spoof.screen[prop] });
1874
677
  }
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
- };
678
+ });
679
+
680
+ // Language spoofing
681
+ const languages = Array.isArray(spoof.language) ? spoof.language : [spoof.language, spoof.language.split('-')[0]];
682
+ safeDefinePropertyLocal(navigator, 'languages', { get: () => languages });
683
+ safeDefinePropertyLocal(navigator, 'language', { get: () => languages[0] });
684
+
685
+ // Timezone spoofing
686
+ if (spoof.timezone && window.Intl?.DateTimeFormat) {
687
+ const OriginalDateTimeFormat = window.Intl.DateTimeFormat;
688
+ window.Intl.DateTimeFormat = function(...args) {
689
+ const instance = new OriginalDateTimeFormat(...args);
690
+ const originalResolvedOptions = instance.resolvedOptions;
1908
691
 
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
- }
692
+ instance.resolvedOptions = function() {
693
+ const opts = originalResolvedOptions.call(this);
694
+ opts.timeZone = spoof.timezone;
695
+ return opts;
696
+ };
697
+ return instance;
698
+ };
699
+ Object.setPrototypeOf(window.Intl.DateTimeFormat, OriginalDateTimeFormat);
700
+
701
+ // Timezone offset spoofing
702
+ const timezoneOffsets = {
703
+ 'America/New_York': 300,
704
+ 'America/Los_Angeles': 480,
705
+ 'Europe/London': 0,
706
+ 'America/Chicago': 360
707
+ };
1925
708
 
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');
709
+ const originalGetTimezoneOffset = Date.prototype.getTimezoneOffset;
710
+ Date.prototype.getTimezoneOffset = function() {
711
+ return timezoneOffsets[spoof.timezone] || originalGetTimezoneOffset.call(this);
712
+ };
713
+ }
1967
714
 
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');
715
+ // Cookie and DNT spoofing
716
+ if (spoof.cookieEnabled !== undefined) {
717
+ safeDefinePropertyLocal(navigator, 'cookieEnabled', { get: () => spoof.cookieEnabled });
718
+ }
719
+ if (spoof.doNotTrack !== undefined) {
720
+ safeDefinePropertyLocal(navigator, 'doNotTrack', { get: () => spoof.doNotTrack });
721
+ }
1982
722
 
1983
723
  }, { spoof, debugEnabled: forceDebug });
1984
724
  } catch (err) {
1985
- console.warn(`[enhanced fingerprint spoof failed] ${currentUrl}: ${err.message}`);
725
+ console.warn(`[fingerprint protection failed] ${currentUrl}: ${err.message}`);
1986
726
  }
1987
727
  }
1988
728
 
1989
729
  /**
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>}
730
+ * Simulate human-like behavior
1995
731
  */
1996
732
  async function simulateHumanBehavior(page, forceDebug) {
1997
733
  try {
1998
734
  await page.evaluateOnNewDocument((debugEnabled) => {
1999
735
 
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(() => {
736
+ try {
2014
737
  let mouseX = Math.random() * window.innerWidth;
2015
738
  let mouseY = Math.random() * window.innerHeight;
2016
739
 
@@ -2027,74 +750,41 @@ async function simulateHumanBehavior(page, forceDebug) {
2027
750
  clientY: mouseY,
2028
751
  bubbles: true
2029
752
  }));
2030
- } catch (mouseErr) {
2031
- // Ignore mouse event errors
2032
- }
753
+ } catch (e) {}
2033
754
  }, 1000 + Math.random() * 2000);
2034
755
 
2035
- // Simulate occasional clicks and scrolls with safe handling
2036
- setTimeout(() => {
2037
- try {
2038
- if (Math.random() > 0.7) {
2039
- document.dispatchEvent(new MouseEvent('click', {
2040
- clientX: mouseX,
2041
- clientY: mouseY,
2042
- bubbles: true
2043
- }));
2044
- }
2045
-
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);
2054
-
2055
- // Stop simulation after 30 seconds to avoid detection
756
+ // Stop after 30 seconds
2056
757
  setTimeout(() => {
2057
- try {
2058
- clearInterval(moveInterval);
2059
- } catch (clearErr) {
2060
- // Ignore clear errors
2061
- }
758
+ try { clearInterval(moveInterval); } catch (e) {}
2062
759
  }, 30000);
2063
- }, 'human behavior simulation');
760
+
761
+ } catch (err) {
762
+ if (debugEnabled) console.log(`[fingerprint] Human behavior simulation failed: ${err.message}`);
763
+ }
2064
764
 
2065
765
  }, forceDebug);
2066
766
  } catch (err) {
2067
- if (forceDebug) console.log(`[debug] Human behavior simulation failed: ${err.message}`);
767
+ if (forceDebug) console.log(`[debug] Human behavior simulation setup failed: ${err.message}`);
2068
768
  }
2069
769
  }
2070
770
 
2071
771
  /**
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>}
772
+ * Main function that applies all fingerprint spoofing techniques
2079
773
  */
2080
774
  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}`);
775
+
776
+ const techniques = [
777
+ { fn: applyUserAgentSpoofing, name: 'User agent spoofing' },
778
+ { fn: applyBraveSpoofing, name: 'Brave spoofing' },
779
+ { fn: applyFingerprintProtection, name: 'Fingerprint protection' }
780
+ ];
781
+
782
+ for (const { fn, name } of techniques) {
783
+ try {
784
+ await fn(page, siteConfig, forceDebug, currentUrl);
785
+ } catch (err) {
786
+ if (forceDebug) console.log(`[debug] ${name} failed for ${currentUrl}: ${err.message}`);
787
+ }
2098
788
  }
2099
789
 
2100
790
  // Add human behavior simulation if user agent spoofing is enabled
@@ -2107,6 +797,12 @@ async function applyAllFingerprintSpoofing(page, siteConfig, forceDebug, current
2107
797
  }
2108
798
  }
2109
799
 
800
+ // Legacy compatibility function - maintained for backwards compatibility
801
+ function safeExecuteSpoofing(spoofFunction, description, forceDebug = false) {
802
+ return safeSpoofingExecution(spoofFunction, description, { debug: forceDebug });
803
+ }
804
+
805
+
2110
806
  module.exports = {
2111
807
  getRandomFingerprint,
2112
808
  getRealisticScreenResolution,
@@ -2116,7 +812,10 @@ module.exports = {
2116
812
  applyAllFingerprintSpoofing,
2117
813
  simulateHumanBehavior,
2118
814
  safeDefineProperty,
2119
- safeExecuteSpoofing,
815
+ safeExecuteSpoofing, // Legacy compatibility
816
+ safeSpoofingExecution,
817
+ createFingerprintMocks,
818
+ applyTimezoneSpoofing,
2120
819
  DEFAULT_PLATFORM,
2121
820
  DEFAULT_TIMEZONE
2122
821
  };