@fanboynz/network-scanner 2.0.9 → 2.0.11
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/browserhealth.js +86 -1
- package/lib/cloudflare.js +7 -1
- package/lib/fingerprint.js +51 -2
- package/nwss.js +132 -55
- package/package.json +1 -1
package/lib/browserhealth.js
CHANGED
|
@@ -970,6 +970,90 @@ async function isBrowserHealthy(browserInstance, includeNetworkTest = true) {
|
|
|
970
970
|
}
|
|
971
971
|
}
|
|
972
972
|
|
|
973
|
+
/**
|
|
974
|
+
* Performs comprehensive cleanup of page resources before operations that might cause detached frames
|
|
975
|
+
* Also attempts to stop any pending navigations that might interfere
|
|
976
|
+
* Used before reloads, navigations, and other operations that can trigger frame detachment
|
|
977
|
+
* @param {import('puppeteer').Page} page - Page to clean up
|
|
978
|
+
* @param {boolean} forceDebug - Debug logging flag
|
|
979
|
+
* @returns {Promise<boolean>} True if cleanup succeeded
|
|
980
|
+
*/
|
|
981
|
+
async function cleanupPageBeforeReload(page, forceDebug = false) {
|
|
982
|
+
try {
|
|
983
|
+
if (page.isClosed()) {
|
|
984
|
+
return false;
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
// First, try to stop any pending navigation
|
|
988
|
+
try {
|
|
989
|
+
await page.evaluate(() => {
|
|
990
|
+
// Stop any ongoing navigation
|
|
991
|
+
if (window.stop) {
|
|
992
|
+
window.stop();
|
|
993
|
+
}
|
|
994
|
+
});
|
|
995
|
+
} catch (e) {
|
|
996
|
+
// Page might be mid-navigation, that's ok
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
// Wait a bit for navigation to stop
|
|
1000
|
+
await new Promise(resolve => setTimeout(resolve, 500));
|
|
1001
|
+
|
|
1002
|
+
// Now do the full cleanup
|
|
1003
|
+
await page.evaluate(() => {
|
|
1004
|
+
// Stop all media elements
|
|
1005
|
+
document.querySelectorAll('video, audio').forEach(media => {
|
|
1006
|
+
try {
|
|
1007
|
+
media.pause();
|
|
1008
|
+
media.src = '';
|
|
1009
|
+
media.load();
|
|
1010
|
+
} catch(e) {}
|
|
1011
|
+
});
|
|
1012
|
+
|
|
1013
|
+
// Clear all timers and intervals
|
|
1014
|
+
const highestId = setTimeout(() => {}, 0);
|
|
1015
|
+
for (let i = highestId; i >= 0; i--) {
|
|
1016
|
+
clearTimeout(i);
|
|
1017
|
+
clearInterval(i);
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
// Stop all animations
|
|
1021
|
+
if (typeof cancelAnimationFrame !== 'undefined') {
|
|
1022
|
+
const highestRAF = requestAnimationFrame(() => {});
|
|
1023
|
+
for (let i = highestRAF; i >= 0; i--) {
|
|
1024
|
+
cancelAnimationFrame(i);
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
// Clear all iframes properly
|
|
1029
|
+
document.querySelectorAll('iframe').forEach(iframe => {
|
|
1030
|
+
try {
|
|
1031
|
+
// Stop iframe content first
|
|
1032
|
+
if (iframe.contentWindow) {
|
|
1033
|
+
iframe.contentWindow.stop();
|
|
1034
|
+
}
|
|
1035
|
+
iframe.src = 'about:blank';
|
|
1036
|
+
iframe.remove();
|
|
1037
|
+
} catch(e) {}
|
|
1038
|
+
});
|
|
1039
|
+
|
|
1040
|
+
// Force garbage collection if available
|
|
1041
|
+
if (window.gc) window.gc();
|
|
1042
|
+
});
|
|
1043
|
+
|
|
1044
|
+
if (forceDebug) {
|
|
1045
|
+
console.log(formatLogMessage('debug', 'Page resources cleaned before reload'));
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
return true;
|
|
1049
|
+
} catch (err) {
|
|
1050
|
+
if (forceDebug) {
|
|
1051
|
+
console.log(formatLogMessage('debug', `Page cleanup error: ${err.message}`));
|
|
1052
|
+
}
|
|
1053
|
+
return false;
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
|
|
973
1057
|
module.exports = {
|
|
974
1058
|
checkBrowserHealth,
|
|
975
1059
|
checkBrowserMemory,
|
|
@@ -983,7 +1067,8 @@ module.exports = {
|
|
|
983
1067
|
monitorBrowserHealth,
|
|
984
1068
|
isBrowserHealthy,
|
|
985
1069
|
isCriticalProtocolError,
|
|
986
|
-
updatePageUsage
|
|
1070
|
+
updatePageUsage,
|
|
1071
|
+
cleanupPageBeforeReload
|
|
987
1072
|
};
|
|
988
1073
|
|
|
989
1074
|
// Clean up tracking maps when pages are closed
|
package/lib/cloudflare.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Cloudflare bypass and challenge handling module - Optimized with smart detection and adaptive timeouts
|
|
3
|
+
* Version: 2.6.3 - Fixes Cannot read properties of undefined (reading 'hasIndicators')
|
|
3
4
|
* Version: 2.6.2 - Further detached Frame fixes
|
|
4
5
|
* Version: 2.6.1 - timeoutId is not defined & race condition fix
|
|
5
6
|
* Version: 2.6.0 - Memory leak fixes and timeout cleanup
|
|
@@ -19,7 +20,7 @@ const { formatLogMessage } = require('./colorize');
|
|
|
19
20
|
/**
|
|
20
21
|
* Module version information
|
|
21
22
|
*/
|
|
22
|
-
const CLOUDFLARE_MODULE_VERSION = '2.6.
|
|
23
|
+
const CLOUDFLARE_MODULE_VERSION = '2.6.3';
|
|
23
24
|
|
|
24
25
|
/**
|
|
25
26
|
* Timeout constants for various operations (in milliseconds)
|
|
@@ -1570,6 +1571,11 @@ async function handleCloudflareProtection(page, currentUrl, siteConfig, forceDeb
|
|
|
1570
1571
|
|
|
1571
1572
|
// Quick detection first - exit early if no Cloudflare detected and no explicit config
|
|
1572
1573
|
const quickDetection = await quickCloudflareDetection(page, forceDebug);
|
|
1574
|
+
|
|
1575
|
+
// Safety check: ensure quickDetection is valid
|
|
1576
|
+
if (!quickDetection) {
|
|
1577
|
+
return { phishingWarning: { attempted: false, success: true }, verificationChallenge: { attempted: false, success: true }, overallSuccess: true, errors: [], quickDetectionFailed: true };
|
|
1578
|
+
}
|
|
1573
1579
|
|
|
1574
1580
|
// Early return structure when no Cloudflare indicators found
|
|
1575
1581
|
// Sets attempted: false, success: true for both protection types
|
package/lib/fingerprint.js
CHANGED
|
@@ -173,6 +173,23 @@ function generateCSIData() {
|
|
|
173
173
|
};
|
|
174
174
|
}
|
|
175
175
|
|
|
176
|
+
/**
|
|
177
|
+
* Validates page state before script injection to avoid timeouts
|
|
178
|
+
*/
|
|
179
|
+
async function validatePageForInjection(page, currentUrl, forceDebug) {
|
|
180
|
+
try {
|
|
181
|
+
if (page.isClosed()) return false;
|
|
182
|
+
await Promise.race([
|
|
183
|
+
page.evaluate(() => document.readyState),
|
|
184
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error('Page unresponsive')), 3000))
|
|
185
|
+
]);
|
|
186
|
+
return true;
|
|
187
|
+
} catch (validationErr) {
|
|
188
|
+
if (forceDebug) console.log(`[debug] Skipping fingerprint protection - page unresponsive: ${currentUrl}`);
|
|
189
|
+
return false;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
176
193
|
/**
|
|
177
194
|
* Creates mock fingerprinting objects
|
|
178
195
|
*/
|
|
@@ -251,6 +268,9 @@ async function applyUserAgentSpoofing(page, siteConfig, forceDebug, currentUrl)
|
|
|
251
268
|
|
|
252
269
|
if (forceDebug) console.log(`[debug] User agent spoofing: ${siteConfig.userAgent}`);
|
|
253
270
|
|
|
271
|
+
// Validate page state before injection
|
|
272
|
+
if (!(await validatePageForInjection(page, currentUrl, forceDebug))) return;
|
|
273
|
+
|
|
254
274
|
const selectedUserAgents = USER_AGENT_COLLECTIONS[siteConfig.userAgent.toLowerCase()];
|
|
255
275
|
const ua = selectedUserAgents ? selectedUserAgents[Math.floor(Math.random() * selectedUserAgents.length)] : null;
|
|
256
276
|
|
|
@@ -660,6 +680,12 @@ async function applyUserAgentSpoofing(page, siteConfig, forceDebug, currentUrl)
|
|
|
660
680
|
|
|
661
681
|
}, ua, forceDebug);
|
|
662
682
|
} catch (stealthErr) {
|
|
683
|
+
if (stealthErr.message.includes('Session closed') ||
|
|
684
|
+
stealthErr.message.includes('addScriptToEvaluateOnNewDocument timed out') ||
|
|
685
|
+
stealthErr.message.includes('Target closed')) {
|
|
686
|
+
if (forceDebug) console.log(`[debug] Page closed during stealth injection: ${currentUrl}`);
|
|
687
|
+
return;
|
|
688
|
+
}
|
|
663
689
|
console.warn(`[stealth protection failed] ${currentUrl}: ${stealthErr.message}`);
|
|
664
690
|
}
|
|
665
691
|
}
|
|
@@ -672,8 +698,23 @@ async function applyBraveSpoofing(page, siteConfig, forceDebug, currentUrl) {
|
|
|
672
698
|
if (!siteConfig.isBrave) return;
|
|
673
699
|
|
|
674
700
|
if (forceDebug) console.log(`[debug] Brave spoofing enabled for ${currentUrl}`);
|
|
701
|
+
|
|
702
|
+
// Validate page state before injection
|
|
703
|
+
if (!(await validatePageForInjection(page, currentUrl, forceDebug))) return;
|
|
675
704
|
|
|
676
|
-
|
|
705
|
+
try {
|
|
706
|
+
await page.evaluateOnNewDocument((debugEnabled) => {
|
|
707
|
+
// ... existing Brave spoofing code ...
|
|
708
|
+
}, forceDebug);
|
|
709
|
+
} catch (braveErr) {
|
|
710
|
+
if (braveErr.message.includes('Session closed') ||
|
|
711
|
+
braveErr.message.includes('addScriptToEvaluateOnNewDocument timed out') ||
|
|
712
|
+
braveErr.message.includes('Target closed')) {
|
|
713
|
+
if (forceDebug) console.log(`[debug] Page closed during Brave injection: ${currentUrl}`);
|
|
714
|
+
return;
|
|
715
|
+
}
|
|
716
|
+
if (forceDebug) console.log(`[debug] Brave spoofing failed: ${currentUrl} - ${braveErr.message}`);
|
|
717
|
+
}
|
|
677
718
|
try {
|
|
678
719
|
Object.defineProperty(navigator, 'brave', {
|
|
679
720
|
get: () => ({
|
|
@@ -695,7 +736,6 @@ async function applyBraveSpoofing(page, siteConfig, forceDebug, currentUrl) {
|
|
|
695
736
|
} catch (err) {
|
|
696
737
|
if (debugEnabled) console.log(`[fingerprint] Brave spoofing error: ${err.message}`);
|
|
697
738
|
}
|
|
698
|
-
}, forceDebug);
|
|
699
739
|
}
|
|
700
740
|
|
|
701
741
|
/**
|
|
@@ -707,6 +747,9 @@ async function applyFingerprintProtection(page, siteConfig, forceDebug, currentU
|
|
|
707
747
|
|
|
708
748
|
if (forceDebug) console.log(`[debug] Fingerprint protection enabled for ${currentUrl}`);
|
|
709
749
|
|
|
750
|
+
// Validate page state before injection
|
|
751
|
+
if (!(await validatePageForInjection(page, currentUrl, forceDebug))) return;
|
|
752
|
+
|
|
710
753
|
const spoof = fingerprintSetting === 'random' ? getRandomFingerprint() : {
|
|
711
754
|
deviceMemory: 8,
|
|
712
755
|
hardwareConcurrency: 4,
|
|
@@ -796,6 +839,12 @@ async function applyFingerprintProtection(page, siteConfig, forceDebug, currentU
|
|
|
796
839
|
|
|
797
840
|
}, { spoof, debugEnabled: forceDebug });
|
|
798
841
|
} catch (err) {
|
|
842
|
+
if (err.message.includes('Session closed') ||
|
|
843
|
+
err.message.includes('addScriptToEvaluateOnNewDocument timed out') ||
|
|
844
|
+
err.message.includes('Target closed')) {
|
|
845
|
+
if (forceDebug) console.log(`[debug] Page closed during fingerprint injection: ${currentUrl}`);
|
|
846
|
+
return;
|
|
847
|
+
}
|
|
799
848
|
console.warn(`[fingerprint protection failed] ${currentUrl}: ${err.message}`);
|
|
800
849
|
}
|
|
801
850
|
}
|
package/nwss.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// === Network scanner script (nwss.js) v2.0.
|
|
1
|
+
// === Network scanner script (nwss.js) v2.0.11 ===
|
|
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
|
|
@@ -55,19 +55,20 @@ function fastTimeout(ms) {
|
|
|
55
55
|
|
|
56
56
|
// --- Configuration Constants ---
|
|
57
57
|
const TIMEOUTS = {
|
|
58
|
-
DEFAULT_PAGE:
|
|
59
|
-
DEFAULT_NAVIGATION: 25000,
|
|
58
|
+
DEFAULT_PAGE: 35000, // Standard page load timeout (35s)
|
|
59
|
+
DEFAULT_NAVIGATION: 25000, // Navigation operation timeout
|
|
60
60
|
DEFAULT_NAVIGATION_REDUCED: 20000, // Reduced timeout for faster failures
|
|
61
|
-
DEFAULT_PAGE_REDUCED: 15000, //
|
|
62
|
-
FRAME_LOAD_WAIT: 2000,
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
61
|
+
DEFAULT_PAGE_REDUCED: 15000, // Faster page timeout for quick failures
|
|
62
|
+
FRAME_LOAD_WAIT: 2000, // Wait time for iframes to load
|
|
63
|
+
DEFAULT_DELAY: 6000, // Default delay: after page load
|
|
64
|
+
NETWORK_IDLE: 2000, // Network idle detection time
|
|
65
|
+
NETWORK_IDLE_MAX: 10000, // Maximum network idle wait time
|
|
66
|
+
FAST_SITE_THRESHOLD: 15000, // Threshold for "fast site" optimizations
|
|
67
|
+
EMERGENCY_RESTART_DELAY: 2000, // Delay after emergency browser restart
|
|
68
|
+
BROWSER_STABILIZE_DELAY: 1000, // Browser stabilization after restart
|
|
69
|
+
CURL_HANDLER_DELAY: 3000, // Wait for async curl operations
|
|
70
|
+
PROTOCOL_TIMEOUT: 180000, // Chrome DevTools Protocol timeout
|
|
71
|
+
REDIRECT_JS_TIMEOUT: 5000 // JavaScript redirect detection timeout
|
|
71
72
|
};
|
|
72
73
|
|
|
73
74
|
const CACHE_LIMITS = {
|
|
@@ -126,10 +127,10 @@ function detectPuppeteerVersion() {
|
|
|
126
127
|
// Enhanced redirect handling
|
|
127
128
|
const { navigateWithRedirectHandling, handleRedirectTimeout } = require('./lib/redirect');
|
|
128
129
|
// Ensure web browser is working correctly
|
|
129
|
-
const { monitorBrowserHealth, isBrowserHealthy, isQuicklyResponsive, performGroupWindowCleanup, performRealtimeWindowCleanup, trackPageForRealtime, updatePageUsage } = require('./lib/browserhealth');
|
|
130
|
+
const { monitorBrowserHealth, isBrowserHealthy, isQuicklyResponsive, performGroupWindowCleanup, performRealtimeWindowCleanup, trackPageForRealtime, updatePageUsage, cleanupPageBeforeReload } = require('./lib/browserhealth');
|
|
130
131
|
|
|
131
132
|
// --- Script Configuration & Constants ---
|
|
132
|
-
const VERSION = '2.0.
|
|
133
|
+
const VERSION = '2.0.11'; // Script version
|
|
133
134
|
|
|
134
135
|
// get startTime
|
|
135
136
|
const startTime = Date.now();
|
|
@@ -1087,7 +1088,7 @@ function matchesIgnoreDomain(domain, ignorePatterns) {
|
|
|
1087
1088
|
|
|
1088
1089
|
function setupFrameHandling(page, forceDebug) {
|
|
1089
1090
|
// Track active frames and clear on navigation to prevent detached frame access
|
|
1090
|
-
let activeFrames = new
|
|
1091
|
+
let activeFrames = new Map(); // Use Map to track frame state
|
|
1091
1092
|
|
|
1092
1093
|
// Clear frame tracking on navigation to prevent stale references
|
|
1093
1094
|
page.on('framenavigated', (frame) => {
|
|
@@ -1101,6 +1102,15 @@ function setupFrameHandling(page, forceDebug) {
|
|
|
1101
1102
|
page.on('frameattached', async (frame) => {
|
|
1102
1103
|
// Enhanced frame handling with detached frame protection
|
|
1103
1104
|
try {
|
|
1105
|
+
// Test frame accessibility first with safe method
|
|
1106
|
+
let isFrameValid = false;
|
|
1107
|
+
try {
|
|
1108
|
+
frame.url(); // This will throw if frame is detached
|
|
1109
|
+
isFrameValid = true;
|
|
1110
|
+
} catch (e) {
|
|
1111
|
+
return; // Frame is already detached, skip
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1104
1114
|
// Multiple checks for frame validity to prevent detached frame errors
|
|
1105
1115
|
if (!frame) {
|
|
1106
1116
|
if (forceDebug) {
|
|
@@ -1136,7 +1146,10 @@ function setupFrameHandling(page, forceDebug) {
|
|
|
1136
1146
|
return;
|
|
1137
1147
|
}
|
|
1138
1148
|
|
|
1139
|
-
|
|
1149
|
+
// Store frame with timestamp for tracking
|
|
1150
|
+
activeFrames.set(frame, Date.now());
|
|
1151
|
+
|
|
1152
|
+
if (frame !== page.mainFrame() && frame.parentFrame()) { // Only handle child frames
|
|
1140
1153
|
try {
|
|
1141
1154
|
if (forceDebug) {
|
|
1142
1155
|
console.log(formatLogMessage('debug', `New frame attached: ${frameUrl || 'about:blank'}`));
|
|
@@ -1979,7 +1992,17 @@ function setupFrameHandling(page, forceDebug) {
|
|
|
1979
1992
|
}
|
|
1980
1993
|
|
|
1981
1994
|
// --- Apply all fingerprint spoofing (user agent, Brave, fingerprint protection) ---
|
|
1982
|
-
|
|
1995
|
+
try {
|
|
1996
|
+
await applyAllFingerprintSpoofing(page, siteConfig, forceDebug, currentUrl);
|
|
1997
|
+
} catch (fingerprintErr) {
|
|
1998
|
+
if (fingerprintErr.message.includes('Session closed') ||
|
|
1999
|
+
fingerprintErr.message.includes('Protocol error') ||
|
|
2000
|
+
fingerprintErr.message.includes('addScriptToEvaluateOnNewDocument')) {
|
|
2001
|
+
console.warn(`[fingerprint protection failed] ${currentUrl}: ${fingerprintErr.message}`);
|
|
2002
|
+
} else {
|
|
2003
|
+
throw fingerprintErr;
|
|
2004
|
+
}
|
|
2005
|
+
}
|
|
1983
2006
|
|
|
1984
2007
|
const regexes = Array.isArray(siteConfig.filterRegex)
|
|
1985
2008
|
? siteConfig.filterRegex.map(r => new RegExp(r.replace(/^\/(.*)\/$/, '$1')))
|
|
@@ -3082,6 +3105,18 @@ function setupFrameHandling(page, forceDebug) {
|
|
|
3082
3105
|
// Page finished initial loading - mark as idle
|
|
3083
3106
|
updatePageUsage(page, false);
|
|
3084
3107
|
} catch (err) {
|
|
3108
|
+
// Handle detached frame errors during navigation
|
|
3109
|
+
if (err.message.includes('Navigating frame was detached') ||
|
|
3110
|
+
err.message.includes('Attempted to use detached')) {
|
|
3111
|
+
// Silent handling - this is expected for iframe-heavy sites
|
|
3112
|
+
if (forceDebug) {
|
|
3113
|
+
console.log(formatLogMessage('debug', `Frame detachment during navigation (expected): ${currentUrl}`));
|
|
3114
|
+
}
|
|
3115
|
+
// Continue with partial success - don't fail completely
|
|
3116
|
+
currentPageUrl = currentUrl;
|
|
3117
|
+
siteCounter++;
|
|
3118
|
+
// Skip to post-navigation processing
|
|
3119
|
+
} else {
|
|
3085
3120
|
// Enhanced error handling for redirect timeouts using redirect module
|
|
3086
3121
|
const timeoutResult = await handleRedirectTimeout(page, currentUrl, err, safeGetDomain, forceDebug, formatLogMessage);
|
|
3087
3122
|
|
|
@@ -3095,6 +3130,7 @@ function setupFrameHandling(page, forceDebug) {
|
|
|
3095
3130
|
throw err;
|
|
3096
3131
|
}
|
|
3097
3132
|
}
|
|
3133
|
+
}
|
|
3098
3134
|
|
|
3099
3135
|
if (interactEnabled && !disableInteract) {
|
|
3100
3136
|
if (forceDebug) console.log(formatLogMessage('debug', `interaction simulation enabled for ${currentUrl}`));
|
|
@@ -3105,7 +3141,7 @@ function setupFrameHandling(page, forceDebug) {
|
|
|
3105
3141
|
await performPageInteraction(page, currentUrl, interactionConfig, forceDebug);
|
|
3106
3142
|
}
|
|
3107
3143
|
|
|
3108
|
-
const delayMs =
|
|
3144
|
+
const delayMs = DEFAULT_DELAY;
|
|
3109
3145
|
|
|
3110
3146
|
// Optimized delays for Puppeteer 23.x performance
|
|
3111
3147
|
const isFastSite = timeout <= TIMEOUTS.FAST_SITE_THRESHOLD;
|
|
@@ -3141,13 +3177,41 @@ function setupFrameHandling(page, forceDebug) {
|
|
|
3141
3177
|
}
|
|
3142
3178
|
|
|
3143
3179
|
for (let i = 1; i <= totalReloads; i++) {
|
|
3144
|
-
|
|
3180
|
+
// Check browser health before attempting reload
|
|
3181
|
+
try {
|
|
3182
|
+
const browserHealthy = await isQuicklyResponsive(browser, 2000);
|
|
3183
|
+
if (!browserHealthy) {
|
|
3184
|
+
if (forceDebug) {
|
|
3185
|
+
console.log(formatLogMessage('debug', `Browser unresponsive before reload #${i}, skipping remaining reloads`));
|
|
3186
|
+
}
|
|
3187
|
+
console.warn(`Browser unresponsive before reload #${i}, skipping remaining reloads`);
|
|
3188
|
+
break;
|
|
3189
|
+
}
|
|
3190
|
+
} catch (healthErr) {
|
|
3191
|
+
console.warn(`Browser health check failed before reload #${i}: ${healthErr.message}`);
|
|
3192
|
+
break;
|
|
3193
|
+
}
|
|
3194
|
+
// Check if page is still valid before attempting reload
|
|
3195
|
+
let pageStillValid = false;
|
|
3145
3196
|
try {
|
|
3146
|
-
|
|
3147
|
-
|
|
3148
|
-
|
|
3149
|
-
|
|
3150
|
-
|
|
3197
|
+
// Add timeout to page validity check
|
|
3198
|
+
await Promise.race([
|
|
3199
|
+
page.evaluate(() => true),
|
|
3200
|
+
new Promise((_, reject) =>
|
|
3201
|
+
setTimeout(() => reject(new Error('Page validity check timeout')), 3000)
|
|
3202
|
+
)
|
|
3203
|
+
]);
|
|
3204
|
+
pageStillValid = true;
|
|
3205
|
+
} catch (validityCheck) {
|
|
3206
|
+
console.warn(`Page invalid before reload #${i}, skipping remaining reloads`);
|
|
3207
|
+
break;
|
|
3208
|
+
}
|
|
3209
|
+
|
|
3210
|
+
// Use comprehensive cleanup from browserhealth module
|
|
3211
|
+
await cleanupPageBeforeReload(page, forceDebug);
|
|
3212
|
+
|
|
3213
|
+
// Add stabilization delay after cleanup
|
|
3214
|
+
await fastTimeout(1000);
|
|
3151
3215
|
|
|
3152
3216
|
if (siteConfig.clear_sitedata === true) {
|
|
3153
3217
|
try {
|
|
@@ -3159,8 +3223,11 @@ function setupFrameHandling(page, forceDebug) {
|
|
|
3159
3223
|
}
|
|
3160
3224
|
|
|
3161
3225
|
let reloadSuccess = false;
|
|
3226
|
+
|
|
3227
|
+
// Skip force reload if browser seems unhealthy
|
|
3228
|
+
const skipForceReload = i > 2; // After 2 attempts, skip force reload
|
|
3162
3229
|
|
|
3163
|
-
if (useForceReload && !reloadSuccess) {
|
|
3230
|
+
if (useForceReload && !reloadSuccess && !skipForceReload) {
|
|
3164
3231
|
// Attempt force reload: disable cache, reload, re-enable cache
|
|
3165
3232
|
try {
|
|
3166
3233
|
// Timeout-protected cache disable
|
|
@@ -3169,28 +3236,23 @@ function setupFrameHandling(page, forceDebug) {
|
|
|
3169
3236
|
new Promise((_, reject) => setTimeout(() => reject(new Error('Cache disable timeout')), 8000))
|
|
3170
3237
|
]);
|
|
3171
3238
|
|
|
3172
|
-
|
|
3239
|
+
// Use networkidle2 for force reload to better detect when page is actually loaded
|
|
3240
|
+
await page.reload({ waitUntil: 'networkidle2', timeout: Math.min(timeout, 15000) });
|
|
3173
3241
|
|
|
3174
3242
|
// Timeout-protected cache enable
|
|
3175
3243
|
await Promise.race([
|
|
3176
3244
|
page.setCacheEnabled(true),
|
|
3177
3245
|
new Promise((_, reject) => setTimeout(() => reject(new Error('Cache enable timeout')), 8000))
|
|
3178
3246
|
]);
|
|
3179
|
-
|
|
3180
|
-
await page.reload({ waitUntil: 'domcontentloaded', timeout: Math.min(timeout, 12000) });
|
|
3181
|
-
|
|
3182
|
-
await Promise.race([
|
|
3183
|
-
page.setCacheEnabled(true),
|
|
3184
|
-
new Promise((_, reject) =>
|
|
3185
|
-
setTimeout(() => reject(new Error('setCacheEnabled(true) timeout')), 5000)
|
|
3186
|
-
)
|
|
3187
|
-
]);
|
|
3188
|
-
|
|
3247
|
+
|
|
3189
3248
|
reloadSuccess = true;
|
|
3190
3249
|
if (forceDebug) console.log(formatLogMessage('debug', `Force reload #${i} completed for ${currentUrl}`));
|
|
3191
3250
|
|
|
3192
3251
|
} catch (forceReloadErr) {
|
|
3193
|
-
|
|
3252
|
+
// Don't warn for timeouts on problematic sites, just fall back silently
|
|
3253
|
+
if (forceDebug || !forceReloadErr.message.includes('timeout')) {
|
|
3254
|
+
console.warn(messageColors.warn(`[force reload #${i} failed] ${currentUrl}: ${forceReloadErr.message} - falling back to standard reload`));
|
|
3255
|
+
}
|
|
3194
3256
|
reloadSuccess = false; // Ensure we try standard reload
|
|
3195
3257
|
}
|
|
3196
3258
|
}
|
|
@@ -3198,24 +3260,39 @@ function setupFrameHandling(page, forceDebug) {
|
|
|
3198
3260
|
// Fallback to standard reload if force reload failed or wasn't attempted
|
|
3199
3261
|
if (!reloadSuccess) {
|
|
3200
3262
|
try {
|
|
3201
|
-
|
|
3202
|
-
|
|
3203
|
-
|
|
3263
|
+
const canReload = await page.evaluate(() => {
|
|
3264
|
+
return !!(document && document.body);
|
|
3265
|
+
}).catch(() => false);
|
|
3266
|
+
|
|
3267
|
+
if (!canReload) {
|
|
3268
|
+
throw new Error('Page document invalid for reload');
|
|
3269
|
+
}
|
|
3270
|
+
|
|
3271
|
+
// Use networkidle2 with reasonable timeout
|
|
3272
|
+
// Use simpler reload for problematic pages
|
|
3273
|
+
const reloadOptions = i > 1
|
|
3274
|
+
? { waitUntil: 'domcontentloaded', timeout: 10000 } // Simpler after failures
|
|
3275
|
+
: { waitUntil: 'networkidle2', timeout: 15000 }; // Full wait first time
|
|
3276
|
+
|
|
3277
|
+
await page.reload(reloadOptions);
|
|
3278
|
+
|
|
3204
3279
|
if (forceDebug) console.log(formatLogMessage('debug', `Standard reload #${i} completed for ${currentUrl}`));
|
|
3205
3280
|
} catch (standardReloadErr) {
|
|
3206
|
-
|
|
3281
|
+
// Only warn for non-timeout errors
|
|
3282
|
+
if (!standardReloadErr.message.includes('timeout')) {
|
|
3283
|
+
console.warn(messageColors.warn(`[standard reload #${i} failed] ${currentUrl}: ${standardReloadErr.message}`));
|
|
3284
|
+
} else if (forceDebug) {
|
|
3285
|
+
console.log(formatLogMessage('debug', `Reload #${i} timed out for ${currentUrl}, continuing anyway`));
|
|
3286
|
+
}
|
|
3207
3287
|
|
|
3208
3288
|
// Check if this is a persistent failure that should skip remaining reloads
|
|
3209
|
-
|
|
3289
|
+
const isPersistentFailure = standardReloadErr.message.includes('detached Frame') ||
|
|
3290
|
+
standardReloadErr.message.includes('Attempted to use detached') ||
|
|
3291
|
+
standardReloadErr.message.includes('Navigating frame was detached') ||
|
|
3292
|
+
standardReloadErr.message.includes('document invalid') ||
|
|
3210
3293
|
standardReloadErr.message.includes('net::ERR_') ||
|
|
3211
3294
|
standardReloadErr.message.includes('Protocol error') ||
|
|
3212
|
-
|
|
3213
|
-
// CDP and injection failures
|
|
3214
|
-
standardReloadErr.constructor.name === 'ProtocolError' ||
|
|
3215
|
-
standardReloadErr.name === 'ProtocolError' ||
|
|
3216
|
-
standardReloadErr.message.includes('addScriptToEvaluateOnNewDocument timed out') ||
|
|
3217
|
-
standardReloadErr.message.includes('Runtime.callFunctionOn timed out') ||
|
|
3218
|
-
standardReloadErr.message.includes('CDP injection timeout');
|
|
3295
|
+
standardReloadErr.message.includes('Page crashed');
|
|
3219
3296
|
|
|
3220
3297
|
if (isPersistentFailure) {
|
|
3221
3298
|
const remainingReloads = totalReloads - i;
|
|
@@ -3225,16 +3302,16 @@ function setupFrameHandling(page, forceDebug) {
|
|
|
3225
3302
|
// Break out of reload loop to move to next URL faster
|
|
3226
3303
|
break;
|
|
3227
3304
|
}
|
|
3305
|
+
// For navigation timeouts, we can continue - the page might still be partially loaded
|
|
3306
|
+
// Don't break the loop for simple timeouts
|
|
3228
3307
|
}
|
|
3229
|
-
} else {
|
|
3230
|
-
// Regular reload
|
|
3231
|
-
await page.reload({ waitUntil: 'domcontentloaded', timeout: Math.min(timeout, 15000) });
|
|
3232
3308
|
}
|
|
3233
3309
|
|
|
3234
3310
|
// Only add delay if we're continuing with more reloads
|
|
3235
3311
|
if (i < totalReloads) {
|
|
3236
|
-
|
|
3237
|
-
|
|
3312
|
+
// Reduce delay for problematic sites
|
|
3313
|
+
const adjustedDelay = i > 1 ? Math.min(DEFAULT_DELAY, 2000) : DEFAULT_DELAY;
|
|
3314
|
+
await fastTimeout(adjustedDelay);
|
|
3238
3315
|
}
|
|
3239
3316
|
}
|
|
3240
3317
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fanboynz/network-scanner",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.11",
|
|
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": {
|