crawlforge-mcp-server 3.0.0

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 (75) hide show
  1. package/CLAUDE.md +315 -0
  2. package/LICENSE +21 -0
  3. package/README.md +181 -0
  4. package/package.json +115 -0
  5. package/server.js +1963 -0
  6. package/setup.js +112 -0
  7. package/src/constants/config.js +615 -0
  8. package/src/core/ActionExecutor.js +1104 -0
  9. package/src/core/AlertNotificationSystem.js +601 -0
  10. package/src/core/AuthManager.js +315 -0
  11. package/src/core/ChangeTracker.js +2306 -0
  12. package/src/core/JobManager.js +687 -0
  13. package/src/core/LLMsTxtAnalyzer.js +753 -0
  14. package/src/core/LocalizationManager.js +1615 -0
  15. package/src/core/PerformanceManager.js +828 -0
  16. package/src/core/ResearchOrchestrator.js +1327 -0
  17. package/src/core/SnapshotManager.js +1037 -0
  18. package/src/core/StealthBrowserManager.js +1795 -0
  19. package/src/core/WebhookDispatcher.js +745 -0
  20. package/src/core/analysis/ContentAnalyzer.js +749 -0
  21. package/src/core/analysis/LinkAnalyzer.js +972 -0
  22. package/src/core/cache/CacheManager.js +821 -0
  23. package/src/core/connections/ConnectionPool.js +553 -0
  24. package/src/core/crawlers/BFSCrawler.js +845 -0
  25. package/src/core/integrations/PerformanceIntegration.js +377 -0
  26. package/src/core/llm/AnthropicProvider.js +135 -0
  27. package/src/core/llm/LLMManager.js +415 -0
  28. package/src/core/llm/LLMProvider.js +97 -0
  29. package/src/core/llm/OpenAIProvider.js +127 -0
  30. package/src/core/processing/BrowserProcessor.js +986 -0
  31. package/src/core/processing/ContentProcessor.js +505 -0
  32. package/src/core/processing/PDFProcessor.js +448 -0
  33. package/src/core/processing/StreamProcessor.js +673 -0
  34. package/src/core/queue/QueueManager.js +98 -0
  35. package/src/core/workers/WorkerPool.js +585 -0
  36. package/src/core/workers/worker.js +743 -0
  37. package/src/monitoring/healthCheck.js +600 -0
  38. package/src/monitoring/metrics.js +761 -0
  39. package/src/optimization/wave3-optimizations.js +932 -0
  40. package/src/security/security-patches.js +120 -0
  41. package/src/security/security-tests.js +355 -0
  42. package/src/security/wave3-security.js +652 -0
  43. package/src/tools/advanced/BatchScrapeTool.js +1089 -0
  44. package/src/tools/advanced/ScrapeWithActionsTool.js +669 -0
  45. package/src/tools/crawl/crawlDeep.js +449 -0
  46. package/src/tools/crawl/mapSite.js +400 -0
  47. package/src/tools/extract/analyzeContent.js +624 -0
  48. package/src/tools/extract/extractContent.js +329 -0
  49. package/src/tools/extract/processDocument.js +503 -0
  50. package/src/tools/extract/summarizeContent.js +376 -0
  51. package/src/tools/llmstxt/generateLLMsTxt.js +570 -0
  52. package/src/tools/research/deepResearch.js +706 -0
  53. package/src/tools/search/adapters/duckduckgoSearch.js +398 -0
  54. package/src/tools/search/adapters/googleSearch.js +236 -0
  55. package/src/tools/search/adapters/searchProviderFactory.js +96 -0
  56. package/src/tools/search/queryExpander.js +543 -0
  57. package/src/tools/search/ranking/ResultDeduplicator.js +676 -0
  58. package/src/tools/search/ranking/ResultRanker.js +497 -0
  59. package/src/tools/search/searchWeb.js +482 -0
  60. package/src/tools/tracking/trackChanges.js +1355 -0
  61. package/src/utils/CircuitBreaker.js +515 -0
  62. package/src/utils/ErrorHandlingConfig.js +342 -0
  63. package/src/utils/HumanBehaviorSimulator.js +569 -0
  64. package/src/utils/Logger.js +568 -0
  65. package/src/utils/MemoryMonitor.js +173 -0
  66. package/src/utils/RetryManager.js +386 -0
  67. package/src/utils/contentUtils.js +588 -0
  68. package/src/utils/domainFilter.js +612 -0
  69. package/src/utils/inputValidation.js +766 -0
  70. package/src/utils/rateLimiter.js +196 -0
  71. package/src/utils/robotsChecker.js +91 -0
  72. package/src/utils/securityMiddleware.js +416 -0
  73. package/src/utils/sitemapParser.js +678 -0
  74. package/src/utils/ssrfProtection.js +640 -0
  75. package/src/utils/urlNormalizer.js +168 -0
