@fanboynz/network-scanner 1.0.80 → 1.0.82

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/cloudflare.js CHANGED
@@ -1,13 +1,20 @@
1
1
  /**
2
2
  * Cloudflare bypass and challenge handling module - Optimized with smart detection and adaptive timeouts
3
- * Version: 2.3.0 - Support iframe CF, and better error handling
3
+ * Version: 2.4.0 - Fix possible endless loops with retry logic and loop detection
4
+ * Version: 2.3.1 - Colorize CF
5
+ * Version: 2.3.0 - Support CF iframe challenges, and better error handling
6
+ * Version: 2.2.0 - Enhanced with retry logic, caching, and improved error handling
7
+ * Version: 2.1.0 - Enhanced with quick detection, adaptive timeouts, and comprehensive debug logging
4
8
  * Handles phishing warnings, Turnstile challenges, and modern Cloudflare protections
5
9
  */
6
10
 
11
+ // Import color utilities
12
+ const { formatLogMessage } = require('./colorize');
13
+
7
14
  /**
8
15
  * Module version information
9
16
  */
10
- const CLOUDFLARE_MODULE_VERSION = '2.3.0';
17
+ const CLOUDFLARE_MODULE_VERSION = '2.4.0';
11
18
 
12
19
  /**
13
20
  * Timeout constants for various operations (in milliseconds)
@@ -62,6 +69,46 @@ const ERROR_TYPES = {
62
69
  UNKNOWN: 'unknown'
63
70
  };
64
71
 
72
+ /**
73
+ * Gets the retry configuration for a site, merging site-specific and global settings
74
+ * @param {Object} siteConfig - Site configuration object
75
+ * @returns {Object} Merged retry configuration
76
+ */
77
+ function getRetryConfig(siteConfig) {
78
+ return {
79
+ maxAttempts: siteConfig.cloudflare_max_retries || RETRY_CONFIG.maxAttempts,
80
+ baseDelay: RETRY_CONFIG.baseDelay,
81
+ maxDelay: RETRY_CONFIG.maxDelay,
82
+ backoffMultiplier: RETRY_CONFIG.backoffMultiplier,
83
+ retryableErrors: RETRY_CONFIG.retryableErrors,
84
+ retryOnError: siteConfig.cloudflare_retry_on_error !== false // Default to true
85
+ };
86
+ }
87
+
88
+ /**
89
+ * Detects if we're in a challenge redirect loop by checking URL patterns
90
+ */
91
+ function detectChallengeLoop(url, previousUrls = []) {
92
+ // Check if current URL contains challenge indicators and we've seen similar URLs
93
+ const isChallengeUrl = url.includes('/cdn-cgi/challenge-platform/') ||
94
+ url.includes('challenges.cloudflare.com') ||
95
+ url.includes('cf-ray');
96
+
97
+ if (!isChallengeUrl) return false;
98
+
99
+ // Check if we've seen this exact URL or very similar challenge URLs
100
+ const similarUrls = previousUrls.filter(prevUrl => {
101
+ if (prevUrl === url) return true; // Exact match
102
+ // Check for similar challenge URLs with different ray IDs
103
+ if (prevUrl.includes('/cdn-cgi/challenge-platform/') && url.includes('/cdn-cgi/challenge-platform/')) {
104
+ return true;
105
+ }
106
+ return false;
107
+ });
108
+
109
+ return similarUrls.length >= 2; // Loop detected if we've seen similar URLs 2+ times
110
+ }
111
+
65
112
  /**
66
113
  * Retry configuration with exponential backoff
67
114
  */
