@fanboynz/network-scanner 1.0.35

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.
@@ -0,0 +1,660 @@
1
+ /**
2
+ * Cloudflare bypass and challenge handling module - Updated for 2025
3
+ * Handles phishing warnings, Turnstile challenges, and modern Cloudflare protections
4
+ */
5
+
6
+ /**
7
+ * Cross-version compatible timeout function for Puppeteer
8
+ * @param {import('puppeteer').Page} page - Puppeteer page instance
9
+ * @param {number} timeout - Timeout in milliseconds
10
+ * @returns {Promise<void>}
11
+ */
12
+ async function waitForTimeout(page, timeout) {
13
+ try {
14
+ // Try newer Puppeteer method first
15
+ if (typeof page.waitForTimeout === 'function') {
16
+ await page.waitForTimeout(timeout);
17
+ } else if (typeof page.waitFor === 'function') {
18
+ // Fallback to older Puppeteer method
19
+ await page.waitFor(timeout);
20
+ } else {
21
+ // Ultimate fallback using setTimeout
22
+ await new Promise(resolve => setTimeout(resolve, timeout));
23
+ }
24
+ } catch (error) {
25
+ // If all else fails, use setTimeout
26
+ await new Promise(resolve => setTimeout(resolve, timeout));
27
+ }
28
+ }
29
+
30
+ /**
31
+ * Analyzes the current page to detect Cloudflare challenges - Updated for 2025
32
+ * @param {import('puppeteer').Page} page - Puppeteer page instance
33
+ * @returns {Promise<object>} Challenge information object
34
+ */
35
+ async function analyzeCloudflareChallenge(page) {
36
+ try {
37
+ return await page.evaluate(() => {
38
+ const title = document.title || '';
39
+ const bodyText = document.body ? document.body.textContent : '';
40
+
41
+ // Updated selectors for 2025 Cloudflare challenges
42
+ const hasTurnstileIframe = document.querySelector('iframe[title*="Cloudflare security challenge"]') !== null ||
43
+ document.querySelector('iframe[src*="challenges.cloudflare.com"]') !== null ||
44
+ document.querySelector('iframe[title*="Widget containing a Cloudflare"]') !== null;
45
+
46
+ const hasTurnstileContainer = document.querySelector('.cf-turnstile') !== null ||
47
+ document.querySelector('.ctp-checkbox-container') !== null ||
48
+ document.querySelector('.ctp-checkbox-label') !== null;
49
+
50
+ const hasTurnstileCheckbox = document.querySelector('input[type="checkbox"].ctp-checkbox') !== null ||
51
+ document.querySelector('.ctp-checkbox') !== null;
52
+
53
+ // Legacy challenge detection (still used on some sites)
54
+ const hasLegacyCheckbox = document.querySelector('input[type="checkbox"]#challenge-form') !== null ||
55
+ document.querySelector('input[type="checkbox"][name="cf_captcha_kind"]') !== null;
56
+
57
+ const hasChallengeRunning = document.querySelector('.cf-challenge-running') !== null ||
58
+ document.querySelector('.cf-challenge-container') !== null ||
59
+ document.querySelector('.challenge-stage') !== null ||
60
+ document.querySelector('.challenge-form') !== null;
61
+
62
+ const hasDataRay = document.querySelector('[data-ray]') !== null ||
63
+ document.querySelector('[data-cf-challenge]') !== null;
64
+
65
+ const hasCaptcha = bodyText.includes('CAPTCHA') || bodyText.includes('captcha') ||
66
+ bodyText.includes('hCaptcha') || bodyText.includes('reCAPTCHA');
67
+
68
+ const hasJSChallenge = document.querySelector('script[src*="/cdn-cgi/challenge-platform/"]') !== null ||
69
+ bodyText.includes('Checking your browser') ||
70
+ bodyText.includes('Please wait while we verify');
71
+
72
+ const hasPhishingWarning = bodyText.includes('This website has been reported for potential phishing') ||
73
+ title.includes('Attention Required') ||
74
+ document.querySelector('a[href*="continue"]') !== null;
75
+
76
+ // Check for Turnstile response token
77
+ const hasTurnstileResponse = document.querySelector('input[name="cf-turnstile-response"]') !== null;
78
+
79
+ // Check for challenge completion indicators
80
+ const isChallengeCompleted = hasTurnstileResponse &&
81
+ document.querySelector('input[name="cf-turnstile-response"]')?.value;
82
+
83
+ // Enhanced challenge detection logic
84
+ const isChallengePresent = title.includes('Just a moment') ||
85
+ title.includes('Checking your browser') ||
86
+ bodyText.includes('Verify you are human') ||
87
+ hasLegacyCheckbox ||
88
+ hasChallengeRunning ||
89
+ hasDataRay ||
90
+ hasTurnstileIframe ||
91
+ hasTurnstileContainer ||
92
+ hasJSChallenge;
93
+
94
+ return {
95
+ isChallengePresent,
96
+ isPhishingWarning: hasPhishingWarning,
97
+ isTurnstile: hasTurnstileIframe || hasTurnstileContainer || hasTurnstileCheckbox,
98
+ isJSChallenge: hasJSChallenge,
99
+ isChallengeCompleted,
100
+ title,
101
+ hasLegacyCheckbox,
102
+ hasTurnstileIframe,
103
+ hasTurnstileContainer,
104
+ hasTurnstileCheckbox,
105
+ hasChallengeRunning,
106
+ hasDataRay,
107
+ hasCaptcha,
108
+ hasTurnstileResponse,
109
+ url: window.location.href,
110
+ bodySnippet: bodyText.substring(0, 200) // First 200 chars for debugging
111
+ };
112
+ });
113
+ } catch (error) {
114
+ return {
115
+ isChallengePresent: false,
116
+ isPhishingWarning: false,
117
+ isTurnstile: false,
118
+ isJSChallenge: false,
119
+ isChallengeCompleted: false,
120
+ error: error.message
121
+ };
122
+ }
123
+ }
124
+
125
+ /**
126
+ * Handles Cloudflare phishing warnings by clicking the continue button
127
+ * @param {import('puppeteer').Page} page - Puppeteer page instance
128
+ * @param {string} currentUrl - Current URL being processed
129
+ * @param {boolean} forceDebug - Debug mode flag
130
+ * @returns {Promise<object>} Result object with success status and details
131
+ */
132
+ async function handlePhishingWarning(page, currentUrl, forceDebug = false) {
133
+ const result = {
134
+ success: false,
135
+ attempted: false,
136
+ error: null,
137
+ details: null
138
+ };
139
+
140
+ try {
141
+ if (forceDebug) console.log(`[debug][cloudflare] Checking for phishing warning on ${currentUrl}`);
142
+
143
+ // Wait a moment for the warning page to load
144
+ await waitForTimeout(page, 2000);
145
+
146
+ const challengeInfo = await analyzeCloudflareChallenge(page);
147
+
148
+ if (challengeInfo.isPhishingWarning) {
149
+ result.attempted = true;
150
+ result.details = challengeInfo;
151
+
152
+ if (forceDebug) {
153
+ console.log(`[debug][cloudflare] Phishing warning detected on ${currentUrl}:`);
154
+ console.log(`[debug][cloudflare] Page Title: "${challengeInfo.title}"`);
155
+ console.log(`[debug][cloudflare] Current URL: ${challengeInfo.url}`);
156
+ }
157
+
158
+ try {
159
+ await page.click('a[href*="continue"]', { timeout: 5000 });
160
+ await page.waitForNavigation({ waitUntil: 'load', timeout: 30000 });
161
+
162
+ result.success = true;
163
+ if (forceDebug) console.log(`[debug][cloudflare] Successfully bypassed phishing warning for ${currentUrl}`);
164
+ } catch (clickError) {
165
+ result.error = `Failed to click continue button: ${clickError.message}`;
166
+ if (forceDebug) console.log(`[debug][cloudflare] Failed to bypass phishing warning: ${clickError.message}`);
167
+ }
168
+ } else {
169
+ if (forceDebug) console.log(`[debug][cloudflare] No phishing warning detected on ${currentUrl}`);
170
+ result.success = true; // No warning to handle
171
+ }
172
+ } catch (error) {
173
+ result.error = error.message;
174
+ if (forceDebug) console.log(`[debug][cloudflare] Phishing warning check failed for ${currentUrl}: ${error.message}`);
175
+ }
176
+
177
+ return result;
178
+ }
179
+
180
+ /**
181
+ * Attempts to solve Cloudflare challenges including Turnstile - Updated for 2025
182
+ * @param {import('puppeteer').Page} page - Puppeteer page instance
183
+ * @param {string} currentUrl - Current URL being processed
184
+ * @param {boolean} forceDebug - Debug mode flag
185
+ * @returns {Promise<object>} Result object with success status and details
186
+ */
187
+ async function handleVerificationChallenge(page, currentUrl, forceDebug = false) {
188
+ const result = {
189
+ success: false,
190
+ attempted: false,
191
+ error: null,
192
+ details: null,
193
+ requiresHuman: false
194
+ };
195
+
196
+ try {
197
+ if (forceDebug) console.log(`[debug][cloudflare] Checking for verification challenge on ${currentUrl}`);
198
+
199
+ // Wait for potential Cloudflare challenge to appear
200
+ await waitForTimeout(page, 3000);
201
+
202
+ const challengeInfo = await analyzeCloudflareChallenge(page);
203
+ result.details = challengeInfo;
204
+
205
+ if (challengeInfo.isChallengePresent) {
206
+ result.attempted = true;
207
+
208
+ if (forceDebug) {
209
+ console.log(`[debug][cloudflare] Challenge detected on ${currentUrl}:`);
210
+ console.log(`[debug][cloudflare] Page Title: "${challengeInfo.title}"`);
211
+ console.log(`[debug][cloudflare] Current URL: ${challengeInfo.url}`);
212
+ console.log(`[debug][cloudflare] Is Turnstile: ${challengeInfo.isTurnstile}`);
213
+ console.log(`[debug][cloudflare] Is JS Challenge: ${challengeInfo.isJSChallenge}`);
214
+ console.log(`[debug][cloudflare] Has Legacy Checkbox: ${challengeInfo.hasLegacyCheckbox}`);
215
+ console.log(`[debug][cloudflare] Has Turnstile Iframe: ${challengeInfo.hasTurnstileIframe}`);
216
+ console.log(`[debug][cloudflare] Has Turnstile Container: ${challengeInfo.hasTurnstileContainer}`);
217
+ console.log(`[debug][cloudflare] Has CAPTCHA: ${challengeInfo.hasCaptcha}`);
218
+ }
219
+
220
+ // Check for CAPTCHA that requires human intervention
221
+ if (challengeInfo.hasCaptcha) {
222
+ result.requiresHuman = true;
223
+ result.error = 'CAPTCHA detected - requires human intervention';
224
+ console.warn(`? [cloudflare] CAPTCHA detected on ${currentUrl} - requires human intervention`);
225
+ if (forceDebug) console.log(`[debug][cloudflare] Skipping automatic bypass due to CAPTCHA requirement`);
226
+ return result;
227
+ }
228
+
229
+ // Attempt to solve the challenge with updated methods
230
+ const solveResult = await attemptChallengeSolve(page, currentUrl, challengeInfo, forceDebug);
231
+ result.success = solveResult.success;
232
+ result.error = solveResult.error;
233
+
234
+ } else {
235
+ if (forceDebug) console.log(`[debug][cloudflare] No verification challenge detected on ${currentUrl}`);
236
+ result.success = true; // No challenge to handle
237
+ }
238
+ } catch (error) {
239
+ result.error = error.message;
240
+ if (forceDebug) {
241
+ console.log(`[debug][cloudflare] Challenge check failed for ${currentUrl}:`);
242
+ console.log(`[debug][cloudflare] Error: ${error.message}`);
243
+ console.log(`[debug][cloudflare] Stack: ${error.stack}`);
244
+ }
245
+ }
246
+
247
+ return result;
248
+ }
249
+
250
+ /**
251
+ * Attempts to solve a Cloudflare challenge with modern techniques - Updated for 2025
252
+ * @param {import('puppeteer').Page} page - Puppeteer page instance
253
+ * @param {string} currentUrl - Current URL being processed
254
+ * @param {object} challengeInfo - Challenge analysis results
255
+ * @param {boolean} forceDebug - Debug mode flag
256
+ * @returns {Promise<object>} Solve attempt result
257
+ */
258
+ async function attemptChallengeSolve(page, currentUrl, challengeInfo, forceDebug = false) {
259
+ const result = {
260
+ success: false,
261
+ error: null,
262
+ method: null
263
+ };
264
+
265
+ // Method 1: Handle Turnstile challenges (2025 primary method)
266
+ if (challengeInfo.isTurnstile) {
267
+ try {
268
+ if (forceDebug) console.log(`[debug][cloudflare] Attempting Turnstile method for ${currentUrl}`);
269
+
270
+ const turnstileResult = await handleTurnstileChallenge(page, forceDebug);
271
+ if (turnstileResult.success) {
272
+ result.success = true;
273
+ result.method = 'turnstile';
274
+ if (forceDebug) console.log(`[debug][cloudflare] Turnstile challenge solved successfully for ${currentUrl}`);
275
+ return result;
276
+ } else {
277
+ if (forceDebug) console.log(`[debug][cloudflare] Turnstile method failed: ${turnstileResult.error}`);
278
+ }
279
+ } catch (turnstileError) {
280
+ if (forceDebug) console.log(`[debug][cloudflare] Turnstile method error: ${turnstileError.message}`);
281
+ }
282
+ }
283
+
284
+ // Method 2: Handle JS challenges (wait for automatic completion)
285
+ if (challengeInfo.isJSChallenge) {
286
+ try {
287
+ if (forceDebug) console.log(`[debug][cloudflare] Attempting JS challenge wait for ${currentUrl}`);
288
+
289
+ const jsResult = await waitForJSChallengeCompletion(page, forceDebug);
290
+ if (jsResult.success) {
291
+ result.success = true;
292
+ result.method = 'js_challenge_wait';
293
+ if (forceDebug) console.log(`[debug][cloudflare] JS challenge completed successfully for ${currentUrl}`);
294
+ return result;
295
+ }
296
+ } catch (jsError) {
297
+ if (forceDebug) console.log(`[debug][cloudflare] JS challenge wait error: ${jsError.message}`);
298
+ }
299
+ }
300
+
301
+ // Method 3: Legacy checkbox interaction (fallback)
302
+ if (challengeInfo.hasLegacyCheckbox) {
303
+ try {
304
+ if (forceDebug) console.log(`[debug][cloudflare] Attempting legacy checkbox method for ${currentUrl}`);
305
+
306
+ const legacyResult = await handleLegacyCheckbox(page, forceDebug);
307
+ if (legacyResult.success) {
308
+ result.success = true;
309
+ result.method = 'legacy_checkbox';
310
+ if (forceDebug) console.log(`[debug][cloudflare] Legacy checkbox method succeeded for ${currentUrl}`);
311
+ return result;
312
+ }
313
+ } catch (legacyError) {
314
+ if (forceDebug) console.log(`[debug][cloudflare] Legacy checkbox method error: ${legacyError.message}`);
315
+ }
316
+ }
317
+
318
+ // Method 4: Alternative element clicking (final fallback)
319
+ try {
320
+ if (forceDebug) console.log(`[debug][cloudflare] Trying alternative click method for ${currentUrl}`);
321
+
322
+ const alternatives = [
323
+ '.cf-challenge-running',
324
+ '[data-ray]',
325
+ '.challenge-stage',
326
+ '.challenge-form'
327
+ ];
328
+
329
+ for (const selector of alternatives) {
330
+ try {
331
+ await page.click(selector, { timeout: 3000 });
332
+ await waitForTimeout(page, 3000);
333
+
334
+ // Check if challenge is solved
335
+ const completionCheck = await checkChallengeCompletion(page);
336
+ if (completionCheck.isCompleted) {
337
+ result.success = true;
338
+ result.method = 'alternative_click';
339
+ if (forceDebug) console.log(`[debug][cloudflare] Alternative click method succeeded for ${currentUrl} using ${selector}`);
340
+ return result;
341
+ }
342
+ } catch (clickError) {
343
+ // Continue to next selector
344
+ continue;
345
+ }
346
+ }
347
+ } catch (altError) {
348
+ result.error = `All bypass methods failed. Last error: ${altError.message}`;
349
+ if (forceDebug) console.log(`[debug][cloudflare] All bypass methods failed for ${currentUrl}: ${altError.message}`);
350
+ }
351
+
352
+ if (!result.success) {
353
+ result.error = result.error || 'All challenge bypass methods failed';
354
+ }
355
+
356
+ return result;
357
+ }
358
+
359
+ /**
360
+ * Handles modern Turnstile challenges - New for 2025
361
+ * @param {import('puppeteer').Page} page - Puppeteer page instance
362
+ * @param {boolean} forceDebug - Debug mode flag
363
+ * @returns {Promise<object>} Turnstile handling result
364
+ */
365
+ async function handleTurnstileChallenge(page, forceDebug = false) {
366
+ const result = {
367
+ success: false,
368
+ error: null
369
+ };
370
+
371
+ try {
372
+ // Wait for Turnstile iframe to load
373
+ const turnstileSelectors = [
374
+ 'iframe[src*="challenges.cloudflare.com"]',
375
+ 'iframe[title*="Widget containing a Cloudflare"]',
376
+ 'iframe[title*="Cloudflare security challenge"]'
377
+ ];
378
+
379
+ let turnstileFrame = null;
380
+ for (const selector of turnstileSelectors) {
381
+ try {
382
+ await page.waitForSelector(selector, { timeout: 5000 });
383
+ const frames = await page.frames();
384
+ turnstileFrame = frames.find(frame =>
385
+ frame.url().includes('challenges.cloudflare.com') ||
386
+ frame.url().includes('turnstile')
387
+ );
388
+ if (turnstileFrame) break;
389
+ } catch (e) {
390
+ continue;
391
+ }
392
+ }
393
+
394
+ if (turnstileFrame) {
395
+ if (forceDebug) console.log(`[debug][cloudflare] Found Turnstile iframe`);
396
+
397
+ // Wait for checkbox in iframe
398
+ const checkboxSelectors = [
399
+ 'input[type="checkbox"].ctp-checkbox',
400
+ 'input[type="checkbox"]',
401
+ '.ctp-checkbox-label',
402
+ '.ctp-checkbox'
403
+ ];
404
+
405
+ for (const selector of checkboxSelectors) {
406
+ try {
407
+ await turnstileFrame.waitForSelector(selector, { timeout: 5000 });
408
+
409
+ // Simulate human-like interaction
410
+ await waitForTimeout(page, Math.random() * 1000 + 500);
411
+ await turnstileFrame.click(selector);
412
+
413
+ if (forceDebug) console.log(`[debug][cloudflare] Clicked Turnstile checkbox: ${selector}`);
414
+ break;
415
+ } catch (e) {
416
+ continue;
417
+ }
418
+ }
419
+
420
+ // Wait for Turnstile completion
421
+ await page.waitForFunction(
422
+ () => {
423
+ const responseInput = document.querySelector('input[name="cf-turnstile-response"]');
424
+ return responseInput && responseInput.value && responseInput.value.length > 0;
425
+ },
426
+ { timeout: 30000 }
427
+ );
428
+
429
+ result.success = true;
430
+ } else {
431
+ // Try container-based Turnstile (non-iframe)
432
+ const containerSelectors = [
433
+ '.cf-turnstile',
434
+ '.ctp-checkbox-container',
435
+ '.ctp-checkbox-label'
436
+ ];
437
+
438
+ for (const selector of containerSelectors) {
439
+ try {
440
+ await page.waitForSelector(selector, { timeout: 5000 });
441
+
442
+ // Human-like interaction
443
+ await waitForTimeout(page, Math.random() * 1000 + 500);
444
+ await page.click(selector);
445
+
446
+ if (forceDebug) console.log(`[debug][cloudflare] Clicked Turnstile container: ${selector}`);
447
+
448
+ // Wait for completion
449
+ const completionCheck = await checkChallengeCompletion(page);
450
+ if (completionCheck.isCompleted) {
451
+ result.success = true;
452
+ break;
453
+ }
454
+ } catch (e) {
455
+ continue;
456
+ }
457
+ }
458
+
459
+ if (!result.success) {
460
+ result.error = 'Turnstile iframe/container not found or not interactive';
461
+ }
462
+ }
463
+
464
+ } catch (error) {
465
+ result.error = `Turnstile handling failed: ${error.message}`;
466
+ }
467
+
468
+ return result;
469
+ }
470
+
471
+ /**
472
+ * Waits for JS challenge completion - New for 2025
473
+ * @param {import('puppeteer').Page} page - Puppeteer page instance
474
+ * @param {boolean} forceDebug - Debug mode flag
475
+ * @returns {Promise<object>} JS challenge result
476
+ */
477
+ async function waitForJSChallengeCompletion(page, forceDebug = false) {
478
+ const result = {
479
+ success: false,
480
+ error: null
481
+ };
482
+
483
+ try {
484
+ if (forceDebug) console.log(`[debug][cloudflare] Waiting for JS challenge completion`);
485
+
486
+ // Wait for challenge to complete automatically
487
+ await page.waitForFunction(
488
+ () => {
489
+ return !document.body.textContent.includes('Checking your browser') &&
490
+ !document.body.textContent.includes('Please wait while we verify') &&
491
+ !document.querySelector('.cf-challenge-running') &&
492
+ !document.querySelector('[data-cf-challenge]');
493
+ },
494
+ { timeout: 30000 }
495
+ );
496
+
497
+ result.success = true;
498
+ } catch (error) {
499
+ result.error = `JS challenge timeout: ${error.message}`;
500
+ }
501
+
502
+ return result;
503
+ }
504
+
505
+ /**
506
+ * Handles legacy checkbox challenges - Fallback for older implementations
507
+ * @param {import('puppeteer').Page} page - Puppeteer page instance
508
+ * @param {boolean} forceDebug - Debug mode flag
509
+ * @returns {Promise<object>} Legacy challenge result
510
+ */
511
+ async function handleLegacyCheckbox(page, forceDebug = false) {
512
+ const result = {
513
+ success: false,
514
+ error: null
515
+ };
516
+
517
+ try {
518
+ const legacySelectors = [
519
+ 'input[type="checkbox"]#challenge-form',
520
+ 'input[type="checkbox"][name="cf_captcha_kind"]',
521
+ '.cf-turnstile input[type="checkbox"]'
522
+ ];
523
+
524
+ for (const selector of legacySelectors) {
525
+ try {
526
+ await page.waitForSelector(selector, { timeout: 5000 });
527
+
528
+ const checkbox = await page.$(selector);
529
+ if (checkbox) {
530
+ const box = await checkbox.boundingBox();
531
+ if (box) {
532
+ // Simulate human-like mouse movement
533
+ await page.mouse.move(box.x - 50, box.y - 50);
534
+ await waitForTimeout(page, Math.random() * 500 + 200);
535
+ await page.mouse.move(box.x + box.width/2, box.y + box.height/2, { steps: 5 });
536
+ await waitForTimeout(page, Math.random() * 300 + 100);
537
+
538
+ await checkbox.click();
539
+ if (forceDebug) console.log(`[debug][cloudflare] Clicked legacy checkbox: ${selector}`);
540
+
541
+ // Wait for challenge completion
542
+ const completionCheck = await checkChallengeCompletion(page);
543
+ if (completionCheck.isCompleted) {
544
+ result.success = true;
545
+ break;
546
+ }
547
+ }
548
+ }
549
+ } catch (e) {
550
+ continue;
551
+ }
552
+ }
553
+
554
+ if (!result.success) {
555
+ result.error = 'No interactive legacy checkbox found';
556
+ }
557
+
558
+ } catch (error) {
559
+ result.error = `Legacy checkbox handling failed: ${error.message}`;
560
+ }
561
+
562
+ return result;
563
+ }
564
+
565
+ /**
566
+ * Checks if challenge has been completed - New utility function
567
+ * @param {import('puppeteer').Page} page - Puppeteer page instance
568
+ * @returns {Promise<object>} Completion status
569
+ */
570
+ async function checkChallengeCompletion(page) {
571
+ try {
572
+ const isCompleted = await page.evaluate(() => {
573
+ // Check for absence of challenge indicators
574
+ const noChallengeRunning = !document.querySelector('.cf-challenge-running');
575
+ const noChallengeContainer = !document.querySelector('.cf-challenge-container');
576
+ const noChallengePage = !document.body.textContent.includes('Checking your browser') &&
577
+ !document.body.textContent.includes('Just a moment') &&
578
+ !document.body.textContent.includes('Verify you are human');
579
+
580
+ // Check for completion indicators
581
+ const hasClearanceCookie = document.cookie.includes('cf_clearance');
582
+ const hasTurnstileResponse = document.querySelector('input[name="cf-turnstile-response"]')?.value;
583
+
584
+ return (noChallengeRunning && noChallengeContainer && noChallengePage) ||
585
+ hasClearanceCookie ||
586
+ hasTurnstileResponse;
587
+ });
588
+
589
+ return { isCompleted };
590
+ } catch (error) {
591
+ return { isCompleted: false, error: error.message };
592
+ }
593
+ }
594
+
595
+ /**
596
+ * Main function to handle all Cloudflare challenges for a given page - Updated for 2025
597
+ * @param {import('puppeteer').Page} page - Puppeteer page instance
598
+ * @param {string} currentUrl - Current URL being processed
599
+ * @param {object} siteConfig - Site configuration object
600
+ * @param {boolean} forceDebug - Debug mode flag
601
+ * @returns {Promise<object>} Combined result of all Cloudflare handling
602
+ */
603
+ async function handleCloudflareProtection(page, currentUrl, siteConfig, forceDebug = false) {
604
+ const result = {
605
+ phishingWarning: { attempted: false, success: false },
606
+ verificationChallenge: { attempted: false, success: false },
607
+ overallSuccess: true,
608
+ errors: []
609
+ };
610
+
611
+ // Handle phishing warnings if enabled
612
+ if (siteConfig.cloudflare_phish === true) {
613
+ const phishingResult = await handlePhishingWarning(page, currentUrl, forceDebug);
614
+ result.phishingWarning = phishingResult;
615
+
616
+ if (phishingResult.attempted && !phishingResult.success) {
617
+ result.overallSuccess = false;
618
+ result.errors.push(`Phishing warning bypass failed: ${phishingResult.error}`);
619
+ }
620
+ }
621
+
622
+ // Handle verification challenges if enabled
623
+ if (siteConfig.cloudflare_bypass === true) {
624
+ const challengeResult = await handleVerificationChallenge(page, currentUrl, forceDebug);
625
+ result.verificationChallenge = challengeResult;
626
+
627
+ if (challengeResult.attempted && !challengeResult.success) {
628
+ result.overallSuccess = false;
629
+ if (challengeResult.requiresHuman) {
630
+ result.errors.push(`Human intervention required: ${challengeResult.error}`);
631
+ } else {
632
+ result.errors.push(`Challenge bypass failed: ${challengeResult.error}`);
633
+ }
634
+ }
635
+ }
636
+
637
+ // Log overall result
638
+ if (!result.overallSuccess && forceDebug) {
639
+ console.log(`[debug][cloudflare] Overall Cloudflare handling failed for ${currentUrl}:`);
640
+ result.errors.forEach(error => {
641
+ console.log(`[debug][cloudflare] - ${error}`);
642
+ });
643
+ } else if ((result.phishingWarning.attempted || result.verificationChallenge.attempted) && forceDebug) {
644
+ console.log(`[debug][cloudflare] Successfully handled Cloudflare protections for ${currentUrl}`);
645
+ }
646
+
647
+ return result;
648
+ }
649
+
650
+ module.exports = {
651
+ analyzeCloudflareChallenge,
652
+ handlePhishingWarning,
653
+ handleVerificationChallenge,
654
+ handleCloudflareProtection,
655
+ waitForTimeout,
656
+ handleTurnstileChallenge,
657
+ waitForJSChallengeCompletion,
658
+ handleLegacyCheckbox,
659
+ checkChallengeCompletion
660
+ };