@fanboynz/network-scanner 1.0.88 → 1.0.89
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/nwss.js +114 -67
- package/package.json +1 -1
package/nwss.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// === Network scanner script (nwss.js) v1.0.
|
|
1
|
+
// === Network scanner script (nwss.js) v1.0.89 ===
|
|
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
|
|
@@ -125,7 +125,7 @@ const { navigateWithRedirectHandling, handleRedirectTimeout } = require('./lib/r
|
|
|
125
125
|
const { monitorBrowserHealth, isBrowserHealthy, isQuicklyResponsive } = require('./lib/browserhealth');
|
|
126
126
|
|
|
127
127
|
// --- Script Configuration & Constants ---
|
|
128
|
-
const VERSION = '1.0.
|
|
128
|
+
const VERSION = '1.0.89'; // Script version
|
|
129
129
|
|
|
130
130
|
// get startTime
|
|
131
131
|
const startTime = Date.now();
|
|
@@ -1460,21 +1460,7 @@ function setupFrameHandling(page, forceDebug) {
|
|
|
1460
1460
|
];
|
|
1461
1461
|
|
|
1462
1462
|
try {
|
|
1463
|
-
|
|
1464
|
-
const isHealthy = await isBrowserHealthy(browserInstance);
|
|
1465
|
-
if (!isHealthy) {
|
|
1466
|
-
if (forceDebug) {
|
|
1467
|
-
console.log(formatLogMessage('debug', `Browser health degraded before processing ${currentUrl} - forcing immediate restart`));
|
|
1468
|
-
}
|
|
1469
|
-
// Return special code to trigger immediate browser restart
|
|
1470
|
-
return {
|
|
1471
|
-
url: currentUrl,
|
|
1472
|
-
rules: [],
|
|
1473
|
-
success: false,
|
|
1474
|
-
needsImmediateRestart: true,
|
|
1475
|
-
error: 'Browser health degraded - restart required'
|
|
1476
|
-
};
|
|
1477
|
-
}
|
|
1463
|
+
|
|
1478
1464
|
// Check for Protocol timeout errors that indicate browser is broken
|
|
1479
1465
|
if (browserInstance.process() && browserInstance.process().killed) {
|
|
1480
1466
|
throw new Error('Browser process was killed - restart required');
|
|
@@ -1486,22 +1472,7 @@ function setupFrameHandling(page, forceDebug) {
|
|
|
1486
1472
|
throw new Error('Failed to create valid page instance');
|
|
1487
1473
|
}
|
|
1488
1474
|
|
|
1489
|
-
|
|
1490
|
-
const stillHealthy = await isQuicklyResponsive(browserInstance, 3000);
|
|
1491
|
-
|
|
1492
|
-
if (!stillHealthy) {
|
|
1493
|
-
if (forceDebug) {
|
|
1494
|
-
console.log(formatLogMessage('debug', `Browser unresponsive during page setup for ${currentUrl} - triggering restart`));
|
|
1495
|
-
}
|
|
1496
|
-
return {
|
|
1497
|
-
url: currentUrl,
|
|
1498
|
-
rules: [],
|
|
1499
|
-
success: false,
|
|
1500
|
-
needsImmediateRestart: true,
|
|
1501
|
-
error: 'Browser became unresponsive during page setup - restart required'
|
|
1502
|
-
};
|
|
1503
|
-
}
|
|
1504
|
-
|
|
1475
|
+
|
|
1505
1476
|
// Set aggressive timeouts for problematic operations
|
|
1506
1477
|
// Optimized timeouts for Puppeteer 23.x responsiveness
|
|
1507
1478
|
page.setDefaultTimeout(Math.min(timeout, TIMEOUTS.DEFAULT_PAGE_REDUCED));
|
|
@@ -1582,6 +1553,8 @@ function setupFrameHandling(page, forceDebug) {
|
|
|
1582
1553
|
// --- START: evaluateOnNewDocument for Fetch/XHR Interception (Moved and Fixed) ---
|
|
1583
1554
|
// This script is injected if --eval-on-doc is used or siteConfig.evaluateOnNewDocument is true.
|
|
1584
1555
|
const shouldInjectEvalForPage = siteConfig.evaluateOnNewDocument === true || globalEvalOnDoc;
|
|
1556
|
+
let evalOnDocSuccess = false; // Track injection success for fallback logic
|
|
1557
|
+
|
|
1585
1558
|
if (shouldInjectEvalForPage) {
|
|
1586
1559
|
if (forceDebug) {
|
|
1587
1560
|
if (globalEvalOnDoc) {
|
|
@@ -1590,24 +1563,29 @@ function setupFrameHandling(page, forceDebug) {
|
|
|
1590
1563
|
console.log(formatLogMessage('debug', `[evalOnDoc] Site-specific Fetch/XHR interception enabled for: ${currentUrl}`));
|
|
1591
1564
|
}
|
|
1592
1565
|
}
|
|
1593
|
-
|
|
1566
|
+
|
|
1567
|
+
// Strategy 1: Try full injection with health check
|
|
1594
1568
|
let browserResponsive = false;
|
|
1595
1569
|
try {
|
|
1596
1570
|
await Promise.race([
|
|
1597
1571
|
browserInstance.version(), // Quick responsiveness test
|
|
1598
1572
|
new Promise((_, reject) =>
|
|
1599
|
-
setTimeout(() => reject(new Error('Browser health check timeout')),
|
|
1573
|
+
setTimeout(() => reject(new Error('Browser health check timeout')), 3000)
|
|
1600
1574
|
)
|
|
1601
1575
|
]);
|
|
1602
1576
|
browserResponsive = true;
|
|
1603
1577
|
} catch (healthErr) {
|
|
1604
|
-
|
|
1578
|
+
if (forceDebug) {
|
|
1579
|
+
console.log(formatLogMessage('debug', `[evalOnDoc] Browser health check failed: ${healthErr.message}`));
|
|
1580
|
+
}
|
|
1605
1581
|
browserResponsive = false;
|
|
1606
1582
|
}
|
|
1607
1583
|
|
|
1584
|
+
// Strategy 2: Try injection with reduced complexity if browser is responsive
|
|
1608
1585
|
if (browserResponsive) {
|
|
1609
|
-
|
|
1610
|
-
|
|
1586
|
+
try {
|
|
1587
|
+
await Promise.race([
|
|
1588
|
+
page.evaluateOnNewDocument(() => {
|
|
1611
1589
|
// Prevent infinite reload loops
|
|
1612
1590
|
let reloadCount = 0;
|
|
1613
1591
|
const MAX_RELOADS = 2;
|
|
@@ -1668,18 +1646,62 @@ function setupFrameHandling(page, forceDebug) {
|
|
|
1668
1646
|
return originalXHROpen.apply(this, arguments);
|
|
1669
1647
|
}
|
|
1670
1648
|
};
|
|
1671
|
-
})
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1649
|
+
}),
|
|
1650
|
+
new Promise((_, reject) =>
|
|
1651
|
+
setTimeout(() => reject(new Error('Injection timeout')), 8000)
|
|
1652
|
+
)
|
|
1653
|
+
]);
|
|
1654
|
+
evalOnDocSuccess = true;
|
|
1655
|
+
if (forceDebug) {
|
|
1656
|
+
console.log(formatLogMessage('debug', `[evalOnDoc] Full injection successful for ${currentUrl}`));
|
|
1677
1657
|
}
|
|
1678
|
-
|
|
1658
|
+
} catch (fullInjectionErr) {
|
|
1659
|
+
if (forceDebug) {
|
|
1660
|
+
console.log(formatLogMessage('debug', `[evalOnDoc] Full injection failed: ${fullInjectionErr.message}, trying simplified fallback`));
|
|
1661
|
+
}
|
|
1662
|
+
|
|
1663
|
+
// Strategy 3: Fallback - Try minimal injection (just fetch monitoring)
|
|
1664
|
+
try {
|
|
1665
|
+
await Promise.race([
|
|
1666
|
+
page.evaluateOnNewDocument(() => {
|
|
1667
|
+
// Minimal injection - just fetch monitoring
|
|
1668
|
+
if (window.fetch) {
|
|
1669
|
+
const originalFetch = window.fetch;
|
|
1670
|
+
window.fetch = (...args) => {
|
|
1671
|
+
try {
|
|
1672
|
+
console.log('[evalOnDoc][fetch-minimal]', args[0]);
|
|
1673
|
+
return originalFetch.apply(this, args);
|
|
1674
|
+
} catch (err) {
|
|
1675
|
+
return originalFetch.apply(this, args);
|
|
1676
|
+
}
|
|
1677
|
+
};
|
|
1678
|
+
}
|
|
1679
|
+
}),
|
|
1680
|
+
new Promise((_, reject) =>
|
|
1681
|
+
setTimeout(() => reject(new Error('Minimal injection timeout')), 3000)
|
|
1682
|
+
)
|
|
1683
|
+
]);
|
|
1684
|
+
evalOnDocSuccess = true;
|
|
1685
|
+
if (forceDebug) {
|
|
1686
|
+
console.log(formatLogMessage('debug', `[evalOnDoc] Minimal injection successful for ${currentUrl}`));
|
|
1687
|
+
}
|
|
1688
|
+
} catch (minimalInjectionErr) {
|
|
1689
|
+
if (forceDebug) {
|
|
1690
|
+
console.log(formatLogMessage('debug', `[evalOnDoc] Minimal injection also failed: ${minimalInjectionErr.message}`));
|
|
1691
|
+
}
|
|
1692
|
+
evalOnDocSuccess = false;
|
|
1693
|
+
}
|
|
1694
|
+
}
|
|
1679
1695
|
} else {
|
|
1680
1696
|
if (forceDebug) {
|
|
1681
|
-
console.log(formatLogMessage('debug', `[evalOnDoc]
|
|
1697
|
+
console.log(formatLogMessage('debug', `[evalOnDoc] Browser unresponsive, skipping injection for ${currentUrl}`));
|
|
1682
1698
|
}
|
|
1699
|
+
evalOnDocSuccess = false;
|
|
1700
|
+
}
|
|
1701
|
+
|
|
1702
|
+
// Final status logging
|
|
1703
|
+
if (!evalOnDocSuccess) {
|
|
1704
|
+
console.warn(formatLogMessage('warn', `[evalOnDoc] All injection strategies failed for ${currentUrl} - continuing with standard request monitoring only`));
|
|
1683
1705
|
}
|
|
1684
1706
|
}
|
|
1685
1707
|
// --- END: evaluateOnNewDocument for Fetch/XHR Interception ---
|
|
@@ -3132,20 +3154,31 @@ function setupFrameHandling(page, forceDebug) {
|
|
|
3132
3154
|
const batchEnd = Math.min(batchStart + RESOURCE_CLEANUP_INTERVAL, totalUrls);
|
|
3133
3155
|
const currentBatch = allTasks.slice(batchStart, batchEnd);
|
|
3134
3156
|
|
|
3135
|
-
//
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
|
|
3146
|
-
|
|
3147
|
-
|
|
3148
|
-
|
|
3157
|
+
// IMPROVED: Only check health if we have indicators of problems
|
|
3158
|
+
let healthCheck = { shouldRestart: false, reason: null };
|
|
3159
|
+
const recentResults = results.slice(-8); // Check more results for better pattern detection
|
|
3160
|
+
const recentFailureRate = recentResults.length > 0 ?
|
|
3161
|
+
recentResults.filter(r => !r.success).length / recentResults.length : 0;
|
|
3162
|
+
const hasHighFailureRate = recentFailureRate > 0.75; // 75% failure threshold (more conservative)
|
|
3163
|
+
const hasCriticalErrors = recentResults.filter(r => r.needsImmediateRestart).length > 2;
|
|
3164
|
+
|
|
3165
|
+
// Only run health checks when we have STRONG indicators of problems
|
|
3166
|
+
if (urlsSinceLastCleanup > 15 && (
|
|
3167
|
+
(hasHighFailureRate && recentResults.length >= 5) || // Need sufficient sample size
|
|
3168
|
+
hasCriticalErrors ||
|
|
3169
|
+
urlsSinceLastCleanup > RESOURCE_CLEANUP_INTERVAL * 0.9 // Very close to cleanup limit
|
|
3170
|
+
)) {
|
|
3171
|
+
healthCheck = await monitorBrowserHealth(browser, {}, {
|
|
3172
|
+
siteIndex: Math.floor(batchStart / RESOURCE_CLEANUP_INTERVAL),
|
|
3173
|
+
totalSites: Math.ceil(totalUrls / RESOURCE_CLEANUP_INTERVAL),
|
|
3174
|
+
urlsSinceCleanup: urlsSinceLastCleanup,
|
|
3175
|
+
cleanupInterval: RESOURCE_CLEANUP_INTERVAL,
|
|
3176
|
+
forceDebug,
|
|
3177
|
+
silentMode
|
|
3178
|
+
});
|
|
3179
|
+
} else if (forceDebug && urlsSinceLastCleanup > 10) {
|
|
3180
|
+
console.log(formatLogMessage('debug', `Skipping health check: failure rate ${Math.round(recentFailureRate * 100)}%, critical errors: ${hasCriticalErrors ? 'yes' : 'no'}`));
|
|
3181
|
+
}
|
|
3149
3182
|
|
|
3150
3183
|
const batchSize = currentBatch.length;
|
|
3151
3184
|
|
|
@@ -3155,17 +3188,21 @@ function setupFrameHandling(page, forceDebug) {
|
|
|
3155
3188
|
// Check if processing this entire site would exceed cleanup interval OR health check suggests restart
|
|
3156
3189
|
const wouldExceedLimit = urlsSinceLastCleanup + batchSize >= Math.min(RESOURCE_CLEANUP_INTERVAL, 100);
|
|
3157
3190
|
const isNotLastBatch = batchEnd < totalUrls;
|
|
3191
|
+
// IMPROVED: More restrictive health-based restart conditions
|
|
3192
|
+
const shouldRestartFromHealth = healthCheck.shouldRestart &&
|
|
3193
|
+
!healthCheck.reason?.includes('Scheduled cleanup') &&
|
|
3194
|
+
(healthCheck.reason?.includes('Critical') || healthCheck.reason?.includes('disconnected'));
|
|
3158
3195
|
|
|
3159
3196
|
// Restart browser if we've processed enough URLs, health check suggests it, and this isn't the last site
|
|
3160
|
-
if ((wouldExceedLimit ||
|
|
3197
|
+
if ((wouldExceedLimit || shouldRestartFromHealth || (hasHighFailureRate && recentResults.length >= 6)) && urlsSinceLastCleanup > 8 && isNotLastBatch) {
|
|
3161
3198
|
|
|
3162
3199
|
let restartReason = 'Unknown';
|
|
3163
|
-
if (
|
|
3200
|
+
if (shouldRestartFromHealth) {
|
|
3164
3201
|
restartReason = healthCheck.reason;
|
|
3165
|
-
} else if (
|
|
3166
|
-
restartReason =
|
|
3202
|
+
} else if (hasHighFailureRate) {
|
|
3203
|
+
restartReason = `High failure rate: ${Math.round(recentFailureRate * 100)}% in recent batch`;
|
|
3167
3204
|
} else if (wouldExceedLimit) {
|
|
3168
|
-
restartReason = `Processed ${urlsSinceLastCleanup} URLs`;
|
|
3205
|
+
restartReason = `Processed ${urlsSinceLastCleanup} URLs (scheduled maintenance)`;
|
|
3169
3206
|
}
|
|
3170
3207
|
|
|
3171
3208
|
if (!silentMode) {
|
|
@@ -3233,8 +3270,18 @@ function setupFrameHandling(page, forceDebug) {
|
|
|
3233
3270
|
const batchTasks = currentBatch.map(task => originalLimit(() => processUrl(task.url, task.config, browser)));
|
|
3234
3271
|
const batchResults = await Promise.all(batchTasks);
|
|
3235
3272
|
|
|
3236
|
-
//
|
|
3237
|
-
const
|
|
3273
|
+
// IMPROVED: Much more conservative emergency restart logic
|
|
3274
|
+
const criticalRestartCount = batchResults.filter(r => r.needsImmediateRestart).length;
|
|
3275
|
+
// Require either:
|
|
3276
|
+
// - More than 50% of batch has critical errors, OR
|
|
3277
|
+
// - At least 3 critical errors in any size batch
|
|
3278
|
+
const restartThreshold = Math.max(3, Math.floor(batchSize * 0.5)); // 50% of batch or min 3
|
|
3279
|
+
const needsImmediateRestart = criticalRestartCount >= restartThreshold && criticalRestartCount >= 2;
|
|
3280
|
+
|
|
3281
|
+
// Log restart decision for debugging
|
|
3282
|
+
if (forceDebug && criticalRestartCount > 0) {
|
|
3283
|
+
console.log(formatLogMessage('debug', `Emergency restart decision: ${criticalRestartCount}/${batchSize} critical errors (threshold: ${restartThreshold}, restart: ${needsImmediateRestart ? 'YES' : 'NO'})`));
|
|
3284
|
+
}
|
|
3238
3285
|
|
|
3239
3286
|
// Log completion of concurrent processing
|
|
3240
3287
|
if (forceDebug) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fanboynz/network-scanner",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.89",
|
|
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": {
|