@@ -165,7 +212,7 @@ function getModuleInfo() {
165
212
  */
166
213
  function shouldProcessUrl(url, forceDebug = false) {
167
214
  if (!url || typeof url !== 'string') {
168
- if (forceDebug) console.log(`[cloudflare][url-validation] Skipping invalid URL: ${url}`);
215
+ if (forceDebug) console.log(formatLogMessage('cloudflare', `[url-validation] Skipping invalid URL: ${url}`));
169
216
  return false;
170
217
  }
171
218
 
@@ -180,7 +227,7 @@ function shouldProcessUrl(url, forceDebug = false) {
180
227
  for (const pattern of skipPatterns) {
181
228
  if (urlLower.startsWith(pattern)) {
182
229
  if (forceDebug) {
183
- console.log(`[cloudflare][url-validation] Skipping ${pattern} URL: ${url.substring(0, 100)}${url.length > 100 ? '...' : ''}`);
230
+ console.log(formatLogMessage('cloudflare', `[url-validation] Skipping ${pattern} URL: ${url.substring(0, 100)}${url.length > 100 ? '...' : ''}`));
184
231
  }
185
232
  return false;
186
233
  }
@@ -189,7 +236,7 @@ function shouldProcessUrl(url, forceDebug = false) {
189
236
  // Only process HTTP/HTTPS URLs
190
237
  if (!urlLower.startsWith('http://') && !urlLower.startsWith('https://')) {
191
238
  if (forceDebug) {
192
- console.log(`[cloudflare][url-validation] Skipping non-HTTP(S) URL: ${url.substring(0, 100)}${url.length > 100 ? '...' : ''}`);
239
+ console.log(formatLogMessage('cloudflare', `[url-validation] Skipping non-HTTP(S) URL: ${url.substring(0, 100)}${url.length > 100 ? '...' : ''}`));
193
240
  }
194
241
  return false;
195
242
  }
@@ -257,7 +304,7 @@ async function safePageEvaluate(page, func, timeout = TIMEOUTS.PAGE_EVALUATION_S
257
304
  ]);
258
305
 
259
306
  if (forceDebug && attempt > 1) {
260
- console.log(`[cloudflare] Page evaluation succeeded on attempt ${attempt}`);
307
+ console.log(formatLogMessage('cloudflare', `Page evaluation succeeded on attempt ${attempt}`));
261
308
  }
262
309
 
263
310
  return result;
@@ -266,7 +313,7 @@ async function safePageEvaluate(page, func, timeout = TIMEOUTS.PAGE_EVALUATION_S
266
313
  const errorType = categorizeError(error);
267
314
 
268
315
  if (forceDebug) {
269
- console.warn(`[cloudflare] Page evaluation failed (attempt ${attempt}/${maxRetries}): ${error.message} [${errorType}]`);
316
+ console.warn(formatLogMessage('cloudflare', `Page evaluation failed (attempt ${attempt}/${maxRetries}): ${error.message} [${errorType}]`));
270
317
  }
271
318
 
272
319
  // Don't retry if error type is not retryable or if it's the last attempt
@@ -328,7 +375,7 @@ async function safeWaitForNavigation(page, timeout = TIMEOUTS.NAVIGATION_TIMEOUT
328
375
  )
329
376
  ]);
330
377
  } catch (error) {
331
- console.warn(`[cloudflare] Navigation wait failed: ${error.message}`);
378
+ console.warn(formatLogMessage('cloudflare', `Navigation wait failed: ${error.message}`));
332
379
  // Don't throw - just continue
333
380
  }
334
381
  }
@@ -343,7 +390,7 @@ async function quickCloudflareDetection(page, forceDebug = false) {
343
390
 
344
391
  if (!shouldProcessUrl(currentPageUrl, forceDebug)) {
345
392
  if (forceDebug) {
346
- console.log(`[debug][cloudflare] Quick detection skipping non-HTTP(S) page: ${currentPageUrl}`);
393
+ console.log(formatLogMessage('cloudflare', `Quick detection skipping non-HTTP(S) page: ${currentPageUrl}`));
347
394
  }
348
395
  return { hasIndicators: false, skippedInvalidUrl: true };
349
396
  }
@@ -353,7 +400,7 @@ async function quickCloudflareDetection(page, forceDebug = false) {
353
400
  if (cachedResult !== null) {
354
401
  if (forceDebug) {
355
402
  const stats = detectionCache.getStats();
356
- console.log(`[debug][cloudflare] Using cached detection result (cache hit rate: ${stats.hitRate})`);
403
+ console.log(formatLogMessage('cloudflare', `Using cached detection result (cache hit rate: ${stats.hitRate})`));
357
404
  }
358
405
  return cachedResult;
359
406
  }
@@ -405,19 +452,19 @@ async function quickCloudflareDetection(page, forceDebug = false) {
405
452
 
406
453
  if (forceDebug) {
407
454
  if (quickCheck.hasIndicators) {
408
- console.log(`[debug][cloudflare] Quick detection found Cloudflare indicators on ${quickCheck.url}`);
455
+ console.log(formatLogMessage('cloudflare', `Quick detection found Cloudflare indicators on ${quickCheck.url}`));
409
456
  } else {
410
- console.log(`[debug][cloudflare] Quick detection found no Cloudflare indicators on ${quickCheck.url}`);
457
+ console.log(formatLogMessage('cloudflare', `Quick detection found no Cloudflare indicators on ${quickCheck.url}`));
411
458
  }
412
459
 
413
460
  if (quickCheck.attempts && quickCheck.attempts > 1) {
414
- console.log(`[debug][cloudflare] Detection required ${quickCheck.attempts} attempts`);
461
+ console.log(formatLogMessage('cloudflare', `Detection required ${quickCheck.attempts} attempts`));
415
462
  }
416
463
  }
417
464
 
418
465
  return quickCheck;
419
466
  } catch (error) {
420
- if (forceDebug) console.log(`[debug][cloudflare] Quick detection failed: ${error.message}`);
467
+ if (forceDebug) console.log(formatLogMessage('cloudflare', `Quick detection failed: ${error.message}`));
421
468
  return { hasIndicators: false, error: error.message };
422
469
  }
423
470
  }
@@ -534,7 +581,7 @@ async function handlePhishingWarning(page, currentUrl, forceDebug = false) {
534
581
  };
535
582
 
536
583
  try {
537
- if (forceDebug) console.log(`[debug][cloudflare] Checking for phishing warning on ${currentUrl}`);
584
+ if (forceDebug) console.log(formatLogMessage('cloudflare', `Checking for phishing warning on ${currentUrl}`));
538
585
 
539
586
  // Shorter wait with timeout protection
540
587
  await waitForTimeout(page, FAST_TIMEOUTS.PHISHING_WAIT);
@@ -546,10 +593,10 @@ async function handlePhishingWarning(page, currentUrl, forceDebug = false) {
546
593
  result.details = challengeInfo;
547
594
 
548
595
  if (forceDebug) {
549
- console.log(`[debug][cloudflare] Phishing warning detected on ${currentUrl}:`);
550
- console.log(`[debug][cloudflare] Page Title: "${challengeInfo.title}"`);
551
- console.log(`[debug][cloudflare] Current URL: ${challengeInfo.url}`);
552
- console.log(`[debug][cloudflare] Body snippet: ${challengeInfo.bodySnippet}`);
596
+ console.log(formatLogMessage('cloudflare', `Phishing warning detected on ${currentUrl}:`));
597
+ console.log(formatLogMessage('cloudflare', ` Page Title: "${challengeInfo.title}"`));
598
+ console.log(formatLogMessage('cloudflare', ` Current URL: ${challengeInfo.url}`));
599
+ console.log(formatLogMessage('cloudflare', ` Body snippet: ${challengeInfo.bodySnippet}`));
553
600
  }
554
601
 
555
602
  try {
@@ -558,18 +605,18 @@ async function handlePhishingWarning(page, currentUrl, forceDebug = false) {
558
605
  await safeWaitForNavigation(page, TIMEOUTS.PHISHING_NAVIGATION);
559
606
 
560
607
  result.success = true;
561
- if (forceDebug) console.log(`[debug][cloudflare] Successfully bypassed phishing warning for ${currentUrl}`);
608
+ if (forceDebug) console.log(formatLogMessage('cloudflare', `Successfully bypassed phishing warning for ${currentUrl}`));
562
609
  } catch (clickError) {
563
610
  result.error = `Failed to click continue button: ${clickError.message}`;
564
- if (forceDebug) console.log(`[debug][cloudflare] Failed to bypass phishing warning: ${clickError.message}`);
611
+ if (forceDebug) console.log(formatLogMessage('cloudflare', `Failed to bypass phishing warning: ${clickError.message}`));
565
612
  }
566
613
  } else {
567
- if (forceDebug) console.log(`[debug][cloudflare] No phishing warning detected on ${currentUrl}`);
614
+ if (forceDebug) console.log(formatLogMessage('cloudflare', `No phishing warning detected on ${currentUrl}`));
568
615
  result.success = true; // No warning to handle
569
616
  }
570
617
  } catch (error) {
571
618
  result.error = error.message;
572
- if (forceDebug) console.log(`[debug][cloudflare] Phishing warning check failed for ${currentUrl}: ${error.message}`);
619
+ if (forceDebug) console.log(formatLogMessage('cloudflare', `Phishing warning check failed for ${currentUrl}: ${error.message}`));
573
620
  }
574
621
 
575
622
  return result;
@@ -602,7 +649,7 @@ async function handleVerificationChallenge(page, currentUrl, forceDebug = false)
602
649
  };
603
650
 
604
651
  try {
605
- if (forceDebug) console.log(`[debug][cloudflare] Checking for verification challenge on ${currentUrl}`);
652
+ if (forceDebug) console.log(formatLogMessage('cloudflare', `Checking for verification challenge on ${currentUrl}`));
606
653
 
607
654
  // Reduced wait time
608
655
  await waitForTimeout(page, FAST_TIMEOUTS.CHALLENGE_WAIT);
@@ -614,27 +661,27 @@ async function handleVerificationChallenge(page, currentUrl, forceDebug = false)
614
661
  result.attempted = true;
615
662
 
616
663
  if (forceDebug) {
617
- console.log(`[debug][cloudflare] Challenge detected on ${currentUrl}:`);
618
- console.log(`[debug][cloudflare] Page Title: "${challengeInfo.title}"`);
619
- console.log(`[debug][cloudflare] Current URL: ${challengeInfo.url}`);
620
- console.log(`[debug][cloudflare] Is Turnstile: ${challengeInfo.isTurnstile}`);
621
- console.log(`[debug][cloudflare] Is JS Challenge: ${challengeInfo.isJSChallenge}`);
622
- console.log(`[debug][cloudflare] Has Legacy Checkbox: ${challengeInfo.hasLegacyCheckbox}`);
623
- console.log(`[debug][cloudflare] Has Turnstile Iframe: ${challengeInfo.hasTurnstileIframe}`);
624
- console.log(`[debug][cloudflare] Has Turnstile Container: ${challengeInfo.hasTurnstileContainer}`);
625
- console.log(`[debug][cloudflare] Has Turnstile Checkbox: ${challengeInfo.hasTurnstileCheckbox}`);
626
- console.log(`[debug][cloudflare] Has CAPTCHA: ${challengeInfo.hasCaptcha}`);
627
- console.log(`[debug][cloudflare] Has Challenge Running: ${challengeInfo.hasChallengeRunning}`);
628
- console.log(`[debug][cloudflare] Has Data Ray: ${challengeInfo.hasDataRay}`);
629
- console.log(`[debug][cloudflare] Has Turnstile Response: ${challengeInfo.hasTurnstileResponse}`);
630
- console.log(`[debug][cloudflare] Body snippet: ${challengeInfo.bodySnippet}`);
664
+ console.log(formatLogMessage('cloudflare', `Challenge detected on ${currentUrl}:`));
665
+ console.log(formatLogMessage('cloudflare', ` Page Title: "${challengeInfo.title}"`));
666
+ console.log(formatLogMessage('cloudflare', ` Current URL: ${challengeInfo.url}`));
667
+ console.log(formatLogMessage('cloudflare', ` Is Turnstile: ${challengeInfo.isTurnstile}`));
668
+ console.log(formatLogMessage('cloudflare', ` Is JS Challenge: ${challengeInfo.isJSChallenge}`));
669
+ console.log(formatLogMessage('cloudflare', ` Has Legacy Checkbox: ${challengeInfo.hasLegacyCheckbox}`));
670
+ console.log(formatLogMessage('cloudflare', ` Has Turnstile Iframe: ${challengeInfo.hasTurnstileIframe}`));
671
+ console.log(formatLogMessage('cloudflare', ` Has Turnstile Container: ${challengeInfo.hasTurnstileContainer}`));
672
+ console.log(formatLogMessage('cloudflare', ` Has Turnstile Checkbox: ${challengeInfo.hasTurnstileCheckbox}`));
673
+ console.log(formatLogMessage('cloudflare', ` Has CAPTCHA: ${challengeInfo.hasCaptcha}`));
674
+ console.log(formatLogMessage('cloudflare', ` Has Challenge Running: ${challengeInfo.hasChallengeRunning}`));
675
+ console.log(formatLogMessage('cloudflare', ` Has Data Ray: ${challengeInfo.hasDataRay}`));
676
+ console.log(formatLogMessage('cloudflare', ` Has Turnstile Response: ${challengeInfo.hasTurnstileResponse}`));
677
+ console.log(formatLogMessage('cloudflare', ` Body snippet: ${challengeInfo.bodySnippet}`));
631
678
  }
632
679
 
633
680
  // Check for CAPTCHA that requires human intervention
634
681
  if (challengeInfo.hasCaptcha) {
635
682
  result.requiresHuman = true;
636
683
  result.error = 'CAPTCHA detected - requires human intervention';
637
- if (forceDebug) console.log(`[debug][cloudflare] Skipping automatic bypass due to CAPTCHA requirement`);
684
+ if (forceDebug) console.log(formatLogMessage('cloudflare', `Skipping automatic bypass due to CAPTCHA requirement`));
638
685
  return result;
639
686
  }
640
687
 
@@ -645,17 +692,196 @@ async function handleVerificationChallenge(page, currentUrl, forceDebug = false)
645
692
  result.method = solveResult.method;
646
693
 
647
694
  } else {
648
- if (forceDebug) console.log(`[debug][cloudflare] No verification challenge detected on ${currentUrl}`);
695
+ if (forceDebug) console.log(formatLogMessage('cloudflare', `No verification challenge detected on ${currentUrl}`));
649
696
  result.success = true;
650
697
  }
651
698
  } catch (error) {
652
699
  result.error = error.message;
653
- if (forceDebug) console.log(`[debug][cloudflare] Challenge check failed for ${currentUrl}: ${error.message}`);
700
+ if (forceDebug) console.log(formatLogMessage('cloudflare', `Challenge check failed for ${currentUrl}: ${error.message}`));
654
701
  }
655
702
 
656
703
  return result;
657
704
  }
