@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 +30 -0
- package/lib/fingerprint.js +104 -24
- package/lib/nettools.js +46 -22
- package/nwss.js +26 -10
- package/package.json +1 -1
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
|
+
}
|
package/lib/fingerprint.js
CHANGED
|
@@ -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:
|
|
100
|
-
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:
|
|
101
|
-
"Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:
|
|
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
|
|
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: () => ({
|
|
538
|
-
|
|
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
|
-
|
|
953
|
+
|
|
878
954
|
const permissionName = descriptor.name || descriptor;
|
|
879
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
//
|
|
794
|
-
|
|
795
|
-
const
|
|
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
|
-
|
|
859
|
-
const
|
|
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
|
-
|
|
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(
|
|
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 = `${
|
|
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 ${
|
|
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 ${
|
|
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 ${
|
|
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(
|
|
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 ${
|
|
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 ${
|
|
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 ${
|
|
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 ${
|
|
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(
|
|
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.
|
|
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:
|
|
96
|
-
['firefox_mac', "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:
|
|
97
|
-
['firefox_linux', "Mozilla/5.0 (X11; Linux x86_64; rv:
|
|
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.
|
|
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.
|
|
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": {
|