@fanboynz/network-scanner 2.0.34 → 2.0.36

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/lib/browserexit.js +15 -20
  2. package/nwss.js +105 -64
  3. package/package.json +1 -1
@@ -3,6 +3,10 @@
3
3
  * Provides graceful and forced browser closure functionality with comprehensive temp file cleanup
4
4
  */
5
5
 
6
+
7
+ const fs = require('fs');
8
+ const { execSync } = require('child_process');
9
+
6
10
  // Constants for temp file cleanup
7
11
  const CHROME_TEMP_PATHS = [
8
12
  '/tmp',
@@ -13,8 +17,7 @@ const CHROME_TEMP_PATHS = [
13
17
  const CHROME_TEMP_PATTERNS = [
14
18
  '.com.google.Chrome.*', // Google Chrome temp files
15
19
  '.org.chromium.Chromium.*',
16
- 'puppeteer-*',
17
- '.com.google.Chrome.*' // Ensure Google Chrome pattern is included
20
+ 'puppeteer-*'
18
21
  ];
19
22
 
20
23
  /**
@@ -33,7 +36,6 @@ async function cleanupChromeTempFiles(options = {}) {
33
36
  } = options;
34
37
 
35
38
  try {
36
- const { execSync } = require('child_process');
37
39
 
38
40
  // Base cleanup commands for standard temp directories
39
41
  const cleanupCommands = [
@@ -56,17 +58,17 @@ async function cleanupChromeTempFiles(options = {}) {
56
58
 
57
59
  for (const command of cleanupCommands) {
58
60
  try {
59
- // Get file count before cleanup for reporting
60
- const listCommand = command.replace('rm -rf', 'ls -1d').replace(' 2>/dev/null || true', ' 2>/dev/null | wc -l || echo 0');
61
- const fileCount = parseInt(execSync(listCommand, { stdio: 'pipe' }).toString().trim()) || 0;
61
+ // Extract glob pattern and count matches before deletion
62
+ const globPattern = command.match(/rm -rf ([^ ]+)/)?.[1];
63
+ if (!globPattern) continue;
64
+ const fileCount = parseInt(execSync(`ls -1d ${globPattern} 2>/dev/null | wc -l || echo 0`, { stdio: 'pipe' }).toString().trim()) || 0;
62
65
 
63
66
  if (fileCount > 0) {
64
67
  execSync(command, { stdio: 'ignore' });
65
68
  totalCleaned += fileCount;
66
69
 
67
70
  if (forceDebug) {
68
- const pathPattern = command.match(/rm -rf ([^ ]+)/)?.[1] || 'unknown';
69
- console.log(`[debug] [temp-cleanup] Cleaned ${fileCount} items from ${pathPattern}`);
71
+ console.log(`[debug] [temp-cleanup] Cleaned ${fileCount} items from ${globPattern}`);
70
72
  }
71
73
  }
72
74
  } catch (cmdErr) {
@@ -102,7 +104,6 @@ async function comprehensiveChromeTempCleanup(options = {}) {
102
104
  const { forceDebug = false, verbose = false } = options;
103
105
 
104
106
  try {
105
- const { execSync } = require('child_process');
106
107
  let totalCleaned = 0;
107
108
 
108
109
  if (verbose && !forceDebug) {
@@ -112,8 +113,7 @@ async function comprehensiveChromeTempCleanup(options = {}) {
112
113
  for (const basePath of CHROME_TEMP_PATHS) {
113
114
  // Check if the base path exists before trying to clean it
114
115
  try {
115
- const pathExists = execSync(`test -d "${basePath}" && echo "exists" || echo "missing"`, { stdio: 'pipe' })
116
- .toString().trim() === 'exists';
116
+ const pathExists = fs.existsSync(basePath);
117
117
 
118
118
  if (!pathExists) {
119
119
  if (forceDebug) {
@@ -149,7 +149,7 @@ async function comprehensiveChromeTempCleanup(options = {}) {
149
149
  if (verbose && totalCleaned > 0) {
150
150
  console.log(`[temp-cleanup] ? Removed ${totalCleaned} temporary file(s)/folder(s)`);
151
151
  } else if (verbose && totalCleaned === 0) {
152
- console.log(`[temp-cleanup] ?? No temporary files found to remove`);
152
+ console.log(`[temp-cleanup] ? Clean - no remaining temporary files`);
153
153
  } else if (forceDebug) {
154
154
  console.log(`[debug] [temp-cleanup] Comprehensive cleanup completed (${totalCleaned} items)`);
155
155
  }
@@ -179,7 +179,6 @@ async function cleanupUserDataDir(userDataDir, forceDebug = false) {
179
179
  }
180
180
 
181
181
  try {
182
- const fs = require('fs');
183
182
 
184
183
  if (!fs.existsSync(userDataDir)) {
185
184
  if (forceDebug) {
@@ -281,9 +280,7 @@ async function forceBrowserKill(browser, forceDebug = false) {
281
280
  const mainPid = browserProcess.pid;
282
281
  if (forceDebug) console.log(`[debug] [browser] Main Chrome PID: ${mainPid}`);
283
282
 
284
- // Find and kill ALL related Chrome processes
285
- const { execSync } = require('child_process');
286
-
283
+ // Find and kill ALL related Chrome processes
287
284
 
288
285
  try {
289
286
  // Find all Chrome processes with puppeteer in command line
@@ -390,9 +387,7 @@ async function forceBrowserKill(browser, forceDebug = false) {
390
387
  * @returns {Promise<void>}
391
388
  */
392
389
  async function killAllPuppeteerChrome(forceDebug = false) {
393
- try {
394
- const { execSync } = require('child_process');
395
-
390
+ try {
396
391
  if (forceDebug) console.log(`[debug] [browser] Nuclear option: killing all puppeteer Chrome processes...`);
397
392
 
398
393
  try {
@@ -439,7 +434,7 @@ async function handleBrowserExit(browser, options = {}) {
439
434
 
440
435
  const results = {
441
436
  browserClosed: false,
442
- tempFilescleaned: 0,
437
+ tempFilesCleanedCount: 0,
443
438
  userDataCleaned: false,
444
439
  success: false,
445
440
  errors: []
package/nwss.js CHANGED
@@ -694,6 +694,11 @@ const {
694
694
  ...otherGlobalConfig
695
695
  } = config;
696
696
 
697
+ // Pre-compile global blocked regexes ONCE (used in every processUrl call)
698
+ const globalBlockedRegexes = Array.isArray(globalBlocked)
699
+ ? globalBlocked.map(pattern => new RegExp(pattern))
700
+ : [];
701
+
697
702
  // Apply global configuration overrides with validation
698
703
  // Priority: Command line args > config.json > defaults
699
704
  const MAX_CONCURRENT_SITES = (() => {
@@ -943,6 +948,39 @@ if (dumpUrls) {
943
948
  }
944
949
  }
945
950
 
951
+ // --- Buffered Log Writer ---
952
+ // Avoids blocking I/O on every intercepted request in debug/dumpurls mode
953
+ const _logBuffers = new Map(); // filePath -> string[]
954
+ const LOG_FLUSH_INTERVAL = 2000; // Flush every 2 seconds
955
+ let _logFlushTimer = null;
956
+
957
+ function bufferedLogWrite(filePath, entry) {
958
+ if (!filePath) return;
959
+ if (!_logBuffers.has(filePath)) {
960
+ _logBuffers.set(filePath, []);
961
+ }
962
+ _logBuffers.get(filePath).push(entry);
963
+ }
964
+
965
+ function flushLogBuffers() {
966
+ for (const [filePath, entries] of _logBuffers) {
967
+ if (entries.length > 0) {
968
+ try {
969
+ fs.appendFileSync(filePath, entries.join(''));
970
+ } catch (err) {
971
+ console.warn(formatLogMessage('warn', `Failed to flush log buffer to ${filePath}: ${err.message}`));
972
+ }
973
+ entries.length = 0; // Clear buffer
974
+ }
975
+ }
976
+ }
977
+
978
+ // Start periodic flush if any logging is enabled
979
+ if (forceDebug || dumpUrls) {
980
+ _logFlushTimer = setInterval(flushLogBuffers, LOG_FLUSH_INTERVAL);
981
+ _logFlushTimer.unref(); // Don't keep process alive just for flushing
982
+ }
983
+
946
984
  // Log comments if debug mode is enabled and comments exist
947
985
  if (forceDebug && globalComments) {
948
986
  const commentList = Array.isArray(globalComments) ? globalComments : [globalComments];
@@ -1047,15 +1085,21 @@ function shouldBypassCacheForUrl(url, siteConfig) {
1047
1085
  return siteConfig.bypass_cache === true;
1048
1086
  }
1049
1087
 
1050
- // ability to use widcards in ignoreDomains
1088
+ // ability to use wildcards in ignoreDomains
1089
+ // Cache compiled wildcard regexes to avoid recompilation on every request
1090
+ const _wildcardRegexCache = new Map();
1051
1091
  function matchesIgnoreDomain(domain, ignorePatterns) {
1052
1092
  return ignorePatterns.some(pattern => {
1053
1093
  if (pattern.includes('*')) {
1054
- // Convert wildcard pattern to regex
1055
- const regexPattern = pattern
1056
- .replace(/\./g, '\\.') // Escape dots
1057
- .replace(/\*/g, '.*'); // Convert * to .*
1058
- return new RegExp(`^${regexPattern}$`).test(domain);
1094
+ let compiled = _wildcardRegexCache.get(pattern);
1095
+ if (!compiled) {
1096
+ const regexPattern = pattern
1097
+ .replace(/[.*+?^${}()|[\]\\]/g, '\\$&') // Escape all regex specials including *
1098
+ .replace(/\\\*/g, '.*'); // Convert escaped \* back to .*
1099
+ compiled = new RegExp(`^${regexPattern}$`);
1100
+ _wildcardRegexCache.set(pattern, compiled);
1101
+ }
1102
+ return compiled.test(domain);
1059
1103
  }
1060
1104
  return domain.endsWith(pattern);
1061
1105
  });
@@ -1065,14 +1109,6 @@ function setupFrameHandling(page, forceDebug) {
1065
1109
  // Track active frames and clear on navigation to prevent detached frame access
1066
1110
  let activeFrames = new Set(); // Use Set to track frame references
1067
1111
 
1068
- // Clear frame tracking on navigation to prevent stale references
1069
- page.on('framenavigated', (frame) => {
1070
- if (frame === page.mainFrame()) {
1071
- // Main frame navigated - clear all tracked frames
1072
- activeFrames.clear();
1073
- }
1074
- });
1075
-
1076
1112
  // Handle frame creation with error suppression
1077
1113
  page.on('frameattached', async (frame) => {
1078
1114
  // Enhanced frame handling with detached frame protection
@@ -1182,10 +1218,16 @@ function setupFrameHandling(page, forceDebug) {
1182
1218
  }
1183
1219
  }
1184
1220
  });
1185
- // Handle frame navigations (keep this for monitoring)
1221
+ // Handle frame navigations - clear stale tracking and monitor activity
1186
1222
  page.on('framenavigated', (frame) => {
1187
1223
 
1188
- // Skip if frame is not in our active set
1224
+ // Main frame navigated - clear all tracked frames to prevent stale references
1225
+ if (frame === page.mainFrame()) {
1226
+ activeFrames.clear();
1227
+ return;
1228
+ }
1229
+
1230
+ // Skip child frames not in our active set
1189
1231
  if (!activeFrames.has(frame)) return;
1190
1232
 
1191
1233
  let frameUrl;
@@ -1399,12 +1441,16 @@ function setupFrameHandling(page, forceDebug) {
1399
1441
  // Set up cleanup on process termination
1400
1442
  process.on('SIGINT', async () => {
1401
1443
  if (forceDebug) console.log(formatLogMessage('debug', 'SIGINT received, performing cleanup...'));
1444
+ flushLogBuffers();
1445
+ if (_logFlushTimer) clearInterval(_logFlushTimer);
1402
1446
  await performEmergencyCleanup();
1403
1447
  process.exit(0);
1404
1448
  });
1405
1449
 
1406
1450
  process.on('SIGTERM', async () => {
1407
1451
  if (forceDebug) console.log(formatLogMessage('debug', 'SIGTERM received, performing cleanup...'));
1452
+ flushLogBuffers();
1453
+ if (_logFlushTimer) clearInterval(_logFlushTimer);
1408
1454
  await performEmergencyCleanup();
1409
1455
  process.exit(0);
1410
1456
  });
@@ -2195,11 +2241,10 @@ function setupFrameHandling(page, forceDebug) {
2195
2241
  ? siteConfig.blocked.map(pattern => new RegExp(pattern))
2196
2242
  : [];
2197
2243
 
2198
- // Add global blocked patterns
2199
- const globalBlockedRegexes = Array.isArray(globalBlocked)
2200
- ? globalBlocked.map(pattern => new RegExp(pattern))
2201
- : [];
2202
- const allBlockedRegexes = [...blockedRegexes, ...globalBlockedRegexes];
2244
+ // Combine site-specific with pre-compiled global blocked patterns
2245
+ const allBlockedRegexes = blockedRegexes.length > 0
2246
+ ? [...blockedRegexes, ...globalBlockedRegexes]
2247
+ : globalBlockedRegexes; // Avoid spread when no site-specific patterns
2203
2248
 
2204
2249
  /**
2205
2250
  * Helper function to add domain to matched collection
@@ -2329,12 +2374,14 @@ function setupFrameHandling(page, forceDebug) {
2329
2374
  // - URL matching against blocklists (`blockedRegexes`).
2330
2375
  // - URL matching against filter patterns (`regexes`) for domain extraction.
2331
2376
  // - Global `ignoreDomains` list.
2377
+ // Pre-compute values that are constant for this URL
2378
+ const simplifiedCurrentUrl = getRootDomain(currentUrl);
2379
+
2332
2380
  page.on('request', request => {
2333
2381
  const checkedUrl = request.url();
2334
- const checkedHostname = safeGetDomain(checkedUrl, true);
2335
- const checkedRootDomain = safeGetDomain(checkedUrl, false); // Root domain for first-party detection
2382
+ const fullSubdomain = safeGetDomain(checkedUrl, true); // Full hostname for cache
2383
+ const checkedRootDomain = safeGetDomain(checkedUrl, false);
2336
2384
  // Check against ALL first-party domains (original + all redirects)
2337
- // This prevents redirect destinations from being marked as third-party
2338
2385
  const isFirstParty = checkedRootDomain && firstPartyDomains.has(checkedRootDomain);
2339
2386
 
2340
2387
  // Block infinite iframe loops - safely access frame URL
@@ -2347,9 +2394,9 @@ function setupFrameHandling(page, forceDebug) {
2347
2394
  }
2348
2395
  })();
2349
2396
  if (frameUrl && frameUrl.includes('creative.dmzjmp.com') &&
2350
- request.url().includes('go.dmzjmp.com/api/models')) {
2397
+ checkedUrl.includes('go.dmzjmp.com/api/models')) {
2351
2398
  if (forceDebug) {
2352
- console.log(formatLogMessage('debug', `Blocking potential infinite iframe loop: ${request.url()}`));
2399
+ console.log(formatLogMessage('debug', `Blocking potential infinite iframe loop: ${checkedUrl}`));
2353
2400
  }
2354
2401
  request.abort();
2355
2402
  return;
@@ -2357,19 +2404,19 @@ function setupFrameHandling(page, forceDebug) {
2357
2404
 
2358
2405
  // Enhanced debug logging to show which frame the request came from
2359
2406
  if (forceDebug) {
2360
- let frameUrl = 'unknown-frame';
2407
+ let debugFrameUrl = 'unknown-frame';
2361
2408
  let isMainFrame = false;
2362
2409
 
2363
2410
  try {
2364
2411
  const frame = request.frame();
2365
2412
  if (frame) {
2366
- frameUrl = frame.url();
2413
+ debugFrameUrl = frame.url();
2367
2414
  isMainFrame = frame === page.mainFrame();
2368
2415
  }
2369
2416
  } catch (frameErr) {
2370
- frameUrl = 'detached-frame';
2417
+ debugFrameUrl = 'detached-frame';
2371
2418
  }
2372
- console.log(formatLogMessage('debug', `${messageColors.highlight('[req]')}[frame: ${isMainFrame ? 'main' : 'iframe'}] ${frameUrl} → ${request.url()}`));
2419
+ console.log(formatLogMessage('debug', `${messageColors.highlight('[req]')}[frame: ${isMainFrame ? 'main' : 'iframe'}] ${debugFrameUrl} → ${checkedUrl}`));
2373
2420
  }
2374
2421
 
2375
2422
  // Apply adblock rules BEFORE expensive regex checks for better performance
@@ -2395,46 +2442,36 @@ function setupFrameHandling(page, forceDebug) {
2395
2442
 
2396
2443
  // Show --debug output and the url while its scanning
2397
2444
  if (forceDebug) {
2398
- const simplifiedUrl = getRootDomain(currentUrl);
2399
2445
  const timestamp = new Date().toISOString();
2400
- const logEntry = `${timestamp} [debug req][${simplifiedUrl}] ${request.url()}`;
2446
+ const logEntry = `${timestamp} [debug req][${simplifiedCurrentUrl}] ${checkedUrl}\n`;
2401
2447
 
2402
2448
  // Output to console
2403
- console.log(formatLogMessage('debug', `${messageColors.highlight('[req]')}[${simplifiedUrl}] ${request.url()}`));
2449
+ console.log(formatLogMessage('debug', `${messageColors.highlight('[req]')}[${simplifiedCurrentUrl}] ${checkedUrl}`));
2404
2450
 
2405
- // Output to file
2406
- if (debugLogFile) {
2407
- try {
2408
- fs.appendFileSync(debugLogFile, logEntry + '\n');
2409
- } catch (logErr) {
2410
- console.warn(formatLogMessage('warn', `Failed to write to debug log file: ${logErr.message}`));
2411
- }
2412
- }
2451
+ // Output to file (buffered)
2452
+ bufferedLogWrite(debugLogFile, logEntry);
2413
2453
  }
2414
- const reqUrl = request.url();
2454
+ const reqUrl = checkedUrl;
2415
2455
 
2416
- // ALWAYS extract the FULL subdomain for cache checking to preserve unique subdomains
2417
- const fullSubdomain = safeGetDomain(reqUrl, true); // Always get full subdomain for cache
2418
2456
  const reqDomain = safeGetDomain(reqUrl, perSiteSubDomains); // Output domain based on config
2419
2457
 
2420
2458
  if (allBlockedRegexes.some(re => re.test(reqUrl))) {
2421
2459
  if (forceDebug) {
2422
- // Find which specific pattern matched for debug logging
2423
- const allPatterns = [...(siteConfig.blocked || []), ...globalBlocked];
2424
- const matchedPattern = allPatterns.find(pattern => new RegExp(pattern).test(reqUrl));
2425
- const patternSource = siteConfig.blocked && siteConfig.blocked.includes(matchedPattern) ? 'site' : 'global';
2426
- const simplifiedUrl = getRootDomain(currentUrl);
2427
- console.log(formatLogMessage('debug', `${messageColors.blocked('[blocked]')}[${simplifiedUrl}] ${reqUrl} blocked by ${patternSource} pattern: ${matchedPattern}`));
2428
-
2429
- // Also log to file if debug logging is enabled
2430
- if (debugLogFile) {
2431
- try {
2432
- const timestamp = new Date().toISOString();
2433
- fs.appendFileSync(debugLogFile, `${timestamp} [blocked][${simplifiedUrl}] ${reqUrl} (${patternSource} pattern: ${matchedPattern})\n`);
2434
- } catch (logErr) {
2435
- console.warn(formatLogMessage('warn', `Failed to write blocked domain to debug log: ${logErr.message}`));
2460
+ // Find which specific pattern matched using already-compiled regexes
2461
+ let matchedPattern = '(unknown)';
2462
+ let patternSource = 'global';
2463
+ for (let i = 0; i < allBlockedRegexes.length; i++) {
2464
+ if (allBlockedRegexes[i].test(reqUrl)) {
2465
+ matchedPattern = allBlockedRegexes[i].source;
2466
+ patternSource = i < blockedRegexes.length ? 'site' : 'global';
2467
+ break;
2436
2468
  }
2437
2469
  }
2470
+ console.log(formatLogMessage('debug', `${messageColors.blocked('[blocked]')}[${simplifiedCurrentUrl}] ${reqUrl} blocked by ${patternSource} pattern: ${matchedPattern}`));
2471
+
2472
+ // Also log to file (buffered)
2473
+ const timestamp = new Date().toISOString();
2474
+ bufferedLogWrite(debugLogFile, `${timestamp} [blocked][${simplifiedCurrentUrl}] ${reqUrl} (${patternSource} pattern: ${matchedPattern})\n`);
2438
2475
  }
2439
2476
 
2440
2477
  // NEW: Check if even_blocked is enabled and this URL matches filter regex
@@ -2461,15 +2498,14 @@ function setupFrameHandling(page, forceDebug) {
2461
2498
  addMatchedDomain(reqDomain, resourceType, fullSubdomain);
2462
2499
  }
2463
2500
 
2464
- const simplifiedUrl = getRootDomain(currentUrl);
2465
2501
  if (siteConfig.verbose === 1) {
2466
2502
  const resourceInfo = (adblockRulesMode || siteConfig.adblock_rules) ? ` (${resourceType})` : '';
2467
- console.log(formatLogMessage('match', `[${simplifiedUrl}] ${reqUrl} matched regex: ${matchedRegexPattern} and resourceType: ${resourceType}${resourceInfo}`));
2503
+ console.log(formatLogMessage('match', `[${simplifiedCurrentUrl}] ${reqUrl} matched regex: ${matchedRegexPattern} and resourceType: ${resourceType}${resourceInfo}`));
2468
2504
  }
2469
2505
  if (dumpUrls) {
2470
2506
  const timestamp = new Date().toISOString();
2471
2507
  const resourceInfo = (adblockRulesMode || siteConfig.adblock_rules) ? ` (${resourceType})` : '';
2472
- fs.appendFileSync(matchedUrlsLogFile, `${timestamp} [match][${simplifiedUrl}] ${reqUrl} (resourceType: ${resourceType})${resourceInfo} [BLOCKED BUT ADDED]\n`);
2508
+ bufferedLogWrite(matchedUrlsLogFile, `${timestamp} [match][${simplifiedCurrentUrl}] ${reqUrl} (resourceType: ${resourceType})${resourceInfo} [BLOCKED BUT ADDED]\n`);
2473
2509
  }
2474
2510
  break; // Only match once per URL
2475
2511
  }
@@ -2625,15 +2661,14 @@ function setupFrameHandling(page, forceDebug) {
2625
2661
  } else {
2626
2662
  addMatchedDomain(reqDomain, resourceType);
2627
2663
  }
2628
- const simplifiedUrl = getRootDomain(currentUrl);
2629
2664
  if (siteConfig.verbose === 1) {
2630
2665
  const resourceInfo = (adblockRulesMode || siteConfig.adblock_rules) ? ` (${resourceType})` : '';
2631
- console.log(formatLogMessage('match', `[${simplifiedUrl}] ${reqUrl} matched regex: ${matchedRegexPattern} and resourceType: ${resourceType}${resourceInfo}`));
2666
+ console.log(formatLogMessage('match', `[${simplifiedCurrentUrl}] ${reqUrl} matched regex: ${matchedRegexPattern} and resourceType: ${resourceType}${resourceInfo}`));
2632
2667
  }
2633
2668
  if (dumpUrls) {
2634
2669
  const timestamp = new Date().toISOString();
2635
2670
  const resourceInfo = (adblockRulesMode || siteConfig.adblock_rules) ? ` (${resourceType})` : '';
2636
- fs.appendFileSync(matchedUrlsLogFile, `${timestamp} [match][${simplifiedUrl}] ${reqUrl} (resourceType: ${resourceType})${resourceInfo}\n`);
2671
+ bufferedLogWrite(matchedUrlsLogFile, `${timestamp} [match][${simplifiedCurrentUrl}] ${reqUrl} (resourceType: ${resourceType})${resourceInfo}\n`);
2637
2672
  }
2638
2673
  } else if (hasNetTools && !hasSearchString && !hasSearchStringAnd) {
2639
2674
  // If nettools are configured (whois/dig), perform checks on the domain
@@ -4117,6 +4152,12 @@ function setupFrameHandling(page, forceDebug) {
4117
4152
  }
4118
4153
  }
4119
4154
 
4155
+ // Flush any remaining buffered log entries before compression/exit
4156
+ flushLogBuffers();
4157
+ if (_logFlushTimer) {
4158
+ clearInterval(_logFlushTimer);
4159
+ }
4160
+
4120
4161
  // Compress log files if --compress-logs is enabled
4121
4162
  if (compressLogs && dumpUrls && !dryRunMode) {
4122
4163
  // Collect all existing log files for compression
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fanboynz/network-scanner",
3
- "version": "2.0.34",
3
+ "version": "2.0.36",
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": {