658
705
 
706
+ /**
707
+ * Enhanced challenge handling with retry logic and loop detection
708
+ */
709
+ async function handleVerificationChallengeWithRetries(page, currentUrl, siteConfig, forceDebug = false) {
710
+ const retryConfig = getRetryConfig(siteConfig);
711
+ const visitedUrls = []; // Track URLs to detect redirect loops
712
+ let lastError = null;
713
+
714
+ if (forceDebug) {
715
+ console.log(formatLogMessage('cloudflare', `Starting verification challenge with max ${retryConfig.maxAttempts} attempts`));
716
+ }
717
+
718
+ for (let attempt = 1; attempt <= retryConfig.maxAttempts; attempt++) {
719
+ try {
720
+ const currentPageUrl = await page.url();
721
+ visitedUrls.push(currentPageUrl);
722
+
723
+ // Check for redirect loops
724
+ if (detectChallengeLoop(currentPageUrl, visitedUrls)) {
725
+ const error = `Challenge redirect loop detected after ${attempt} attempts. URLs: ${visitedUrls.slice(-3).join(' -> ')}`;
726
+ if (forceDebug) {
727
+ console.log(formatLogMessage('cloudflare', error));
728
+ }
729
+ return {
730
+ success: false,
731
+ attempted: true,
732
+ error: error,
733
+ details: null,
734
+ requiresHuman: false,
735
+ method: null,
736
+ attempts: attempt,
737
+ loopDetected: true
738
+ };
739
+ }
740
+
741
+ if (forceDebug && attempt > 1) {
742
+ console.log(formatLogMessage('cloudflare', `Challenge attempt ${attempt}/${retryConfig.maxAttempts} for ${currentUrl}`));
743
+ }
744
+
745
+ const result = await handleVerificationChallenge(page, currentUrl, forceDebug);
746
+
747
+ if (result.success || result.requiresHuman || !retryConfig.retryOnError) {
748
+ if (forceDebug && attempt > 1) {
749
+ console.log(`[debug][cloudflare] Challenge ${result.success ? 'succeeded' : 'failed'} on attempt ${attempt}`);
750
+ }
751
+ return { ...result, attempts: attempt };
752
+ }
753
+
754
+ // If this wasn't the last attempt, wait before retrying
755
+ if (attempt < retryConfig.maxAttempts) {
756
+ const delay = await getRetryDelay(attempt);
757
+ if (forceDebug) {
758
+ console.log(formatLogMessage('cloudflare', `Challenge attempt ${attempt} failed, retrying in ${delay}ms: ${result.error}`));
759
+ }
760
+ await new Promise(resolve => setTimeout(resolve, delay));
761
+
762
+ // Refresh the page to get a fresh challenge
763
+ try {
764
+ await page.reload({ waitUntil: 'domcontentloaded', timeout: 10000 });
765
+ await waitForTimeout(page, 2000); // Give challenge time to load
766
+ } catch (reloadErr) {
767
+ if (forceDebug) {
768
+ console.log(formatLogMessage('cloudflare', `Page reload failed on attempt ${attempt}: ${reloadErr.message}`));
769
+ }
770
+ }
771
+ }
772
+
773
+ lastError = result.error;
774
+ } catch (error) {
775
+ lastError = error.message;
776
+ const errorType = categorizeError(error);
777
+
778
+ if (forceDebug) {
779
+ console.warn(formatLogMessage('cloudflare', `Challenge attempt ${attempt}/${retryConfig.maxAttempts} failed: ${error.message} [${errorType}]`));
780
+ }
781
+
782
+ // Don't retry if error type is not retryable or if it's the last attempt
783
+ if (!retryConfig.retryableErrors.includes(errorType) || attempt === retryConfig.maxAttempts) {
784
+ return {
785
+ success: false,
786
+ attempted: true,
787
+ error: lastError,
788
+ details: null,
789
+ requiresHuman: false,
790
+ method: null,
791
+ attempts: attempt,
792
+ errorType: errorType
793
+ };
794
+ }
795
+
796
+ // Wait before retrying with exponential backoff
797
+ if (attempt < retryConfig.maxAttempts) {
798
+ await getRetryDelay(attempt);
799
+ }
800
+ }
801
+ }
802
+
803
+ return {
804
+ success: false,
805
+ attempted: true,
806
+ error: `All ${retryConfig.maxAttempts} challenge attempts failed. Last error: ${lastError}`,
807
+ details: null,
808
+ requiresHuman: false,
809
+ method: null,
810
+ attempts: retryConfig.maxAttempts,
811
+ maxRetriesExceeded: true
812
+ };
813
+ }
814
+
815
+ /**
816
+ * Enhanced phishing warning handling with retry logic
817
+ */
818
+ async function handlePhishingWarningWithRetries(page, currentUrl, siteConfig, forceDebug = false) {
819
+ const retryConfig = getRetryConfig(siteConfig);
820
+ let lastError = null;
821
+
822
+ for (let attempt = 1; attempt <= retryConfig.maxAttempts; attempt++) {
823
+ try {
824
+ if (forceDebug && attempt > 1) {
825
+ console.log(formatLogMessage('cloudflare', `Phishing warning attempt ${attempt}/${retryConfig.maxAttempts} for ${currentUrl}`));
826
+ }
827
+
828
+ const result = await handlePhishingWarning(page, currentUrl, forceDebug);
829
+
830
+ if (result.success || !retryConfig.retryOnError) {
831
+ if (forceDebug && attempt > 1) {
832
+ console.log(`[debug][cloudflare] Phishing warning ${result.success ? 'succeeded' : 'failed'} on attempt ${attempt}`);
833
+ }
834
+ return { ...result, attempts: attempt };
835
+ }
836
+
837
+ // If this wasn't the last attempt, wait before retrying
838
+ if (attempt < retryConfig.maxAttempts) {
839
+ const delay = await getRetryDelay(attempt);
840
+ if (forceDebug) {
841
+ console.log(formatLogMessage('cloudflare', `Phishing warning attempt ${attempt} failed, retrying in ${delay}ms: ${result.error}`));
842
+ }
843
+ await new Promise(resolve => setTimeout(resolve, delay));
844
+ }
845
+
846
+ lastError = result.error;
847
+ } catch (error) {
848
+ lastError = error.message;
849
+ const errorType = categorizeError(error);
850
+
851
+ if (forceDebug) {
852
+ console.warn(formatLogMessage('cloudflare', `Phishing warning attempt ${attempt}/${retryConfig.maxAttempts} failed: ${error.message} [${errorType}]`));
853
+ }
854
+
855
+ // Don't retry if error type is not retryable or if it's the last attempt
856
+ if (!retryConfig.retryableErrors.includes(errorType) || attempt === retryConfig.maxAttempts) {
857
+ return {
858
+ success: false,
859
+ attempted: true,
860
+ error: lastError,
861
+ details: null,
862
+ attempts: attempt,
863
+ errorType: errorType
864
+ };
865
+ }
866
+
867
+ // Wait before retrying with exponential backoff
868
+ if (attempt < retryConfig.maxAttempts) {
869
+ await getRetryDelay(attempt);
870
+ }
871
+ }
872
+ }
873
+
874
+ return {
875
+ success: false,
876
+ attempted: true,
877
+ error: `All ${retryConfig.maxAttempts} phishing warning attempts failed. Last error: ${lastError}`,
878
+ details: null,
879
+ attempts: retryConfig.maxAttempts,
880
+ maxRetriesExceeded: true
881
+ };
882
+ }
883
+
884
+
659
885
  /**
660
886
  * Challenge solving with overall timeout protection
661
887
  */
