@fanboynz/network-scanner 2.0.28 → 2.0.29

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/.clauderc ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "description": "Network scanner that monitors website requests and generates blocking rules. Uses Puppeteer to load sites, intercepts network traffic, matches patterns, and outputs rules in various formats (adblock, dnsmasq, hosts file, etc.).",
3
+
4
+ "conventions": [
5
+ "Store modular functionality in ./lib/ directory with focused, single-purpose modules",
6
+ "Use messageColors and formatLogMessage from ./lib/colorize for consistent console output",
7
+ "Implement timeout protection for all Puppeteer operations using Promise.race patterns",
8
+ "Handle browser lifecycle with comprehensive cleanup in try-finally blocks",
9
+ "Validate all external tool availability before use (grep, curl, whois, dig)",
10
+ "Use forceDebug flag for detailed logging, silentMode for minimal output"
11
+ ],
12
+
13
+ "files": {
14
+ "important": [
15
+ "nwss.js",
16
+ "config.json",
17
+ "lib/*.js",
18
+ "*.md",
19
+ "nwss.1"
20
+ ],
21
+ "ignore": [
22
+ "node_modules/**",
23
+ "logs/**",
24
+ "sources/**",
25
+ ".cache/**",
26
+ "*.log",
27
+ "*.gz"
28
+ ]
29
+ }
30
+ }
@@ -96,9 +96,9 @@ const USER_AGENT_COLLECTIONS = {
96
96
  "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36"
97
97
  ],
98
98
  firefox: [
99
- "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:143.0) Gecko/20100101 Firefox/143.0",
100
- "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:143.0) Gecko/20100101 Firefox/143.0",
101
- "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:143.0) Gecko/20100101 Firefox/143.0"
99
+ "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:144.0) Gecko/20100101 Firefox/144.0",
100
+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:144.0) Gecko/20100101 Firefox/144.0",
101
+ "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:144.0) Gecko/20100101 Firefox/144.0"
102
102
  ],