@@ -0,0 +1,1795 @@
1
+ /**
2
+ * StealthBrowserManager - Comprehensive Anti-detection browser management
3
+ * Phase 2.2 Features:
4
+ * - Advanced browser fingerprint randomization (User Agent, Canvas, WebGL, Screen, Plugins)
5
+ * - Human behavior simulation (Bezier mouse movements, realistic typing, scroll patterns)
6
+ * - Anti-detection features (CloudFlare bypass, reCAPTCHA handling, proxy rotation)
7
+ * - WebRTC leak prevention and automation indicator hiding
8
+ * - Stealth mode robust enough to bypass common bot detection services
9
+ */
10
+
11
+ import { chromium } from 'playwright';
12
+ import { z } from 'zod';
13
+ import crypto from 'crypto';
14
+ import HumanBehaviorSimulator from '../utils/HumanBehaviorSimulator.js';
15
+
16
+ const StealthConfigSchema = z.object({
17
+ level: z.enum(['basic', 'medium', 'advanced']).default('medium'),
18
+ randomizeFingerprint: z.boolean().default(true),
19
+ hideWebDriver: z.boolean().default(true),
20
+ blockWebRTC: z.boolean().default(true),
21
+ spoofTimezone: z.boolean().default(true),
22
+ randomizeHeaders: z.boolean().default(true),
23
+ useRandomUserAgent: z.boolean().default(true),
24
+ simulateHumanBehavior: z.boolean().default(true),
25
+ customUserAgent: z.string().optional(),
26
+ customViewport: z.object({
27
+ width: z.number().min(800).max(1920),
28
+ height: z.number().min(600).max(1080)
29
+ }).optional(),
30
+ locale: z.string().default('en-US'),
31
+ timezone: z.string().optional(),
32
+ webRTCPublicIP: z.string().optional(),
33
+ webRTCLocalIPs: z.array(z.string()).optional(),
34
+
35
+ // Advanced stealth options
36
+ proxyRotation: z.object({
37
+ enabled: z.boolean().default(false),
38
+ proxies: z.array(z.string()).optional(),
39
+ rotationInterval: z.number().default(300000) // 5 minutes
40
+ }).optional(),
41
+
42
+ antiDetection: z.object({
43
+ cloudflareBypass: z.boolean().default(true),
44
+ recaptchaHandling: z.boolean().default(true),
45
+ hideAutomation: z.boolean().default(true),
46
+ spoofMediaDevices: z.boolean().default(true),
47
+ spoofBatteryAPI: z.boolean().default(true)
48
+ }).optional(),
49
+
50
+ fingerprinting: z.object({
51
+ canvasNoise: z.boolean().default(true),
52
+ webglSpoofing: z.boolean().default(true),
53
+ audioContextSpoofing: z.boolean().default(true),
54
+ fontSpoofing: z.boolean().default(true),
55
+ hardwareSpoofing: z.boolean().default(true)
56
+ }).optional()
57
+ });
58
+
59
+ export class StealthBrowserManager {
60
+ constructor(options = {}) {
61
+ this.browser = null;
62
+ this.contexts = new Map();
63
+ this.fingerprints = new Map();
64
+
65
+ // Enhanced stealth components
66
+ this.humanBehaviorSimulator = null;
67
+ this.proxyManager = {
68
+ currentProxy: null,
69
+ proxyIndex: 0,
70
+ lastRotation: 0,
71
+ activeProxies: []
72
+ };
73
+
74
+ // Detection bypass cache
75
+ this.bypassCache = new Map();
76
+
77
+ // Canvas fingerprint cache to maintain consistency
78
+ this.canvasCache = new Map();
79
+
80
+ // Performance monitoring
81
+ this.performanceMetrics = {
82
+ detectionAttempts: 0,
83
+ successfulBypasses: 0,
84
+ failedBypasses: 0,
85
+ averageResponseTime: 0
86
+ };
87
+
88
+ // Default stealth configuration
89
+ this.defaultConfig = {
90
+ level: 'medium',
91
+ randomizeFingerprint: true,
92
+ hideWebDriver: true,
93
+ blockWebRTC: true,
94
+ spoofTimezone: true,
95
+ randomizeHeaders: true,
96
+ useRandomUserAgent: true,
97
+ simulateHumanBehavior: true,
98
+ locale: 'en-US',
99
+ proxyRotation: { enabled: false },
100
+ antiDetection: {
101
+ cloudflareBypass: true,
102
+ recaptchaHandling: true,
103
+ hideAutomation: true,
104
+ spoofMediaDevices: true,
105
+ spoofBatteryAPI: true
106
+ },
107
+ fingerprinting: {
108
+ canvasNoise: true,
109
+ webglSpoofing: true,
110
+ audioContextSpoofing: true,
111
+ fontSpoofing: true,
112
+ hardwareSpoofing: true
113
+ }
114
+ };
115
+
116
+ // Enhanced User agent pools with realistic patterns
117
+ this.userAgentPools = {
118
+ chrome: {
119
+ windows: [
120
+ 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36',
121
+ 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
122
+ 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36',
123
+ 'Mozilla/5.0 (Windows NT 11.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36'
124
+ ],
125
+ macos: [
126
+ 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36',
127
+ 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
128
+ 'Mozilla/5.0 (Macintosh; Intel Mac OS X 13_6_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36'
129
+ ],
130
+ linux: [
131
+ 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36',
132
+ 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
133
+ ]
134
+ },
135
+ firefox: {
136
+ windows: [
137
+ 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:122.0) Gecko/20100101 Firefox/122.0',
138
+ 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:121.0) Gecko/20100101 Firefox/121.0'
139
+ ],
140
+ macos: [
141
+ 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:122.0) Gecko/20100101 Firefox/122.0',
142
+ 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:121.0) Gecko/20100101 Firefox/121.0'
143
+ ],
144
+ linux: [
145
+ 'Mozilla/5.0 (X11; Linux x86_64; rv:122.0) Gecko/20100101 Firefox/122.0',
146
+ 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:122.0) Gecko/20100101 Firefox/122.0'
147
+ ]
148
+ },
149
+ safari: {
150
+ macos: [
151
+ 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.2 Safari/605.1.15',
152
+ 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.1 Safari/605.1.15'
153
+ ]
154
+ }
155
+ };
156
+
157
+ // Operating system distributions for realistic user agent selection
158
+ this.osDistribution = {
159
+ windows: 0.75,
160
+ macos: 0.15,
161
+ linux: 0.10
162
+ };
163
+
164
+ // Browser market share for realistic selection
165
+ this.browserDistribution = {
166
+ chrome: 0.65,
167
+ firefox: 0.20,
168
+ safari: 0.15
169
+ };
170
+
171
+ // Realistic viewport sizes with market distribution
172
+ this.viewportSizes = [
173
+ { width: 1920, height: 1080, weight: 0.25 }, // Most common
174
+ { width: 1366, height: 768, weight: 0.20 }, // Second most common
175
+ { width: 1536, height: 864, weight: 0.15 },
176
+ { width: 1440, height: 900, weight: 0.12 },
177
+ { width: 1280, height: 720, weight: 0.10 },
178
+ { width: 1600, height: 900, weight: 0.08 },
179
+ { width: 1024, height: 768, weight: 0.05 }, // Legacy but still used
180
+ { width: 2560, height: 1440, weight: 0.03 }, // High-res displays
181
+ { width: 3840, height: 2160, weight: 0.02 } // 4K displays
182
+ ];
183
+
184
+ // Mobile viewport sizes for mobile emulation
185
+ this.mobileViewportSizes = [
186
+ { width: 375, height: 667, weight: 0.25 }, // iPhone SE/8
187
+ { width: 414, height: 896, weight: 0.20 }, // iPhone 11/XR
188
+ { width: 390, height: 844, weight: 0.15 }, // iPhone 12/13/14
189
+ { width: 360, height: 640, weight: 0.15 }, // Android common
190
+ { width: 412, height: 915, weight: 0.10 }, // Pixel
191
+ { width: 393, height: 851, weight: 0.10 }, // Pixel 7
192
+ { width: 320, height: 568, weight: 0.05 } // iPhone 5s (legacy)
193
+ ];
194
+
195
+ // Timezone options
196
+ this.timezones = [
197
+ 'America/New_York',
198
+ 'America/Los_Angeles',
199
+ 'America/Chicago',
200
+ 'America/Denver',
201
+ 'Europe/London',
202
+ 'Europe/Berlin',
203
+ 'Europe/Paris',
204
+ 'Europe/Madrid',
205
+ 'Asia/Tokyo',
206
+ 'Asia/Shanghai',
207
+ 'Asia/Seoul',
208
+ 'Australia/Sydney',
209
+ 'Australia/Melbourne'
210
+ ];
211
+
212
+ // WebRTC leak prevention IPs
213
+ this.webRTCIPs = [
214
+ '192.168.1.1',
215
+ '192.168.0.1',
216
+ '10.0.0.1',
217
+ '172.16.0.1',
218
+ '172.16.1.1'
219
+ ];
220
+ }
221
+
222
+ /**
223
+ * Launch stealth browser with anti-detection configurations
224
+ */
225
+ async launchStealthBrowser(config = {}) {
226
+ if (this.browser) {
227
+ return this.browser;
228
+ }
229
+
230
+ const validatedConfig = StealthConfigSchema.parse({ ...this.defaultConfig, ...config });
231
+
232
+ // Base browser args for stealth
233
+ const stealthArgs = [
234
+ '--no-sandbox',
235
+ '--disable-dev-shm-usage',
236
+ '--disable-blink-features=AutomationControlled',
237
+ '--disable-web-security',
238
+ '--disable-features=VizDisplayCompositor',
239
+ '--disable-extensions',
240
+ '--disable-plugins',
241
+ '--disable-background-timer-throttling',
242
+ '--disable-backgrounding-occluded-windows',
243
+ '--disable-renderer-backgrounding',
244
+ '--disable-field-trial-config',
245
+ '--disable-back-forward-cache',
246
+ '--disable-hang-monitor',
247
+ '--disable-prompt-on-repost',
248
+ '--disable-sync',
249
+ '--disable-translate',
250
+ '--metrics-recording-only',
251
+ '--no-first-run',
252
+ '--safebrowsing-disable-auto-update',
253
+ '--password-store=basic',
254
+ '--use-mock-keychain',
255
+
256
+ // Additional stealth arguments
257
+ '--disable-default-apps',
258
+ '--disable-component-extensions-with-background-pages',
259
+ '--disable-background-networking',
260
+ '--disable-component-update',
261
+ '--disable-client-side-phishing-detection',
262
+ '--disable-domain-reliability',
263
+ '--disable-ipc-flooding-protection',
264
+ '--no-default-browser-check',
265
+ '--no-pings',
266
+ '--disable-notifications'
267
+ ];
268
+
269
+ // Advanced stealth args based on level
270
+ if (validatedConfig.level === 'advanced') {
271
+ stealthArgs.push(
272
+ '--disable-gpu-sandbox',
273
+ '--disable-popup-blocking',
274
+ '--disable-setuid-sandbox',
275
+ '--disable-site-isolation-trials',
276
+ '--disable-threaded-animation',
277
+ '--disable-threaded-scrolling',
278
+ '--disable-in-process-stack-traces',
279
+ '--disable-histogram-customizer',
280
+ '--disable-gl-extensions',
281
+ '--disable-composited-antialiasing',
282
+ '--disable-canvas-aa',
283
+ '--disable-3d-apis',
284
+ '--disable-accelerated-2d-canvas',
285
+ '--disable-accelerated-jpeg-decoding',
286
+ '--disable-accelerated-mjpeg-decode',
287
+ '--disable-app-list-dismiss-on-blur',
288
+ '--disable-accelerated-video-decode',
289
+ '--disable-logging',
290
+ '--silent'
291
+ );
292
+ }
293
+
294
+ // WebRTC blocking
295
+ if (validatedConfig.blockWebRTC) {
296
+ stealthArgs.push(
297
+ '--disable-webrtc-hw-decoding',
298
+ '--disable-webrtc-hw-encoding',
299
+ '--disable-webrtc-multiple-routes',
300
+ '--disable-webrtc-hw-vp8-encoding',
301
+ '--enforce-webrtc-ip-permission-check'
302
+ );
303
+ }
304
+
305
+ // Handle proxy configuration
306
+ const currentProxy = await this.rotateProxy(validatedConfig);
307
+ if (currentProxy) {
308
+ stealthArgs.push(`--proxy-server=${currentProxy}`);
309
+ }
310
+
311
+ this.browser = await chromium.launch({
312
+ headless: true,
313
+ args: stealthArgs,
314
+ ignoreDefaultArgs: [
315
+ '--enable-blink-features=IdleDetection',
316
+ '--enable-automation'
317
+ ]
318
+ });
319
+
320
+ return this.browser;
321
+ }
322
+
323
+ /**
324
+ * Create stealth browser context with anti-fingerprinting
325
+ */
326
+ async createStealthContext(config = {}) {
327
+ const validatedConfig = StealthConfigSchema.parse({ ...this.defaultConfig, ...config });
328
+
329
+ if (!this.browser) {
330
+ await this.launchStealthBrowser(validatedConfig);
331
+ }
332
+
333
+ // Generate fingerprint for this context
334
+ const fingerprint = this.generateAdvancedFingerprint(validatedConfig);
335
+
336
+ const contextOptions = {
337
+ viewport: fingerprint.viewport,
338
+ userAgent: fingerprint.userAgent,
339
+ locale: validatedConfig.locale,
340
+ timezoneId: fingerprint.timezone,
341
+ deviceScaleFactor: fingerprint.deviceScaleFactor,
342
+ isMobile: fingerprint.isMobile,
343
+ hasTouch: fingerprint.hasTouch,
344
+ colorScheme: fingerprint.colorScheme,
345
+ reducedMotion: fingerprint.reducedMotion,
346
+ forcedColors: fingerprint.forcedColors,
347
+ extraHTTPHeaders: fingerprint.headers,
348
+
349
+ // Geolocation spoofing
350
+ geolocation: fingerprint.geolocation,
351
+ permissions: ['geolocation'],
352
+
353
+ // Media spoofing
354
+ screen: {
355
+ width: fingerprint.screen.width,
356
+ height: fingerprint.screen.height
357
+ },
358
+
359
+ // Bypass various detections
360
+ bypassCSP: true,
361
+ javaScriptEnabled: true
362
+ };
363
+
364
+ const context = await this.browser.newContext(contextOptions);
365
+ const contextId = this.generateContextId();
366
+
367
+ // Apply stealth scripts and configurations
368
+ await this.applyAdvancedStealthConfigurations(context, validatedConfig, fingerprint);
369
+
370
+ this.contexts.set(contextId, { context, fingerprint, config: validatedConfig });
371
+ this.fingerprints.set(contextId, fingerprint);
372
+
373
+ return { context, contextId, fingerprint };
374
+ }
375
+
376
+ /**
377
+ * Generate advanced browser fingerprint with enhanced randomization
378
+ */
379
+ generateAdvancedFingerprint(config = {}) {
380
+ const fingerprint = {
381
+ userAgent: this.selectRealisticUserAgent(config),
382
+ viewport: config.customViewport || this.selectWeightedViewport(),
383
+ timezone: config.timezone || this.selectTimezone(),
384
+ deviceScaleFactor: this.randomFloat(1, 2, 1),
385
+ isMobile: Math.random() < 0.1, // 10% mobile
386
+ hasTouch: Math.random() < 0.15, // 15% touch
387
+ colorScheme: Math.random() < 0.3 ? 'dark' : 'light',
388
+ reducedMotion: Math.random() < 0.1 ? 'reduce' : 'no-preference',
389
+ forcedColors: Math.random() < 0.05 ? 'active' : 'none',
390
+ headers: this.generateAdvancedHeaders(config),
391
+ webRTC: this.generateWebRTCConfig(config),
392
+ canvas: this.generateAdvancedCanvasFingerprint(),
393
+ webGL: this.generateAdvancedWebGLFingerprint(),
394
+ audioContext: this.generateAudioContextFingerprint(),
395
+ mediaDevices: this.generateMediaDevicesFingerprint(),
396
+ hardware: this.generateHardwareFingerprint(),
397
+ fonts: this.generateAdvancedFontList(),
398
+ plugins: this.generateAdvancedPluginList(),
399
+ geolocation: this.generateRealisticGeolocation(),
400
+ screen: this.generateAdvancedScreenProperties(),
401
+ battery: this.generateBatteryFingerprint()
402
+ };
403
+
404
+ return fingerprint;
405
+ }
406
+
407
+ /**
408
+ * Select realistic user agent based on market distribution
409
+ */
410
+ selectRealisticUserAgent(config) {
411
+ if (config.customUserAgent) {
412
+ return config.customUserAgent;
413
+ }
414
+
415
+ if (!config.useRandomUserAgent) {
416
+ return this.userAgentPools.chrome.windows[0];
417
+ }
418
+
419
+ // Select OS based on distribution
420
+ const selectedOS = this.weightedRandom(this.osDistribution);
421
+
422
+ // Select browser based on distribution and OS compatibility
423
+ let availableBrowsers = { ...this.browserDistribution };
424
+ if (selectedOS === 'linux' && availableBrowsers.safari) {
425
+ delete availableBrowsers.safari;
426
+ // Redistribute safari's weight
427
+ availableBrowsers.chrome += 0.075;
428
+ availableBrowsers.firefox += 0.075;
429
+ }
430
+
431
+ const selectedBrowser = this.weightedRandom(availableBrowsers);
432
+ const pool = this.userAgentPools[selectedBrowser][selectedOS];
433
+
434
+ if (!pool || pool.length === 0) {
435
+ // Fallback to Chrome Windows
436
+ return this.userAgentPools.chrome.windows[0];
437
+ }
438
+
439
+ return pool[Math.floor(Math.random() * pool.length)];
440
+ }
441
+
442
+ /**
443
+ * Select viewport size based on weights
444
+ */
445
+ selectWeightedViewport() {
446
+ return this.weightedRandomFromArray(this.viewportSizes);
447
+ }
448
+
449
+ /**
450
+ * Select timezone
451
+ */
452
+ selectTimezone() {
453
+ return this.timezones[Math.floor(Math.random() * this.timezones.length)];
454
+ }
455
+
456
+ /**
457
+ * Generate advanced HTTP headers with realistic patterns
458
+ */
459
+ generateAdvancedHeaders(config) {
460
+ const headers = {
461
+ 'Accept-Language': `${(config.locale || 'en-US').toLowerCase()},en;q=0.9`,
462
+ 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
463
+ 'Accept-Encoding': 'gzip, deflate, br',
464
+ 'Cache-Control': 'max-age=0',
465
+ 'Upgrade-Insecure-Requests': '1',
466
+ 'Sec-Fetch-Dest': 'document',
467
+ 'Sec-Fetch-Mode': 'navigate',
468
+ 'Sec-Fetch-Site': 'none',
469
+ 'Sec-Fetch-User': '?1',
470
+ 'sec-ch-ua-mobile': '?0',
471
+ 'sec-ch-ua-platform': this.generateSecChUaPlatform()
472
+ };
473
+
474
+ // Add sec-ch-ua header
475
+ headers['sec-ch-ua'] = this.generateSecChUaHeader();
476
+
477
+ // Randomize some headers
478
+ if (Math.random() < 0.25) {
479
+ headers['DNT'] = '1';
480
+ }
481
+
482
+ if (Math.random() < 0.6) {
483
+ headers['Connection'] = 'keep-alive';
484
+ }
485
+
486
+ // Add Save-Data header occasionally
487
+ if (Math.random() < 0.1) {
488
+ headers['Save-Data'] = 'on';
489
+ }
490
+
491
+ return headers;
492
+ }
493
+
494
+ /**
495
+ * Generate sec-ch-ua header
496
+ */
497
+ generateSecChUaHeader() {
498
+ const brands = [
499
+ { brand: 'Not_A Brand', version: '8' },
500
+ { brand: 'Chromium', version: '120' },
501
+ { brand: 'Google Chrome', version: '120' }
502
+ ];
503
+
504
+ return brands
505
+ .map(b => `"${b.brand}";v="${b.version}"`)
506
+ .join(', ');
507
+ }
508
+
509
+ /**
510
+ * Generate sec-ch-ua-platform header
511
+ */
512
+ generateSecChUaPlatform() {
513
+ const platforms = {
514
+ windows: '"Windows"',
515
+ macos: '"macOS"',
516
+ linux: '"Linux"'
517
+ };
518
+
519
+ const selectedOS = this.weightedRandom(this.osDistribution);
520
+ return platforms[selectedOS] || '"Windows"';
521
+ }
522
+
523
+ /**
524
+ * Generate WebRTC configuration for leak prevention
525
+ */
526
+ generateWebRTCConfig(config) {
527
+ return {
528
+ publicIP: config.webRTCPublicIP || '192.168.1.' + Math.floor(Math.random() * 255),
529
+ localIPs: config.webRTCLocalIPs || [
530
+ '192.168.1.' + Math.floor(Math.random() * 255),
531
+ '10.0.0.' + Math.floor(Math.random() * 255)
532
+ ]
533
+ };
534
+ }
535
+
536
+ /**
537
+ * Advanced Canvas fingerprinting protection with noise injection
538
+ */
539
+ generateAdvancedCanvasFingerprint() {
540
+ const seed = crypto.randomBytes(16).toString('hex');
541
+
542
+ return {
543
+ seed,
544
+ noisePattern: this.generateCanvasNoise(seed),
545
+ textMetrics: {
546
+ width: this.randomFloat(45, 210, 3),
547
+ height: this.randomFloat(8, 35, 3),
548
+ actualBoundingBoxLeft: this.randomFloat(-2, 5, 3),
549
+ actualBoundingBoxRight: this.randomFloat(50, 200, 3),
550
+ actualBoundingBoxAscent: this.randomFloat(10, 25, 3),
551
+ actualBoundingBoxDescent: this.randomFloat(2, 8, 3)
552
+ },
553
+ imageData: this.generateCanvasImageData(seed)
554
+ };
555
+ }
556
+
557
+ /**
558
+ * Generate consistent canvas noise based on seed
559
+ */
560
+ generateCanvasNoise(seed) {
561
+ const noise = [];
562
+ let seedNum = parseInt(seed.substring(0, 8), 16);
563
+
564
+ for (let i = 0; i < 100; i++) {
565
+ seedNum = (seedNum * 9301 + 49297) % 233280;
566
+ noise.push((seedNum / 233280) * 2 - 1); // -1 to 1
567
+ }
568
+
569
+ return noise;
570
+ }
571
+
572
+ /**
573
+ * Generate canvas image data with controlled randomness
574
+ */
575
+ generateCanvasImageData(seed) {
576
+ const hash = crypto.createHash('md5').update(seed).digest('hex');
577
+ return {
578
+ checksum: hash.substring(0, 16),
579
+ variance: parseFloat('0.' + hash.substring(16, 24)),
580
+ pixelShift: parseInt(hash.substring(24, 26), 16) % 3
581
+ };
582
+ }
583
+
584
+ /**
585
+ * Enhanced WebGL fingerprinting with realistic spoofing
586
+ */
587
+ generateAdvancedWebGLFingerprint() {
588
+ const gpuVendors = [
589
+ { vendor: 'Google Inc. (NVIDIA)', renderer: 'ANGLE (NVIDIA, NVIDIA GeForce GTX 1060 6GB Direct3D11 vs_5_0 ps_5_0, D3D11)' },
590
+ { vendor: 'Google Inc. (NVIDIA)', renderer: 'ANGLE (NVIDIA, NVIDIA GeForce RTX 3070 Direct3D11 vs_5_0 ps_5_0, D3D11)' },
591
+ { vendor: 'Google Inc. (Intel)', renderer: 'ANGLE (Intel, Intel(R) HD Graphics 630 Direct3D11 vs_5_0 ps_5_0, D3D11)' },
592
+ { vendor: 'Google Inc. (AMD)', renderer: 'ANGLE (AMD, AMD Radeon RX 580 Series Direct3D11 vs_5_0 ps_5_0, D3D11)' },
593
+ { vendor: 'Google Inc. (Intel)', renderer: 'ANGLE (Intel, Intel(R) Iris(R) Xe Graphics Direct3D11 vs_5_0 ps_5_0, D3D11)' }
594
+ ];
595
+
596
+ const selectedGpu = gpuVendors[Math.floor(Math.random() * gpuVendors.length)];
597
+
598
+ return {
599
+ vendor: selectedGpu.vendor,
600
+ renderer: selectedGpu.renderer,
601
+ version: 'WebGL 1.0 (OpenGL ES 2.0 Chromium)',
602
+ shadingLanguageVersion: 'WebGL GLSL ES 1.0 (OpenGL ES GLSL ES 1.0 Chromium)',
603
+ extensions: this.generateWebGLExtensions(),
604
+ parameters: this.generateWebGLParameters(),
605
+ supportedFormats: this.generateWebGLFormats()
606
+ };
607
+ }
608
+
609
+ /**
610
+ * Generate realistic WebGL extensions list
611
+ */
612
+ generateWebGLExtensions() {
613
+ const baseExtensions = [
614
+ 'ANGLE_instanced_arrays',
615
+ 'EXT_blend_minmax',
616
+ 'EXT_color_buffer_half_float',
617
+ 'EXT_disjoint_timer_query',
618
+ 'EXT_float_blend',
619
+ 'EXT_frag_depth',
620
+ 'EXT_shader_texture_lod',
621
+ 'EXT_texture_compression_rgtc',
622
+ 'EXT_texture_filter_anisotropic',
623
+ 'EXT_sRGB',
624
+ 'OES_texture_float',
625
+ 'OES_texture_float_linear',
626
+ 'OES_texture_half_float',
627
+ 'OES_texture_half_float_linear',
628
+ 'OES_vertex_array_object',
629
+ 'WEBKIT_EXT_texture_filter_anisotropic',
630
+ 'WEBKIT_WEBGL_depth_texture'
631
+ ];
632
+
633
+ const optionalExtensions = [
634
+ 'EXT_color_buffer_float',
635
+ 'EXT_texture_compression_bptc',
636
+ 'EXT_texture_norm16',
637
+ 'OES_draw_buffers_indexed',
638
+ 'WEBGL_color_buffer_float',
639
+ 'WEBGL_compressed_texture_s3tc',
640
+ 'WEBGL_debug_renderer_info',
641
+ 'WEBGL_debug_shaders',
642
+ 'WEBGL_depth_texture',
643
+ 'WEBGL_draw_buffers',
644
+ 'WEBGL_lose_context'
645
+ ];
646
+
647
+ const extensions = [...baseExtensions];
648
+
649
+ // Randomly include optional extensions (60-90% chance each)
650
+ optionalExtensions.forEach(ext => {
651
+ if (Math.random() < 0.6 + Math.random() * 0.3) {
652
+ extensions.push(ext);
653
+ }
654
+ });
655
+
656
+ return extensions.sort();
657
+ }
658
+
659
+ /**
660
+ * Generate WebGL parameters with realistic values
661
+ */
662
+ generateWebGLParameters() {
663
+ return {
664
+ MAX_TEXTURE_SIZE: 16384,
665
+ MAX_CUBE_MAP_TEXTURE_SIZE: 16384,
666
+ MAX_RENDERBUFFER_SIZE: 16384,
667
+ MAX_VERTEX_ATTRIBS: 16,
668
+ MAX_VERTEX_UNIFORM_VECTORS: 1024,
669
+ MAX_FRAGMENT_UNIFORM_VECTORS: 1024,
670
+ MAX_VARYING_VECTORS: 30,
671
+ MAX_COMBINED_TEXTURE_IMAGE_UNITS: 32,
672
+ MAX_VERTEX_TEXTURE_IMAGE_UNITS: 16,
673
+ MAX_TEXTURE_IMAGE_UNITS: 16,
674
+ MAX_VIEWPORT_DIMS: [16384, 16384],
675
+ ALIASED_LINE_WIDTH_RANGE: [1, 1],
676
+ ALIASED_POINT_SIZE_RANGE: [1, 1024]
677
+ };
678
+ }
679
+
680
+ /**
681
+ * Generate WebGL supported formats
682
+ */
683
+ generateWebGLFormats() {
684
+ return {
685
+ textureFormats: ['RGB', 'RGBA', 'LUMINANCE', 'LUMINANCE_ALPHA', 'ALPHA'],
686
+ compressedFormats: ['COMPRESSED_RGB_S3TC_DXT1_EXT', 'COMPRESSED_RGBA_S3TC_DXT5_EXT'],
687
+ depthFormats: ['DEPTH_COMPONENT16', 'DEPTH_STENCIL'],
688
+ pixelTypes: ['UNSIGNED_BYTE', 'UNSIGNED_SHORT_4_4_4_4', 'UNSIGNED_SHORT_5_5_5_1', 'UNSIGNED_SHORT_5_6_5']
689
+ };
690
+ }
691
+
692
+ /**
693
+ * Advanced audio context spoofing
694
+ */
695
+ generateAudioContextFingerprint() {
696
+ return {
697
+ sampleRate: 44100 + Math.floor(Math.random() * 2000), // Slight variation
698
+ baseLatency: this.randomFloat(0.005, 0.02, 6),
699
+ outputLatency: this.randomFloat(0.01, 0.05, 6),
700
+ maxChannelCount: 2 + Math.floor(Math.random() * 6), // 2-8 channels
701
+ numberOfInputs: Math.floor(Math.random() * 2) + 1,
702
+ numberOfOutputs: Math.floor(Math.random() * 2) + 1,
703
+ channelCount: 2,
704
+ channelCountMode: 'max',
705
+ channelInterpretation: 'speakers'
706
+ };
707
+ }
708
+
709
+ /**
710
+ * Enhanced media devices spoofing
711
+ */
712
+ generateMediaDevicesFingerprint() {
713
+ const videoDevices = [
714
+ { deviceId: crypto.randomUUID(), kind: 'videoinput', label: 'HD Pro Webcam C920 (046d:082d)', groupId: crypto.randomUUID() },
715
+ { deviceId: crypto.randomUUID(), kind: 'videoinput', label: 'FaceTime HD Camera', groupId: crypto.randomUUID() },
716
+ { deviceId: crypto.randomUUID(), kind: 'videoinput', label: 'Integrated Camera', groupId: crypto.randomUUID() }
717
+ ];
718
+
719
+ const audioDevices = [
720
+ { deviceId: crypto.randomUUID(), kind: 'audioinput', label: 'Default - Internal Microphone', groupId: crypto.randomUUID() },
721
+ { deviceId: crypto.randomUUID(), kind: 'audiooutput', label: 'Default - Internal Speakers', groupId: crypto.randomUUID() },
722
+ { deviceId: crypto.randomUUID(), kind: 'audioinput', label: 'Communications - Internal Microphone', groupId: crypto.randomUUID() }
723
+ ];
724
+
725
+ // Randomly select devices
726
+ const selectedDevices = [];
727
+ if (Math.random() < 0.8) selectedDevices.push(videoDevices[Math.floor(Math.random() * videoDevices.length)]);
728
+ if (Math.random() < 0.9) selectedDevices.push(...audioDevices.slice(0, Math.floor(Math.random() * audioDevices.length) + 1));
729
+
730
+ return selectedDevices;
731
+ }
732
+
733
+ /**
734
+ * Generate realistic hardware fingerprint
735
+ */
736
+ generateHardwareFingerprint() {
737
+ const processors = [
738
+ { cores: 4, threads: 8, name: 'Intel(R) Core(TM) i5-8250U CPU @ 1.60GHz' },
739
+ { cores: 6, threads: 12, name: 'Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz' },
740
+ { cores: 8, threads: 16, name: 'Intel(R) Core(TM) i7-10700K CPU @ 3.80GHz' },
741
+ { cores: 4, threads: 4, name: 'Intel(R) Core(TM) i5-7400 CPU @ 3.00GHz' },
742
+ { cores: 6, threads: 6, name: 'AMD Ryzen 5 3600 6-Core Processor' },
743
+ { cores: 8, threads: 16, name: 'AMD Ryzen 7 3700X 8-Core Processor' }
744
+ ];
745
+
746
+ const selectedProcessor = processors[Math.floor(Math.random() * processors.length)];
747
+
748
+ return {
749
+ hardwareConcurrency: selectedProcessor.threads,
750
+ processor: selectedProcessor.name,
751
+ architecture: Math.random() < 0.9 ? 'x86_64' : 'arm64',
752
+ memory: Math.floor(Math.random() * 24) + 8, // 8-32 GB
753
+ deviceMemory: Math.pow(2, Math.floor(Math.random() * 3) + 3), // 8, 16, or 32 GB
754
+ platform: this.selectRealisticPlatform()
755
+ };
756
+ }
757
+
758
+ /**
759
+ * Select realistic platform based on distribution
760
+ */
761
+ selectRealisticPlatform() {
762
+ const platforms = {
763
+ 'Win32': 0.75,
764
+ 'MacIntel': 0.15,
765
+ 'Linux x86_64': 0.08,
766
+ 'Linux armv7l': 0.02
767
+ };
768
+
769
+ return this.weightedRandom(platforms);
770
+ }
771
+
772
+ /**
773
+ * Generate advanced font list with realistic variation
774
+ */
775
+ generateAdvancedFontList() {
776
+ const baseFonts = [
777
+ 'Arial', 'Helvetica', 'Times New Roman', 'Courier New', 'Verdana',
778
+ 'Georgia', 'Palatino', 'Garamond', 'Bookman', 'Tahoma', 'Geneva'
779
+ ];
780
+
781
+ const systemFonts = {
782
+ windows: ['Segoe UI', 'Calibri', 'Consolas', 'Cambria', 'Candara'],
783
+ macos: ['SF Pro Display', 'Helvetica Neue', 'Menlo', 'Avenir', 'Optima'],
784
+ linux: ['Ubuntu', 'DejaVu Sans', 'Liberation Sans', 'Noto Sans', 'Source Sans Pro']
785
+ };
786
+
787
+ const additionalFonts = [
788
+ 'Comic Sans MS', 'Trebuchet MS', 'Arial Black', 'Impact',
789
+ 'Lucida Sans Unicode', 'Franklin Gothic Medium', 'Arial Narrow'
790
+ ];
791
+
792
+ // Start with base fonts
793
+ const fonts = [...baseFonts];
794
+
795
+ // Add system-specific fonts based on platform
796
+ const platform = this.selectRealisticPlatform();
797
+ let osKey = 'windows';
798
+ if (platform.includes('Mac')) osKey = 'macos';
799
+ else if (platform.includes('Linux')) osKey = 'linux';
800
+
801
+ systemFonts[osKey].forEach(font => {
802
+ if (Math.random() < 0.8) { // 80% chance to include
803
+ fonts.push(font);
804
+ }
805
+ });
806
+
807
+ // Randomly include additional fonts
808
+ additionalFonts.forEach(font => {
809
+ if (Math.random() < 0.6) {
810
+ fonts.push(font);
811
+ }
812
+ });
813
+
814
+ return fonts.sort();
815
+ }
816
+
817
+ /**
818
+ * Generate advanced plugin list
819
+ */
820
+ generateAdvancedPluginList() {
821
+ const plugins = [];
822
+
823
+ // Chrome PDF Plugin (almost always present)
824
+ if (Math.random() < 0.95) {
825
+ plugins.push({
826
+ name: 'Chrome PDF Plugin',
827
+ filename: 'internal-pdf-viewer',
828
+ description: 'Portable Document Format',
829
+ version: '1'
830
+ });
831
+ }
832
+
833
+ // Chrome PDF Viewer
834
+ if (Math.random() < 0.8) {
835
+ plugins.push({
836
+ name: 'Chrome PDF Viewer',
837
+ filename: 'mhjfbmdgcfjbbpaeojofohoefgiehjai',
838
+ description: 'Portable Document Format',
839
+ version: '1'
840
+ });
841
+ }
842
+
843
+ // Native Client
844
+ if (Math.random() < 0.3) {
845
+ plugins.push({
846
+ name: 'Native Client',
847
+ filename: 'internal-nacl-plugin',
848
+ description: 'Native Client Executable',
849
+ version: '1'
850
+ });
851
+ }
852
+
853
+ return plugins;
854
+ }
855
+
856
+ /**
857
+ * Generate realistic geolocation data
858
+ */
859
+ generateRealisticGeolocation() {
860
+ // Random coordinates in major cities with realistic distribution
861
+ const cities = [
862
+ { latitude: 40.7128, longitude: -74.0060, weight: 0.15 }, // New York
863
+ { latitude: 34.0522, longitude: -118.2437, weight: 0.12 }, // Los Angeles
864
+ { latitude: 51.5074, longitude: -0.1278, weight: 0.10 }, // London
865
+ { latitude: 48.8566, longitude: 2.3522, weight: 0.08 }, // Paris
866
+ { latitude: 35.6762, longitude: 139.6503, weight: 0.07 }, // Tokyo
867
+ { latitude: -33.8688, longitude: 151.2093, weight: 0.05 }, // Sydney
868
+ { latitude: 52.5200, longitude: 13.4050, weight: 0.05 }, // Berlin
869
+ { latitude: 37.7749, longitude: -122.4194, weight: 0.08 }, // San Francisco
870
+ { latitude: 41.8781, longitude: -87.6298, weight: 0.06 }, // Chicago
871
+ { latitude: 55.7558, longitude: 37.6176, weight: 0.04 }, // Moscow
872
+ { latitude: 39.9042, longitude: 116.4074, weight: 0.06 }, // Beijing
873
+ { latitude: 28.6139, longitude: 77.2090, weight: 0.05 }, // Delhi
874
+ { latitude: -23.5505, longitude: -46.6333, weight: 0.04 }, // São Paulo
875
+ { latitude: 19.4326, longitude: -99.1332, weight: 0.05 } // Mexico City
876
+ ];
877
+
878
+ const city = this.weightedRandomFromArray(cities);
879
+
880
+ return {
881
+ latitude: city.latitude + (Math.random() - 0.5) * 0.05, // ±0.025 degrees (~2.8km)
882
+ longitude: city.longitude + (Math.random() - 0.5) * 0.05,
883
+ accuracy: Math.floor(Math.random() * 50) + 20 // 20-70m accuracy
884
+ };
885
+ }
886
+
887
+ /**
888
+ * Generate advanced screen properties
889
+ */
890
+ generateAdvancedScreenProperties() {
891
+ const viewport = this.selectWeightedViewport();
892
+
893
+ return {
894
+ width: viewport.width,
895
+ height: viewport.height,
896
+ availWidth: viewport.width,
897
+ availHeight: viewport.height - (30 + Math.floor(Math.random() * 20)), // Account for taskbar
898
+ colorDepth: Math.random() < 0.95 ? 24 : 32,
899
+ pixelDepth: 24,
900
+ orientation: {
901
+ angle: 0,
902
+ type: 'landscape-primary'
903
+ }
904
+ };
905
+ }
906
+
907
+ /**
908
+ * Generate battery API fingerprint
909
+ */
910
+ generateBatteryFingerprint() {
911
+ return {
912
+ charging: Math.random() < 0.7, // 70% chance charging
913
+ chargingTime: Math.random() < 0.3 ? Math.floor(Math.random() * 7200) : Infinity,
914
+ dischargingTime: Math.random() < 0.7 ? Math.floor(Math.random() * 28800) + 3600 : Infinity, // 1-9 hours
915
+ level: Math.random() * 0.7 + 0.2 // 20-90%
916
+ };
917
+ }
918
+
919
+ /**
920
+ * Apply advanced stealth configurations to browser context
921
+ */
922
+ async applyAdvancedStealthConfigurations(context, config, fingerprint) {
923
+ // Enhanced initialization script with comprehensive stealth measures
924
+ await context.addInitScript(() => {
925
+ // Remove webdriver property completely
926
+ Object.defineProperty(navigator, 'webdriver', {
927
+ get: () => undefined,
928
+ configurable: true
929
+ });
930
+
931
+ // Hide automation indicators
932
+ delete window.navigator.__proto__.webdriver;
933
+ delete window.navigator.webdriver;
934
+ delete window.webdriver;
935
+ delete window._phantom;
936
+ delete window.__nightmare;
937
+ delete window._selenium;
938
+
939
+ // Override chrome runtime
940
+ if (!window.chrome) {
941
+ window.chrome = {};
942
+ }
943
+ window.chrome.runtime = {
944
+ onConnect: undefined,
945
+ onMessage: undefined,
946
+ connect: undefined,
947
+ sendMessage: undefined
948
+ };
949
+
950
+ // Override permissions API
951
+ const originalQuery = window.navigator.permissions.query;
952
+ window.navigator.permissions.query = (parameters) => (
953
+ parameters.name === 'notifications' ?
954
+ Promise.resolve({ state: Notification.permission }) :
955
+ originalQuery(parameters)
956
+ );
957
+
958
+ // Hide headless indicators
959
+ Object.defineProperty(navigator, 'hardwareConcurrency', {
960
+ get: () => 4
961
+ });
962
+
963
+ // Spoof connection
964
+ Object.defineProperty(navigator, 'connection', {
965
+ get: () => ({
966
+ effectiveType: '4g',
967
+ rtt: 50 + Math.random() * 50,
968
+ downlink: 10,
969
+ saveData: false
970
+ })
971
+ });
972
+
973
+ // Override plugin array
974
+ Object.defineProperty(navigator, 'plugins', {
975
+ get: function() {
976
+ const plugins = [
977
+ {
978
+ 0: {
979
+ type: "application/x-google-chrome-pdf",
980
+ suffixes: "pdf",
981
+ description: "Portable Document Format"
982
+ },
983
+ description: "Portable Document Format",
984
+ filename: "internal-pdf-viewer",
985
+ length: 1,
986
+ name: "Chrome PDF Plugin"
987
+ }
988
+ ];
989
+ plugins.item = function(index) { return this[index] || null; };
990
+ plugins.namedItem = function(name) {
991
+ return this.find(plugin => plugin.name === name) || null;
992
+ };
993
+ return plugins;
994
+ }
995
+ });
996
+
997
+ // Override languages with realistic patterns
998
+ Object.defineProperty(navigator, 'languages', {
999
+ get: function() {
1000
+ return ['en-US', 'en'];
1001
+ }
1002
+ });
1003
+
1004
+ // Mock battery API with realistic values
1005
+ Object.defineProperty(navigator, 'getBattery', {
1006
+ get: function() {
1007
+ return function() {
1008
+ return Promise.resolve({
1009
+ charging: true,
1010
+ chargingTime: 0,
1011
+ dischargingTime: Infinity,
1012
+ level: 0.8 + Math.random() * 0.19 // 80-99%
1013
+ });
1014
+ };
1015
+ }
1016
+ });
1017
+
1018
+ // Override Date.prototype.getTimezoneOffset if timezone spoofing is enabled
1019
+ if (window.stealthTimezone) {
1020
+ const originalGetTimezoneOffset = Date.prototype.getTimezoneOffset;
1021
+ Date.prototype.getTimezoneOffset = function() {
1022
+ // Return offset for spoofed timezone
1023
+ const timezoneOffsets = {
1024
+ 'America/New_York': 300,
1025
+ 'America/Los_Angeles': 480,
1026
+ 'Europe/London': 0,
1027
+ 'Asia/Tokyo': -540
1028
+ };
1029
+ return timezoneOffsets[window.stealthTimezone] || originalGetTimezoneOffset.call(this);
1030
+ };
1031
+ }
1032
+
1033
+ // Modify Error.prepareStackTrace to hide automation
1034
+ if (Error.prepareStackTrace) {
1035
+ const originalPrepareStackTrace = Error.prepareStackTrace;
1036
+ Error.prepareStackTrace = function(error, stack) {
1037
+ const filteredStack = stack.filter(frame => {
1038
+ const frameString = frame.toString();
1039
+ return !frameString.includes('puppeteer') &&
1040
+ !frameString.includes('playwright') &&
1041
+ !frameString.includes('selenium');
1042
+ });
1043
+ return originalPrepareStackTrace.call(this, error, filteredStack);
1044
+ };
1045
+ }
1046
+ });
1047
+
1048
+ // WebRTC leak prevention with advanced spoofing
1049
+ if (config.blockWebRTC) {
1050
+ await context.addInitScript((webrtcConfig) => {
1051
+ // Override RTCPeerConnection
1052
+ const originalRTCPeerConnection = window.RTCPeerConnection ||
1053
+ window.webkitRTCPeerConnection ||
1054
+ window.mozRTCPeerConnection;
1055
+
1056
+ if (originalRTCPeerConnection) {
1057
+ const StealthRTCPeerConnection = function(...args) {
1058
+ const pc = new originalRTCPeerConnection(...args);
1059
+
1060
+ const originalCreateOffer = pc.createOffer;
1061
+ pc.createOffer = function(...offerArgs) {
1062
+ return originalCreateOffer.apply(this, offerArgs).then(offer => {
1063
+ // Modify SDP to use fake IP
1064
+ offer.sdp = offer.sdp.replace(
1065
+ /c=IN IP4 .*\r\n/g,
1066
+ 'c=IN IP4 ' + webrtcConfig.publicIP + '\r\n'
1067
+ );
1068
+ return offer;
1069
+ });
1070
+ };
1071
+
1072
+ return pc;
1073
+ };
1074
+
1075
+ StealthRTCPeerConnection.prototype = originalRTCPeerConnection.prototype;
1076
+ window.RTCPeerConnection = StealthRTCPeerConnection;
1077
+ window.webkitRTCPeerConnection = StealthRTCPeerConnection;
1078
+ }
1079
+ }, fingerprint.webRTC);
1080
+ }
1081
+
1082
+ // Advanced canvas fingerprinting protection
1083
+ if (config.fingerprinting?.canvasNoise) {
1084
+ await context.addInitScript((canvasConfig) => {
1085
+ const getContext = HTMLCanvasElement.prototype.getContext;
1086
+ HTMLCanvasElement.prototype.getContext = function(contextType, contextAttributes) {
1087
+ const ctx = getContext.call(this, contextType, contextAttributes);
1088
+
1089
+ if (contextType === '2d') {
1090
+ const originalToDataURL = this.toDataURL;
1091
+ this.toDataURL = function(...args) {
1092
+ // Add controlled noise based on seed
1093
+ const imageData = ctx.getImageData(0, 0, this.width, this.height);
1094
+ const noise = canvasConfig.noisePattern;
1095
+
1096
+ for (let i = 0; i < imageData.data.length; i += 4) {
1097
+ const noiseIndex = i % noise.length;
1098
+ const noiseValue = noise[noiseIndex] * canvasConfig.imageData.pixelShift;
1099
+
1100
+ imageData.data[i] = Math.min(255, Math.max(0, imageData.data[i] + noiseValue));
1101
+ imageData.data[i + 1] = Math.min(255, Math.max(0, imageData.data[i + 1] + noiseValue));
1102
+ imageData.data[i + 2] = Math.min(255, Math.max(0, imageData.data[i + 2] + noiseValue));
1103
+ }
1104
+
1105
+ ctx.putImageData(imageData, 0, 0);
1106
+ return originalToDataURL.apply(this, args);
1107
+ };
1108
+ }
1109
+
1110
+ return ctx;
1111
+ };
1112
+ }, fingerprint.canvas);
1113
+ }
1114
+
1115
+ // WebGL spoofing
1116
+ if (config.fingerprinting?.webglSpoofing) {
1117
+ await context.addInitScript((webglConfig) => {
1118
+ const getContext = HTMLCanvasElement.prototype.getContext;
1119
+ HTMLCanvasElement.prototype.getContext = function(contextType, contextAttributes) {
1120
+ const ctx = getContext.call(this, contextType, contextAttributes);
1121
+
1122
+ if (contextType === 'webgl' || contextType === 'experimental-webgl') {
1123
+ const originalGetParameter = ctx.getParameter;
1124
+ ctx.getParameter = function(parameter) {
1125
+ // Spoof specific WebGL parameters
1126
+ if (parameter === ctx.RENDERER) {
1127
+ return webglConfig.renderer;
1128
+ }
1129
+ if (parameter === ctx.VENDOR) {
1130
+ return webglConfig.vendor;
1131
+ }
1132
+ if (parameter === ctx.VERSION) {
1133
+ return webglConfig.version;
1134
+ }
1135
+ if (parameter === ctx.SHADING_LANGUAGE_VERSION) {
1136
+ return webglConfig.shadingLanguageVersion;
1137
+ }
1138
+
1139
+ return originalGetParameter.call(this, parameter);
1140
+ };
1141
+
1142
+ const originalGetExtension = ctx.getExtension;
1143
+ ctx.getExtension = function(name) {
1144
+ if (webglConfig.extensions.includes(name)) {
1145
+ return originalGetExtension.call(this, name) || {};
1146
+ }
1147
+ return null;
1148
+ };
1149
+ }
1150
+
1151
+ return ctx;
1152
+ };
1153
+ }, fingerprint.webGL);
1154
+ }
1155
+
1156
+ // Audio context spoofing
1157
+ if (config.fingerprinting?.audioContextSpoofing) {
1158
+ await context.addInitScript((audioConfig) => {
1159
+ const OriginalAudioContext = window.AudioContext || window.webkitAudioContext;
1160
+
1161
+ if (OriginalAudioContext) {
1162
+ window.AudioContext = function(...args) {
1163
+ const ctx = new OriginalAudioContext(...args);
1164
+
1165
+ Object.defineProperty(ctx, 'sampleRate', {
1166
+ get: () => audioConfig.sampleRate
1167
+ });
1168
+
1169
+ Object.defineProperty(ctx, 'baseLatency', {
1170
+ get: () => audioConfig.baseLatency
1171
+ });
1172
+
1173
+ Object.defineProperty(ctx, 'outputLatency', {
1174
+ get: () => audioConfig.outputLatency
1175
+ });
1176
+
1177
+ return ctx;
1178
+ };
1179
+
1180
+ if (window.webkitAudioContext) {
1181
+ window.webkitAudioContext = window.AudioContext;
1182
+ }
1183
+ }
1184
+ }, fingerprint.audioContext);
1185
+ }
1186
+
1187
+ // Media devices spoofing
1188
+ if (config.antiDetection?.spoofMediaDevices) {
1189
+ await context.addInitScript((mediaDevices) => {
1190
+ if (navigator.mediaDevices && navigator.mediaDevices.enumerateDevices) {
1191
+ const originalEnumerateDevices = navigator.mediaDevices.enumerateDevices;
1192
+ navigator.mediaDevices.enumerateDevices = function() {
1193
+ return Promise.resolve(mediaDevices);
1194
+ };
1195
+ }
1196
+ }, fingerprint.mediaDevices);
1197
+ }
1198
+
1199
+ // Hardware spoofing
1200
+ if (config.fingerprinting?.hardwareSpoofing) {
1201
+ await context.addInitScript((hardware) => {
1202
+ Object.defineProperty(navigator, 'hardwareConcurrency', {
1203
+ get: () => hardware.hardwareConcurrency
1204
+ });
1205
+
1206
+ Object.defineProperty(navigator, 'platform', {
1207
+ get: () => hardware.platform
1208
+ });
1209
+
1210
+ if (navigator.deviceMemory !== undefined) {
1211
+ Object.defineProperty(navigator, 'deviceMemory', {
1212
+ get: () => hardware.deviceMemory
1213
+ });
1214
+ }
1215
+ }, fingerprint.hardware);
1216
+ }
1217
+
1218
+ // Font spoofing
1219
+ if (config.fingerprinting?.fontSpoofing) {
1220
+ await context.addInitScript((fonts) => {
1221
+ // Override font detection methods
1222
+ const originalMeasureText = CanvasRenderingContext2D.prototype.measureText;
1223
+ CanvasRenderingContext2D.prototype.measureText = function(text) {
1224
+ const result = originalMeasureText.call(this, text);
1225
+
1226
+ // Add slight variations to font measurements
1227
+ const variance = 0.1 + Math.random() * 0.1;
1228
+ return {
1229
+ width: result.width * variance,
1230
+ actualBoundingBoxLeft: result.actualBoundingBoxLeft || 0,
1231
+ actualBoundingBoxRight: result.actualBoundingBoxRight || result.width,
1232
+ fontBoundingBoxAscent: result.fontBoundingBoxAscent || 10,
1233
+ fontBoundingBoxDescent: result.fontBoundingBoxDescent || 2,
1234
+ actualBoundingBoxAscent: result.actualBoundingBoxAscent || 8,
1235
+ actualBoundingBoxDescent: result.actualBoundingBoxDescent || 2,
1236
+ emHeightAscent: result.emHeightAscent || 8,
1237
+ emHeightDescent: result.emHeightDescent || 2,
1238
+ hangingBaseline: result.hangingBaseline || 6,
1239
+ alphabeticBaseline: result.alphabeticBaseline || 0,
1240
+ ideographicBaseline: result.ideographicBaseline || -2
1241
+ };
1242
+ };
1243
+ }, fingerprint.fonts);
1244
+ }
1245
+
1246
+ // Screen resolution spoofing
1247
+ await context.addInitScript((screenConfig) => {
1248
+ Object.defineProperties(screen, {
1249
+ width: { value: screenConfig.width, configurable: true },
1250
+ height: { value: screenConfig.height, configurable: true },
1251
+ availWidth: { value: screenConfig.availWidth, configurable: true },
1252
+ availHeight: { value: screenConfig.availHeight, configurable: true },
1253
+ colorDepth: { value: screenConfig.colorDepth, configurable: true },
1254
+ pixelDepth: { value: screenConfig.pixelDepth, configurable: true }
1255
+ });
1256
+ }, fingerprint.screen);
1257
+
1258
+ // Timezone spoofing
1259
+ if (config.spoofTimezone) {
1260
+ await context.addInitScript((timezone) => {
1261
+ window.stealthTimezone = timezone;
1262
+
1263
+ // Override Intl.DateTimeFormat
1264
+ const originalDateTimeFormat = Intl.DateTimeFormat;
1265
+ Intl.DateTimeFormat = function(locales, options = {}) {
1266
+ if (!options.timeZone) {
1267
+ options.timeZone = timezone;
1268
+ }
1269
+ return new originalDateTimeFormat(locales, options);
1270
+ };
1271
+
1272
+ // Override Date methods
1273
+ const originalToLocaleDateString = Date.prototype.toLocaleDateString;
1274
+ Date.prototype.toLocaleDateString = function(locales, options = {}) {
1275
+ if (!options.timeZone) {
1276
+ options.timeZone = timezone;
1277
+ }
1278
+ return originalToLocaleDateString.call(this, locales, options);
1279
+ };
1280
+ }, fingerprint.timezone);
1281
+ }
1282
+
1283
+ // Battery API spoofing
1284
+ if (config.antiDetection?.spoofBatteryAPI) {
1285
+ await context.addInitScript((battery) => {
1286
+ if (navigator.getBattery) {
1287
+ navigator.getBattery = function() {
1288
+ return Promise.resolve(battery);
1289
+ };
1290
+ }
1291
+ }, fingerprint.battery);
1292
+ }
1293
+ }
1294
+
1295
+ /**
1296
+ * Enhanced human behavior simulation using dedicated simulator
1297
+ */
1298
+ async initializeHumanBehaviorSimulator(config = {}) {
1299
+ if (!this.humanBehaviorSimulator) {
1300
+ this.humanBehaviorSimulator = new HumanBehaviorSimulator({
1301
+ mouseMovements: {
1302
+ enabled: true,
1303
+ speed: 'normal',
1304
+ accuracy: 0.85,
1305
+ naturalCurves: true,
1306
+ randomMicroMovements: true
1307
+ },
1308
+ typing: {
1309
+ enabled: true,
1310
+ speed: 'normal',
1311
+ variability: 0.3,
1312
+ mistakes: {
1313
+ enabled: true,
1314
+ frequency: 0.015, // 1.5% mistake rate
1315
+ correctionDelay: 600
1316
+ }
1317
+ },
1318
+ scrolling: {
1319
+ enabled: true,
1320
+ naturalAcceleration: true,
1321
+ randomPauses: true,
1322
+ scrollBackProbability: 0.12
1323
+ },
1324
+ interactions: {
1325
+ hoverBeforeClick: true,
1326
+ clickDelay: { min: 120, max: 350 },
1327
+ focusBlurSimulation: true,
1328
+ idlePeriods: {
1329
+ enabled: true,
1330
+ frequency: 0.08,
1331
+ minDuration: 800,
1332
+ maxDuration: 3500
1333
+ }
1334
+ },
1335
+ ...config
1336
+ });
1337
+ }
1338
+ return this.humanBehaviorSimulator;
1339
+ }
1340
+
1341
+ /**
1342
+ * Advanced CloudFlare detection and bypass
1343
+ */
1344
+ async bypassCloudflareChallenge(page) {
1345
+ try {
1346
+ this.performanceMetrics.detectionAttempts++;
1347
+
1348
+ // Wait for potential challenge page
1349
+ await page.waitForTimeout(2000);
1350
+
1351
+ // Check for CloudFlare challenge indicators
1352
+ const challengeDetected = await page.evaluate(() => {
1353
+ const indicators = [
1354
+ 'cf-browser-verification',
1355
+ 'cf-challenge-running',
1356
+ 'Checking your browser',
1357
+ 'DDoS protection by Cloudflare',
1358
+ 'Ray ID'
1359
+ ];
1360
+
1361
+ const pageText = document.body.innerText;
1362
+ return indicators.some(indicator => pageText.includes(indicator));
1363
+ });
1364
+
1365
+ if (challengeDetected) {
1366
+ console.log('CloudFlare challenge detected, attempting bypass...');
1367
+
1368
+ // Simulate human behavior during challenge
1369
+ if (this.humanBehaviorSimulator) {
1370
+ await this.humanBehaviorSimulator.simulateIdlePeriod();
1371
+
1372
+ // Random mouse movements during challenge
1373
+ const viewport = await page.viewportSize();
1374
+ for (let i = 0; i < 3; i++) {
1375
+ const x = Math.random() * viewport.width;
1376
+ const y = Math.random() * viewport.height;
1377
+ await this.humanBehaviorSimulator.simulateMouseMovement(
1378
+ page, x - 50, y - 50, x, y
1379
+ );
1380
+ await this.humanBehaviorSimulator.delay(1000, 0.3);
1381
+ }
1382
+ }
1383
+
1384
+ // Wait for challenge to complete (up to 30 seconds)
1385
+ await page.waitForFunction(() => {
1386
+ const indicators = [
1387
+ 'cf-browser-verification',
1388
+ 'cf-challenge-running',
1389
+ 'Checking your browser'
1390
+ ];
1391
+ const pageText = document.body.innerText;
1392
+ return !indicators.some(indicator => pageText.includes(indicator));
1393
+ }, { timeout: 30000 }).catch(() => {});
1394
+
1395
+ this.performanceMetrics.successfulBypasses++;
1396
+ return true;
1397
+ }
1398
+
1399
+ return false;
1400
+ } catch (error) {
1401
+ this.performanceMetrics.failedBypasses++;
1402
+ console.warn('CloudFlare bypass failed:', error.message);
1403
+ return false;
1404
+ }
1405
+ }
1406
+
1407
+ /**
1408
+ * Enhanced reCAPTCHA detection and handling
1409
+ */
1410
+ async handleRecaptcha(page) {
1411
+ try {
1412
+ // Check for reCAPTCHA elements
1413
+ const recaptchaDetected = await page.evaluate(() => {
1414
+ const recaptchaElements = [
1415
+ '.g-recaptcha',
1416
+ '#recaptcha',
1417
+ '[data-sitekey]',
1418
+ 'iframe[src*="recaptcha"]'
1419
+ ];
1420
+
1421
+ return recaptchaElements.some(selector =>
1422
+ document.querySelector(selector) !== null
1423
+ );
1424
+ });
1425
+
1426
+ if (recaptchaDetected) {
1427
+ console.log('reCAPTCHA detected, implementing human behavior...');
1428
+
1429
+ // Simulate human inspection of the reCAPTCHA
1430
+ if (this.humanBehaviorSimulator) {
1431
+ // Look around the page naturally
1432
+ await this.humanBehaviorSimulator.simulateReadingTime(page, 'body');
1433
+
1434
+ // Hover over the reCAPTCHA area
1435
+ try {
1436
+ const recaptchaBox = await page.$('.g-recaptcha, #recaptcha, [data-sitekey]');
1437
+ if (recaptchaBox) {
1438
+ const boundingBox = await recaptchaBox.boundingBox();
1439
+ if (boundingBox) {
1440
+ await this.humanBehaviorSimulator.simulateMouseMovement(
1441
+ page,
1442
+ boundingBox.x - 100,
1443
+ boundingBox.y - 100,
1444
+ boundingBox.x + boundingBox.width / 2,
1445
+ boundingBox.y + boundingBox.height / 2
1446
+ );
1447
+ await this.humanBehaviorSimulator.delay(2000, 0.4);
1448
+ }
1449
+ }
1450
+ } catch (error) {
1451
+ console.warn('reCAPTCHA interaction failed:', error.message);
1452
+ }
1453
+ }
1454
+
1455
+ return true;
1456
+ }
1457
+
1458
+ return false;
1459
+ } catch (error) {
1460
+ console.warn('reCAPTCHA handling failed:', error.message);
1461
+ return false;
1462
+ }
1463
+ }
1464
+
1465
+ /**
1466
+ * Proxy rotation management
1467
+ */
1468
+ async rotateProxy(config) {
1469
+ if (!config.proxyRotation?.enabled || !config.proxyRotation?.proxies?.length) {
1470
+ return null;
1471
+ }
1472
+
1473
+ const now = Date.now();
1474
+ const { rotationInterval, proxies } = config.proxyRotation;
1475
+
1476
+ if (now - this.proxyManager.lastRotation > rotationInterval) {
1477
+ this.proxyManager.proxyIndex = (this.proxyManager.proxyIndex + 1) % proxies.length;
1478
+ this.proxyManager.currentProxy = proxies[this.proxyManager.proxyIndex];
1479
+ this.proxyManager.lastRotation = now;
1480
+
1481
+ console.log('Rotated to proxy:', this.proxyManager.currentProxy);
1482
+ }
1483
+
1484
+ return this.proxyManager.currentProxy;
1485
+ }
1486
+
1487
+ /**
1488
+ * Create stealth page with anti-detection measures
1489
+ */
1490
+ async createStealthPage(contextId) {
1491
+ const contextData = this.contexts.get(contextId);
1492
+ if (!contextData) {
1493
+ throw new Error('Context not found');
1494
+ }
1495
+
1496
+ const page = await contextData.context.newPage();
1497
+
1498
+ // Apply additional page-level stealth measures
1499
+ await this.applyPageStealthMeasures(page, contextData.config, contextData.fingerprint);
1500
+
1501
+ return page;
1502
+ }
1503
+
1504
+ /**
1505
+ * Apply page-level stealth measures
1506
+ */
1507
+ async applyPageStealthMeasures(page, config, fingerprint) {
1508
+ // Enhanced resource blocking with stealth considerations
1509
+ await page.route('**/*', route => {
1510
+ const resourceType = route.request().resourceType();
1511
+ const url = route.request().url();
1512
+
1513
+ // Block known bot detection resources
1514
+ const blockedDomains = [
1515
+ 'botd.fpjs.io',
1516
+ 'challenges.cloudflare.com',
1517
+ 'datadome.co',
1518
+ 'perimeterx.net',
1519
+ 'distilnetworks.com'
1520
+ ];
1521
+
1522
+ if (blockedDomains.some(domain => url.includes(domain))) {
1523
+ route.abort();
1524
+ return;
1525
+ }
1526
+
1527
+ // Don't block detection-related resources that might be expected
1528
+ if (url.includes('webdriver') || url.includes('selenium') || url.includes('puppeteer')) {
1529
+ route.abort();
1530
+ return;
1531
+ }
1532
+
1533
+ // Selective resource blocking based on level
1534
+ if (config.level === 'advanced') {
1535
+ if (['image', 'font', 'stylesheet'].includes(resourceType)) {
1536
+ // Allow some images/fonts to maintain realism
1537
+ if (Math.random() < 0.3) {
1538
+ route.continue();
1539
+ } else {
1540
+ route.abort();
1541
+ }
1542
+ } else {
1543
+ route.continue();
1544
+ }
1545
+ } else {
1546
+ route.continue();
1547
+ }
1548
+ });
1549
+
1550
+ // Add request headers
1551
+ await page.setExtraHTTPHeaders(fingerprint.headers);
1552
+
1553
+ // Emulate realistic network conditions
1554
+ if (config.level === 'advanced') {
1555
+ const client = await page.context().newCDPSession(page);
1556
+ await client.send('Network.emulateNetworkConditions', {
1557
+ offline: false,
1558
+ downloadThroughput: (1.5 + Math.random() * 2) * 1024 * 1024 / 8, // 1.5-3.5 Mbps
1559
+ uploadThroughput: (0.75 + Math.random() * 1.25) * 1024 * 1024 / 8, // 0.75-2 Mbps
1560
+ latency: 40 + Math.random() * 60 // 40-100ms
1561
+ });
1562
+ }
1563
+
1564
+ // Set up human behavior if enabled
1565
+ if (config.simulateHumanBehavior) {
1566
+ await this.initializeHumanBehaviorSimulator();
1567
+ }
1568
+
1569
+ return page;
1570
+ }
1571
+
1572
+ /**
1573
+ * Simulate realistic mouse movements using Bezier curves
1574
+ */
1575
+ async simulateRealisticMouseMovements(page) {
1576
+ if (!this.humanBehaviorSimulator) return;
1577
+
1578
+ const viewport = await page.viewportSize();
1579
+ const movements = Math.floor(Math.random() * 4) + 2; // 2-5 movements
1580
+
1581
+ let currentX = Math.random() * viewport.width;
1582
+ let currentY = Math.random() * viewport.height;
1583
+
1584
+ for (let i = 0; i < movements; i++) {
1585
+ const targetX = Math.random() * viewport.width;
1586
+ const targetY = Math.random() * viewport.height;
1587
+
1588
+ await this.humanBehaviorSimulator.simulateMouseMovement(
1589
+ page, currentX, currentY, targetX, targetY
1590
+ );
1591
+
1592
+ currentX = targetX;
1593
+ currentY = targetY;
1594
+
1595
+ await this.humanBehaviorSimulator.delay(300, 0.5);
1596
+ }
1597
+ }
1598
+
1599
+ /**
1600
+ * Simulate natural scrolling behavior
1601
+ */
1602
+ async simulateNaturalScrolling(page) {
1603
+ if (!this.humanBehaviorSimulator) return;
1604
+
1605
+ // Random scroll behavior
1606
+ if (Math.random() < 0.7) { // 70% chance to scroll
1607
+ const direction = Math.random() < 0.8 ? 'down' : 'up';
1608
+ const distance = 100 + Math.random() * 300;
1609
+ const duration = 800 + Math.random() * 1200;
1610
+
1611
+ await this.humanBehaviorSimulator.simulateScroll(page, {
1612
+ direction,
1613
+ distance,
1614
+ duration
1615
+ });
1616
+ }
1617
+ }
1618
+
1619
+ /**
1620
+ * Utility functions
1621
+ */
1622
+ weightedRandom(weights) {
1623
+ const random = Math.random();
1624
+ let sum = 0;
1625
+ for (const [option, weight] of Object.entries(weights)) {
1626
+ sum += weight;
1627
+ if (random <= sum) {
1628
+ return option;
1629
+ }
1630
+ }
1631
+ return Object.keys(weights)[0];
1632
+ }
1633
+
1634
+ weightedRandomFromArray(items) {
1635
+ const totalWeight = items.reduce((sum, item) => sum + (item.weight || 1), 0);
1636
+ let random = Math.random() * totalWeight;
1637
+
1638
+ for (const item of items) {
1639
+ random -= (item.weight || 1);
1640
+ if (random <= 0) {
1641
+ return item;
1642
+ }
1643
+ }
1644
+
1645
+ return items[0];
1646
+ }
1647
+
1648
+ randomFloat(min, max, decimals = 2) {
1649
+ return parseFloat((Math.random() * (max - min) + min).toFixed(decimals));
1650
+ }
1651
+
1652
+ randomHex(length) {
1653
+ return Array.from({ length }, () => Math.floor(Math.random() * 16).toString(16)).join('');
1654
+ }
1655
+
1656
+ generateContextId() {
1657
+ return 'stealth_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
1658
+ }
1659
+
1660
+ /**
1661
+ * Get context information
1662
+ */
1663
+ getContextInfo(contextId) {
1664
+ const contextData = this.contexts.get(contextId);
1665
+ if (!contextData) {
1666
+ return null;
1667
+ }
1668
+
1669
+ return {
1670
+ contextId,
1671
+ fingerprint: contextData.fingerprint,
1672
+ config: contextData.config,
1673
+ created: contextData.created || Date.now()
1674
+ };
1675
+ }
1676
+
1677
+ /**
1678
+ * Close specific context
1679
+ */
1680
+ async closeContext(contextId) {
1681
+ const contextData = this.contexts.get(contextId);
1682
+ if (contextData) {
1683
+ await contextData.context.close();
1684
+ this.contexts.delete(contextId);
1685
+ this.fingerprints.delete(contextId);
1686
+ }
1687
+ }
1688
+
1689
+ /**
1690
+ * Close all contexts and browser
1691
+ */
1692
+ async cleanup() {
1693
+ // Close all contexts
1694
+ for (const [contextId, contextData] of this.contexts.entries()) {
1695
+ try {
1696
+ await contextData.context.close();
1697
+ } catch (error) {
1698
+ console.warn(`Failed to close context ${contextId}:`, error.message);
1699
+ }
1700
+ }
1701
+
1702
+ this.contexts.clear();
1703
+ this.fingerprints.clear();
1704
+
1705
+ // Reset human behavior simulator
1706
+ if (this.humanBehaviorSimulator) {
1707
+ this.humanBehaviorSimulator.resetStats();
1708
+ this.humanBehaviorSimulator = null;
1709
+ }
1710
+
1711
+ // Close browser
1712
+ if (this.browser) {
1713
+ try {
1714
+ await this.browser.close();
1715
+ } catch (error) {
1716
+ console.warn('Failed to close browser:', error.message);
1717
+ }
1718
+ this.browser = null;
1719
+ }
1720
+ }
1721
+
1722
+ /**
1723
+ * Get comprehensive statistics
1724
+ */
1725
+ getStats() {
1726
+ return {
1727
+ activeContexts: this.contexts.size,
1728
+ totalFingerprintsSaved: this.fingerprints.size,
1729
+ browserRunning: !!this.browser,
1730
+ humanBehaviorActive: !!this.humanBehaviorSimulator,
1731
+ performanceMetrics: this.performanceMetrics,
1732
+ proxyStatus: {
1733
+ enabled: this.proxyManager.activeProxies.length > 0,
1734
+ currentProxy: this.proxyManager.currentProxy,
1735
+ totalProxies: this.proxyManager.activeProxies.length
1736
+ },
1737
+ bypassCacheSize: this.bypassCache.size,
1738
+ canvasCacheSize: this.canvasCache.size
1739
+ };
1740
+ }
1741
+
1742
+ /**
1743
+ * Validate stealth configuration
1744
+ */
1745
+ validateConfig(config) {
1746
+ try {
1747
+ return StealthConfigSchema.parse(config);
1748
+ } catch (error) {
1749
+ throw new Error(`Invalid stealth configuration: ${error.message}`);
1750
+ }
1751
+ }
1752
+
1753
+ /**
1754
+ * Get the stealth configuration schema
1755
+ */
1756
+ getStealthConfigSchema() {
1757
+ return StealthConfigSchema;
1758
+ }
1759
+
1760
+ /**
1761
+ * Update performance metrics
1762
+ */
1763
+ updatePerformanceMetrics(metric, value) {
1764
+ if (this.performanceMetrics.hasOwnProperty(metric)) {
1765
+ this.performanceMetrics[metric] = value;
1766
+ }
1767
+ }
1768
+
1769
+ /**
1770
+ * Clear bypass cache
1771
+ */
1772
+ clearBypassCache() {
1773
+ this.bypassCache.clear();
1774
+ }
1775
+
1776
+ /**
1777
+ * Enable stealth mode with specified level
1778
+ */
1779
+ enableStealthMode(level = 'medium') {
1780
+ this.defaultConfig.level = level;
1781
+ this.defaultConfig.randomizeFingerprint = true;
1782
+ this.defaultConfig.simulateHumanBehavior = true;
1783
+ }
1784
+
1785
+ /**
1786
+ * Disable stealth mode
1787
+ */
1788
+ disableStealthMode() {
1789
+ this.defaultConfig.level = 'basic';
1790
+ this.defaultConfig.randomizeFingerprint = false;
1791
+ this.defaultConfig.simulateHumanBehavior = false;
1792
+ }
1793
+ }
1794
+
1795
+ export default StealthBrowserManager;