@fanboynz/network-scanner 1.0.62 → 1.0.64
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 +104 -14
- package/lib/cdp.js +8 -3
- package/lib/fingerprint.js +1870 -361
- package/lib/interaction.js +101 -1
- package/nwss.js +286 -69
- package/package.json +1 -1
package/lib/interaction.js
CHANGED
|
@@ -471,6 +471,25 @@ async function interactWithElements(page, options = {}) {
|
|
|
471
471
|
} = options;
|
|
472
472
|
|
|
473
473
|
try {
|
|
474
|
+
// Ensure page is in valid state for element interaction
|
|
475
|
+
try {
|
|
476
|
+
// Check if page is closed before attempting interaction
|
|
477
|
+
if (page.isClosed()) {
|
|
478
|
+
if (options.forceDebug) {
|
|
479
|
+
console.log(`[interaction] Page is closed, skipping element interaction`);
|
|
480
|
+
}
|
|
481
|
+
return;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
// Shorter timeout for element interaction since body should already exist
|
|
485
|
+
await page.waitForSelector('body', { timeout: 2000 });
|
|
486
|
+
} catch (bodyWaitErr) {
|
|
487
|
+
if (options.forceDebug) {
|
|
488
|
+
console.log(`[interaction] Page not ready for element interaction: ${bodyWaitErr.message}`);
|
|
489
|
+
}
|
|
490
|
+
return;
|
|
491
|
+
}
|
|
492
|
+
|
|
474
493
|
// Get viewport dimensions for coordinate bounds
|
|
475
494
|
const viewport = await page.viewport();
|
|
476
495
|
const maxX = viewport ? viewport.width : DEFAULT_VIEWPORT.WIDTH;
|
|
@@ -589,6 +608,13 @@ async function simulateTyping(page, text, options = {}) {
|
|
|
589
608
|
} = options;
|
|
590
609
|
|
|
591
610
|
try {
|
|
611
|
+
// Ensure page is ready for typing
|
|
612
|
+
try {
|
|
613
|
+
await page.waitForSelector('body', { timeout: 1000 });
|
|
614
|
+
} catch (bodyWaitErr) {
|
|
615
|
+
return; // Silently skip typing if page not ready
|
|
616
|
+
}
|
|
617
|
+
|
|
592
618
|
for (let i = 0; i < text.length; i++) {
|
|
593
619
|
const char = text[i];
|
|
594
620
|
|
|
@@ -683,6 +709,42 @@ async function performPageInteraction(page, currentUrl, options = {}, forceDebug
|
|
|
683
709
|
} = options;
|
|
684
710
|
|
|
685
711
|
try {
|
|
712
|
+
// Validate page state before starting interaction
|
|
713
|
+
try {
|
|
714
|
+
// Use site-specific timeout based on configuration
|
|
715
|
+
// Sites with longer configured timeouts get more time for body detection
|
|
716
|
+
const siteTimeout = options.siteTimeout || 30000; // From site config
|
|
717
|
+
const bodyTimeout = Math.min(Math.max(siteTimeout / 6, 3000), 8000); // 3-8 seconds
|
|
718
|
+
|
|
719
|
+
await page.waitForSelector('body', { timeout: bodyTimeout });
|
|
720
|
+
|
|
721
|
+
// Additional check: ensure page is not closed/crashed
|
|
722
|
+
if (page.isClosed()) {
|
|
723
|
+
if (forceDebug) {
|
|
724
|
+
console.log(`[interaction] Page is closed for ${currentUrl}, skipping interaction`);
|
|
725
|
+
}
|
|
726
|
+
return;
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
const bodyExists = await page.$('body');
|
|
730
|
+
if (!bodyExists) {
|
|
731
|
+
if (forceDebug) {
|
|
732
|
+
console.log(`[interaction] Body element not found for ${currentUrl}, skipping interaction`);
|
|
733
|
+
}
|
|
734
|
+
return;
|
|
735
|
+
}
|
|
736
|
+
} catch (bodyCheckErr) {
|
|
737
|
+
if (forceDebug) {
|
|
738
|
+
console.log(`[interaction] Page not ready for interaction on ${currentUrl} (waited ${bodyTimeout}ms): ${bodyCheckErr.message}`);
|
|
739
|
+
// For very slow sites, we might want to try a minimal interaction anyway
|
|
740
|
+
if (bodyTimeout >= 6000 && !bodyCheckErr.message.includes('closed')) {
|
|
741
|
+
console.log(`[interaction] Attempting minimal mouse movement only for slow-loading ${currentUrl}`);
|
|
742
|
+
return await performMinimalInteraction(page, currentUrl, options, forceDebug);
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
return;
|
|
746
|
+
}
|
|
747
|
+
|
|
686
748
|
// Get viewport dimensions
|
|
687
749
|
const viewport = await page.viewport();
|
|
688
750
|
const maxX = viewport ? viewport.width : DEFAULT_VIEWPORT.WIDTH;
|
|
@@ -753,7 +815,16 @@ async function performPageInteraction(page, currentUrl, options = {}, forceDebug
|
|
|
753
815
|
// Final hover position
|
|
754
816
|
const finalPos = generateRandomCoordinates(maxX, maxY);
|
|
755
817
|
await humanLikeMouseMove(page, currentPos.x, currentPos.y, finalPos.x, finalPos.y);
|
|
756
|
-
|
|
818
|
+
|
|
819
|
+
// Safe hover with validation
|
|
820
|
+
try {
|
|
821
|
+
const bodyElement = await page.$('body');
|
|
822
|
+
if (bodyElement) {
|
|
823
|
+
await page.hover('body');
|
|
824
|
+
}
|
|
825
|
+
} catch (hoverErr) {
|
|
826
|
+
// Silently handle hover failures - not critical
|
|
827
|
+
}
|
|
757
828
|
|
|
758
829
|
const elapsedTime = Date.now() - startTime;
|
|
759
830
|
if (forceDebug) {
|
|
@@ -768,6 +839,35 @@ async function performPageInteraction(page, currentUrl, options = {}, forceDebug
|
|
|
768
839
|
}
|
|
769
840
|
}
|
|
770
841
|
|
|
842
|
+
/**
|
|
843
|
+
* Performs minimal interaction for very slow or problematic pages
|
|
844
|
+
* Only does basic mouse movement without body validation
|
|
845
|
+
*/
|
|
846
|
+
async function performMinimalInteraction(page, currentUrl, options = {}, forceDebug = false) {
|
|
847
|
+
try {
|
|
848
|
+
if (page.isClosed()) return;
|
|
849
|
+
|
|
850
|
+
const viewport = await page.viewport();
|
|
851
|
+
const maxX = viewport ? viewport.width : DEFAULT_VIEWPORT.WIDTH;
|
|
852
|
+
const maxY = viewport ? viewport.height : DEFAULT_VIEWPORT.HEIGHT;
|
|
853
|
+
|
|
854
|
+
if (forceDebug) {
|
|
855
|
+
console.log(`[interaction] Performing minimal interaction for slow page: ${new URL(currentUrl).hostname}`);
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
// Just do basic mouse movement without body-dependent operations
|
|
859
|
+
const startPos = generateRandomCoordinates(maxX, maxY);
|
|
860
|
+
const endPos = generateRandomCoordinates(maxX, maxY);
|
|
861
|
+
|
|
862
|
+
await page.mouse.move(startPos.x, startPos.y);
|
|
863
|
+
await fastTimeout(500);
|
|
864
|
+
await humanLikeMouseMove(page, startPos.x, startPos.y, endPos.x, endPos.y);
|
|
865
|
+
|
|
866
|
+
} catch (minimalErr) {
|
|
867
|
+
// Even minimal interaction failed - page is truly broken
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
|
|
771
871
|
/**
|
|
772
872
|
* Creates an optimized interaction configuration based on site characteristics
|
|
773
873
|
*
|
package/nwss.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// === Network scanner script (nwss.js) v1.0.
|
|
1
|
+
// === Network scanner script (nwss.js) v1.0.64 ===
|
|
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
|
|
@@ -42,13 +42,50 @@ function fastTimeout(ms) {
|
|
|
42
42
|
return new Promise(resolve => setTimeout(resolve, ms));
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
+
/**
|
|
46
|
+
* Detects the installed Puppeteer version dynamically
|
|
47
|
+
* @returns {Object} Version info and compatibility settings
|
|
48
|
+
*/
|
|
49
|
+
function detectPuppeteerVersion() {
|
|
50
|
+
try {
|
|
51
|
+
const puppeteer = require('puppeteer');
|
|
52
|
+
let versionString = null;
|
|
53
|
+
|
|
54
|
+
// Try multiple methods to get version
|
|
55
|
+
if (puppeteer.version) {
|
|
56
|
+
versionString = puppeteer.version;
|
|
57
|
+
} else if (puppeteer._version) {
|
|
58
|
+
versionString = puppeteer._version;
|
|
59
|
+
} else {
|
|
60
|
+
// Fallback: try to get from Browser.version() after launch
|
|
61
|
+
return { majorVersion: 22, useShellMode: true, detected: false };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const majorVersion = parseInt(versionString.split('.')[0]);
|
|
65
|
+
const useShellMode = majorVersion >= 22;
|
|
66
|
+
|
|
67
|
+
return {
|
|
68
|
+
version: versionString,
|
|
69
|
+
majorVersion,
|
|
70
|
+
useShellMode,
|
|
71
|
+
detected: true
|
|
72
|
+
};
|
|
73
|
+
} catch (err) {
|
|
74
|
+
if (forceDebug) {
|
|
75
|
+
console.log(formatLogMessage('debug', `Could not detect Puppeteer version: ${err.message}`));
|
|
76
|
+
}
|
|
77
|
+
// Safe fallback - assume newer version
|
|
78
|
+
return { majorVersion: 22, useShellMode: true, detected: false };
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
45
82
|
// Enhanced redirect handling
|
|
46
83
|
const { navigateWithRedirectHandling, handleRedirectTimeout } = require('./lib/redirect');
|
|
47
84
|
// Ensure web browser is working correctly
|
|
48
85
|
const { monitorBrowserHealth, isBrowserHealthy } = require('./lib/browserhealth');
|
|
49
86
|
|
|
50
87
|
// --- Script Configuration & Constants ---
|
|
51
|
-
const VERSION = '1.0.
|
|
88
|
+
const VERSION = '1.0.64'; // Script version
|
|
52
89
|
|
|
53
90
|
// get startTime
|
|
54
91
|
const startTime = Date.now();
|
|
@@ -916,6 +953,23 @@ function matchesIgnoreDomain(domain, ignorePatterns) {
|
|
|
916
953
|
function setupFrameHandling(page, forceDebug) {
|
|
917
954
|
// Handle frame creation with error suppression
|
|
918
955
|
page.on('frameattached', async (frame) => {
|
|
956
|
+
// Enhanced frame handling for Puppeteer 23.x
|
|
957
|
+
try {
|
|
958
|
+
// Check if frame is valid before processing
|
|
959
|
+
if (frame.isDetached()) {
|
|
960
|
+
if (forceDebug) {
|
|
961
|
+
console.log(formatLogMessage('debug', `Skipping detached frame`));
|
|
962
|
+
}
|
|
963
|
+
return;
|
|
964
|
+
}
|
|
965
|
+
} catch (detachError) {
|
|
966
|
+
// Frame state checking can throw in 23.x, handle gracefully
|
|
967
|
+
if (forceDebug) {
|
|
968
|
+
console.log(formatLogMessage('debug', `Frame state check failed: ${detachError.message}`));
|
|
969
|
+
}
|
|
970
|
+
return;
|
|
971
|
+
}
|
|
972
|
+
|
|
919
973
|
if (frame.parentFrame()) { // Only handle child frames, not main frame
|
|
920
974
|
try {
|
|
921
975
|
const frameUrl = frame.url();
|
|
@@ -1024,6 +1078,23 @@ function setupFrameHandling(page, forceDebug) {
|
|
|
1024
1078
|
userDataDir = tempUserDataDir; // Store for cleanup tracking (use outer scope variable)
|
|
1025
1079
|
|
|
1026
1080
|
// Try to find system Chrome installation to avoid Puppeteer downloads
|
|
1081
|
+
|
|
1082
|
+
// Detect Puppeteer version for headless mode compatibility
|
|
1083
|
+
let headlessMode = launchHeadless;
|
|
1084
|
+
if (launchHeadless) {
|
|
1085
|
+
const puppeteerInfo = detectPuppeteerVersion();
|
|
1086
|
+
|
|
1087
|
+
if (puppeteerInfo.useShellMode) {
|
|
1088
|
+
headlessMode = 'shell'; // Use fast chrome-headless-shell for 22.x+
|
|
1089
|
+
if (forceDebug) console.log(formatLogMessage('debug', `Using chrome-headless-shell (Puppeteer ${puppeteerInfo.version || 'v' + puppeteerInfo.majorVersion + '.x'})`));
|
|
1090
|
+
} else {
|
|
1091
|
+
headlessMode = true; // Use regular headless for older versions
|
|
1092
|
+
if (forceDebug) console.log(formatLogMessage('debug', 'Could not detect Puppeteer version, using regular headless mode'));
|
|
1093
|
+
}
|
|
1094
|
+
} else {
|
|
1095
|
+
headlessMode = false; // Headful mode
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1027
1098
|
const systemChromePaths = [
|
|
1028
1099
|
'/usr/bin/google-chrome-stable',
|
|
1029
1100
|
'/usr/bin/google-chrome',
|
|
@@ -1048,28 +1119,24 @@ function setupFrameHandling(page, forceDebug) {
|
|
|
1048
1119
|
// Force temporary user data directory for complete cleanup control
|
|
1049
1120
|
userDataDir: tempUserDataDir,
|
|
1050
1121
|
// Puppeteer 22.x headless mode optimization
|
|
1051
|
-
//
|
|
1052
|
-
|
|
1053
|
-
// false = headful mode (GUI visible)
|
|
1054
|
-
headless: launchHeadless ? 'shell' : false, // Use shell for maximum performance
|
|
1122
|
+
// Auto-detect best headless mode based on Puppeteer version
|
|
1123
|
+
headless: headlessMode,
|
|
1055
1124
|
args: [
|
|
1056
1125
|
// Disk space controls - 50MB cache limits
|
|
1126
|
+
'--disable-features=VizDisplayCompositor',
|
|
1057
1127
|
'--disk-cache-size=52428800', // 50MB disk cache (50 * 1024 * 1024)
|
|
1058
1128
|
'--media-cache-size=52428800', // 50MB media cache
|
|
1059
1129
|
'--disable-application-cache',
|
|
1060
1130
|
'--disable-offline-load-stale-cache',
|
|
1061
1131
|
'--disable-background-downloads',
|
|
1062
|
-
// PERFORMANCE:
|
|
1063
|
-
'--disable-features=VizDisplayCompositor
|
|
1132
|
+
// PERFORMANCE: Enhanced Puppeteer 23.x optimizations
|
|
1133
|
+
'--disable-features=AudioServiceOutOfProcess,VizDisplayCompositor',
|
|
1134
|
+
'--disable-features=TranslateUI,BlinkGenPropertyTrees,Translate',
|
|
1135
|
+
'--disable-features=BackForwardCache,AcceptCHFrame',
|
|
1064
1136
|
'--disable-ipc-flooding-protection',
|
|
1065
|
-
'--disable-renderer-backgrounding',
|
|
1066
|
-
'--disable-backgrounding-occluded-windows',
|
|
1067
|
-
'--disable-features=TranslateUI,BlinkGenPropertyTrees',
|
|
1068
|
-
'--disable-features=Translate,BackForwardCache,AcceptCHFrame',
|
|
1069
1137
|
'--aggressive-cache-discard',
|
|
1070
1138
|
'--memory-pressure-off',
|
|
1071
1139
|
'--max_old_space_size=2048',
|
|
1072
|
-
'--disable-features=VizDisplayCompositor',
|
|
1073
1140
|
'--no-first-run',
|
|
1074
1141
|
'--disable-default-apps',
|
|
1075
1142
|
'--disable-component-extensions-with-background-pages',
|
|
@@ -1094,13 +1161,19 @@ function setupFrameHandling(page, forceDebug) {
|
|
|
1094
1161
|
'--disable-web-security',
|
|
1095
1162
|
'--allow-running-insecure-content',
|
|
1096
1163
|
'--disable-features=HttpsFirstBalancedModeAutoEnable',
|
|
1097
|
-
|
|
1098
|
-
'--disable-
|
|
1099
|
-
'--disable-
|
|
1100
|
-
'--disable-
|
|
1101
|
-
'--disable-
|
|
1164
|
+
// Puppeteer 23.x: Enhanced performance and stability args
|
|
1165
|
+
'--disable-renderer-backgrounding',
|
|
1166
|
+
'--disable-backgrounding-occluded-windows',
|
|
1167
|
+
'--disable-background-timer-throttling',
|
|
1168
|
+
'--disable-features=site-per-process', // Better for single-site scanning
|
|
1169
|
+
'--disable-blink-features=AutomationControlled', // Avoid detection
|
|
1170
|
+
'--no-zygote', // Better process isolation
|
|
1102
1171
|
],
|
|
1103
|
-
|
|
1172
|
+
// Optimized timeouts for Puppeteer 23.x performance
|
|
1173
|
+
protocolTimeout: 60000, // Reduced from 60s to 45s for faster failure detection
|
|
1174
|
+
slowMo: 0, // No artificial delays
|
|
1175
|
+
defaultViewport: null, // Use system default viewport
|
|
1176
|
+
ignoreDefaultArgs: ['--enable-automation'] // Avoid automation detection
|
|
1104
1177
|
});
|
|
1105
1178
|
|
|
1106
1179
|
// Store the user data directory on the browser object for cleanup
|
|
@@ -1117,6 +1190,15 @@ function setupFrameHandling(page, forceDebug) {
|
|
|
1117
1190
|
// launch with no safe browsing
|
|
1118
1191
|
let browser = await createBrowser();
|
|
1119
1192
|
if (forceDebug) console.log(formatLogMessage('debug', `Launching browser with headless: ${launchHeadless}`));
|
|
1193
|
+
|
|
1194
|
+
// Enhanced browser validation for Puppeteer 23.x
|
|
1195
|
+
try {
|
|
1196
|
+
const browserVersion = await browser.version();
|
|
1197
|
+
if (forceDebug) console.log(formatLogMessage('debug', `Browser launched successfully: ${browserVersion}`));
|
|
1198
|
+
} catch (versionError) {
|
|
1199
|
+
console.error(formatLogMessage('error', `Browser version check failed: ${versionError.message}`));
|
|
1200
|
+
throw new Error(`Browser startup validation failed: ${versionError.message}`);
|
|
1201
|
+
}
|
|
1120
1202
|
|
|
1121
1203
|
// Log which headless mode is being used
|
|
1122
1204
|
if (forceDebug && launchHeadless) {
|
|
@@ -1246,6 +1328,16 @@ function setupFrameHandling(page, forceDebug) {
|
|
|
1246
1328
|
// Track the effective current URL for first-party detection (updates after redirects)
|
|
1247
1329
|
let effectiveCurrentUrl = currentUrl;
|
|
1248
1330
|
|
|
1331
|
+
// Enhanced error types for Puppeteer 23.x compatibility
|
|
1332
|
+
const CRITICAL_BROWSER_ERRORS = [
|
|
1333
|
+
'Protocol error',
|
|
1334
|
+
'Target closed',
|
|
1335
|
+
'Browser has been closed',
|
|
1336
|
+
'Browser protocol broken',
|
|
1337
|
+
'Browser process exited',
|
|
1338
|
+
'Browser disconnected'
|
|
1339
|
+
];
|
|
1340
|
+
|
|
1249
1341
|
try {
|
|
1250
1342
|
// Health check before creating new page
|
|
1251
1343
|
const isHealthy = await isBrowserHealthy(browserInstance);
|
|
@@ -1267,11 +1359,17 @@ function setupFrameHandling(page, forceDebug) {
|
|
|
1267
1359
|
throw new Error('Browser process was killed - restart required');
|
|
1268
1360
|
}
|
|
1269
1361
|
page = await browserInstance.newPage();
|
|
1362
|
+
|
|
1363
|
+
// Enhanced page validation for Puppeteer 23.x
|
|
1364
|
+
if (!page || page.isClosed()) {
|
|
1365
|
+
throw new Error('Failed to create valid page instance');
|
|
1366
|
+
}
|
|
1270
1367
|
|
|
1271
1368
|
// Set aggressive timeouts for problematic operations
|
|
1272
|
-
|
|
1273
|
-
page.
|
|
1274
|
-
//
|
|
1369
|
+
// Optimized timeouts for Puppeteer 23.x responsiveness
|
|
1370
|
+
page.setDefaultTimeout(Math.min(timeout, 20000)); // Reduced from 15000 for faster failures
|
|
1371
|
+
page.setDefaultNavigationTimeout(Math.min(timeout, 25000)); // Reduced from 18000
|
|
1372
|
+
// Aggressive timeouts prevent hanging in Puppeteer 23.x while maintaining speed
|
|
1275
1373
|
|
|
1276
1374
|
page.on('console', (msg) => {
|
|
1277
1375
|
if (forceDebug && msg.type() === 'error') console.log(`[debug] Console error: ${msg.text()}`);
|
|
@@ -1282,6 +1380,57 @@ function setupFrameHandling(page, forceDebug) {
|
|
|
1282
1380
|
if (forceDebug) console.log(formatLogMessage('debug', `Page crashed: ${err.message}`));
|
|
1283
1381
|
// Don't throw here as it might cause hanging - let the timeout handle it
|
|
1284
1382
|
});
|
|
1383
|
+
|
|
1384
|
+
// Enhanced error handling for Puppeteer 23.x
|
|
1385
|
+
page.on('pageerror', (err) => {
|
|
1386
|
+
// Safe error message extraction for Puppeteer 23.x compatibility
|
|
1387
|
+
const getErrorMessage = (error) => {
|
|
1388
|
+
if (!error) return 'Unknown error';
|
|
1389
|
+
if (typeof error === 'string') return error;
|
|
1390
|
+
if (error.message) return error.message;
|
|
1391
|
+
if (error.toString && typeof error.toString === 'function') {
|
|
1392
|
+
try {
|
|
1393
|
+
return error.toString();
|
|
1394
|
+
} catch (toStringErr) {
|
|
1395
|
+
return 'Error object toString failed';
|
|
1396
|
+
}
|
|
1397
|
+
}
|
|
1398
|
+
return JSON.stringify(error) || 'Unparseable error object';
|
|
1399
|
+
};
|
|
1400
|
+
|
|
1401
|
+
const errorMessage = getErrorMessage(err);
|
|
1402
|
+
|
|
1403
|
+
// Handle specific service worker errors
|
|
1404
|
+
if (errorMessage.includes('ServiceWorker') ||
|
|
1405
|
+
errorMessage.includes('service worker') ||
|
|
1406
|
+
errorMessage.includes('TCPusher service worker') ||
|
|
1407
|
+
errorMessage.includes('failed to register')) {
|
|
1408
|
+
if (forceDebug) {
|
|
1409
|
+
console.log(formatLogMessage('debug', `Service worker error suppressed: ${errorMessage}`));
|
|
1410
|
+
}
|
|
1411
|
+
// Don't propagate service worker errors
|
|
1412
|
+
return;
|
|
1413
|
+
}
|
|
1414
|
+
|
|
1415
|
+
// Handle network-related service worker errors
|
|
1416
|
+
if (errorMessage.includes('TypeError: failed to register') ||
|
|
1417
|
+
errorMessage.includes('SecurityError') ||
|
|
1418
|
+
errorMessage.includes('The operation is insecure')) {
|
|
1419
|
+
if (forceDebug) {
|
|
1420
|
+
console.log(formatLogMessage('debug', `Registration security error suppressed: ${errorMessage}`));
|
|
1421
|
+
}
|
|
1422
|
+
return;
|
|
1423
|
+
}
|
|
1424
|
+
|
|
1425
|
+
// Log other page errors normally
|
|
1426
|
+
if (forceDebug) {
|
|
1427
|
+
console.log(formatLogMessage('debug', `Page JavaScript error: ${errorMessage}`));
|
|
1428
|
+
}
|
|
1429
|
+
});
|
|
1430
|
+
|
|
1431
|
+
page.on('response', (response) => {
|
|
1432
|
+
// Response handler - removed incorrect error logging
|
|
1433
|
+
});
|
|
1285
1434
|
|
|
1286
1435
|
// Apply flowProxy timeouts if detection is enabled
|
|
1287
1436
|
if (flowproxyDetection) {
|
|
@@ -1310,14 +1459,36 @@ function setupFrameHandling(page, forceDebug) {
|
|
|
1310
1459
|
// from within the page context at the earliest possible moment.
|
|
1311
1460
|
const originalFetch = window.fetch;
|
|
1312
1461
|
window.fetch = (...args) => {
|
|
1313
|
-
|
|
1314
|
-
|
|
1462
|
+
try {
|
|
1463
|
+
console.log('[evalOnDoc][fetch]', args[0]); // Log fetch requests
|
|
1464
|
+
const fetchPromise = originalFetch.apply(this, args);
|
|
1465
|
+
|
|
1466
|
+
// Add network error handling to prevent page errors
|
|
1467
|
+
return fetchPromise.catch(fetchErr => {
|
|
1468
|
+
console.log('[evalOnDoc][fetch-error]', args[0], fetchErr.message);
|
|
1469
|
+
throw fetchErr; // Re-throw to maintain normal error flow
|
|
1470
|
+
});
|
|
1471
|
+
} catch (fetchWrapperErr) {
|
|
1472
|
+
console.log('[evalOnDoc][fetch-wrapper-error]', fetchWrapperErr.message);
|
|
1473
|
+
return originalFetch.apply(this, args);
|
|
1474
|
+
}
|
|
1315
1475
|
};
|
|
1316
1476
|
|
|
1317
1477
|
const originalXHROpen = XMLHttpRequest.prototype.open;
|
|
1318
|
-
XMLHttpRequest.prototype.open = function (method, xhrUrl) {
|
|
1319
|
-
|
|
1320
|
-
|
|
1478
|
+
XMLHttpRequest.prototype.open = function (method, xhrUrl) {
|
|
1479
|
+
try {
|
|
1480
|
+
console.log('[evalOnDoc][xhr]', xhrUrl); // Log XHR requests
|
|
1481
|
+
|
|
1482
|
+
// Add error handling for XHR
|
|
1483
|
+
this.addEventListener('error', function(event) {
|
|
1484
|
+
console.log('[evalOnDoc][xhr-error]', xhrUrl, 'Network error occurred');
|
|
1485
|
+
});
|
|
1486
|
+
|
|
1487
|
+
return originalXHROpen.apply(this, arguments);
|
|
1488
|
+
} catch (xhrOpenErr) {
|
|
1489
|
+
console.log('[evalOnDoc][xhr-open-error]', xhrOpenErr.message);
|
|
1490
|
+
return originalXHROpen.apply(this, arguments);
|
|
1491
|
+
}
|
|
1321
1492
|
};
|
|
1322
1493
|
});
|
|
1323
1494
|
} catch (evalErr) {
|
|
@@ -2105,10 +2276,22 @@ function setupFrameHandling(page, forceDebug) {
|
|
|
2105
2276
|
|
|
2106
2277
|
// Use faster defaults for sites with long timeouts to improve responsiveness
|
|
2107
2278
|
const isFastSite = timeout <= 15000;
|
|
2108
|
-
const defaultWaitUntil = 'domcontentloaded'; // Always use faster option in Puppeteer
|
|
2279
|
+
const defaultWaitUntil = 'domcontentloaded'; // Always use faster option in Puppeteer 23.x
|
|
2280
|
+
|
|
2281
|
+
// Enhanced navigation options for Puppeteer 23.x
|
|
2109
2282
|
const defaultGotoOptions = {
|
|
2110
2283
|
waitUntil: defaultWaitUntil,
|
|
2111
|
-
timeout: Math.min(timeout,
|
|
2284
|
+
timeout: Math.min(timeout, 30000), // Reduced from 20000 for faster failures
|
|
2285
|
+
// Puppeteer 23.x: Fixed referrer header handling
|
|
2286
|
+
...(siteConfig.referrer_headers && (() => {
|
|
2287
|
+
const referrerUrl = Array.isArray(siteConfig.referrer_headers)
|
|
2288
|
+
? siteConfig.referrer_headers[0]
|
|
2289
|
+
: siteConfig.referrer_headers;
|
|
2290
|
+
// Ensure referrer is a valid string URL, not an object
|
|
2291
|
+
return typeof referrerUrl === 'string' && referrerUrl.startsWith('http')
|
|
2292
|
+
? { referer: referrerUrl }
|
|
2293
|
+
: {};
|
|
2294
|
+
})())
|
|
2112
2295
|
};
|
|
2113
2296
|
const gotoOptions = siteConfig.goto_options
|
|
2114
2297
|
? { ...defaultGotoOptions, ...siteConfig.goto_options } : defaultGotoOptions;
|
|
@@ -2132,20 +2315,30 @@ function setupFrameHandling(page, forceDebug) {
|
|
|
2132
2315
|
console.log(formatLogMessage('debug', `Full redirect chain: ${redirectChain.join(' → ')}`));
|
|
2133
2316
|
}
|
|
2134
2317
|
|
|
2135
|
-
//
|
|
2136
|
-
|
|
2318
|
+
// VALIDATION: Only update currentUrl if finalUrl is a valid HTTP/HTTPS URL
|
|
2319
|
+
if (shouldProcessUrl(finalUrl, forceDebug)) {
|
|
2320
|
+
// Update currentUrl for all subsequent processing to use the final redirected URL
|
|
2321
|
+
currentUrl = finalUrl;
|
|
2137
2322
|
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
effectiveCurrentUrl = finalUrl;
|
|
2141
|
-
|
|
2142
|
-
// Update the redirect domains to exclude from matching
|
|
2143
|
-
if (redirectDomains && redirectDomains.length > 0) {
|
|
2144
|
-
redirectDomainsToExclude = redirectDomains;
|
|
2323
|
+
// IMPORTANT: Also update effectiveCurrentUrl for first-party detection
|
|
2324
|
+
effectiveCurrentUrl = finalUrl;
|
|
2145
2325
|
|
|
2326
|
+
// Update the redirect domains to exclude from matching
|
|
2327
|
+
if (redirectDomains && redirectDomains.length > 0) {
|
|
2328
|
+
redirectDomainsToExclude = redirectDomains;
|
|
2329
|
+
|
|
2330
|
+
if (forceDebug) {
|
|
2331
|
+
console.log(formatLogMessage('debug', `Excluding redirect domains from matching: ${redirectDomains.join(', ')}`));
|
|
2332
|
+
}
|
|
2333
|
+
}
|
|
2334
|
+
} else {
|
|
2335
|
+
// Invalid final URL - don't update currentUrl, treat as failed redirect
|
|
2336
|
+
console.warn(`⚠ Redirect to invalid URL ignored: ${originalDomain} → ${finalUrl}`);
|
|
2146
2337
|
if (forceDebug) {
|
|
2147
|
-
console.log(formatLogMessage('debug', `
|
|
2338
|
+
console.log(formatLogMessage('debug', `Redirect chain ended with invalid URL, keeping original: ${originalUrl}`));
|
|
2148
2339
|
}
|
|
2340
|
+
// Keep processing with the original URL or throw an error
|
|
2341
|
+
throw new Error(`Redirect resulted in invalid URL: ${finalUrl}`);
|
|
2149
2342
|
}
|
|
2150
2343
|
}
|
|
2151
2344
|
}
|
|
@@ -2235,17 +2428,17 @@ function setupFrameHandling(page, forceDebug) {
|
|
|
2235
2428
|
|
|
2236
2429
|
const delayMs = siteConfig.delay || 4000;
|
|
2237
2430
|
|
|
2238
|
-
//
|
|
2431
|
+
// Optimized delays for Puppeteer 23.x performance
|
|
2239
2432
|
const isFastSite = timeout <= 15000;
|
|
2240
|
-
const networkIdleTime = 2000; //
|
|
2241
|
-
const networkIdleTimeout = Math.min(timeout / 2,
|
|
2242
|
-
const actualDelay = Math.min(delayMs,
|
|
2433
|
+
const networkIdleTime = 2000; // Balanced: 2s for reliable network detection
|
|
2434
|
+
const networkIdleTimeout = Math.min(timeout / 2, 10000); // Balanced: 10s timeout
|
|
2435
|
+
const actualDelay = Math.min(delayMs, 2000); // Balanced: 2s delay for stability
|
|
2243
2436
|
|
|
2244
2437
|
await page.waitForNetworkIdle({
|
|
2245
2438
|
idleTime: networkIdleTime,
|
|
2246
2439
|
timeout: networkIdleTimeout
|
|
2247
2440
|
});
|
|
2248
|
-
// Use fast timeout helper for Puppeteer
|
|
2441
|
+
// Use fast timeout helper for Puppeteer 23.x compatibility with better performance
|
|
2249
2442
|
await fastTimeout(actualDelay);
|
|
2250
2443
|
|
|
2251
2444
|
// Apply additional delay for flowProxy if detected
|
|
@@ -2255,8 +2448,7 @@ function setupFrameHandling(page, forceDebug) {
|
|
|
2255
2448
|
await fastTimeout(additionalDelay);
|
|
2256
2449
|
}
|
|
2257
2450
|
|
|
2258
|
-
//
|
|
2259
|
-
// This ensures compatibility with Puppeteer 22.x where waitForTimeout was removed
|
|
2451
|
+
// Use fast timeout helper for consistent Puppeteer 23.x compatibility
|
|
2260
2452
|
|
|
2261
2453
|
for (let i = 1; i < (siteConfig.reload || 1); i++) {
|
|
2262
2454
|
if (siteConfig.clear_sitedata === true) {
|
|
@@ -2349,35 +2541,23 @@ function setupFrameHandling(page, forceDebug) {
|
|
|
2349
2541
|
}
|
|
2350
2542
|
|
|
2351
2543
|
} catch (err) {
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
err.message.includes(
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2544
|
+
// Only restart for truly fatal browser errors
|
|
2545
|
+
const isFatalError = CRITICAL_BROWSER_ERRORS.some(errorType =>
|
|
2546
|
+
err.message.includes(errorType)
|
|
2547
|
+
) && !err.message.includes('timeout') && !err.message.includes('Navigation');
|
|
2548
|
+
|
|
2549
|
+
if (isFatalError) {
|
|
2550
|
+
console.error(formatLogMessage('error', `Fatal browser error on ${currentUrl}: ${err.message}`));
|
|
2358
2551
|
return {
|
|
2359
2552
|
url: currentUrl,
|
|
2360
2553
|
rules: [],
|
|
2361
2554
|
success: false,
|
|
2362
2555
|
needsImmediateRestart: true,
|
|
2363
|
-
error: `
|
|
2556
|
+
error: `Fatal error: ${err.message}`,
|
|
2557
|
+
errorType: 'fatal'
|
|
2364
2558
|
};
|
|
2365
2559
|
}
|
|
2366
|
-
|
|
2367
|
-
if (err.message.includes('Protocol error') ||
|
|
2368
|
-
err.message.includes('Target closed') ||
|
|
2369
|
-
err.message.includes('Browser process was killed') ||
|
|
2370
|
-
err.message.includes('Browser protocol broken')) {
|
|
2371
|
-
console.error(formatLogMessage('error', `Critical browser error on ${currentUrl}: ${err.message}`));
|
|
2372
|
-
return {
|
|
2373
|
-
url: currentUrl,
|
|
2374
|
-
rules: [],
|
|
2375
|
-
success: false,
|
|
2376
|
-
needsImmediateRestart: true,
|
|
2377
|
-
error: err.message
|
|
2378
|
-
};
|
|
2379
|
-
}
|
|
2380
|
-
|
|
2560
|
+
|
|
2381
2561
|
// For other errors, preserve any matches we found before the error
|
|
2382
2562
|
if (matchedDomains && (matchedDomains.size > 0 || (matchedDomains instanceof Map && matchedDomains.size > 0))) {
|
|
2383
2563
|
const globalOptions = {
|
|
@@ -2549,6 +2729,23 @@ function setupFrameHandling(page, forceDebug) {
|
|
|
2549
2729
|
// Check if any results indicate immediate restart is needed
|
|
2550
2730
|
const needsImmediateRestart = siteResults.some(r => r.needsImmediateRestart);
|
|
2551
2731
|
|
|
2732
|
+
// Enhanced error reporting for Puppeteer 23.x
|
|
2733
|
+
if (forceDebug) {
|
|
2734
|
+
const errorSummary = siteResults.reduce((acc, result) => {
|
|
2735
|
+
if (!result.success && result.errorType) {
|
|
2736
|
+
acc[result.errorType] = (acc[result.errorType] || 0) + 1;
|
|
2737
|
+
}
|
|
2738
|
+
return acc;
|
|
2739
|
+
}, {});
|
|
2740
|
+
|
|
2741
|
+
if (Object.keys(errorSummary).length > 0) {
|
|
2742
|
+
console.log(formatLogMessage('debug', `Site ${siteIndex + 1} error summary:`));
|
|
2743
|
+
Object.entries(errorSummary).forEach(([errorType, count]) => {
|
|
2744
|
+
console.log(formatLogMessage('debug', ` ${errorType}: ${count} error(s)`));
|
|
2745
|
+
});
|
|
2746
|
+
}
|
|
2747
|
+
}
|
|
2748
|
+
|
|
2552
2749
|
results.push(...siteResults);
|
|
2553
2750
|
|
|
2554
2751
|
processedUrlCount += siteUrlCount;
|
|
@@ -2562,6 +2759,19 @@ function setupFrameHandling(page, forceDebug) {
|
|
|
2562
2759
|
|
|
2563
2760
|
// Force browser restart immediately
|
|
2564
2761
|
try {
|
|
2762
|
+
// Enhanced emergency restart for Puppeteer 23.x
|
|
2763
|
+
if (forceDebug) {
|
|
2764
|
+
console.log(formatLogMessage('debug', `Emergency restart triggered by errors: ${siteResults.filter(r => r.needsImmediateRestart).map(r => r.error).join(', ')}`));
|
|
2765
|
+
}
|
|
2766
|
+
|
|
2767
|
+
// Try to gracefully close all pages first
|
|
2768
|
+
try {
|
|
2769
|
+
const pages = await browser.pages();
|
|
2770
|
+
await Promise.all(pages.map(page => page.close().catch(() => {})));
|
|
2771
|
+
} catch (pageCloseErr) {
|
|
2772
|
+
if (forceDebug) console.log(formatLogMessage('debug', `Page cleanup during emergency restart failed: ${pageCloseErr.message}`));
|
|
2773
|
+
}
|
|
2774
|
+
|
|
2565
2775
|
await handleBrowserExit(browser, { forceDebug, timeout: 5000, exitOnFailure: false, cleanTempFiles: true, comprehensiveCleanup: removeTempFiles });
|
|
2566
2776
|
// Additional cleanup after emergency restart
|
|
2567
2777
|
if (removeTempFiles) {
|
|
@@ -2702,7 +2912,14 @@ function setupFrameHandling(page, forceDebug) {
|
|
|
2702
2912
|
|
|
2703
2913
|
// Perform comprehensive final cleanup using enhanced browserexit module
|
|
2704
2914
|
if (forceDebug) console.log(formatLogMessage('debug', `Starting comprehensive browser cleanup...`));
|
|
2705
|
-
|
|
2915
|
+
|
|
2916
|
+
// Enhanced final validation for Puppeteer 23.x
|
|
2917
|
+
try {
|
|
2918
|
+
const isStillConnected = browser.isConnected();
|
|
2919
|
+
if (forceDebug) console.log(formatLogMessage('debug', `Browser connection status before cleanup: ${isStillConnected}`));
|
|
2920
|
+
} catch (connErr) {
|
|
2921
|
+
if (forceDebug) console.log(formatLogMessage('debug', `Browser connection check failed: ${connErr.message}`));
|
|
2922
|
+
}
|
|
2706
2923
|
|
|
2707
2924
|
const cleanupResult = await handleBrowserExit(browser, {
|
|
2708
2925
|
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.64",
|
|
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": {
|