103
103
  safari: [
104
104
  "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.6 Safari/605.1.15",
@@ -528,16 +528,45 @@ async function applyUserAgentSpoofing(page, siteConfig, forceDebug, currentUrl)
528
528
  // Simulate Chrome runtime
529
529
  //
530
530
  safeExecute(() => {
531
- if (!window.chrome?.runtime) {
531
+ if (!window.chrome) {
532
532
  window.chrome = {
533
533
  runtime: {
534
534
  onConnect: { addListener: () => {}, removeListener: () => {} },
535
535
  onMessage: { addListener: () => {}, removeListener: () => {} },
536
536
  sendMessage: () => {},
537
- connect: () => ({ onMessage: { addListener: () => {}, removeListener: () => {} }, postMessage: () => {}, disconnect: () => {} }),
538
- getManifest: () => ({ name: "Chrome", version: "141.0.0.0" }),
537
+ connect: () => ({
538
+ onMessage: { addListener: () => {}, removeListener: () => {} },
539
+ postMessage: () => {},
540
+ disconnect: () => {}
541
+ }),
542
+ getManifest: () => ({
543
+ name: "Chrome",
544
+ version: "141.0.0.0",
545
+ manifest_version: 3,
546
+ description: "Chrome Browser"
547
+ }),
539
548
  getURL: (path) => `chrome-extension://invalid/${path}`,
540
- id: undefined
549
+ id: undefined,
550
+ getPlatformInfo: (callback) => callback({
551
+ os: navigator.platform.includes('Win') ? 'win' :
552
+ navigator.platform.includes('Mac') ? 'mac' : 'linux',
553
+ arch: 'x86-64',
554
+ nacl_arch: 'x86-64'
555
+ })
556
+ },
557
+ storage: {
558
+ local: {
559
+ get: (keys, callback) => callback && callback({}),
560
+ set: (items, callback) => callback && callback(),
561
+ remove: (keys, callback) => callback && callback(),
562
+ clear: (callback) => callback && callback()
563
+ },
564
+ sync: {
565
+ get: (keys, callback) => callback && callback({}),
566
+ set: (items, callback) => callback && callback(),
567
+ remove: (keys, callback) => callback && callback(),
568
+ clear: (callback) => callback && callback()
569
+ }
541
570
  },
542
571
  loadTimes: () => {
543
572
  const now = performance.now();
@@ -555,9 +584,56 @@ async function applyUserAgentSpoofing(page, siteConfig, forceDebug, currentUrl)
555
584
  startE: Date.now() - Math.random() * 2000
556
585
  })
557
586
  };
587
+
588
+ // Make chrome object non-enumerable to match real Chrome
589
+ Object.defineProperty(window, 'chrome', {
590
+ value: window.chrome,
591
+ writable: false,
592
+ enumerable: false,
593
+ configurable: true
594
+ });
595
+
596
+ // Add Chrome-specific globals that Cloudflare might check
597
+ if (!window.external) {
598
+ window.external = {
599
+ AddSearchProvider: () => {},
600
+ IsSearchProviderInstalled: () => 0
601
+ };
602
+ }
603
+
604
+ // Ensure chrome.runtime appears as a native object
605
+ Object.defineProperty(window.chrome.runtime, 'toString', {
606
+ value: () => '[object Object]'
607
+ });
558
608
  }
559
609
  }, 'Chrome runtime simulation');
560
610
 
611
+ // Add realistic Chrome browser behavior
612
+ //
613
+ safeExecute(() => {
614
+ // Remove Puppeteer-specific properties not covered in main automation cleanup
615
+ delete window.__puppeteer_evaluation_script__;
616
+ delete window.__runtime;
617
+ delete window._asyncToGenerator;
618
+ delete window.__puppeteer;
619
+ delete window.__cdp;
620
+ delete window.__REACT_DEVTOOLS_GLOBAL_HOOK__;
621
+
622
+ // Simulate Chrome's performance observer
623
+ if (window.PerformanceObserver) {
624
+ try {
625
+ const observer = new PerformanceObserver(() => {});
626
+ observer.observe({entryTypes: ['navigation']});
627
+ } catch(e) {
628
+ // Silently ignore if PerformanceObserver fails
629
+ }
630
+ }
631
+
632
+ // Ensure external object exists (don't overwrite chrome object)
633
+ window.external = window.external || {};
634
+
635
+ }, 'realistic Chrome behavior');
636
+
561
637
  // Spoof plugins based on user agent
562
638
  //
563
639
  safeExecute(() => {
@@ -874,29 +950,33 @@ async function applyUserAgentSpoofing(page, siteConfig, forceDebug, currentUrl)
874
950
  if (navigator.permissions?.query) {
875
951
  const originalQuery = navigator.permissions.query;
876
952
  navigator.permissions.query = function(descriptor) {
877
- // More realistic permission states based on permission type
953
+
878
954
  const permissionName = descriptor.name || descriptor;
879
- let state = 'prompt'; // Default state
955
+ // Realistic Chrome permission defaults
956
+ const chromeDefaults = {
957
+ 'notifications': 'default',
958
+ 'geolocation': 'prompt',
959
+ 'camera': 'prompt',
960
+ 'microphone': 'prompt',
961
+ 'persistent-storage': 'granted',
962
+ 'background-sync': 'granted',
963
+ 'midi': 'prompt',
964
+ 'push': 'prompt',
965
+ 'accelerometer': 'granted',
966
+ 'gyroscope': 'granted',
967
+ 'magnetometer': 'granted'
968
+ };
880
969
 
881
- // Common permissions that are usually granted
882
- if (['notifications', 'persistent-storage'].includes(permissionName)) {
883
- state = Math.random() > 0.3 ? 'granted' : 'prompt';
884
- }
885
- // Privacy-sensitive permissions that are usually denied/prompt
886
- else if (['camera', 'microphone', 'geolocation'].includes(permissionName)) {
887
- state = Math.random() > 0.8 ? 'granted' : 'prompt';
888
- }
889
- // Other permissions random
890
- else {
891
- state = Math.random() > 0.5 ? 'granted' : 'prompt';
892
- }
970
+ const state = chromeDefaults[permissionName] || 'prompt';
893
971
 
894
972
  return Promise.resolve({
895
- state: state,
973
+ state,
896
974
  onchange: null
897
975
  });
898
- };
976
+ }
977
+
899
978
  }
979
+
900
980
  // Block permission prompts from actually appearing
901
981
  if (window.Notification && Notification.requestPermission) {
902
982
  Notification.requestPermission = () => Promise.resolve('default');
@@ -1637,4 +1717,4 @@ module.exports = {
1637
1717
  applyTimezoneSpoofing,
1638
1718
  DEFAULT_PLATFORM,
1639
1719
  DEFAULT_TIMEZONE
1640
- };
1720
+ };
package/lib/nettools.js CHANGED
@@ -779,6 +779,8 @@ function createNetToolsHandler(config) {
779
779
  isDomainAlreadyDetected,
780
780
  getRootDomain,
781
781
  siteConfig,
782
+ processedWhoisDomains = new Set(), // Accept global sets, fallback to new for backward compatibility
783
+ processedDigDomains = new Set(),
782
784
  dumpUrls,
783
785
  matchedUrlsLogFile,
784
786
  forceDebug,
@@ -790,9 +792,22 @@ function createNetToolsHandler(config) {
790
792
  const hasDig = digTerms && Array.isArray(digTerms) && digTerms.length > 0;
791
793
  const hasDigOr = digOrTerms && Array.isArray(digOrTerms) && digOrTerms.length > 0;
792
794
 
793
- // Add separate deduplication caches for different lookup types
794
- const processedWhoisDomains = new Set();
795
- const processedDigDomains = new Set();
795
+ // Create config-aware cache keys for deduplication
796
+ // Whois: Only include search terms + server (domain registry data is consistent across subdomains)
797
+ const whoisConfigKey = JSON.stringify({
798
+ terms: whoisTerms || [],
799
+ orTerms: whoisOrTerms || [],
800
+ server: whoisServer || 'default',
801
+ serverMode: whoisServerMode || 'random'
802
+ });
803
+ // Dig: Include all config (DNS records can vary by specific subdomain)
804
+ const digConfigKey = JSON.stringify({
805
+ terms: digTerms || [],
806
+ orTerms: digOrTerms || [],
807
+ recordType: digRecordType,
808
+ subdomain: digSubdomain
809
+ });
810
+
796
811
  // Add whois resolution caching to avoid redundant whois lookups
797
812
  const whoisResultCache = new Map();
798
813
  const WHOIS_CACHE_TTL = 900000; // 15 minutes cache TTL (whois data changes less frequently)
@@ -853,15 +868,24 @@ function createNetToolsHandler(config) {
853
868
 
854
869
  // Determine which domain will be used for dig lookup
855
870
  const digDomain = digSubdomain && originalDomain ? originalDomain : domain;
871
+
872
+ // For whois: use root domain only (whois data is consistent for entire domain)
873
+ const whoisRootDomain = getRootDomain ? getRootDomain(`http://${domain}`) : domain;
856
874
 
857
- // Check if we need to perform any lookups
858
- const needsWhoisLookup = (hasWhois || hasWhoisOr) && !processedWhoisDomains.has(domain);
859
- const needsDigLookup = (hasDig || hasDigOr) && !processedDigDomains.has(digDomain);
875
+ // Check if we need to perform any lookups with appropriate deduplication
876
+ // Whois: root domain + config (whois data same for sub.example.com and example.com)
877
+ const whoisDedupeKey = `${whoisRootDomain}:${whoisConfigKey}`;
878
+ // Dig: specific subdomain + config (DNS records can differ between subdomains)
879
+ const digDedupeKey = `${digDomain}:${digConfigKey}`;
880
+ const needsWhoisLookup = (hasWhois || hasWhoisOr) && !processedWhoisDomains.has(whoisDedupeKey);
881
+ const needsDigLookup = (hasDig || hasDigOr) && !processedDigDomains.has(digDedupeKey);
860
882
 
861
883
  // Skip if we don't need to perform any lookups
862
884
  if (!needsWhoisLookup && !needsDigLookup) {
863
885
  if (forceDebug) {
864
- logToConsoleAndFile(`${messageColors.highlight('[nettools]')} Skipping duplicate lookups for ${domain} (whois: ${!needsWhoisLookup}, dig: ${!needsDigLookup})`);
886
+ const whoisSkipped = (hasWhois || hasWhoisOr) ? `cached(${whoisRootDomain})` : 'n/a';
887
+ const digSkipped = (hasDig || hasDigOr) ? `cached(${digDomain})` : 'n/a';
888
+ logToConsoleAndFile(`${messageColors.highlight('[nettools]')} Skipping duplicate lookups for ${domain} (whois: ${whoisSkipped}, dig: ${digSkipped})`);
865
889
  }
866
890
  return;
867
891
  }
@@ -869,7 +893,7 @@ function createNetToolsHandler(config) {
869
893
 
870
894
  if (forceDebug) {
871
895
  const totalProcessed = processedWhoisDomains.size + processedDigDomains.size;
872
- logToConsoleAndFile(`${messageColors.highlight('[nettools]')} Processing domain: ${domain} (whois: ${needsWhoisLookup}, dig: ${needsDigLookup}) (${totalProcessed} total processed)`);
896
+ logToConsoleAndFile(`${messageColors.highlight('[nettools]')} Processing domain: ${domain} (whois: ${needsWhoisLookup ? whoisRootDomain : 'skip'}, dig: ${needsDigLookup ? digDomain : 'skip'}) (${totalProcessed} total processed)`);
873
897
  }
874
898
 
875
899
  // Log site-specific whois delay if different from default
@@ -955,12 +979,12 @@ function createNetToolsHandler(config) {
955
979
 
956
980
  // Perform whois lookup if either whois or whois-or is configured
957
981
  if (needsWhoisLookup) {
958
- // Mark whois domain as being processed
959
- processedWhoisDomains.add(domain);
982
+ // Mark whois root domain+config as being processed
983
+ processedWhoisDomains.add(whoisDedupeKey);
960
984
 
961
985
  // Check whois cache first - cache key includes server for accuracy
962
986
  const selectedServer = selectWhoisServer(whoisServer, whoisServerMode);
963
- const whoisCacheKey = `${domain}-${(selectedServer && selectedServer !== '') ? selectedServer : 'default'}`;
987
+ const whoisCacheKey = `${whoisRootDomain}-${(selectedServer && selectedServer !== '') ? selectedServer : 'default'}`;
964
988
  const now = Date.now();
965
989
  let whoisResult = null;
966
990
 
@@ -970,7 +994,7 @@ function createNetToolsHandler(config) {
970
994
  if (forceDebug) {
971
995
  const age = Math.round((now - cachedEntry.timestamp) / 1000);
972
996
  const serverInfo = (selectedServer && selectedServer !== '') ? ` (server: ${selectedServer})` : ' (default server)';
973
- logToConsoleAndFile(`${messageColors.highlight('[whois-cache]')} Using cached result for ${domain}${serverInfo} [age: ${age}s]`);
997
+ logToConsoleAndFile(`${messageColors.highlight('[whois-cache]')} Using cached result for ${whoisRootDomain}${serverInfo} [age: ${age}s]`);
974
998
  }
975
999
  // V8 Optimized: Object.assign is faster than spread for object merging
976
1000
  whoisResult = Object.assign({}, cachedEntry.result, {
@@ -982,7 +1006,7 @@ function createNetToolsHandler(config) {
982
1006
  // Cache expired, remove it
983
1007
  whoisResultCache.delete(whoisCacheKey);
984
1008
  if (forceDebug) {
985
- logToConsoleAndFile(`${messageColors.highlight('[whois-cache]')} Cache expired for ${domain}, performing fresh lookup`);
1009
+ logToConsoleAndFile(`${messageColors.highlight('[whois-cache]')} Cache expired for ${whoisRootDomain}, performing fresh lookup`);
986
1010
  }
987
1011
  }
988
1012
  }
@@ -991,7 +1015,7 @@ function createNetToolsHandler(config) {
991
1015
  if (!whoisResult) {
992
1016
  if (forceDebug) {
993
1017
  const serverInfo = (selectedServer && selectedServer !== '') ? ` using server ${selectedServer}` : ' using default server';
994
- logToConsoleAndFile(`${messageColors.highlight('[whois]')} Performing fresh whois lookup for ${domain}${serverInfo}`);
1018
+ logToConsoleAndFile(`${messageColors.highlight('[whois]')} Performing fresh whois lookup for ${whoisRootDomain}${serverInfo}`);
995
1019
  }
996
1020
 
997
1021
  // Configure retry options based on site config or use defaults
@@ -1004,7 +1028,7 @@ function createNetToolsHandler(config) {
1004
1028
  };
1005
1029
 
1006
1030
  try {
1007
- whoisResult = await whoisLookupWithRetry(domain, 8000, whoisServer, forceDebug, retryOptions, whoisDelay, logToConsoleAndFile);
1031
+ whoisResult = await whoisLookupWithRetry(whoisRootDomain, 8000, whoisServer, forceDebug, retryOptions, whoisDelay, logToConsoleAndFile);
1008
1032
 
1009
1033
  // Cache successful results (and certain types of failures)
1010
1034
  if (whoisResult.success ||
@@ -1020,13 +1044,13 @@ function createNetToolsHandler(config) {
1020
1044
  if (forceDebug) {
1021
1045
  const cacheType = whoisResult.success ? 'successful' : 'failed';
1022
1046
  const serverInfo = selectedServer ? ` (server: ${selectedServer})` : ' (default server)';
1023
- logToConsoleAndFile(`${messageColors.highlight('[whois-cache]')} Cached ${cacheType} result for ${domain}${serverInfo}`);
1047
+ logToConsoleAndFile(`${messageColors.highlight('[whois-cache]')} Cached ${cacheType} result for ${whoisRootDomain}${serverInfo}`);
1024
1048
  }
1025
1049
  }
1026
1050
  } catch (whoisError) {
1027
1051
  // Handle exceptions from whois lookup
1028
1052
  if (forceDebug) {
1029
- logToConsoleAndFile(`${messageColors.highlight('[whois]')} Exception during lookup for ${domain}: ${whoisError.message}`);
1053
+ logToConsoleAndFile(`${messageColors.highlight('[whois]')} Exception during lookup for ${whoisRootDomain}: ${whoisError.message}`);
1030
1054
  logToConsoleAndFile(`${messageColors.highlight('[whois]')} Exception type: ${whoisError.constructor.name}`);
1031
1055
  if (whoisError.stack) {
1032
1056
  logToConsoleAndFile(`${messageColors.highlight('[whois]')} Stack trace: ${whoisError.stack.split('\n').slice(0, 3).join(' -> ')}`);
@@ -1035,7 +1059,7 @@ function createNetToolsHandler(config) {
1035
1059
 
1036
1060
  // Log whois exceptions in dry run mode
1037
1061
  if (dryRunCallback && forceDebug) {
1038
- logToConsoleAndFile(`${messageColors.highlight('[whois-dryrun]')} Exception: ${whoisError.message}`);
1062
+ logToConsoleAndFile(`${messageColors.highlight('[whois-dryrun]')} Exception for ${whoisRootDomain}: ${whoisError.message}`);
1039
1063
  }
1040
1064
  // Continue with dig if configured
1041
1065
  whoisResult = null; // Ensure we don't process a null result
@@ -1086,7 +1110,7 @@ function createNetToolsHandler(config) {
1086
1110
  const retryInfo = whoisResult.retryInfo ? ` [${whoisResult.retryInfo.totalAttempts}/${whoisResult.retryInfo.maxAttempts} attempts]` : '';
1087
1111
  const cacheInfo = whoisResult.fromCache ? ` [CACHED - ${Math.round(whoisResult.cacheAge / 1000)}s old]` : '';
1088
1112
  const duration = whoisResult.fromCache ? `cached in 0ms` : `in ${whoisResult.duration}ms`;
1089
- logToConsoleAndFile(`${messageColors.highlight('[whois]')} Lookup completed for ${domain}${serverUsed} ${duration}${retryInfo}${cacheInfo}`);
1113
+ logToConsoleAndFile(`${messageColors.highlight('[whois]')} Lookup completed for ${whoisRootDomain}${serverUsed} ${duration}${retryInfo}${cacheInfo}`);
1090
1114
 
1091
1115
  if (whoisResult.retryInfo && whoisResult.retryInfo.retriedAfterFailure) {
1092
1116
  logToConsoleAndFile(`${messageColors.highlight('[whois]')} Success after retry - servers attempted: [${whoisResult.retryInfo.serversAttempted.map(s => s || 'default').join(', ')}]`);
@@ -1099,7 +1123,7 @@ function createNetToolsHandler(config) {
1099
1123
  const errorContext = whoisResult.isTimeout ? 'TIMEOUT' : 'ERROR';
1100
1124
  const retryInfo = whoisResult.retryInfo ? ` [${whoisResult.retryInfo.totalAttempts}/${whoisResult.retryInfo.maxAttempts} attempts]` : '';
1101
1125
 
1102
- logToConsoleAndFile(`${messageColors.highlight('[whois]')} ${errorContext}: Lookup failed for ${domain}${serverUsed} after ${whoisResult.duration}ms${retryInfo}`);
1126
+ logToConsoleAndFile(`${messageColors.highlight('[whois]')} ${errorContext}: Lookup failed for ${whoisRootDomain}${serverUsed} after ${whoisResult.duration}ms${retryInfo}`);
1103
1127
  logToConsoleAndFile(`${messageColors.highlight('[whois]')} Command executed: ${whoisResult.command || 'unknown'}`);
1104
1128
  logToConsoleAndFile(`${messageColors.highlight('[whois]')} Error details: ${whoisResult.error}`);
1105
1129
 
@@ -1182,8 +1206,8 @@ function createNetToolsHandler(config) {
1182
1206
 
1183
1207
  // Perform dig lookup if configured
1184
1208
  if (needsDigLookup) {
1185
- // Mark dig domain as being processed
1186
- processedDigDomains.add(digDomain);
1209
+ // Mark dig domain+config as being processed (includes specific subdomain)
1210
+ processedDigDomains.add(digDedupeKey);
1187
1211
 
1188
1212
  if (forceDebug) {
1189
1213
  const digTypes = [];
package/nwss.js CHANGED
@@ -1,4 +1,4 @@
1
- // === Network scanner script (nwss.js) v2.0.27 ===
1
+ // === Network scanner script (nwss.js) v2.0.28 ===
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
@@ -92,9 +92,9 @@ const USER_AGENTS = Object.freeze(new Map([
92
92
  ['chrome', "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36"],
93
93
  ['chrome_mac', "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36"],
94
94
  ['chrome_linux', "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36"],
95
- ['firefox', "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:142.0) Gecko/20100101 Firefox/143.0"],
96
- ['firefox_mac', "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:142.0) Gecko/20100101 Firefox/143.0"],
97
- ['firefox_linux', "Mozilla/5.0 (X11; Linux x86_64; rv:142.0) Gecko/20100101 Firefox/143.0"],
95
+ ['firefox', "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:144.0) Gecko/20100101 Firefox/144.0"],
96
+ ['firefox_mac', "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:144.0) Gecko/20100101 Firefox/144.0"],
97
+ ['firefox_linux', "Mozilla/5.0 (X11; Linux x86_64; rv:144.0) Gecko/20100101 Firefox/144.0"],
98
98
  ['safari', "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.6 Safari/605.1.15"]
99
99
  ]));
100
100
 
@@ -143,7 +143,7 @@ const { navigateWithRedirectHandling, handleRedirectTimeout } = require('./lib/r
143
143
  const { monitorBrowserHealth, isBrowserHealthy, isQuicklyResponsive, performGroupWindowCleanup, performRealtimeWindowCleanup, trackPageForRealtime, updatePageUsage, cleanupPageBeforeReload } = require('./lib/browserhealth');
144
144
 
145
145
  // --- Script Configuration & Constants ---
146
- const VERSION = '2.0.27'; // Script version
146
+ const VERSION = '2.0.28'; // Script version
147
147
 
148
148
  // get startTime
149
149
  const startTime = Date.now();
@@ -747,6 +747,12 @@ function safeMarkDomainProcessed(domain, context, metadata) {
747
747
  }
748
748
  }
749
749
 
750
+ // Global deduplication for nettools to avoid redundant lookups across all URLs
751
+ // Whois: keyed by root domain only (whois data is consistent for entire domain)
752
+ // Dig: keyed by specific subdomain+config (DNS records can vary by subdomain)
753
+ const globalProcessedWhoisDomains = new Set();
754
+ const globalProcessedDigDomains = new Set();
755
+
750
756
  // Handle --clean-rules after config is loaded (so we have access to sites)
751
757
  if (cleanRules || cleanRulesFile) {
752
758
  const filesToClean = cleanRulesFile ? [cleanRulesFile] : [outputFile, compareFile].filter(Boolean);
@@ -1245,6 +1251,16 @@ function setupFrameHandling(page, forceDebug) {
1245
1251
  // Auto-detect best headless mode based on Puppeteer version
1246
1252
  headless: headlessMode,
1247
1253
  args: [
1254
+ // CRITICAL: Remove automation detection markers
1255
+ '--disable-blink-features=AutomationControlled',
1256
+ '--no-first-run',
1257
+ '--disable-default-apps',
1258
+ '--disable-component-extensions-with-background-pages',
1259
+ // HIGH IMPACT: Normal Chrome behavior simulation
1260
+ '--password-store=basic',
1261
+ '--use-mock-keychain',
1262
+ '--disable-client-side-phishing-detection',
1263
+ '--enable-features=NetworkService',
1248
1264
  // Disk space controls - 50MB cache limits
1249
1265
  '--disable-features=VizDisplayCompositor',
1250
1266
  `--disk-cache-size=${CACHE_LIMITS.DISK_CACHE_SIZE}`, // 50MB disk cache
@@ -1260,10 +1276,7 @@ function setupFrameHandling(page, forceDebug) {
1260
1276
  '--aggressive-cache-discard',
1261
1277
  '--memory-pressure-off',
1262
1278
  '--max_old_space_size=2048',
1263
- '--no-first-run',
1264
1279
  '--disable-prompt-on-repost', // Fixes form popup on page reload
1265
- '--disable-default-apps',
1266
- '--disable-component-extensions-with-background-pages',
1267
1280
  '--disable-background-networking',
1268
1281
  '--no-sandbox',
1269
1282
  '--disable-setuid-sandbox',
@@ -1290,14 +1303,13 @@ function setupFrameHandling(page, forceDebug) {
1290
1303
  '--disable-backgrounding-occluded-windows',
1291
1304
  '--disable-background-timer-throttling',
1292
1305
  '--disable-features=site-per-process', // Better for single-site scanning
1293
- '--disable-blink-features=AutomationControlled', // Avoid detection
1294
1306
  '--no-zygote', // Better process isolation
1295
1307
  ],
1296
1308
  // Optimized timeouts for Puppeteer 23.x performance
1297
1309
  protocolTimeout: TIMEOUTS.PROTOCOL_TIMEOUT,
1298
1310
  slowMo: 0, // No artificial delays
1299
1311
  defaultViewport: null, // Use system default viewport
1300
- ignoreDefaultArgs: ['--enable-automation'] // Avoid automation detection
1312
+ ignoreDefaultArgs: ['--enable-automation', '--enable-blink-features=AutomationControlled'] // Avoid automation detection
1301
1313
  });
1302
1314
 
1303
1315
  // Store the user data directory on the browser object for cleanup
@@ -2485,6 +2497,8 @@ function setupFrameHandling(page, forceDebug) {
2485
2497
  const netToolsHandler = createNetToolsHandler({
2486
2498
  whoisTerms,
2487
2499
  whoisOrTerms,
2500
+ processedWhoisDomains: globalProcessedWhoisDomains,
2501
+ processedDigDomains: globalProcessedDigDomains,
2488
2502
  whoisDelay: siteConfig.whois_delay !== undefined ? siteConfig.whois_delay : whois_delay,
2489
2503
  whoisServer,
2490
2504
  whoisServerMode: siteConfig.whois_server_mode || whois_server_mode,
@@ -2591,6 +2605,8 @@ function setupFrameHandling(page, forceDebug) {
2591
2605
  const netToolsHandler = createNetToolsHandler({
2592
2606
  whoisTerms,
2593
2607
  whoisOrTerms,
2608
+ processedWhoisDomains: globalProcessedWhoisDomains,
2609
+ processedDigDomains: globalProcessedDigDomains,
2594
2610
  whoisDelay: siteConfig.whois_delay !== undefined ? siteConfig.whois_delay : whois_delay, // Site-specific or global fallback
2595
2611
  whoisServer, // Pass whois server configuration
2596
2612
  whoisServerMode: siteConfig.whois_server_mode || whois_server_mode,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fanboynz/network-scanner",
3
- "version": "2.0.28",
3
+ "version": "2.0.29",
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": {