@fanboynz/network-scanner 2.0.3 → 2.0.5

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.
@@ -9,7 +9,7 @@ const { formatLogMessage, messageColors } = require('./colorize');
9
9
  // Window cleanup delay constant
10
10
  const WINDOW_CLEANUP_DELAY_MS = 15000;
11
11
  // window_clean REALTIME
12
- const REALTIME_CLEANUP_BUFFER_MS = 15000; // Additional buffer time after site delay. Delay increased to fix missing hits.
12
+ const REALTIME_CLEANUP_BUFFER_MS = 35000; // Additional buffer time after site delay (increased for Cloudflare)
13
13
  const REALTIME_CLEANUP_THRESHOLD = 8; // Default number of pages to keep
14
14
  const REALTIME_CLEANUP_MIN_PAGES = 3; // Minimum pages before cleanup kicks in
15
15
 
@@ -270,14 +270,14 @@ function updatePageUsage(page, isProcessing = false) {
270
270
 
271
271
  /**
272
272
  * Performs realtime window cleanup - removes oldest pages when threshold is exceeded
273
- * Waits for site delay + 3 seconds before cleanup to ensure delayed requests are captured
273
+ * Waits for site delay + buffer before cleanup, with extended buffer for Cloudflare sites
274
274
  * @param {import('puppeteer').Browser} browserInstance - Browser instance
275
275
  * @param {number} threshold - Maximum number of pages to keep (default: 8)
276
276
  * @param {boolean} forceDebug - Debug logging flag
277
- * @param {number} siteDelay - Current site's delay value in milliseconds (default: 4000)
277
+ * @param {number} totalDelay - Total delay including site delay and appropriate buffer (default: 4000 + 15000)
278
278
  * @returns {Promise<Object>} Cleanup results
279
279
  */
280
- async function performRealtimeWindowCleanup(browserInstance, threshold = REALTIME_CLEANUP_THRESHOLD, forceDebug, siteDelay = 4000) {
280
+ async function performRealtimeWindowCleanup(browserInstance, threshold = REALTIME_CLEANUP_THRESHOLD, forceDebug, totalDelay = 19000) {
281
281
  try {
282
282
  const allPages = await browserInstance.pages();
283
283
 
@@ -289,11 +289,11 @@ async function performRealtimeWindowCleanup(browserInstance, threshold = REALTIM
289
289
  return { success: true, closedCount: 0, totalPages: allPages.length, reason: 'below_threshold' };
290
290
  }
291
291
 
292
- // Calculate cleanup delay: site delay + 3 second buffer
293
- const cleanupDelay = siteDelay + REALTIME_CLEANUP_BUFFER_MS;
292
+ // Use the provided total delay (already includes appropriate buffer)
293
+ const cleanupDelay = totalDelay;
294
294
 
295
295
  if (forceDebug) {
296
- console.log(formatLogMessage('debug', `[realtime_cleanup] Waiting ${cleanupDelay}ms (site delay: ${siteDelay}ms + ${REALTIME_CLEANUP_BUFFER_MS}ms buffer) before cleanup (threshold: ${threshold})`));
296
+ console.log(formatLogMessage('debug', `[realtime_cleanup] Waiting ${cleanupDelay}ms before cleanup (threshold: ${threshold})`));
297
297
  }
298
298
  await new Promise(resolve => setTimeout(resolve, cleanupDelay));
299
299
 
@@ -999,4 +999,4 @@ if (originalPageClose) {
999
999
  }
1000
1000
  return originalPageClose.apply(this, args);
1001
1001
  };
1002
- }
1002
+ }
package/lib/cloudflare.js CHANGED
@@ -1,5 +1,7 @@
1
1
  /**
2
2
  * Cloudflare bypass and challenge handling module - Optimized with smart detection and adaptive timeouts
3
+ * Version: 2.6.2 - Further detached Frame fixes
4
+ * Version: 2.6.1 - timeoutId is not defined & race condition fix
3
5
  * Version: 2.6.0 - Memory leak fixes and timeout cleanup
4
6
  * Version: 2.5.0 - Fix Frame Lifecycle issue, Timing and Race condition
5
7
  * Version: 2.4.1 - Bump timeout values
@@ -17,7 +19,7 @@ const { formatLogMessage } = require('./colorize');
17
19
  /**
18
20
  * Module version information
19
21
  */
20
- const CLOUDFLARE_MODULE_VERSION = '2.6.0';
22
+ const CLOUDFLARE_MODULE_VERSION = '2.6.1';
21
23
 
22
24
  /**
23
25
  * Timeout constants for various operations (in milliseconds)
@@ -25,11 +27,11 @@ const CLOUDFLARE_MODULE_VERSION = '2.6.0';
25
27
  * All values tuned for maximum scanning speed while maintaining functionality
26
28
  */
27
29
  const TIMEOUTS = {
28
- PAGE_EVALUATION: 8000, // Standard page evaluation timeout
29
- PAGE_EVALUATION_SAFE: 10000, // Safe page evaluation with extra buffer
30
+ PAGE_EVALUATION: 12000, // Standard page evaluation timeout
31
+ PAGE_EVALUATION_SAFE: 12000, // Safe page evaluation with extra buffer
30
32
  PHISHING_CLICK: 3000, // Timeout for clicking phishing continue button
31
33
  PHISHING_NAVIGATION: 8000, // Wait for navigation after phishing bypass
32
- JS_CHALLENGE_BUFFER: 30000, // JS challenge with safety buffer
34
+ JS_CHALLENGE_BUFFER: 26000, // JS challenge with safety buffer
33
35
  TURNSTILE_COMPLETION: 20000, // Turnstile completion check
34
36
  TURNSTILE_COMPLETION_BUFFER: 25000, // Turnstile completion with buffer
35
37
  CLICK_TIMEOUT: 5000, // Standard click operation timeout
@@ -49,13 +51,13 @@ const TIMEOUTS = {
49
51
 
50
52
  // Fast timeout constants - optimized for speed
51
53
  const FAST_TIMEOUTS = {
52
- QUICK_DETECTION: 2000, // Fast Cloudflare detection
54
+ QUICK_DETECTION: 4000, // Fast Cloudflare detection
53
55
  PHISHING_WAIT: 1000, // Fast phishing check
54
56
  CHALLENGE_WAIT: 500, // Fast challenge detection
55
57
  ELEMENT_INTERACTION_DELAY: 250, // Fast element interactions
56
- SELECTOR_WAIT: 1500, // Fast selector waits
58
+ SELECTOR_WAIT: 3000, // Fast selector waits
57
59
  TURNSTILE_OPERATION: 6000, // Fast Turnstile operations
58
- JS_CHALLENGE: 22000, // Fast JS challenge completion
60
+ JS_CHALLENGE: 19000, // Fast JS challenge completion
59
61
  CHALLENGE_SOLVING: 30000, // Fast overall challenge solving
60
62
  CHALLENGE_COMPLETION: 8000 // Fast completion check
61
63
  };
@@ -319,21 +321,68 @@ async function safePageEvaluate(page, func, timeout = TIMEOUTS.PAGE_EVALUATION_S
319
321
  let lastError = null;
320
322
 
321
323
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
324
+ let timeoutId = null;
325
+
322
326
  try {
323
- // Validate page/frame state before evaluation
327
+ // Multi-layered page state validation
324
328
  if (page.isClosed()) {
325
- throw new Error('Page is closed');
329
+ throw new Error('Page is closed or invalid');
326
330
  }
327
331
 
328
- // Check if main frame is still attached
329
- const mainFrame = page.mainFrame();
330
- if (mainFrame.isDetached()) {
331
- throw new Error('Main frame is detached');
332
+ // Check if page is still navigating or has valid context
333
+ let currentUrl;
334
+ try {
335
+ currentUrl = await page.url();
336
+ if (!currentUrl || currentUrl === 'about:blank') {
337
+ throw new Error('Page URL is invalid or blank');
338
+ }
339
+ } catch (urlError) {
340
+ throw new Error('Page URL access failed - likely detached');
341
+ }
342
+
343
+ // Quick execution context validation with timeout
344
+ const contextValid = await Promise.race([
345
+ page.evaluate(() => {
346
+ try {
347
+ // Quick context validation
348
+ if (typeof window === 'undefined' || !document) {
349
+ return false;
350
+ }
351
+ // Check if document is ready for interaction
352
+ if (document.readyState === 'uninitialized') {
353
+ return false;
354
+ }
355
+ return true;
356
+ } catch (e) {
357
+ return false;
358
+ }
359
+ }),
360
+ new Promise((_, reject) => {
361
+ setTimeout(() => reject(new Error('Context validation timeout')), 3500);
362
+ })
363
+ ]).catch(() => false);
364
+
365
+ if (!contextValid) {
366
+ throw new Error('Page execution context is invalid');
332
367
  }
333
368
 
334
- let timeoutId = null;
335
369
  const result = await Promise.race([
336
- page.evaluate(func),
370
+ page.evaluate(() => {
371
+ // Additional runtime validation inside evaluation
372
+ try {
373
+ if (typeof window === 'undefined' || !document) {
374
+ throw new Error('Execution context invalid during evaluation');
375
+ }
376
+ return func();
377
+ } catch (evalError) {
378
+ // Return error info instead of throwing to avoid unhandled promise rejections
379
+ return {
380
+ __evaluation_error: true,
381
+ message: evalError.message,
382
+ type: 'evaluation_error'
383
+ };
384
+ }
385
+ }),
337
386
  new Promise((_, reject) => {
338
387
  timeoutId = setTimeout(() => reject(new Error('Page evaluation timeout')), timeout);
339
388
  })
@@ -342,9 +391,13 @@ async function safePageEvaluate(page, func, timeout = TIMEOUTS.PAGE_EVALUATION_S
342
391
  // Clear timeout if evaluation completed first
343
392
  if (timeoutId) {
344
393
  clearTimeout(timeoutId);
345
- timeoutId = null;
346
394
  }
347
-
395
+
396
+ // Check if evaluation returned an error
397
+ if (result && result.__evaluation_error) {
398
+ throw new Error(`Evaluation failed: ${result.message}`);
399
+ }
400
+
348
401
  if (forceDebug && attempt > 1) {
349
402
  console.log(formatLogMessage('cloudflare', `Page evaluation succeeded on attempt ${attempt}`));
350
403
  }
@@ -354,7 +407,6 @@ async function safePageEvaluate(page, func, timeout = TIMEOUTS.PAGE_EVALUATION_S
354
407
  // Ensure timeout is cleared on any error
355
408
  if (timeoutId) {
356
409
  clearTimeout(timeoutId);
357
- timeoutId = null;
358
410
  }
359
411
 
360
412
  lastError = error;
@@ -369,23 +421,19 @@ async function safePageEvaluate(page, func, timeout = TIMEOUTS.PAGE_EVALUATION_S
369
421
  if (forceDebug) {
370
422
  console.warn(formatLogMessage('cloudflare', `Detached frame detected on attempt ${attempt}/${maxRetries} - using longer delay`));
371
423
  }
372
- // For detached frames, wait longer and don't retry as aggressively
373
- await new Promise(resolve => setTimeout(resolve, 2000)); // Longer delay
424
+ // For detached frames, wait longer
425
+ await new Promise(resolve => setTimeout(resolve, 3000)); // Longer delay
426
+
427
+ // For detached frames, only retry once more
428
+ if (attempt >= 2) {
429
+ break;
430
+ }
374
431
  continue;
375
432
  }
376
433
 
377
434
  // Don't retry if error type is not retryable or if it's the last attempt
378
435
  if (!RETRY_CONFIG.retryableErrors.includes(errorType) || attempt === maxRetries) {
379
- return {
380
- isChallengePresent: false,
381
- isPhishingWarning: false,
382
- isTurnstile: false,
383
- isJSChallenge: false,
384
- isChallengeCompleted: false,
385
- error: error.message,
386
- errorType: errorType,
387
- attempts: attempt
388
- };
436
+ break;
389
437
  }
390
438
 
391
439
  // Wait before retrying with exponential backoff
@@ -393,19 +441,7 @@ async function safePageEvaluate(page, func, timeout = TIMEOUTS.PAGE_EVALUATION_S
393
441
  }
394
442
  }
395
443
 
396
- // If all retries failed due to detached frames, return safe defaults
397
- if (lastError?.message.includes('detached Frame') || lastError?.message.includes('Attempted to use detached')) {
398
- return {
399
- isChallengePresent: false,
400
- isPhishingWarning: false,
401
- isTurnstile: false,
402
- isJSChallenge: false,
403
- isChallengeCompleted: false,
404
- error: 'Frame detached - skipping evaluation',
405
- errorType: ERROR_TYPES.DETACHED_FRAME,
406
- attempts: maxRetries
407
- };
408
- }
444
+ // Return safe defaults if all retries failed
409
445
 
410
446
  return {
411
447
  isChallengePresent: false,
@@ -427,8 +463,7 @@ async function safeClick(page, selector, timeout = TIMEOUTS.CLICK_TIMEOUT) {
427
463
  return await Promise.race([
428
464
  page.click(selector, { timeout: timeout }),
429
465
  new Promise((_, reject) => {
430
- const timerId = setTimeout(() => reject(new Error('Click timeout')), timeout + TIMEOUTS.CLICK_TIMEOUT_BUFFER);
431
- // Timer will be cleared when promise resolves/rejects
466
+ setTimeout(() => reject(new Error('Click timeout')), timeout + TIMEOUTS.CLICK_TIMEOUT_BUFFER);
432
467
  })
433
468
  ]);
434
469
  } catch (error) {
@@ -444,8 +479,7 @@ async function safeWaitForNavigation(page, timeout = TIMEOUTS.NAVIGATION_TIMEOUT
444
479
  return await Promise.race([
445
480
  page.waitForNavigation({ waitUntil: 'domcontentloaded', timeout: timeout }),
446
481
  new Promise((_, reject) => {
447
- const timerId = setTimeout(() => reject(new Error('Navigation timeout')), timeout + TIMEOUTS.NAVIGATION_TIMEOUT_BUFFER);
448
- // Timer will be cleared when promise resolves/rejects
482
+ setTimeout(() => reject(new Error('Navigation timeout')), timeout + TIMEOUTS.NAVIGATION_TIMEOUT_BUFFER);
449
483
  })
450
484
  ]);
451
485
  } catch (error) {
@@ -963,13 +997,14 @@ async function attemptChallengeSolveWithTimeout(page, currentUrl, challengeInfo,
963
997
  const result = {
964
998
  success: false,
965
999
  error: null,
966
- method: null,
967
- _timeoutId: null
1000
+ method: null
968
1001
  };
1002
+
1003
+ let timeoutId = null;
969
1004
 
970
1005
  try {
971
1006
  const timeoutPromise = new Promise((_, reject) => {
972
- result._timeoutId = setTimeout(() => reject(new Error('Challenge solving timeout')), FAST_TIMEOUTS.CHALLENGE_SOLVING);
1007
+ timeoutId = setTimeout(() => reject(new Error('Challenge solving timeout')), FAST_TIMEOUTS.CHALLENGE_SOLVING);
973
1008
  });
974
1009
  // Reduced timeout for challenge solving
975
1010
  const finalResult = await Promise.race([
@@ -977,15 +1012,15 @@ async function attemptChallengeSolveWithTimeout(page, currentUrl, challengeInfo,
977
1012
  timeoutPromise
978
1013
  ]);
979
1014
  // Clear timeout if operation completed first
980
- if (result._timeoutId) {
981
- clearTimeout(result._timeoutId);
1015
+ if (timeoutId) {
1016
+ clearTimeout(timeoutId);
982
1017
  }
983
1018
  return finalResult;
984
1019
 
985
1020
  } catch (error) {
986
1021
  // Clear timeout on error
987
- if (result._timeoutId) {
988
- clearTimeout(result._timeoutId);
1022
+ if (timeoutId) {
1023
+ clearTimeout(timeoutId);
989
1024
  }
990
1025
  result.error = `Challenge solving timed out: ${error.message}`;
991
1026
  if (forceDebug) console.log(formatLogMessage('cloudflare', `Challenge solving timeout for ${currentUrl}`));
@@ -1205,15 +1240,16 @@ async function handleEmbeddedIframeChallenge(page, forceDebug = false) {
1205
1240
  async function waitForJSChallengeCompletion(page, forceDebug = false) {
1206
1241
  const result = {
1207
1242
  success: false,
1208
- error: null,
1209
- _timeoutId: null
1243
+ error: null
1210
1244
  };
1211
1245
 
1246
+ let timeoutId = null;
1247
+
1212
1248
  try {
1213
1249
  if (forceDebug) console.log(formatLogMessage('cloudflare', `Waiting for JS challenge completion`));
1214
1250
 
1215
1251
  const timeoutPromise = new Promise((_, reject) => {
1216
- result._timeoutId = setTimeout(() => reject(new Error('JS challenge timeout')), TIMEOUTS.JS_CHALLENGE_BUFFER);
1252
+ timeoutId = setTimeout(() => reject(new Error('JS challenge timeout')), TIMEOUTS.JS_CHALLENGE_BUFFER);
1217
1253
  });
1218
1254
 
1219
1255
  // Reduced timeout for JS challenge completion
@@ -1231,16 +1267,16 @@ async function waitForJSChallengeCompletion(page, forceDebug = false) {
1231
1267
  ]);
1232
1268
 
1233
1269
  // Clear timeout if completion detected first
1234
- if (result._timeoutId) {
1235
- clearTimeout(result._timeoutId);
1270
+ if (timeoutId) {
1271
+ clearTimeout(timeoutId);
1236
1272
  }
1237
1273
 
1238
1274
  result.success = true;
1239
1275
  if (forceDebug) console.log(formatLogMessage('cloudflare', `JS challenge completed automatically`));
1240
1276
  } catch (error) {
1241
1277
  // Clear timeout on error
1242
- if (result._timeoutId) {
1243
- clearTimeout(result._timeoutId);
1278
+ if (timeoutId) {
1279
+ clearTimeout(timeoutId);
1244
1280
  }
1245
1281
 
1246
1282
  result.error = `JS challenge timeout: ${error.message}`;
package/nwss.js CHANGED
@@ -1,4 +1,4 @@
1
- // === Network scanner script (nwss.js) v2.0.3 ===
1
+ // === Network scanner script (nwss.js) v2.0.5 ===
2
2
 
3
3
  // puppeteer for browser automation, fs for file system operations, psl for domain parsing.
4
4
  // const pLimit = require('p-limit'); // Will be dynamically imported
@@ -127,7 +127,7 @@ const { navigateWithRedirectHandling, handleRedirectTimeout } = require('./lib/r
127
127
  const { monitorBrowserHealth, isBrowserHealthy, isQuicklyResponsive, performGroupWindowCleanup, performRealtimeWindowCleanup, trackPageForRealtime, updatePageUsage } = require('./lib/browserhealth');
128
128
 
129
129
  // --- Script Configuration & Constants ---
130
- const VERSION = '2.0.3'; // Script version
130
+ const VERSION = '2.0.5'; // Script version
131
131
 
132
132
  // get startTime
133
133
  const startTime = Date.now();
@@ -1541,10 +1541,17 @@ function setupFrameHandling(page, forceDebug) {
1541
1541
  ? siteConfig.window_cleanup_threshold
1542
1542
  : REALTIME_CLEANUP_THRESHOLD;
1543
1543
 
1544
- // Get the site's delay value for cleanup timing
1544
+ // Calculate appropriate delay based on site configuration
1545
1545
  const siteDelay = siteConfig.delay || 4000;
1546
+ const hasCloudflareConfig = siteConfig.cloudflare_bypass || siteConfig.cloudflare_phish;
1547
+ const bufferTime = hasCloudflareConfig ? 23000 : REALTIME_CLEANUP_BUFFER_MS; // 23s for Cloudflare, 15s for normal
1548
+ const totalDelay = siteDelay + bufferTime;
1546
1549
 
1547
- const realtimeResult = await performRealtimeWindowCleanup(browserInstance, threshold, forceDebug, siteDelay);
1550
+ if (forceDebug && hasCloudflareConfig) {
1551
+ console.log(formatLogMessage('debug', `[realtime_cleanup] Using extended delay for Cloudflare site: ${totalDelay}ms (${siteDelay}ms + ${bufferTime}ms CF buffer)`));
1552
+ }
1553
+
1554
+ const realtimeResult = await performRealtimeWindowCleanup(browserInstance, threshold, forceDebug, totalDelay);
1548
1555
  if (realtimeResult.success && realtimeResult.closedCount > 0 && forceDebug) {
1549
1556
  console.log(formatLogMessage('debug', `[realtime_cleanup] Cleaned ${realtimeResult.closedCount} old pages, ${realtimeResult.remainingPages} remaining`));
1550
1557
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fanboynz/network-scanner",
3
- "version": "2.0.3",
3
+ "version": "2.0.5",
4
4
  "description": "A Puppeteer-based network scanner for analyzing web traffic, generating adblock filter rules, and identifying third-party requests. Features include fingerprint spoofing, Cloudflare bypass, content analysis with curl/grep, and multiple output formats.",
5
5
  "main": "nwss.js",
6
6
  "scripts": {