@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.
@@ -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
- return new Proxy({}, {
1027
- get(_, prop) {
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 === 'canvas') return this;
1036
- if (prop === 'drawingBufferWidth') return 1920;
1037
- if (prop === 'drawingBufferHeight') return 1080;
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
- return {
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
- return {
53
+ validateWhoisAvailability._cached = {
40
54
  isAvailable: true,
41
55
  version: 'whois (version unknown)'
42
56
  };
43
57
  } catch (e) {
44
- return {
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
- return {
75
+ validateDigAvailability._cached = {
60
76
  isAvailable: true,
61
77
  version: result.split('\n')[0].trim()
62
78
  };
63
79
  } catch (error) {
64
- return {
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
- // Get short output first
639
- const { stdout, stderr } = await execWithTimeout(`dig +short "${cleanDomain}" ${recordType}`, timeout);
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
- // Also get full dig output for detailed analysis
651
- const { stdout: fullOutput } = await execWithTimeout(`dig "${cleanDomain}" ${recordType}`, timeout);
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: stdout.trim(),
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
- // Add whois resolution caching to avoid redundant whois lookups
811
- const whoisResultCache = new Map();
812
- const WHOIS_CACHE_TTL = 900000; // 15 minutes cache TTL (whois data changes less frequently)
813
- const MAX_CACHE_SIZE = 400; // Larger cache for whois due to longer TTL
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 (debugLogFile && bufferedLogWrite) {
852
- try {
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 (whoisResultCache.has(whoisCacheKey)) {
986
- const cachedEntry = whoisResultCache.get(whoisCacheKey);
987
- if (now - cachedEntry.timestamp < WHOIS_CACHE_TTL) {
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
- whoisResultCache.delete(whoisCacheKey);
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
- whoisResultCache.set(whoisCacheKey, {
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 (whoisResultCache.size > MAX_CACHE_SIZE) {
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 whoisResultCache.entries()) {
1190
- if (now - entry.timestamp > WHOIS_CACHE_TTL) {
1191
- whoisResultCache.delete(key);
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: ${whoisResultCache.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 (digResultCache.has(digCacheKey)) {
1220
- const cachedEntry = digResultCache.get(digCacheKey);
1221
- if (now - cachedEntry.timestamp < DIG_CACHE_TTL) {
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
- digResultCache.delete(digCacheKey);
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
- digResultCache.set(digCacheKey, {
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 (digResultCache.size > DIG_MAX_CACHE_SIZE) {
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 digResultCache.entries()) {
1310
- if (now - entry.timestamp > DIG_CACHE_TTL) {
1311
- digResultCache.delete(key);
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: ${digResultCache.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
- const logLine = `${timestamp} [match][${simplifiedUrl}] ${domain} (${matchType.join(' + ')})${serverInfo}\n`;
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
- '--use-gl=swiftshader', // Software WebGL prevents ad script crashes in headless
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
- bufferedLogWrite,
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.53",
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": {