@fanboynz/network-scanner 2.0.53 → 2.0.54
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/fingerprint.js +22 -5
- package/lib/nettools.js +68 -61
- package/nwss.js +2 -6
- package/package.json +1 -1
package/lib/fingerprint.js
CHANGED
|
@@ -1023,8 +1023,9 @@ async function applyUserAgentSpoofing(page, siteConfig, forceDebug, currentUrl)
|
|
|
1023
1023
|
|
|
1024
1024
|
// Null context — return mock to prevent crashes
|
|
1025
1025
|
if (ctx === null) {
|
|
1026
|
-
|
|
1027
|
-
|
|
1026
|
+
const canvasEl = this; // capture the actual canvas element
|
|
1027
|
+
const mock = new Proxy({}, {
|
|
1028
|
+
get(target, prop) {
|
|
1028
1029
|
if (prop === 'getShaderPrecisionFormat') return () => ({ rangeMin: 127, rangeMax: 127, precision: 23 });
|
|
1029
1030
|
if (prop === 'getParameter') return (p) => webglSpoofParams[p] || 0;
|
|
1030
1031
|
if (prop === 'getSupportedExtensions') return () => [];
|
|
@@ -1032,12 +1033,28 @@ async function applyUserAgentSpoofing(page, siteConfig, forceDebug, currentUrl)
|
|
|
1032
1033
|
if (name === 'WEBGL_debug_renderer_info') return { UNMASKED_VENDOR_WEBGL: 37445, UNMASKED_RENDERER_WEBGL: 37446 };
|
|
1033
1034
|
return null;
|
|
1034
1035
|
};
|
|
1035
|
-
if (prop === '
|
|
1036
|
-
|
|
1037
|
-
|
|
1036
|
+
if (prop === 'getContextAttributes') return () => ({
|
|
1037
|
+
alpha: true, antialias: true, depth: true, failIfMajorPerformanceCaveat: false,
|
|
1038
|
+
desynchronized: false, premultipliedAlpha: true, preserveDrawingBuffer: false,
|
|
1039
|
+
powerPreference: 'default', stencil: false, xrCompatible: false
|
|
1040
|
+
});
|
|
1041
|
+
if (prop === 'isContextLost') return () => false;
|
|
1042
|
+
if (prop === 'canvas') return canvasEl;
|
|
1043
|
+
if (prop === 'drawingBufferWidth') return canvasEl.width || 1920;
|
|
1044
|
+
if (prop === 'drawingBufferHeight') return canvasEl.height || 1080;
|
|
1045
|
+
if (prop === 'drawingBufferColorSpace') return 'srgb';
|
|
1046
|
+
// Identity — let prototype chain handle constructor/toString/Symbol.toStringTag
|
|
1047
|
+
if (prop === 'constructor') return WebGLRenderingContext;
|
|
1048
|
+
if (prop === Symbol.toStringTag) return 'WebGLRenderingContext';
|
|
1049
|
+
// Common draw/state methods — return noop to prevent crashes
|
|
1038
1050
|
return noop;
|
|
1039
1051
|
}
|
|
1040
1052
|
});
|
|
1053
|
+
// Make mock pass instanceof checks
|
|
1054
|
+
if (window.WebGLRenderingContext) {
|
|
1055
|
+
Object.setPrototypeOf(mock, WebGLRenderingContext.prototype);
|
|
1056
|
+
}
|
|
1057
|
+
return mock;
|
|
1041
1058
|
}
|
|
1042
1059
|
|
|
1043
1060
|
// Real context — wrap in Proxy to intercept getParameter/getExtension
|
package/lib/nettools.js
CHANGED
|
@@ -11,6 +11,20 @@ const execPromise = util.promisify(exec);
|
|
|
11
11
|
// Cycling index for whois server rotation
|
|
12
12
|
let whoisServerCycleIndex = 0;
|
|
13
13
|
|
|
14
|
+
// Global dig result cache — shared across ALL handler instances and processUrl calls
|
|
15
|
+
// Key: `${domain}-${recordType}`, Value: { result, timestamp }
|
|
16
|
+
// DNS records don't change based on what terms you're searching for,
|
|
17
|
+
// so we cache the raw dig output and let each handler check its own terms against it
|
|
18
|
+
const globalDigResultCache = new Map();
|
|
19
|
+
const GLOBAL_DIG_CACHE_TTL = 300000; // 5 minutes
|
|
20
|
+
const GLOBAL_DIG_CACHE_MAX = 500;
|
|
21
|
+
|
|
22
|
+
// Global whois result cache — shared across ALL handler instances and processUrl calls
|
|
23
|
+
// Whois data is per root domain and doesn't change based on search terms
|
|
24
|
+
const globalWhoisResultCache = new Map();
|
|
25
|
+
const GLOBAL_WHOIS_CACHE_TTL = 900000; // 15 minutes (whois data changes less frequently)
|
|
26
|
+
const GLOBAL_WHOIS_CACHE_MAX = 500;
|
|
27
|
+
|
|
14
28
|
/**
|
|
15
29
|
* Strips ANSI color codes from a string for clean file logging
|
|
16
30
|
* @param {string} text - Text that may contain ANSI codes
|
|
@@ -26,27 +40,28 @@ function stripAnsiColors(text) {
|
|
|
26
40
|
* @returns {Object} Object with isAvailable boolean and version/error info
|
|
27
41
|
*/
|
|
28
42
|
function validateWhoisAvailability() {
|
|
43
|
+
if (validateWhoisAvailability._cached) return validateWhoisAvailability._cached;
|
|
29
44
|
try {
|
|
30
45
|
const result = execSync('whois --version 2>&1', { encoding: 'utf8' });
|
|
31
|
-
|
|
46
|
+
validateWhoisAvailability._cached = {
|
|
32
47
|
isAvailable: true,
|
|
33
48
|
version: result.trim()
|
|
34
49
|
};
|
|
35
50
|
} catch (error) {
|
|
36
|
-
// Some systems don't have --version, try just whois
|
|
37
51
|
try {
|
|
38
52
|
execSync('which whois', { encoding: 'utf8' });
|
|
39
|
-
|
|
53
|
+
validateWhoisAvailability._cached = {
|
|
40
54
|
isAvailable: true,
|
|
41
55
|
version: 'whois (version unknown)'
|
|
42
56
|
};
|
|
43
57
|
} catch (e) {
|
|
44
|
-
|
|
58
|
+
validateWhoisAvailability._cached = {
|
|
45
59
|
isAvailable: false,
|
|
46
60
|
error: 'whois command not found'
|
|
47
61
|
};
|
|
48
62
|
}
|
|
49
63
|
}
|
|
64
|
+
return validateWhoisAvailability._cached;
|
|
50
65
|
}
|
|
51
66
|
|
|
52
67
|
/**
|
|
@@ -54,18 +69,20 @@ function validateWhoisAvailability() {
|
|
|
54
69
|
* @returns {Object} Object with isAvailable boolean and version/error info
|
|
55
70
|
*/
|
|
56
71
|
function validateDigAvailability() {
|
|
72
|
+
if (validateDigAvailability._cached) return validateDigAvailability._cached;
|
|
57
73
|
try {
|
|
58
74
|
const result = execSync('dig -v 2>&1', { encoding: 'utf8' });
|
|
59
|
-
|
|
75
|
+
validateDigAvailability._cached = {
|
|
60
76
|
isAvailable: true,
|
|
61
77
|
version: result.split('\n')[0].trim()
|
|
62
78
|
};
|
|
63
79
|
} catch (error) {
|
|
64
|
-
|
|
80
|
+
validateDigAvailability._cached = {
|
|
65
81
|
isAvailable: false,
|
|
66
82
|
error: 'dig command not found'
|
|
67
83
|
};
|
|
68
84
|
}
|
|
85
|
+
return validateDigAvailability._cached;
|
|
69
86
|
}
|
|
70
87
|
|
|
71
88
|
/**
|
|
@@ -635,8 +652,8 @@ async function digLookup(domain = '', recordType = 'A', timeout = 5000) {
|
|
|
635
652
|
// Clean domain
|
|
636
653
|
const cleanDomain = domain.replace(/^https?:\/\//, '').replace(/\/.*$/, '').replace(/:\d+$/, '');
|
|
637
654
|
|
|
638
|
-
//
|
|
639
|
-
const { stdout, stderr } = await execWithTimeout(`dig
|
|
655
|
+
// Single dig command — full output contains everything including the short answers
|
|
656
|
+
const { stdout: fullOutput, stderr } = await execWithTimeout(`dig "${cleanDomain}" ${recordType}`, timeout);
|
|
640
657
|
|
|
641
658
|
if (stderr && stderr.trim()) {
|
|
642
659
|
return {
|
|
@@ -647,13 +664,21 @@ async function digLookup(domain = '', recordType = 'A', timeout = 5000) {
|
|
|
647
664
|
};
|
|
648
665
|
}
|
|
649
666
|
|
|
650
|
-
//
|
|
651
|
-
const
|
|
667
|
+
// Extract short output from ANSWER SECTION of full dig output
|
|
668
|
+
const answerMatch = fullOutput.match(/;; ANSWER SECTION:\n([\s\S]*?)(?:\n;;|\n*$)/);
|
|
669
|
+
let shortOutput = '';
|
|
670
|
+
if (answerMatch) {
|
|
671
|
+
shortOutput = answerMatch[1]
|
|
672
|
+
.split('\n')
|
|
673
|
+
.map(line => line.split(/\s+/).pop())
|
|
674
|
+
.filter(Boolean)
|
|
675
|
+
.join('\n');
|
|
676
|
+
}
|
|
652
677
|
|
|
653
678
|
return {
|
|
654
679
|
success: true,
|
|
655
680
|
output: fullOutput,
|
|
656
|
-
shortOutput
|
|
681
|
+
shortOutput,
|
|
657
682
|
domain: cleanDomain,
|
|
658
683
|
recordType
|
|
659
684
|
};
|
|
@@ -766,7 +791,6 @@ function createNetToolsHandler(config) {
|
|
|
766
791
|
whoisDelay = 4000,
|
|
767
792
|
whoisServer,
|
|
768
793
|
whoisServerMode = 'random',
|
|
769
|
-
bufferedLogWrite = null,
|
|
770
794
|
debugLogFile = null,
|
|
771
795
|
digTerms,
|
|
772
796
|
digOrTerms,
|
|
@@ -807,20 +831,10 @@ function createNetToolsHandler(config) {
|
|
|
807
831
|
subdomain: digSubdomain
|
|
808
832
|
});
|
|
809
833
|
|
|
810
|
-
//
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
// Size Memory
|
|
815
|
-
// 100 ~900KB
|
|
816
|
-
// 200 1.8MB
|
|
817
|
-
// 300 2.6MB
|
|
818
|
-
// 400 3.4MB
|
|
819
|
-
// 500 4.2MB
|
|
820
|
-
// Add DNS resolution caching to avoid redundant dig lookups
|
|
821
|
-
const digResultCache = new Map();
|
|
822
|
-
const DIG_CACHE_TTL = 300000; // 5 minutes cache TTL
|
|
823
|
-
const DIG_MAX_CACHE_SIZE = 400; // Smaller cache for dig due to shorter TTL
|
|
834
|
+
// Whois cache is global (globalWhoisResultCache) — shared across all handler instances
|
|
835
|
+
// Whois data is per root domain and doesn't change based on search terms
|
|
836
|
+
// Dig cache is global (globalDigResultCache) — shared across all handler instances
|
|
837
|
+
// DNS results are the same regardless of search terms
|
|
824
838
|
|
|
825
839
|
return async function handleNetToolsCheck(domain, fullSubdomain) {
|
|
826
840
|
// Use fullSubdomain parameter instead of originalDomain to maintain consistency
|
|
@@ -844,19 +858,17 @@ function createNetToolsHandler(config) {
|
|
|
844
858
|
|
|
845
859
|
// Move the logToConsoleAndFile function declaration from later in the file to here:
|
|
846
860
|
function logToConsoleAndFile(message) {
|
|
861
|
+
// Note: This function needs access to forceDebug, debugLogFile, and fs from the parent scope
|
|
862
|
+
// These are passed in via the config object to createNetToolsHandler
|
|
863
|
+
// forceDebug, debugLogFile, and fs are available in this closure
|
|
864
|
+
|
|
865
|
+
// Always log to console when in debug mode
|
|
847
866
|
if (forceDebug) {
|
|
848
867
|
console.log(formatLogMessage('debug', message));
|
|
849
868
|
}
|
|
850
869
|
|
|
851
|
-
if
|
|
852
|
-
|
|
853
|
-
const timestamp = new Date().toISOString();
|
|
854
|
-
const cleanMessage = stripAnsiColors(message);
|
|
855
|
-
bufferedLogWrite(debugLogFile, `${timestamp} [debug nettools] ${cleanMessage}\n`);
|
|
856
|
-
} catch (logErr) {
|
|
857
|
-
// Silently fail file logging to avoid disrupting whois operations
|
|
858
|
-
}
|
|
859
|
-
} else if (debugLogFile && fs) {
|
|
870
|
+
// Also log to file if debug file logging is enabled
|
|
871
|
+
if (debugLogFile && fs) {
|
|
860
872
|
try {
|
|
861
873
|
const timestamp = new Date().toISOString();
|
|
862
874
|
const cleanMessage = stripAnsiColors(message);
|
|
@@ -982,9 +994,9 @@ function createNetToolsHandler(config) {
|
|
|
982
994
|
const now = Date.now();
|
|
983
995
|
let whoisResult = null;
|
|
984
996
|
|
|
985
|
-
if (
|
|
986
|
-
const cachedEntry =
|
|
987
|
-
if (now - cachedEntry.timestamp <
|
|
997
|
+
if (globalWhoisResultCache.has(whoisCacheKey)) {
|
|
998
|
+
const cachedEntry = globalWhoisResultCache.get(whoisCacheKey);
|
|
999
|
+
if (now - cachedEntry.timestamp < GLOBAL_WHOIS_CACHE_TTL) {
|
|
988
1000
|
if (forceDebug) {
|
|
989
1001
|
const age = Math.round((now - cachedEntry.timestamp) / 1000);
|
|
990
1002
|
const serverInfo = (selectedServer && selectedServer !== '') ? ` (server: ${selectedServer})` : ' (default server)';
|
|
@@ -998,7 +1010,7 @@ function createNetToolsHandler(config) {
|
|
|
998
1010
|
});
|
|
999
1011
|
} else {
|
|
1000
1012
|
// Cache expired, remove it
|
|
1001
|
-
|
|
1013
|
+
globalWhoisResultCache.delete(whoisCacheKey);
|
|
1002
1014
|
if (forceDebug) {
|
|
1003
1015
|
logToConsoleAndFile(`${messageColors.highlight('[whois-cache]')} Cache expired for ${whoisRootDomain}, performing fresh lookup`);
|
|
1004
1016
|
}
|
|
@@ -1030,7 +1042,7 @@ function createNetToolsHandler(config) {
|
|
|
1030
1042
|
!whoisResult.error.toLowerCase().includes('connection') &&
|
|
1031
1043
|
!whoisResult.error.toLowerCase().includes('network'))) {
|
|
1032
1044
|
|
|
1033
|
-
|
|
1045
|
+
globalWhoisResultCache.set(whoisCacheKey, {
|
|
1034
1046
|
result: whoisResult,
|
|
1035
1047
|
timestamp: now
|
|
1036
1048
|
});
|
|
@@ -1183,17 +1195,17 @@ function createNetToolsHandler(config) {
|
|
|
1183
1195
|
}
|
|
1184
1196
|
|
|
1185
1197
|
// Periodic whois cache cleanup to prevent memory leaks
|
|
1186
|
-
if (
|
|
1198
|
+
if (globalWhoisResultCache.size > GLOBAL_WHOIS_CACHE_MAX) {
|
|
1187
1199
|
const now = Date.now();
|
|
1188
1200
|
let cleanedCount = 0;
|
|
1189
|
-
for (const [key, entry] of
|
|
1190
|
-
if (now - entry.timestamp >
|
|
1191
|
-
|
|
1201
|
+
for (const [key, entry] of globalWhoisResultCache.entries()) {
|
|
1202
|
+
if (now - entry.timestamp > GLOBAL_WHOIS_CACHE_TTL) {
|
|
1203
|
+
globalWhoisResultCache.delete(key);
|
|
1192
1204
|
cleanedCount++;
|
|
1193
1205
|
}
|
|
1194
1206
|
}
|
|
1195
1207
|
if (forceDebug && cleanedCount > 0) {
|
|
1196
|
-
logToConsoleAndFile(`${messageColors.highlight('[whois-cache]')} Cleaned ${cleanedCount} expired entries, cache size: ${
|
|
1208
|
+
logToConsoleAndFile(`${messageColors.highlight('[whois-cache]')} Cleaned ${cleanedCount} expired entries, cache size: ${globalWhoisResultCache.size}`);
|
|
1197
1209
|
}
|
|
1198
1210
|
}
|
|
1199
1211
|
}
|
|
@@ -1216,16 +1228,16 @@ function createNetToolsHandler(config) {
|
|
|
1216
1228
|
const now = Date.now();
|
|
1217
1229
|
let digResult = null;
|
|
1218
1230
|
|
|
1219
|
-
if (
|
|
1220
|
-
const cachedEntry =
|
|
1221
|
-
if (now - cachedEntry.timestamp <
|
|
1231
|
+
if (globalDigResultCache.has(digCacheKey)) {
|
|
1232
|
+
const cachedEntry = globalDigResultCache.get(digCacheKey);
|
|
1233
|
+
if (now - cachedEntry.timestamp < GLOBAL_DIG_CACHE_TTL) {
|
|
1222
1234
|
if (forceDebug) {
|
|
1223
1235
|
logToConsoleAndFile(`${messageColors.highlight('[dig-cache]')} Using cached result for ${digDomain} (${digRecordType}) [age: ${Math.round((now - cachedEntry.timestamp) / 1000)}s]`);
|
|
1224
1236
|
}
|
|
1225
1237
|
digResult = cachedEntry.result;
|
|
1226
1238
|
} else {
|
|
1227
1239
|
// Cache expired, remove it
|
|
1228
|
-
|
|
1240
|
+
globalDigResultCache.delete(digCacheKey);
|
|
1229
1241
|
}
|
|
1230
1242
|
}
|
|
1231
1243
|
|
|
@@ -1233,7 +1245,7 @@ function createNetToolsHandler(config) {
|
|
|
1233
1245
|
digResult = await digLookup(digDomain, digRecordType, 5000); // 5 second timeout for dig
|
|
1234
1246
|
|
|
1235
1247
|
// Cache the result for future use
|
|
1236
|
-
|
|
1248
|
+
globalDigResultCache.set(digCacheKey, {
|
|
1237
1249
|
result: digResult,
|
|
1238
1250
|
timestamp: now
|
|
1239
1251
|
});
|
|
@@ -1303,17 +1315,17 @@ function createNetToolsHandler(config) {
|
|
|
1303
1315
|
}
|
|
1304
1316
|
|
|
1305
1317
|
// Periodic dig cache cleanup to prevent memory leaks
|
|
1306
|
-
if (
|
|
1318
|
+
if (globalDigResultCache.size > GLOBAL_DIG_CACHE_MAX) {
|
|
1307
1319
|
const now = Date.now();
|
|
1308
1320
|
let cleanedCount = 0;
|
|
1309
|
-
for (const [key, entry] of
|
|
1310
|
-
if (now - entry.timestamp >
|
|
1311
|
-
|
|
1321
|
+
for (const [key, entry] of globalDigResultCache.entries()) {
|
|
1322
|
+
if (now - entry.timestamp > GLOBAL_DIG_CACHE_TTL) {
|
|
1323
|
+
globalDigResultCache.delete(key);
|
|
1312
1324
|
cleanedCount++;
|
|
1313
1325
|
}
|
|
1314
1326
|
}
|
|
1315
1327
|
if (forceDebug && cleanedCount > 0) {
|
|
1316
|
-
logToConsoleAndFile(`${messageColors.highlight('[dig-cache]')} Cleaned ${cleanedCount} expired entries, cache size: ${
|
|
1328
|
+
logToConsoleAndFile(`${messageColors.highlight('[dig-cache]')} Cleaned ${cleanedCount} expired entries, cache size: ${globalDigResultCache.size}`);
|
|
1317
1329
|
}
|
|
1318
1330
|
}
|
|
1319
1331
|
}
|
|
@@ -1387,12 +1399,7 @@ function createNetToolsHandler(config) {
|
|
|
1387
1399
|
|
|
1388
1400
|
// Add whois server info to log if custom server was used
|
|
1389
1401
|
const serverInfo = whoisServer ? ` (whois-server: ${selectWhoisServer(whoisServer)})` : '';
|
|
1390
|
-
|
|
1391
|
-
if (bufferedLogWrite) {
|
|
1392
|
-
bufferedLogWrite(matchedUrlsLogFile, logLine);
|
|
1393
|
-
} else {
|
|
1394
|
-
fs.appendFileSync(matchedUrlsLogFile, logLine);
|
|
1395
|
-
}
|
|
1402
|
+
fs.appendFileSync(matchedUrlsLogFile, `${timestamp} [match][${simplifiedUrl}] ${domain} (${matchType.join(' + ')})${serverInfo}\n`);
|
|
1396
1403
|
}
|
|
1397
1404
|
}
|
|
1398
1405
|
|
package/nwss.js
CHANGED
|
@@ -1447,7 +1447,7 @@ function setupFrameHandling(page, forceDebug) {
|
|
|
1447
1447
|
'--disable-features=SafeBrowsing',
|
|
1448
1448
|
'--disable-dev-shm-usage',
|
|
1449
1449
|
'--disable-sync',
|
|
1450
|
-
'--
|
|
1450
|
+
'--disable-gpu', // WebGL null-context handled by fingerprint.js Proxy mock
|
|
1451
1451
|
'--mute-audio',
|
|
1452
1452
|
'--disable-translate',
|
|
1453
1453
|
'--window-size=1920,1080',
|
|
@@ -2730,9 +2730,7 @@ function setupFrameHandling(page, forceDebug) {
|
|
|
2730
2730
|
whoisDelay: siteConfig.whois_delay !== undefined ? siteConfig.whois_delay : whois_delay,
|
|
2731
2731
|
whoisServer,
|
|
2732
2732
|
whoisServerMode: siteConfig.whois_server_mode || whois_server_mode,
|
|
2733
|
-
bufferedLogWrite,
|
|
2734
2733
|
debugLogFile,
|
|
2735
|
-
fs,
|
|
2736
2734
|
digTerms,
|
|
2737
2735
|
digOrTerms,
|
|
2738
2736
|
digRecordType,
|
|
@@ -2838,9 +2836,7 @@ function setupFrameHandling(page, forceDebug) {
|
|
|
2838
2836
|
whoisDelay: siteConfig.whois_delay !== undefined ? siteConfig.whois_delay : whois_delay, // Site-specific or global fallback
|
|
2839
2837
|
whoisServer, // Pass whois server configuration
|
|
2840
2838
|
whoisServerMode: siteConfig.whois_server_mode || whois_server_mode,
|
|
2841
|
-
|
|
2842
|
-
debugLogFile, // Pass debug log file for whois error logging
|
|
2843
|
-
fs, // Pass fs module for file operations
|
|
2839
|
+
debugLogFile,
|
|
2844
2840
|
digTerms,
|
|
2845
2841
|
digOrTerms,
|
|
2846
2842
|
digRecordType,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fanboynz/network-scanner",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.54",
|
|
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": {
|