@fanboynz/network-scanner 2.0.51 → 2.0.52
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 -1
- package/nwss.js +42 -18
- package/package.json +1 -1
package/lib/fingerprint.js
CHANGED
|
@@ -843,7 +843,28 @@ async function applyUserAgentSpoofing(page, siteConfig, forceDebug, currentUrl)
|
|
|
843
843
|
}
|
|
844
844
|
}, 'WebGL spoofing');
|
|
845
845
|
|
|
846
|
-
//
|
|
846
|
+
// WebGL null-context safety net � prevents ad script crashes in headless
|
|
847
|
+
safeExecute(() => {
|
|
848
|
+
const originalGetContext = HTMLCanvasElement.prototype.getContext;
|
|
849
|
+
HTMLCanvasElement.prototype.getContext = function(type, attrs) {
|
|
850
|
+
const ctx = originalGetContext.call(this, type, attrs);
|
|
851
|
+
if (ctx !== null || (type !== 'webgl' && type !== 'experimental-webgl' && type !== 'webgl2')) return ctx;
|
|
852
|
+
// Return minimal mock so scripts calling getShaderPrecisionFormat etc. don't crash
|
|
853
|
+
const noop = () => {};
|
|
854
|
+
return new Proxy({}, {
|
|
855
|
+
get(_, prop) {
|
|
856
|
+
if (prop === 'getShaderPrecisionFormat') return () => ({ rangeMin: 127, rangeMax: 127, precision: 23 });
|
|
857
|
+
if (prop === 'getParameter') return (p) => ({ 37445: 'Intel Inc.', 37446: 'Intel(R) UHD Graphics 630' }[p] || 0);
|
|
858
|
+
if (prop === 'getSupportedExtensions') return () => [];
|
|
859
|
+
if (prop === 'getExtension') return () => null;
|
|
860
|
+
if (prop === 'canvas') return this;
|
|
861
|
+
if (prop === 'drawingBufferWidth') return 1920;
|
|
862
|
+
if (prop === 'drawingBufferHeight') return 1080;
|
|
863
|
+
return noop;
|
|
864
|
+
}
|
|
865
|
+
});
|
|
866
|
+
};
|
|
867
|
+
}, 'WebGL null-context safety net'); // Permissions API spoofing
|
|
847
868
|
//
|
|
848
869
|
safeExecute(() => {
|
|
849
870
|
if (navigator.permissions?.query) {
|
package/nwss.js
CHANGED
|
@@ -1367,9 +1367,17 @@ function setupFrameHandling(page, forceDebug) {
|
|
|
1367
1367
|
if (launchHeadless) {
|
|
1368
1368
|
const puppeteerInfo = detectPuppeteerVersion();
|
|
1369
1369
|
|
|
1370
|
+
// Check if any site needs fingerprint protection — use stealth-friendly headless mode
|
|
1371
|
+
const needsStealth = sites.some(site => site.fingerprint_protection);
|
|
1372
|
+
|
|
1370
1373
|
if (puppeteerInfo.useShellMode) {
|
|
1371
|
-
|
|
1372
|
-
|
|
1374
|
+
if (needsStealth) {
|
|
1375
|
+
headlessMode = 'new'; // Full Chrome in headless — harder to detect than chrome-headless-shell
|
|
1376
|
+
if (forceDebug) console.log(formatLogMessage('debug', `Using headless=new for stealth (fingerprint_protection detected)`));
|
|
1377
|
+
} else {
|
|
1378
|
+
headlessMode = 'shell'; // Use fast chrome-headless-shell for 22.x+
|
|
1379
|
+
if (forceDebug) console.log(formatLogMessage('debug', `Using chrome-headless-shell (Puppeteer ${puppeteerInfo.version || 'v' + puppeteerInfo.majorVersion + '.x'})`));
|
|
1380
|
+
}
|
|
1373
1381
|
} else {
|
|
1374
1382
|
headlessMode = true; // Use regular headless for older versions
|
|
1375
1383
|
if (forceDebug) console.log(formatLogMessage('debug', 'Could not detect Puppeteer version, using regular headless mode'));
|
|
@@ -1439,7 +1447,7 @@ function setupFrameHandling(page, forceDebug) {
|
|
|
1439
1447
|
'--disable-features=SafeBrowsing',
|
|
1440
1448
|
'--disable-dev-shm-usage',
|
|
1441
1449
|
'--disable-sync',
|
|
1442
|
-
'--
|
|
1450
|
+
'--use-gl=swiftshader', // Software WebGL — prevents ad script crashes in headless
|
|
1443
1451
|
'--mute-audio',
|
|
1444
1452
|
'--disable-translate',
|
|
1445
1453
|
'--window-size=1920,1080',
|
|
@@ -1494,7 +1502,9 @@ function setupFrameHandling(page, forceDebug) {
|
|
|
1494
1502
|
|
|
1495
1503
|
// Log which headless mode is being used
|
|
1496
1504
|
if (forceDebug && launchHeadless) {
|
|
1497
|
-
|
|
1505
|
+
const needsStealth = sites.some(site => site.fingerprint_protection);
|
|
1506
|
+
const modeLabel = needsStealth ? 'headless=new (stealth mode)' : 'chrome-headless-shell (performance mode)';
|
|
1507
|
+
console.log(formatLogMessage('debug', `Using ${modeLabel}`));
|
|
1498
1508
|
}
|
|
1499
1509
|
|
|
1500
1510
|
// Initial cleanup of any existing Chrome temp files - always comprehensive on startup
|
|
@@ -1652,6 +1662,11 @@ function setupFrameHandling(page, forceDebug) {
|
|
|
1652
1662
|
let cdpSessionManager = null;
|
|
1653
1663
|
// Use Map to track domains and their resource types for --adblock-rules or --dry-run
|
|
1654
1664
|
const matchedDomains = (adblockRulesMode || siteConfig.adblock_rules || dryRunMode) ? new Map() : new Set();
|
|
1665
|
+
|
|
1666
|
+
// Local domain dedup scoped to THIS processUrl call only
|
|
1667
|
+
// Prevents cross-config contamination from the global domain cache
|
|
1668
|
+
const localDetectedDomains = new Set();
|
|
1669
|
+
const isLocallyDetected = (domain) => localDetectedDomains.has(domain);
|
|
1655
1670
|
|
|
1656
1671
|
// Initialize dry run matches collection
|
|
1657
1672
|
if (dryRunMode) {
|
|
@@ -2430,6 +2445,7 @@ function setupFrameHandling(page, forceDebug) {
|
|
|
2430
2445
|
|
|
2431
2446
|
// Mark full subdomain as detected for future reference
|
|
2432
2447
|
markDomainAsDetected(cacheKey);
|
|
2448
|
+
localDetectedDomains.add(cacheKey);
|
|
2433
2449
|
|
|
2434
2450
|
// Also mark in smart cache with context (if cache is enabled)
|
|
2435
2451
|
if (smartCache) {
|
|
@@ -2499,7 +2515,7 @@ function setupFrameHandling(page, forceDebug) {
|
|
|
2499
2515
|
if (forceDebug) {
|
|
2500
2516
|
console.log(formatLogMessage('debug', `Blocking potential infinite iframe loop: ${checkedUrl}`));
|
|
2501
2517
|
}
|
|
2502
|
-
request.abort();
|
|
2518
|
+
request.abort('blockedbyclient');
|
|
2503
2519
|
return;
|
|
2504
2520
|
}
|
|
2505
2521
|
|
|
@@ -2534,7 +2550,7 @@ function setupFrameHandling(page, forceDebug) {
|
|
|
2534
2550
|
if (forceDebug) {
|
|
2535
2551
|
console.log(formatLogMessage('debug', `${messageColors.blocked('[adblock]')} ${checkedUrl} (${result.reason})`));
|
|
2536
2552
|
}
|
|
2537
|
-
request.abort();
|
|
2553
|
+
request.abort('blockedbyclient');
|
|
2538
2554
|
return;
|
|
2539
2555
|
}
|
|
2540
2556
|
adblockStats.allowed++;
|
|
@@ -2614,7 +2630,7 @@ function setupFrameHandling(page, forceDebug) {
|
|
|
2614
2630
|
}
|
|
2615
2631
|
}
|
|
2616
2632
|
|
|
2617
|
-
request.abort();
|
|
2633
|
+
request.abort('blockedbyclient');
|
|
2618
2634
|
return;
|
|
2619
2635
|
}
|
|
2620
2636
|
|
|
@@ -2724,7 +2740,7 @@ function setupFrameHandling(page, forceDebug) {
|
|
|
2724
2740
|
dryRunCallback: dryRunMode ? createEnhancedDryRunCallback(matchedDomains, forceDebug) : null,
|
|
2725
2741
|
matchedDomains,
|
|
2726
2742
|
addMatchedDomain,
|
|
2727
|
-
isDomainAlreadyDetected,
|
|
2743
|
+
isDomainAlreadyDetected: isLocallyDetected,
|
|
2728
2744
|
onWhoisResult: smartCache ? (domain, result) => smartCache.cacheNetTools(domain, 'whois', result) : undefined,
|
|
2729
2745
|
onDigResult: smartCache ? (domain, result, recordType) => smartCache.cacheNetTools(domain, 'dig', result, recordType) : undefined,
|
|
2730
2746
|
cachedWhois: smartCache ? smartCache.getCachedNetTools(reqDomain, 'whois') : null,
|
|
@@ -2773,8 +2789,8 @@ function setupFrameHandling(page, forceDebug) {
|
|
|
2773
2789
|
}
|
|
2774
2790
|
} else if (hasNetTools && !hasSearchString && !hasSearchStringAnd) {
|
|
2775
2791
|
// If nettools are configured (whois/dig), perform checks on the domain
|
|
2776
|
-
// Skip nettools check if full subdomain was already detected
|
|
2777
|
-
if (
|
|
2792
|
+
// Skip nettools check if full subdomain was already detected in THIS scan
|
|
2793
|
+
if (localDetectedDomains.has(fullSubdomain)) {
|
|
2778
2794
|
if (forceDebug) {
|
|
2779
2795
|
console.log(formatLogMessage('debug', `Skipping nettools check for already detected subdomain: ${fullSubdomain}`));
|
|
2780
2796
|
}
|
|
@@ -2833,7 +2849,7 @@ function setupFrameHandling(page, forceDebug) {
|
|
|
2833
2849
|
dryRunCallback: dryRunMode ? createEnhancedDryRunCallback(matchedDomains, forceDebug) : null,
|
|
2834
2850
|
matchedDomains,
|
|
2835
2851
|
addMatchedDomain,
|
|
2836
|
-
isDomainAlreadyDetected,
|
|
2852
|
+
isDomainAlreadyDetected: isLocallyDetected,
|
|
2837
2853
|
// Add cache callbacks if smart cache is available and caching is enabled
|
|
2838
2854
|
onWhoisResult: smartCache ? (domain, result) => {
|
|
2839
2855
|
smartCache.cacheNetTools(domain, 'whois', result);
|
|
@@ -2863,8 +2879,8 @@ function setupFrameHandling(page, forceDebug) {
|
|
|
2863
2879
|
}
|
|
2864
2880
|
} else {
|
|
2865
2881
|
// If searchstring or searchstring_and IS defined (with or without nettools), queue for content checking
|
|
2866
|
-
// Skip searchstring check if full subdomain was already detected
|
|
2867
|
-
if (
|
|
2882
|
+
// Skip searchstring check if full subdomain was already detected in THIS scan
|
|
2883
|
+
if (localDetectedDomains.has(fullSubdomain)) {
|
|
2868
2884
|
if (forceDebug) {
|
|
2869
2885
|
console.log(formatLogMessage('debug', `Skipping searchstring check for already detected subdomain: ${fullSubdomain}`));
|
|
2870
2886
|
}
|
|
@@ -2920,7 +2936,7 @@ function setupFrameHandling(page, forceDebug) {
|
|
|
2920
2936
|
searchStringsAnd,
|
|
2921
2937
|
matchedDomains,
|
|
2922
2938
|
addMatchedDomain, // Pass the helper function
|
|
2923
|
-
isDomainAlreadyDetected,
|
|
2939
|
+
isDomainAlreadyDetected: isLocallyDetected,
|
|
2924
2940
|
onContentFetched: smartCache && !ignoreCache ? (url, content) => {
|
|
2925
2941
|
// Only cache if not bypassing cache
|
|
2926
2942
|
if (!shouldBypassCacheForUrl(url, siteConfig)) {
|
|
@@ -2956,7 +2972,7 @@ function setupFrameHandling(page, forceDebug) {
|
|
|
2956
2972
|
regexes,
|
|
2957
2973
|
matchedDomains,
|
|
2958
2974
|
addMatchedDomain,
|
|
2959
|
-
isDomainAlreadyDetected,
|
|
2975
|
+
isDomainAlreadyDetected: isLocallyDetected,
|
|
2960
2976
|
onContentFetched: smartCache && !ignoreCache ? (url, content) => {
|
|
2961
2977
|
// Only cache if not bypassing cache
|
|
2962
2978
|
if (!shouldBypassCacheForUrl(url, siteConfig)) {
|
|
@@ -3027,7 +3043,7 @@ function setupFrameHandling(page, forceDebug) {
|
|
|
3027
3043
|
matchedDomains,
|
|
3028
3044
|
addMatchedDomain, // Pass the helper function
|
|
3029
3045
|
bypassCache: (url) => shouldBypassCacheForUrl(url, siteConfig),
|
|
3030
|
-
isDomainAlreadyDetected,
|
|
3046
|
+
isDomainAlreadyDetected: isLocallyDetected,
|
|
3031
3047
|
onContentFetched: smartCache && !ignoreCache ? (url, content) => {
|
|
3032
3048
|
// Only cache if not bypassing cache
|
|
3033
3049
|
if (!shouldBypassCacheForUrl(url, siteConfig)) {
|
|
@@ -3374,8 +3390,16 @@ function setupFrameHandling(page, forceDebug) {
|
|
|
3374
3390
|
|
|
3375
3391
|
// Mark page as processing during interactions
|
|
3376
3392
|
updatePageUsage(page, true);
|
|
3377
|
-
// Use enhanced interaction module
|
|
3378
|
-
|
|
3393
|
+
// Use enhanced interaction module with hard abort timeout
|
|
3394
|
+
const INTERACTION_HARD_TIMEOUT = 15000;
|
|
3395
|
+
try {
|
|
3396
|
+
await Promise.race([
|
|
3397
|
+
performPageInteraction(page, currentUrl, interactionConfig, forceDebug),
|
|
3398
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error('interaction hard timeout')), INTERACTION_HARD_TIMEOUT))
|
|
3399
|
+
]);
|
|
3400
|
+
} catch (interactTimeoutErr) {
|
|
3401
|
+
if (forceDebug) console.log(formatLogMessage('debug', `[interaction] Aborted after ${INTERACTION_HARD_TIMEOUT}ms: ${interactTimeoutErr.message}`));
|
|
3402
|
+
}
|
|
3379
3403
|
}
|
|
3380
3404
|
|
|
3381
3405
|
const delayMs = DEFAULT_DELAY;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fanboynz/network-scanner",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.52",
|
|
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": {
|