@@ -676,7 +902,7 @@ async function attemptChallengeSolveWithTimeout(page, currentUrl, challengeInfo,
676
902
  ]);
677
903
  } catch (error) {
678
904
  result.error = `Challenge solving timed out: ${error.message}`;
679
- if (forceDebug) console.log(`[debug][cloudflare] Challenge solving timeout for ${currentUrl}`);
905
+ if (forceDebug) console.log(formatLogMessage('cloudflare', `Challenge solving timeout for ${currentUrl}`));
680
906
  return result;
681
907
  }
682
908
  }
@@ -694,51 +920,51 @@ async function attemptChallengeSolve(page, currentUrl, challengeInfo, forceDebug
694
920
  // Method 1: Handle JS challenges (wait for automatic completion) - Most reliable
695
921
  if (challengeInfo.isJSChallenge) {
696
922
  try {
697
- if (forceDebug) console.log(`[debug][cloudflare] Attempting JS challenge wait for ${currentUrl}`);
923
+ if (forceDebug) console.log(formatLogMessage('cloudflare', `Attempting JS challenge wait for ${currentUrl}`));
698
924
 
699
925
  const jsResult = await waitForJSChallengeCompletion(page, forceDebug);
700
926
  if (jsResult.success) {
701
927
  result.success = true;
702
928
  result.method = 'js_challenge_wait';
703
- if (forceDebug) console.log(`[debug][cloudflare] JS challenge completed successfully for ${currentUrl}`);
929
+ if (forceDebug) console.log(formatLogMessage('cloudflare', `JS challenge completed successfully for ${currentUrl}`));
704
930
  return result;
705
931
  }
706
932
  } catch (jsError) {
707
- if (forceDebug) console.log(`[debug][cloudflare] JS challenge wait failed for ${currentUrl}: ${jsError.message}`);
933
+ if (forceDebug) console.log(formatLogMessage('cloudflare', `JS challenge wait failed for ${currentUrl}: ${jsError.message}`));
708
934
  }
709
935
  }
710
936
 
711
937
  // Method 2: Handle Turnstile challenges (interactive)
712
938
  if (challengeInfo.isTurnstile) {
713
939
  try {
714
- if (forceDebug) console.log(`[debug][cloudflare] Attempting Turnstile method for ${currentUrl}`);
940
+ if (forceDebug) console.log(formatLogMessage('cloudflare', `Attempting Turnstile method for ${currentUrl}`));
715
941
 
716
942
  const turnstileResult = await handleTurnstileChallenge(page, forceDebug);
717
943
  if (turnstileResult.success) {
718
944
  result.success = true;
719
945
  result.method = 'turnstile';
720
- if (forceDebug) console.log(`[debug][cloudflare] Turnstile challenge solved successfully for ${currentUrl}`);
946
+ if (forceDebug) console.log(formatLogMessage('cloudflare', `Turnstile challenge solved successfully for ${currentUrl}`));
721
947
  return result;
722
948
  }
723
949
  } catch (turnstileError) {
724
- if (forceDebug) console.log(`[debug][cloudflare] Turnstile method failed for ${currentUrl}: ${turnstileError.message}`);
950
+ if (forceDebug) console.log(formatLogMessage('cloudflare', `Turnstile method failed for ${currentUrl}: ${turnstileError.message}`));
725
951
  }
726
952
  }
727
953
 
728
954
  // Method 3: Legacy checkbox interaction (fallback)
729
955
  if (challengeInfo.hasLegacyCheckbox) {
730
956
  try {
731
- if (forceDebug) console.log(`[debug][cloudflare] Attempting legacy checkbox method for ${currentUrl}`);
957
+ if (forceDebug) console.log(formatLogMessage('cloudflare', `Attempting legacy checkbox method for ${currentUrl}`));
732
958
 
733
959
  const legacyResult = await handleLegacyCheckbox(page, forceDebug);
734
960
  if (legacyResult.success) {
735
961
  result.success = true;
736
962
  result.method = 'legacy_checkbox';
737
- if (forceDebug) console.log(`[debug][cloudflare] Legacy checkbox method succeeded for ${currentUrl}`);
963
+ if (forceDebug) console.log(formatLogMessage('cloudflare', `Legacy checkbox method succeeded for ${currentUrl}`));
738
964
  return result;
739
965
  }
740
966
  } catch (legacyError) {
741
- if (forceDebug) console.log(`[debug][cloudflare] Legacy checkbox method failed for ${currentUrl}: ${legacyError.message}`);
967
+ if (forceDebug) console.log(formatLogMessage('cloudflare', `Legacy checkbox method failed for ${currentUrl}: ${legacyError.message}`));
742
968
  }
743
969
  }
744
970
 
@@ -759,7 +985,7 @@ async function handleEmbeddedIframeChallenge(page, forceDebug = false) {
759
985
  };
760
986
 
761
987
  try {
762
- if (forceDebug) console.log(`[debug][cloudflare] Checking for embedded iframe challenges`);
988
+ if (forceDebug) console.log(formatLogMessage('cloudflare', `Checking for embedded iframe challenges`));
763
989
 
764
990
  // Enhanced iframe selectors including challenges.cloudflare.com
765
991
  const iframeSelectors = [
@@ -778,7 +1004,7 @@ async function handleEmbeddedIframeChallenge(page, forceDebug = false) {
778
1004
  new Promise((_, reject) => setTimeout(() => reject(new Error('Timeout')), FAST_TIMEOUTS.SELECTOR_WAIT + 1000))
779
1005
  ]);
780
1006
  iframeFound = true;
781
- if (forceDebug) console.log(`[debug][cloudflare] Found iframe: ${selector}`);
1007
+ if (forceDebug) console.log(formatLogMessage('cloudflare', `Found iframe: ${selector}`));
782
1008
  break;
783
1009
  } catch (e) {
784
1010
  continue;
@@ -806,7 +1032,7 @@ async function handleEmbeddedIframeChallenge(page, forceDebug = false) {
806
1032
  return result;
807
1033
  }
808
1034
 
809
- if (forceDebug) console.log(`[debug][cloudflare] Interacting with iframe: ${challengeFrame.url()}`);
1035
+ if (forceDebug) console.log(formatLogMessage('cloudflare', `Interacting with iframe: ${challengeFrame.url()}`));
810
1036
 
811
1037
  // Reuse existing checkbox interaction logic
812
1038
  const checkboxSelectors = [
@@ -828,7 +1054,7 @@ async function handleEmbeddedIframeChallenge(page, forceDebug = false) {
828
1054
  await waitForTimeout(page, FAST_TIMEOUTS.ELEMENT_INTERACTION_DELAY);
829
1055
  await challengeFrame.click(selector);
830
1056
 
831
- if (forceDebug) console.log(`[debug][cloudflare] Clicked iframe element: ${selector}`);
1057
+ if (forceDebug) console.log(formatLogMessage('cloudflare', `Clicked iframe element: ${selector}`));
832
1058
  checkboxInteractionSuccess = true;
833
1059
  break;
834
1060
  } catch (e) {
@@ -838,7 +1064,7 @@ async function handleEmbeddedIframeChallenge(page, forceDebug = false) {
838
1064
 
839
1065
  // Try alternative interaction only if standard selectors failed
840
1066
  if (!checkboxInteractionSuccess) {
841
- if (forceDebug) console.log(`[debug][cloudflare] Checkbox interactions failed, trying container fallback`);
1067
+ if (forceDebug) console.log(formatLogMessage('cloudflare', `Checkbox interactions failed, trying container fallback`));
842
1068
  await waitForTimeout(page, 1000);
843
1069
 
844
1070
  try {
@@ -846,13 +1072,13 @@ async function handleEmbeddedIframeChallenge(page, forceDebug = false) {
846
1072
  const iframeElement = await page.$('iframe[src*="challenges.cloudflare.com"]');
847
1073
  if (iframeElement) {
848
1074
  await iframeElement.click();
849
- if (forceDebug) console.log(`[debug][cloudflare] Clicked iframe container as fallback`);
1075
+ if (forceDebug) console.log(formatLogMessage('cloudflare', `Clicked iframe container as fallback`));
850
1076
  }
851
1077
  } catch (containerClickError) {
852
- if (forceDebug) console.log(`[debug][cloudflare] Container click failed: ${containerClickError.message}`);
1078
+ if (forceDebug) console.log(formatLogMessage('cloudflare', `Container click failed: ${containerClickError.message}`));
853
1079
  }
854
1080
  } else {
855
- if (forceDebug) console.log(`[debug][cloudflare] Checkbox interaction successful, skipping container fallback`);
1081
+ if (forceDebug) console.log(formatLogMessage('cloudflare', `Checkbox interaction successful, skipping container fallback`));
856
1082
  }
857
1083
 
858
1084
  // Reuse existing completion check pattern with error handling
@@ -873,15 +1099,15 @@ async function handleEmbeddedIframeChallenge(page, forceDebug = false) {
873
1099
  ]);
874
1100
 
875
1101
  result.success = true;
876
- if (forceDebug) console.log(`[debug][cloudflare] Embedded iframe challenge completed`);
1102
+ if (forceDebug) console.log(formatLogMessage('cloudflare', `Embedded iframe challenge completed`));
877
1103
  } catch (completionError) {
878
1104
  result.error = `Challenge completion check failed: ${completionError.message}`;
879
- if (forceDebug) console.log(`[debug][cloudflare] Completion check failed: ${completionError.message}`);
1105
+ if (forceDebug) console.log(formatLogMessage('cloudflare', `Completion check failed: ${completionError.message}`));
880
1106
  }
881
1107
 
882
1108
  } catch (error) {
883
1109
  result.error = `Embedded iframe handling failed: ${error.message}`;
884
- if (forceDebug) console.log(`[debug][cloudflare] ${result.error}`);
1110
+ if (forceDebug) console.log(formatLogMessage('cloudflare', result.error));
885
1111
  }
886
1112
 
887
1113
  return result;
@@ -897,7 +1123,7 @@ async function waitForJSChallengeCompletion(page, forceDebug = false) {
897
1123
  };
898
1124
 
899
1125
  try {
900
- if (forceDebug) console.log(`[debug][cloudflare] Waiting for JS challenge completion`);
1126
+ if (forceDebug) console.log(formatLogMessage('cloudflare', `Waiting for JS challenge completion`));
901
1127
 
902
1128
  // Reduced timeout for JS challenge completion
903
1129
  await Promise.race([
@@ -916,10 +1142,10 @@ async function waitForJSChallengeCompletion(page, forceDebug = false) {
916
1142
  ]);
917
1143
 
918
1144
  result.success = true;
919
- if (forceDebug) console.log(`[debug][cloudflare] JS challenge completed automatically`);
1145
+ if (forceDebug) console.log(formatLogMessage('cloudflare', `JS challenge completed automatically`));
920
1146
  } catch (error) {
921
1147
  result.error = `JS challenge timeout: ${error.message}`;
922
- if (forceDebug) console.log(`[debug][cloudflare] JS challenge wait failed: ${error.message}`);
1148
+ if (forceDebug) console.log(formatLogMessage('cloudflare', `JS challenge wait failed: ${error.message}`));
923
1149
  }
924
1150
 
925
1151
  return result;
@@ -940,7 +1166,7 @@ async function handleTurnstileChallenge(page, forceDebug = false) {
940
1166
  return { ...result, success: true };
941
1167
  }
942
1168
 
943
- if (forceDebug) console.log(`[debug][cloudflare] Embedded iframe failed: ${iframeResult.error}, trying legacy method`);
1169
+ if (forceDebug) console.log(formatLogMessage('cloudflare', `Embedded iframe failed: ${iframeResult.error}, trying legacy method`));
944
1170
 
945
1171
  try {
946
1172
  // Use fast timeout for Turnstile operations
@@ -966,18 +1192,18 @@ async function handleTurnstileChallenge(page, forceDebug = false) {
966
1192
  frame.url().includes('turnstile')
967
1193
  );
968
1194
  if (turnstileFrame) {
969
- if (forceDebug) console.log(`[debug][cloudflare] Found Turnstile iframe using selector: ${selector}`);
1195
+ if (forceDebug) console.log(formatLogMessage('cloudflare', `Found Turnstile iframe using selector: ${selector}`));
970
1196
  break;
971
1197
  }
972
1198
  } catch (e) {
973
- if (forceDebug) console.log(`[debug][cloudflare] Selector ${selector} not found or timed out`);
1199
+ if (forceDebug) console.log(formatLogMessage('cloudflare', `Selector ${selector} not found or timed out`));
974
1200
  continue;
975
1201
  }
976
1202
  }
977
1203
 
978
1204
  if (turnstileFrame) {
979
1205
  if (forceDebug) {
980
- console.log(`[debug][cloudflare] Found Turnstile iframe with URL: ${turnstileFrame.url()}`);
1206
+ console.log(formatLogMessage('cloudflare', `Found Turnstile iframe with URL: ${turnstileFrame.url()}`));
981
1207
  }
982
1208
 
983
1209
  const checkboxSelectors = [
@@ -997,10 +1223,10 @@ async function handleTurnstileChallenge(page, forceDebug = false) {
997
1223
  await waitForTimeout(page, FAST_TIMEOUTS.ELEMENT_INTERACTION_DELAY);
998
1224
  await turnstileFrame.click(selector);
999
1225
 
1000
- if (forceDebug) console.log(`[debug][cloudflare] Clicked Turnstile checkbox: ${selector}`);
1226
+ if (forceDebug) console.log(formatLogMessage('cloudflare', `Clicked Turnstile checkbox: ${selector}`));
1001
1227
  break;
1002
1228
  } catch (e) {
1003
- if (forceDebug) console.log(`[debug][cloudflare] Checkbox selector ${selector} not found or failed to click`);
1229
+ if (forceDebug) console.log(formatLogMessage('cloudflare', `Checkbox selector ${selector} not found or failed to click`));
1004
1230
  continue;
1005
1231
  }
1006
1232
  }
@@ -1017,11 +1243,11 @@ async function handleTurnstileChallenge(page, forceDebug = false) {
1017
1243
  new Promise((_, reject) => setTimeout(() => reject(new Error('Turnstile completion timeout')), TIMEOUTS.TURNSTILE_COMPLETION_BUFFER))
1018
1244
  ]);
1019
1245
 
1020
- if (forceDebug) console.log(`[debug][cloudflare] Turnstile response token generated successfully`);
1246
+ if (forceDebug) console.log(formatLogMessage('cloudflare', `Turnstile response token generated successfully`));
1021
1247
  result.success = true;
1022
1248
  } else {
1023
1249
  // Try container-based Turnstile (non-iframe)
1024
- if (forceDebug) console.log(`[debug][cloudflare] No Turnstile iframe found, trying container-based approach`);
1250
+ if (forceDebug) console.log(formatLogMessage('cloudflare', `No Turnstile iframe found, trying container-based approach`));
1025
1251
 
1026
1252
  const containerSelectors = [
1027
1253
  '.cf-turnstile',
@@ -1039,29 +1265,29 @@ async function handleTurnstileChallenge(page, forceDebug = false) {
1039
1265
  await waitForTimeout(page, FAST_TIMEOUTS.ELEMENT_INTERACTION_DELAY);
1040
1266
  await page.click(selector);
1041
1267
 
1042
- if (forceDebug) console.log(`[debug][cloudflare] Clicked Turnstile container: ${selector}`);
1268
+ if (forceDebug) console.log(formatLogMessage('cloudflare', `Clicked Turnstile container: ${selector}`));
1043
1269
 
1044
1270
  const completionCheck = await checkChallengeCompletion(page);
1045
1271
  if (completionCheck.isCompleted) {
1046
1272
  result.success = true;
1047
- if (forceDebug) console.log(`[debug][cloudflare] Container-based Turnstile completed successfully`);
1273
+ if (forceDebug) console.log(formatLogMessage('cloudflare', `Container-based Turnstile completed successfully`));
1048
1274
  break;
1049
1275
  }
1050
1276
  } catch (e) {
1051
- if (forceDebug) console.log(`[debug][cloudflare] Container selector ${selector} not found or failed`);
1277
+ if (forceDebug) console.log(formatLogMessage('cloudflare', `Container selector ${selector} not found or failed`));
1052
1278
  continue;
1053
1279
  }
1054
1280
  }
1055
1281
 
1056
1282
  if (!result.success) {
1057
1283
  result.error = 'Turnstile iframe/container not found or not interactive';
1058
- if (forceDebug) console.log(`[debug][cloudflare] ${result.error}`);
1284
+ if (forceDebug) console.log(formatLogMessage('cloudflare', result.error));
1059
1285
  }
1060
1286
  }
1061
1287
 
1062
1288
  } catch (error) {
1063
1289
  result.error = `Turnstile handling failed: ${error.message}`;
1064
- if (forceDebug) console.log(`[debug][cloudflare] Turnstile handling error: ${error.message}`);
1290
+ if (forceDebug) console.log(formatLogMessage('cloudflare', `Turnstile handling error: ${error.message}`));
1065
1291
  }
1066
1292
 
1067
1293
  return result;
@@ -1077,7 +1303,7 @@ async function handleLegacyCheckbox(page, forceDebug = false) {
1077
1303
  };
1078
1304
 
1079
1305
  try {
1080
- if (forceDebug) console.log(`[debug][cloudflare] Attempting legacy checkbox challenge`);
1306
+ if (forceDebug) console.log(formatLogMessage('cloudflare', `Attempting legacy checkbox challenge`));
1081
1307
 
1082
1308
  const legacySelectors = [
1083
1309
  'input[type="checkbox"]#challenge-form',
@@ -1095,29 +1321,29 @@ async function handleLegacyCheckbox(page, forceDebug = false) {
1095
1321
  const checkbox = await page.$(selector);
1096
1322
  if (checkbox) {
1097
1323
  await checkbox.click();
1098
- if (forceDebug) console.log(`[debug][cloudflare] Clicked legacy checkbox: ${selector}`);
1324
+ if (forceDebug) console.log(formatLogMessage('cloudflare', `Clicked legacy checkbox: ${selector}`));
1099
1325
 
1100
1326
  const completionCheck = await checkChallengeCompletion(page);
1101
1327
  if (completionCheck.isCompleted) {
1102
1328
  result.success = true;
1103
- if (forceDebug) console.log(`[debug][cloudflare] Legacy checkbox challenge completed successfully`);
1329
+ if (forceDebug) console.log(formatLogMessage('cloudflare', `Legacy checkbox challenge completed successfully`));
1104
1330
  break;
1105
1331
  }
1106
1332
  }
1107
1333
  } catch (e) {
1108
- if (forceDebug) console.log(`[debug][cloudflare] Legacy selector ${selector} failed: ${e.message}`);
1334
+ if (forceDebug) console.log(formatLogMessage('cloudflare', `Legacy selector ${selector} failed: ${e.message}`));
1109
1335
  continue;
1110
1336
  }
1111
1337
  }
1112
1338
 
1113
1339
  if (!result.success) {
1114
1340
  result.error = 'No interactive legacy checkbox found';
1115
- if (forceDebug) console.log(`[debug][cloudflare] ${result.error}`);
1341
+ if (forceDebug) console.log(formatLogMessage('cloudflare', result.error));
1116
1342
  }
1117
1343
 
1118
1344
  } catch (error) {
1119
1345
  result.error = `Legacy checkbox handling failed: ${error.message}`;
1120
- if (forceDebug) console.log(`[debug][cloudflare] Legacy checkbox error: ${error.message}`);
1346
+ if (forceDebug) console.log(formatLogMessage('cloudflare', `Legacy checkbox error: ${error.message}`));
1121
1347
  }
1122
1348
 
1123
1349
  return result;
@@ -1191,13 +1417,13 @@ async function checkChallengeCompletion(page) {
1191
1417
  */
1192
1418
  async function handleCloudflareProtection(page, currentUrl, siteConfig, forceDebug = false) {
1193
1419
  if (forceDebug) {
1194
- console.log(`[debug][cloudflare] Using Cloudflare module v${CLOUDFLARE_MODULE_VERSION} for ${currentUrl}`);
1420
+ console.log(formatLogMessage('cloudflare', `Using Cloudflare module v${CLOUDFLARE_MODULE_VERSION} for ${currentUrl}`));
1195
1421
  }
1196
1422
 
1197
1423
  // VALIDATE URL FIRST - Skip protection handling for non-HTTP(S) URLs
1198
1424
  if (!shouldProcessUrl(currentUrl, forceDebug)) {
1199
1425
  if (forceDebug) {
1200
- console.log(`[debug][cloudflare] Skipping protection handling for non-HTTP(S) URL: ${currentUrl}`);
1426
+ console.log(formatLogMessage('cloudflare', `Skipping protection handling for non-HTTP(S) URL: ${currentUrl}`));
1201
1427
  }
1202
1428
  return {
1203
1429
  phishingWarning: { attempted: false, success: true },
@@ -1216,8 +1442,8 @@ async function handleCloudflareProtection(page, currentUrl, siteConfig, forceDeb
1216
1442
 
1217
1443
  // Only proceed if we have indicators OR explicit config enables Cloudflare handling
1218
1444
  if (!quickDetection.hasIndicators && !siteConfig.cloudflare_phish && !siteConfig.cloudflare_bypass) {
1219
- if (forceDebug) console.log(`[debug][cloudflare] No Cloudflare indicators found and no explicit config, skipping protection handling for ${currentUrl}`);
1220
- if (forceDebug) console.log(`[debug][cloudflare] Quick detection details: title="${quickDetection.title}", bodySnippet="${quickDetection.bodySnippet}"`);
1445
+ if (forceDebug) console.log(formatLogMessage('cloudflare', `No Cloudflare indicators found and no explicit config, skipping protection handling for ${currentUrl}`));
1446
+ if (forceDebug) console.log(formatLogMessage('cloudflare', `Quick detection details: title="${quickDetection.title}", bodySnippet="${quickDetection.bodySnippet}"`));
1221
1447
  return {
1222
1448
  phishingWarning: { attempted: false, success: true },
1223
1449
  verificationChallenge: { attempted: false, success: true },
@@ -1249,14 +1475,14 @@ async function handleCloudflareProtection(page, currentUrl, siteConfig, forceDeb
1249
1475
  }
1250
1476
 
1251
1477
  if (forceDebug) {
1252
- console.log(`[debug][cloudflare] Using adaptive timeout of ${adaptiveTimeout}ms for ${currentUrl} (indicators: ${quickDetection.hasIndicators}, explicit config: ${!!(siteConfig.cloudflare_phish || siteConfig.cloudflare_bypass)})`);
1478
+ console.log(formatLogMessage('cloudflare', `Using adaptive timeout of ${adaptiveTimeout}ms for ${currentUrl} (indicators: ${quickDetection.hasIndicators}, explicit config: ${!!(siteConfig.cloudflare_phish || siteConfig.cloudflare_bypass)})`));
1253
1479
  }
1254
1480
 
1255
1481
  return await Promise.race([
1256
1482
  performCloudflareHandling(page, currentUrl, siteConfig, forceDebug),
1257
1483
  new Promise((resolve) => {
1258
1484
  setTimeout(() => {
1259
- console.warn(`[cloudflare] Adaptive timeout (${adaptiveTimeout}ms) for ${currentUrl} - continuing with scan`);
1485
+ console.warn(formatLogMessage('cloudflare', `Adaptive timeout (${adaptiveTimeout}ms) for ${currentUrl} - continuing with scan`));
1260
1486
  resolve({
1261
1487
  phishingWarning: { attempted: false, success: true },
1262
1488
  verificationChallenge: { attempted: false, success: true },
@@ -1270,7 +1496,7 @@ async function handleCloudflareProtection(page, currentUrl, siteConfig, forceDeb
1270
1496
  } catch (error) {
1271
1497
  result.overallSuccess = false;
1272
1498
  result.errors.push(`Cloudflare handling failed: ${error.message}`);
1273
- if (forceDebug) console.log(`[debug][cloudflare] Overall handling failed: ${error.message}`);
1499
+ if (forceDebug) console.log(formatLogMessage('cloudflare', `Overall handling failed: ${error.message}`));
1274
1500
  return result;
1275
1501
  }
1276
1502
  }
@@ -1292,26 +1518,40 @@ async function performCloudflareHandling(page, currentUrl, siteConfig, forceDebu
1292
1518
  errors: []
1293
1519
  };
1294
1520
 
1295
- if (forceDebug) console.log(`[debug][cloudflare] Starting Cloudflare protection handling for ${currentUrl}`);
1521
+ if (forceDebug) console.log(formatLogMessage('cloudflare', `Starting Cloudflare protection handling for ${currentUrl}`));
1296
1522
 
1297
1523
  // Handle phishing warnings first - updates result.phishingWarning
1298
1524
  // Only runs if siteConfig.cloudflare_phish === true
1299
1525
  // Handle phishing warnings if enabled
1300
1526
  if (siteConfig.cloudflare_phish === true) {
1301
- if (forceDebug) console.log(`[debug][cloudflare] Phishing warning bypass enabled for ${currentUrl}`);
1527
+ if (forceDebug) console.log(formatLogMessage('cloudflare', `Phishing warning bypass enabled for ${currentUrl}`));
1302
1528
 
1303
- const phishingResult = await handlePhishingWarning(page, currentUrl, forceDebug);
1529
+ const phishingResult = await handlePhishingWarningWithRetries(page, currentUrl, siteConfig, forceDebug);
1304
1530
  result.phishingWarning = phishingResult;
1531
+
1532
+ // Check for max retries exceeded
1533
+ if (phishingResult.maxRetriesExceeded) {
1534
+ result.overallSuccess = false;
1535
+ result.errors.push(`Phishing warning bypass exceeded max retries (${phishingResult.attempts}): ${phishingResult.error}`);
1536
+ if (forceDebug) console.log(formatLogMessage('cloudflare', `Phishing warning max retries exceeded: ${phishingResult.error}`));
1537
+ // Exit early if max retries exceeded
1538
+ return result;
1539
+ }
1305
1540
 
1306
1541
  if (phishingResult.attempted && !phishingResult.success) {
1307
1542
  result.overallSuccess = false;
1308
- result.errors.push(`Phishing warning bypass failed: ${phishingResult.error}`);
1309
- if (forceDebug) console.log(`[debug][cloudflare] Phishing warning handling failed: ${phishingResult.error}`);
1543
+ if (phishingResult.loopDetected) {
1544
+ result.errors.push(`Phishing warning bypass failed (redirect loop): ${phishingResult.error}`);
1545
+ if (forceDebug) console.log(formatLogMessage('cloudflare', `Phishing warning redirect loop detected: ${phishingResult.error}`));
1546
+ } else {
1547
+ result.errors.push(`Phishing warning bypass failed: ${phishingResult.error}`);
1548
+ if (forceDebug) console.log(formatLogMessage('cloudflare', `Phishing warning handling failed: ${phishingResult.error}`));
1549
+ }
1310
1550
  } else if (phishingResult.attempted && phishingResult.success) {
1311
- if (forceDebug) console.log(`[debug][cloudflare] Phishing warning handled successfully`);
1551
+ if (forceDebug) console.log(formatLogMessage('cloudflare', `Phishing warning handled successfully`));
1312
1552
  }
1313
1553
  } else if (forceDebug) {
1314
- console.log(`[debug][cloudflare] Phishing warning bypass disabled for ${currentUrl}`);
1554
+ console.log(formatLogMessage('cloudflare', `Phishing warning bypass disabled for ${currentUrl}`));
1315
1555
  }
1316
1556
 
1317
1557
  // Handle verification challenges second - updates result.verificationChallenge
@@ -1319,37 +1559,49 @@ async function performCloudflareHandling(page, currentUrl, siteConfig, forceDebu
1319
1559
  // Sets requiresHuman: true if CAPTCHA detected (no bypass attempted)
1320
1560
  // Handle verification challenges if enabled
1321
1561
  if (siteConfig.cloudflare_bypass === true) {
1322
- if (forceDebug) console.log(`[debug][cloudflare] Challenge bypass enabled for ${currentUrl}`);
1562
+ if (forceDebug) console.log(formatLogMessage('cloudflare', `Challenge bypass enabled for ${currentUrl}`));
1323
1563
 
1324
- const challengeResult = await handleVerificationChallenge(page, currentUrl, forceDebug);
1564
+ const challengeResult = await handleVerificationChallengeWithRetries(page, currentUrl, siteConfig, forceDebug);
1325
1565
  result.verificationChallenge = challengeResult;
1566
+
1567
+ // Check for max retries exceeded
1568
+ if (challengeResult.maxRetriesExceeded) {
1569
+ result.overallSuccess = false;
1570
+ result.errors.push(`Challenge bypass exceeded max retries (${challengeResult.attempts}): ${challengeResult.error}`);
1571
+ if (forceDebug) console.log(formatLogMessage('cloudflare', `Challenge bypass max retries exceeded: ${challengeResult.error}`));
1572
+ // Exit early if max retries exceeded
1573
+ return result;
1574
+ }
1326
1575
 
1327
1576
  if (challengeResult.attempted && !challengeResult.success) {
1328
1577
  result.overallSuccess = false;
1329
1578
  if (challengeResult.requiresHuman) {
1330
1579
  result.errors.push(`Human intervention required: ${challengeResult.error}`);
1331
- if (forceDebug) console.log(`[debug][cloudflare] Human intervention required: ${challengeResult.error}`);
1580
+ if (forceDebug) console.log(formatLogMessage('cloudflare', `Human intervention required: ${challengeResult.error}`));
1581
+ } else if (challengeResult.loopDetected) {
1582
+ result.errors.push(`Challenge bypass failed (redirect loop): ${challengeResult.error}`);
1583
+ if (forceDebug) console.log(formatLogMessage('cloudflare', `Challenge redirect loop detected: ${challengeResult.error}`));
1332
1584
  } else {
1333
1585
  result.errors.push(`Challenge bypass failed: ${challengeResult.error}`);
1334
- if (forceDebug) console.log(`[debug][cloudflare] Challenge bypass failed: ${challengeResult.error}`);
1586
+ if (forceDebug) console.log(formatLogMessage('cloudflare', `Challenge bypass failed: ${challengeResult.error}`));
1335
1587
  }
1336
1588
  } else if (challengeResult.attempted && challengeResult.success) {
1337
- if (forceDebug) console.log(`[debug][cloudflare] Challenge handled successfully using method: ${challengeResult.method || 'unknown'}`);
1589
+ if (forceDebug) console.log(formatLogMessage('cloudflare', `Challenge handled successfully using method: ${challengeResult.method || 'unknown'}`));
1338
1590
  }
1339
1591
  } else if (forceDebug) {
1340
- console.log(`[debug][cloudflare] Challenge bypass disabled for ${currentUrl}`);
1592
+ console.log(formatLogMessage('cloudflare', `Challenge bypass disabled for ${currentUrl}`));
1341
1593
  }
1342
1594
 
1343
1595
  // Log overall result
1344
1596
  if (!result.overallSuccess && forceDebug) {
1345
- console.log(`[debug][cloudflare] Overall Cloudflare handling failed for ${currentUrl}:`);
1597
+ console.log(formatLogMessage('cloudflare', `Overall Cloudflare handling failed for ${currentUrl}:`));
1346
1598
  result.errors.forEach(error => {
1347
- console.log(`[debug][cloudflare] - ${error}`);
1599
+ console.log(formatLogMessage('cloudflare', ` - ${error}`));
1348
1600
  });
1349
1601
  } else if ((result.phishingWarning.attempted || result.verificationChallenge.attempted) && forceDebug) {
1350
- console.log(`[debug][cloudflare] Successfully handled Cloudflare protections for ${currentUrl}`);
1602
+ console.log(formatLogMessage('cloudflare', `Successfully handled Cloudflare protections for ${currentUrl}`));
1351
1603
  } else if (forceDebug) {
1352
- console.log(`[debug][cloudflare] No Cloudflare protections detected or enabled for ${currentUrl}`);
1604
+ console.log(formatLogMessage('cloudflare', `No Cloudflare protections detected or enabled for ${currentUrl}`));
1353
1605
  }
1354
1606
 
1355
1607
  return result;
@@ -1412,7 +1664,7 @@ async function parallelChallengeDetection(page, forceDebug = false) {
1412
1664
  const detectedChallenges = results.filter(r => r.detected).map(r => r.type);
1413
1665
 
1414
1666
  if (forceDebug && detectedChallenges.length > 0) {
1415
- console.log(`[debug][cloudflare] Parallel detection found challenges: ${detectedChallenges.join(', ')}`);
1667
+ console.log(formatLogMessage('cloudflare', `Parallel detection found challenges: ${detectedChallenges.join(', ')}`));
1416
1668
  }
1417
1669
 
1418
1670
  return {
@@ -1480,5 +1732,7 @@ module.exports = {
1480
1732
  clearDetectionCache,
1481
1733
  categorizeError,
1482
1734
  ERROR_TYPES,
1483
- RETRY_CONFIG
1735
+ RETRY_CONFIG,
1736
+ getRetryConfig,
1737
+ detectChallengeLoop
1484
1738
  };