@fanboynz/network-scanner 1.0.35 → 1.0.37
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 +46 -7
- package/lib/cloudflare.js +241 -234
- package/nwss.js +18 -4
- package/package.json +1 -1
package/lib/browserhealth.js
CHANGED
|
@@ -17,7 +17,8 @@ async function checkBrowserHealth(browserInstance, timeout = 5000) {
|
|
|
17
17
|
pageCount: 0,
|
|
18
18
|
error: null,
|
|
19
19
|
responseTime: 0,
|
|
20
|
-
recommendations: []
|
|
20
|
+
recommendations: [],
|
|
21
|
+
criticalError: false
|
|
21
22
|
};
|
|
22
23
|
|
|
23
24
|
const startTime = Date.now();
|
|
@@ -27,6 +28,7 @@ async function checkBrowserHealth(browserInstance, timeout = 5000) {
|
|
|
27
28
|
if (!browserInstance || browserInstance.process() === null) {
|
|
28
29
|
healthResult.error = 'Browser process not running';
|
|
29
30
|
healthResult.recommendations.push('Create new browser instance');
|
|
31
|
+
healthResult.criticalError = true;
|
|
30
32
|
return healthResult;
|
|
31
33
|
}
|
|
32
34
|
|
|
@@ -71,7 +73,12 @@ async function checkBrowserHealth(browserInstance, timeout = 5000) {
|
|
|
71
73
|
try { await testPage.close(); } catch (e) { /* ignore */ }
|
|
72
74
|
}
|
|
73
75
|
healthResult.error = `Page creation/navigation failed: ${pageTestError.message}`;
|
|
74
|
-
|
|
76
|
+
if (isCriticalProtocolError(pageTestError)) {
|
|
77
|
+
healthResult.recommendations.push('Browser restart required - critical protocol error');
|
|
78
|
+
healthResult.criticalError = true;
|
|
79
|
+
} else {
|
|
80
|
+
healthResult.recommendations.push('Browser restart recommended');
|
|
81
|
+
}
|
|
75
82
|
return healthResult;
|
|
76
83
|
}
|
|
77
84
|
|
|
@@ -88,10 +95,14 @@ async function checkBrowserHealth(browserInstance, timeout = 5000) {
|
|
|
88
95
|
healthResult.responseTime = Date.now() - startTime;
|
|
89
96
|
|
|
90
97
|
// Categorize error types for better recommendations
|
|
91
|
-
if (error.message.includes('
|
|
98
|
+
if (error.message.includes('Runtime.callFunctionOn timed out') ||
|
|
99
|
+
error.message.includes('Protocol error') ||
|
|
100
|
+
error.message.includes('Target closed')) {
|
|
101
|
+
healthResult.recommendations.push('Browser restart required - critical protocol error');
|
|
102
|
+
healthResult.criticalError = true;
|
|
103
|
+
} else if (error.message.includes('timeout') || error.message.includes('unresponsive')) {
|
|
92
104
|
healthResult.recommendations.push('Browser restart required - unresponsive');
|
|
93
|
-
|
|
94
|
-
healthResult.recommendations.push('Browser restart required - protocol error');
|
|
105
|
+
healthResult.criticalError = true;
|
|
95
106
|
} else {
|
|
96
107
|
healthResult.recommendations.push('Browser restart recommended - unknown error');
|
|
97
108
|
}
|
|
@@ -152,6 +163,27 @@ async function checkBrowserMemory(browserInstance) {
|
|
|
152
163
|
return memoryResult;
|
|
153
164
|
}
|
|
154
165
|
|
|
166
|
+
/**
|
|
167
|
+
* Detects critical protocol errors that require immediate browser restart
|
|
168
|
+
*/
|
|
169
|
+
function isCriticalProtocolError(error) {
|
|
170
|
+
if (!error || !error.message) return false;
|
|
171
|
+
|
|
172
|
+
const criticalErrors = [
|
|
173
|
+
'Runtime.callFunctionOn timed out',
|
|
174
|
+
'Protocol error',
|
|
175
|
+
'Target closed',
|
|
176
|
+
'Session closed',
|
|
177
|
+
'Connection closed',
|
|
178
|
+
'Browser has been closed',
|
|
179
|
+
'Runtime.evaluate timed out'
|
|
180
|
+
];
|
|
181
|
+
|
|
182
|
+
return criticalErrors.some(criticalError =>
|
|
183
|
+
error.message.includes(criticalError)
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
|
|
155
187
|
/**
|
|
156
188
|
* Performs comprehensive browser health assessment
|
|
157
189
|
* @param {import('puppeteer').Browser} browserInstance - Puppeteer browser instance
|
|
@@ -196,6 +228,9 @@ async function performHealthAssessment(browserInstance, options = {}) {
|
|
|
196
228
|
if (!assessment.browser.healthy) {
|
|
197
229
|
assessment.overall = 'unhealthy';
|
|
198
230
|
assessment.needsRestart = true;
|
|
231
|
+
} else if (assessment.browser.criticalError) {
|
|
232
|
+
assessment.overall = 'critical';
|
|
233
|
+
assessment.needsRestart = true;
|
|
199
234
|
} else if (assessment.recommendations.length > 0) {
|
|
200
235
|
assessment.overall = 'degraded';
|
|
201
236
|
assessment.needsRestart = assessment.recommendations.some(rec =>
|
|
@@ -252,7 +287,10 @@ async function monitorBrowserHealth(browserInstance, context = {}, options = {})
|
|
|
252
287
|
result.assessment = assessment;
|
|
253
288
|
|
|
254
289
|
// Decision logic for restart
|
|
255
|
-
if (assessment.
|
|
290
|
+
if (assessment.browser.criticalError) {
|
|
291
|
+
result.shouldRestart = true;
|
|
292
|
+
result.reason = `Critical protocol error detected - immediate restart required`;
|
|
293
|
+
} else if (assessment.needsRestart) {
|
|
256
294
|
result.shouldRestart = true;
|
|
257
295
|
result.reason = `Browser health: ${assessment.overall} - ${assessment.recommendations[0] || 'restart needed'}`;
|
|
258
296
|
} else if (urlsSinceCleanup >= cleanupInterval) {
|
|
@@ -304,5 +342,6 @@ module.exports = {
|
|
|
304
342
|
checkBrowserMemory,
|
|
305
343
|
performHealthAssessment,
|
|
306
344
|
monitorBrowserHealth,
|
|
307
|
-
isBrowserHealthy
|
|
345
|
+
isBrowserHealthy,
|
|
346
|
+
isCriticalProtocolError
|
|
308
347
|
};
|
package/lib/cloudflare.js
CHANGED
|
@@ -1,40 +1,96 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Cloudflare bypass and challenge handling module -
|
|
2
|
+
* Cloudflare bypass and challenge handling module - Enhanced with timeout handling
|
|
3
3
|
* Handles phishing warnings, Turnstile challenges, and modern Cloudflare protections
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
-
* Cross-version compatible timeout function for Puppeteer
|
|
8
|
-
* @param {import('puppeteer').Page} page - Puppeteer page instance
|
|
9
|
-
* @param {number} timeout - Timeout in milliseconds
|
|
10
|
-
* @returns {Promise<void>}
|
|
7
|
+
* Cross-version compatible timeout function for Puppeteer with timeout protection
|
|
11
8
|
*/
|
|
12
9
|
async function waitForTimeout(page, timeout) {
|
|
13
10
|
try {
|
|
14
11
|
// Try newer Puppeteer method first
|
|
15
12
|
if (typeof page.waitForTimeout === 'function') {
|
|
16
|
-
await
|
|
13
|
+
await Promise.race([
|
|
14
|
+
page.waitForTimeout(timeout),
|
|
15
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error('waitForTimeout exceeded')), timeout + 5000))
|
|
16
|
+
]);
|
|
17
17
|
} else if (typeof page.waitFor === 'function') {
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
await Promise.race([
|
|
19
|
+
page.waitFor(timeout),
|
|
20
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error('waitFor exceeded')), timeout + 5000))
|
|
21
|
+
]);
|
|
20
22
|
} else {
|
|
21
|
-
// Ultimate fallback using setTimeout
|
|
22
23
|
await new Promise(resolve => setTimeout(resolve, timeout));
|
|
23
24
|
}
|
|
24
25
|
} catch (error) {
|
|
25
26
|
// If all else fails, use setTimeout
|
|
26
|
-
await new Promise(resolve => setTimeout(resolve, timeout));
|
|
27
|
+
await new Promise(resolve => setTimeout(resolve, Math.min(timeout, 5000)));
|
|
27
28
|
}
|
|
28
29
|
}
|
|
29
30
|
|
|
30
31
|
/**
|
|
31
|
-
*
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
* Safe page evaluation with timeout protection
|
|
33
|
+
*/
|
|
34
|
+
async function safePageEvaluate(page, func, timeout = 10000) {
|
|
35
|
+
try {
|
|
36
|
+
return await Promise.race([
|
|
37
|
+
page.evaluate(func),
|
|
38
|
+
new Promise((_, reject) =>
|
|
39
|
+
setTimeout(() => reject(new Error('Page evaluation timeout')), timeout)
|
|
40
|
+
)
|
|
41
|
+
]);
|
|
42
|
+
} catch (error) {
|
|
43
|
+
console.warn(`[cloudflare] Page evaluation failed: ${error.message}`);
|
|
44
|
+
return {
|
|
45
|
+
isChallengePresent: false,
|
|
46
|
+
isPhishingWarning: false,
|
|
47
|
+
isTurnstile: false,
|
|
48
|
+
isJSChallenge: false,
|
|
49
|
+
isChallengeCompleted: false,
|
|
50
|
+
error: error.message
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Safe element clicking with timeout protection
|
|
57
|
+
*/
|
|
58
|
+
async function safeClick(page, selector, timeout = 5000) {
|
|
59
|
+
try {
|
|
60
|
+
return await Promise.race([
|
|
61
|
+
page.click(selector, { timeout: timeout }),
|
|
62
|
+
new Promise((_, reject) =>
|
|
63
|
+
setTimeout(() => reject(new Error('Click timeout')), timeout + 1000)
|
|
64
|
+
)
|
|
65
|
+
]);
|
|
66
|
+
} catch (error) {
|
|
67
|
+
throw new Error(`Click failed: ${error.message}`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Safe navigation waiting with timeout protection
|
|
73
|
+
*/
|
|
74
|
+
async function safeWaitForNavigation(page, timeout = 15000) {
|
|
75
|
+
try {
|
|
76
|
+
return await Promise.race([
|
|
77
|
+
page.waitForNavigation({ waitUntil: 'domcontentloaded', timeout: timeout }),
|
|
78
|
+
new Promise((_, reject) =>
|
|
79
|
+
setTimeout(() => reject(new Error('Navigation timeout')), timeout + 2000)
|
|
80
|
+
)
|
|
81
|
+
]);
|
|
82
|
+
} catch (error) {
|
|
83
|
+
console.warn(`[cloudflare] Navigation wait failed: ${error.message}`);
|
|
84
|
+
// Don't throw - just continue
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Analyzes the current page to detect Cloudflare challenges - Enhanced with timeout protection
|
|
34
90
|
*/
|
|
35
91
|
async function analyzeCloudflareChallenge(page) {
|
|
36
92
|
try {
|
|
37
|
-
return await page
|
|
93
|
+
return await safePageEvaluate(page, () => {
|
|
38
94
|
const title = document.title || '';
|
|
39
95
|
const bodyText = document.body ? document.body.textContent : '';
|
|
40
96
|
|
|
@@ -50,7 +106,6 @@ async function analyzeCloudflareChallenge(page) {
|
|
|
50
106
|
const hasTurnstileCheckbox = document.querySelector('input[type="checkbox"].ctp-checkbox') !== null ||
|
|
51
107
|
document.querySelector('.ctp-checkbox') !== null;
|
|
52
108
|
|
|
53
|
-
// Legacy challenge detection (still used on some sites)
|
|
54
109
|
const hasLegacyCheckbox = document.querySelector('input[type="checkbox"]#challenge-form') !== null ||
|
|
55
110
|
document.querySelector('input[type="checkbox"][name="cf_captcha_kind"]') !== null;
|
|
56
111
|
|
|
@@ -73,14 +128,11 @@ async function analyzeCloudflareChallenge(page) {
|
|
|
73
128
|
title.includes('Attention Required') ||
|
|
74
129
|
document.querySelector('a[href*="continue"]') !== null;
|
|
75
130
|
|
|
76
|
-
// Check for Turnstile response token
|
|
77
131
|
const hasTurnstileResponse = document.querySelector('input[name="cf-turnstile-response"]') !== null;
|
|
78
132
|
|
|
79
|
-
// Check for challenge completion indicators
|
|
80
133
|
const isChallengeCompleted = hasTurnstileResponse &&
|
|
81
134
|
document.querySelector('input[name="cf-turnstile-response"]')?.value;
|
|
82
135
|
|
|
83
|
-
// Enhanced challenge detection logic
|
|
84
136
|
const isChallengePresent = title.includes('Just a moment') ||
|
|
85
137
|
title.includes('Checking your browser') ||
|
|
86
138
|
bodyText.includes('Verify you are human') ||
|
|
@@ -107,9 +159,9 @@ async function analyzeCloudflareChallenge(page) {
|
|
|
107
159
|
hasCaptcha,
|
|
108
160
|
hasTurnstileResponse,
|
|
109
161
|
url: window.location.href,
|
|
110
|
-
bodySnippet: bodyText.substring(0, 200)
|
|
162
|
+
bodySnippet: bodyText.substring(0, 200)
|
|
111
163
|
};
|
|
112
|
-
});
|
|
164
|
+
}, 10000); // 10 second timeout for page evaluation
|
|
113
165
|
} catch (error) {
|
|
114
166
|
return {
|
|
115
167
|
isChallengePresent: false,
|
|
@@ -123,11 +175,7 @@ async function analyzeCloudflareChallenge(page) {
|
|
|
123
175
|
}
|
|
124
176
|
|
|
125
177
|
/**
|
|
126
|
-
* Handles Cloudflare phishing warnings
|
|
127
|
-
* @param {import('puppeteer').Page} page - Puppeteer page instance
|
|
128
|
-
* @param {string} currentUrl - Current URL being processed
|
|
129
|
-
* @param {boolean} forceDebug - Debug mode flag
|
|
130
|
-
* @returns {Promise<object>} Result object with success status and details
|
|
178
|
+
* Handles Cloudflare phishing warnings with timeout protection
|
|
131
179
|
*/
|
|
132
180
|
async function handlePhishingWarning(page, currentUrl, forceDebug = false) {
|
|
133
181
|
const result = {
|
|
@@ -140,7 +188,7 @@ async function handlePhishingWarning(page, currentUrl, forceDebug = false) {
|
|
|
140
188
|
try {
|
|
141
189
|
if (forceDebug) console.log(`[debug][cloudflare] Checking for phishing warning on ${currentUrl}`);
|
|
142
190
|
|
|
143
|
-
//
|
|
191
|
+
// Shorter wait with timeout protection
|
|
144
192
|
await waitForTimeout(page, 2000);
|
|
145
193
|
|
|
146
194
|
const challengeInfo = await analyzeCloudflareChallenge(page);
|
|
@@ -150,14 +198,13 @@ async function handlePhishingWarning(page, currentUrl, forceDebug = false) {
|
|
|
150
198
|
result.details = challengeInfo;
|
|
151
199
|
|
|
152
200
|
if (forceDebug) {
|
|
153
|
-
console.log(`[debug][cloudflare] Phishing warning detected on ${currentUrl}
|
|
154
|
-
console.log(`[debug][cloudflare] Page Title: "${challengeInfo.title}"`);
|
|
155
|
-
console.log(`[debug][cloudflare] Current URL: ${challengeInfo.url}`);
|
|
201
|
+
console.log(`[debug][cloudflare] Phishing warning detected on ${currentUrl}`);
|
|
156
202
|
}
|
|
157
203
|
|
|
158
204
|
try {
|
|
159
|
-
|
|
160
|
-
await page
|
|
205
|
+
// Use safe click with shorter timeout
|
|
206
|
+
await safeClick(page, 'a[href*="continue"]', 3000);
|
|
207
|
+
await safeWaitForNavigation(page, 10000);
|
|
161
208
|
|
|
162
209
|
result.success = true;
|
|
163
210
|
if (forceDebug) console.log(`[debug][cloudflare] Successfully bypassed phishing warning for ${currentUrl}`);
|
|
@@ -178,11 +225,7 @@ async function handlePhishingWarning(page, currentUrl, forceDebug = false) {
|
|
|
178
225
|
}
|
|
179
226
|
|
|
180
227
|
/**
|
|
181
|
-
* Attempts to solve Cloudflare challenges
|
|
182
|
-
* @param {import('puppeteer').Page} page - Puppeteer page instance
|
|
183
|
-
* @param {string} currentUrl - Current URL being processed
|
|
184
|
-
* @param {boolean} forceDebug - Debug mode flag
|
|
185
|
-
* @returns {Promise<object>} Result object with success status and details
|
|
228
|
+
* Attempts to solve Cloudflare challenges with timeout protection
|
|
186
229
|
*/
|
|
187
230
|
async function handleVerificationChallenge(page, currentUrl, forceDebug = false) {
|
|
188
231
|
const result = {
|
|
@@ -196,8 +239,8 @@ async function handleVerificationChallenge(page, currentUrl, forceDebug = false)
|
|
|
196
239
|
try {
|
|
197
240
|
if (forceDebug) console.log(`[debug][cloudflare] Checking for verification challenge on ${currentUrl}`);
|
|
198
241
|
|
|
199
|
-
//
|
|
200
|
-
await waitForTimeout(page,
|
|
242
|
+
// Shorter wait for challenges
|
|
243
|
+
await waitForTimeout(page, 2000);
|
|
201
244
|
|
|
202
245
|
const challengeInfo = await analyzeCloudflareChallenge(page);
|
|
203
246
|
result.details = challengeInfo;
|
|
@@ -206,82 +249,70 @@ async function handleVerificationChallenge(page, currentUrl, forceDebug = false)
|
|
|
206
249
|
result.attempted = true;
|
|
207
250
|
|
|
208
251
|
if (forceDebug) {
|
|
209
|
-
console.log(`[debug][cloudflare] Challenge detected on ${currentUrl}
|
|
210
|
-
console.log(`[debug][cloudflare] Page Title: "${challengeInfo.title}"`);
|
|
211
|
-
console.log(`[debug][cloudflare] Current URL: ${challengeInfo.url}`);
|
|
212
|
-
console.log(`[debug][cloudflare] Is Turnstile: ${challengeInfo.isTurnstile}`);
|
|
213
|
-
console.log(`[debug][cloudflare] Is JS Challenge: ${challengeInfo.isJSChallenge}`);
|
|
214
|
-
console.log(`[debug][cloudflare] Has Legacy Checkbox: ${challengeInfo.hasLegacyCheckbox}`);
|
|
215
|
-
console.log(`[debug][cloudflare] Has Turnstile Iframe: ${challengeInfo.hasTurnstileIframe}`);
|
|
216
|
-
console.log(`[debug][cloudflare] Has Turnstile Container: ${challengeInfo.hasTurnstileContainer}`);
|
|
217
|
-
console.log(`[debug][cloudflare] Has CAPTCHA: ${challengeInfo.hasCaptcha}`);
|
|
252
|
+
console.log(`[debug][cloudflare] Challenge detected on ${currentUrl}`);
|
|
218
253
|
}
|
|
219
254
|
|
|
220
255
|
// Check for CAPTCHA that requires human intervention
|
|
221
256
|
if (challengeInfo.hasCaptcha) {
|
|
222
257
|
result.requiresHuman = true;
|
|
223
258
|
result.error = 'CAPTCHA detected - requires human intervention';
|
|
224
|
-
console.warn(`? [cloudflare] CAPTCHA detected on ${currentUrl} - requires human intervention`);
|
|
225
259
|
if (forceDebug) console.log(`[debug][cloudflare] Skipping automatic bypass due to CAPTCHA requirement`);
|
|
226
260
|
return result;
|
|
227
261
|
}
|
|
228
262
|
|
|
229
|
-
// Attempt to solve the challenge with
|
|
230
|
-
const solveResult = await
|
|
263
|
+
// Attempt to solve the challenge with timeout protection
|
|
264
|
+
const solveResult = await attemptChallengeSolveWithTimeout(page, currentUrl, challengeInfo, forceDebug);
|
|
231
265
|
result.success = solveResult.success;
|
|
232
266
|
result.error = solveResult.error;
|
|
233
267
|
|
|
234
268
|
} else {
|
|
235
269
|
if (forceDebug) console.log(`[debug][cloudflare] No verification challenge detected on ${currentUrl}`);
|
|
236
|
-
result.success = true;
|
|
270
|
+
result.success = true;
|
|
237
271
|
}
|
|
238
272
|
} catch (error) {
|
|
239
273
|
result.error = error.message;
|
|
240
|
-
if (forceDebug) {
|
|
241
|
-
console.log(`[debug][cloudflare] Challenge check failed for ${currentUrl}:`);
|
|
242
|
-
console.log(`[debug][cloudflare] Error: ${error.message}`);
|
|
243
|
-
console.log(`[debug][cloudflare] Stack: ${error.stack}`);
|
|
244
|
-
}
|
|
274
|
+
if (forceDebug) console.log(`[debug][cloudflare] Challenge check failed for ${currentUrl}: ${error.message}`);
|
|
245
275
|
}
|
|
246
276
|
|
|
247
277
|
return result;
|
|
248
278
|
}
|
|
249
279
|
|
|
250
280
|
/**
|
|
251
|
-
*
|
|
252
|
-
* @param {import('puppeteer').Page} page - Puppeteer page instance
|
|
253
|
-
* @param {string} currentUrl - Current URL being processed
|
|
254
|
-
* @param {object} challengeInfo - Challenge analysis results
|
|
255
|
-
* @param {boolean} forceDebug - Debug mode flag
|
|
256
|
-
* @returns {Promise<object>} Solve attempt result
|
|
281
|
+
* Challenge solving with overall timeout protection
|
|
257
282
|
*/
|
|
258
|
-
async function
|
|
283
|
+
async function attemptChallengeSolveWithTimeout(page, currentUrl, challengeInfo, forceDebug = false) {
|
|
259
284
|
const result = {
|
|
260
285
|
success: false,
|
|
261
286
|
error: null,
|
|
262
287
|
method: null
|
|
263
288
|
};
|
|
264
289
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
if (forceDebug) console.log(`[debug][cloudflare] Turnstile method failed: ${turnstileResult.error}`);
|
|
278
|
-
}
|
|
279
|
-
} catch (turnstileError) {
|
|
280
|
-
if (forceDebug) console.log(`[debug][cloudflare] Turnstile method error: ${turnstileError.message}`);
|
|
281
|
-
}
|
|
290
|
+
try {
|
|
291
|
+
// Overall timeout for all challenge solving attempts
|
|
292
|
+
return await Promise.race([
|
|
293
|
+
attemptChallengeSolve(page, currentUrl, challengeInfo, forceDebug),
|
|
294
|
+
new Promise((_, reject) =>
|
|
295
|
+
setTimeout(() => reject(new Error('Challenge solving timeout')), 30000)
|
|
296
|
+
)
|
|
297
|
+
]);
|
|
298
|
+
} catch (error) {
|
|
299
|
+
result.error = `Challenge solving timed out: ${error.message}`;
|
|
300
|
+
if (forceDebug) console.log(`[debug][cloudflare] Challenge solving timeout for ${currentUrl}`);
|
|
301
|
+
return result;
|
|
282
302
|
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Attempts to solve a Cloudflare challenge with modern techniques
|
|
307
|
+
*/
|
|
308
|
+
async function attemptChallengeSolve(page, currentUrl, challengeInfo, forceDebug = false) {
|
|
309
|
+
const result = {
|
|
310
|
+
success: false,
|
|
311
|
+
error: null,
|
|
312
|
+
method: null
|
|
313
|
+
};
|
|
283
314
|
|
|
284
|
-
// Method
|
|
315
|
+
// Method 1: Handle JS challenges (wait for automatic completion) - Most reliable
|
|
285
316
|
if (challengeInfo.isJSChallenge) {
|
|
286
317
|
try {
|
|
287
318
|
if (forceDebug) console.log(`[debug][cloudflare] Attempting JS challenge wait for ${currentUrl}`);
|
|
@@ -298,6 +329,23 @@ async function attemptChallengeSolve(page, currentUrl, challengeInfo, forceDebug
|
|
|
298
329
|
}
|
|
299
330
|
}
|
|
300
331
|
|
|
332
|
+
// Method 2: Handle Turnstile challenges (interactive)
|
|
333
|
+
if (challengeInfo.isTurnstile) {
|
|
334
|
+
try {
|
|
335
|
+
if (forceDebug) console.log(`[debug][cloudflare] Attempting Turnstile method for ${currentUrl}`);
|
|
336
|
+
|
|
337
|
+
const turnstileResult = await handleTurnstileChallenge(page, forceDebug);
|
|
338
|
+
if (turnstileResult.success) {
|
|
339
|
+
result.success = true;
|
|
340
|
+
result.method = 'turnstile';
|
|
341
|
+
if (forceDebug) console.log(`[debug][cloudflare] Turnstile challenge solved successfully for ${currentUrl}`);
|
|
342
|
+
return result;
|
|
343
|
+
}
|
|
344
|
+
} catch (turnstileError) {
|
|
345
|
+
if (forceDebug) console.log(`[debug][cloudflare] Turnstile method error: ${turnstileError.message}`);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
301
349
|
// Method 3: Legacy checkbox interaction (fallback)
|
|
302
350
|
if (challengeInfo.hasLegacyCheckbox) {
|
|
303
351
|
try {
|
|
@@ -315,52 +363,51 @@ async function attemptChallengeSolve(page, currentUrl, challengeInfo, forceDebug
|
|
|
315
363
|
}
|
|
316
364
|
}
|
|
317
365
|
|
|
318
|
-
|
|
366
|
+
if (!result.success) {
|
|
367
|
+
result.error = result.error || 'All challenge bypass methods failed';
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
return result;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Waits for JS challenge completion with timeout protection
|
|
375
|
+
*/
|
|
376
|
+
async function waitForJSChallengeCompletion(page, forceDebug = false) {
|
|
377
|
+
const result = {
|
|
378
|
+
success: false,
|
|
379
|
+
error: null
|
|
380
|
+
};
|
|
381
|
+
|
|
319
382
|
try {
|
|
320
|
-
if (forceDebug) console.log(`[debug][cloudflare]
|
|
383
|
+
if (forceDebug) console.log(`[debug][cloudflare] Waiting for JS challenge completion`);
|
|
321
384
|
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
385
|
+
// Wait for challenge to complete automatically with shorter timeout
|
|
386
|
+
await Promise.race([
|
|
387
|
+
page.waitForFunction(
|
|
388
|
+
() => {
|
|
389
|
+
return !document.body.textContent.includes('Checking your browser') &&
|
|
390
|
+
!document.body.textContent.includes('Please wait while we verify') &&
|
|
391
|
+
!document.querySelector('.cf-challenge-running') &&
|
|
392
|
+
!document.querySelector('[data-cf-challenge]');
|
|
393
|
+
},
|
|
394
|
+
{ timeout: 20000 }
|
|
395
|
+
),
|
|
396
|
+
new Promise((_, reject) =>
|
|
397
|
+
setTimeout(() => reject(new Error('JS challenge timeout')), 25000)
|
|
398
|
+
)
|
|
399
|
+
]);
|
|
328
400
|
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
await waitForTimeout(page, 3000);
|
|
333
|
-
|
|
334
|
-
// Check if challenge is solved
|
|
335
|
-
const completionCheck = await checkChallengeCompletion(page);
|
|
336
|
-
if (completionCheck.isCompleted) {
|
|
337
|
-
result.success = true;
|
|
338
|
-
result.method = 'alternative_click';
|
|
339
|
-
if (forceDebug) console.log(`[debug][cloudflare] Alternative click method succeeded for ${currentUrl} using ${selector}`);
|
|
340
|
-
return result;
|
|
341
|
-
}
|
|
342
|
-
} catch (clickError) {
|
|
343
|
-
// Continue to next selector
|
|
344
|
-
continue;
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
} catch (altError) {
|
|
348
|
-
result.error = `All bypass methods failed. Last error: ${altError.message}`;
|
|
349
|
-
if (forceDebug) console.log(`[debug][cloudflare] All bypass methods failed for ${currentUrl}: ${altError.message}`);
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
if (!result.success) {
|
|
353
|
-
result.error = result.error || 'All challenge bypass methods failed';
|
|
401
|
+
result.success = true;
|
|
402
|
+
} catch (error) {
|
|
403
|
+
result.error = `JS challenge timeout: ${error.message}`;
|
|
354
404
|
}
|
|
355
405
|
|
|
356
406
|
return result;
|
|
357
407
|
}
|
|
358
408
|
|
|
359
409
|
/**
|
|
360
|
-
* Handles modern Turnstile challenges
|
|
361
|
-
* @param {import('puppeteer').Page} page - Puppeteer page instance
|
|
362
|
-
* @param {boolean} forceDebug - Debug mode flag
|
|
363
|
-
* @returns {Promise<object>} Turnstile handling result
|
|
410
|
+
* Handles modern Turnstile challenges with timeout protection
|
|
364
411
|
*/
|
|
365
412
|
async function handleTurnstileChallenge(page, forceDebug = false) {
|
|
366
413
|
const result = {
|
|
@@ -369,7 +416,9 @@ async function handleTurnstileChallenge(page, forceDebug = false) {
|
|
|
369
416
|
};
|
|
370
417
|
|
|
371
418
|
try {
|
|
372
|
-
//
|
|
419
|
+
// Much shorter timeout for Turnstile operations
|
|
420
|
+
const turnstileTimeout = 10000; // 10 seconds
|
|
421
|
+
|
|
373
422
|
const turnstileSelectors = [
|
|
374
423
|
'iframe[src*="challenges.cloudflare.com"]',
|
|
375
424
|
'iframe[title*="Widget containing a Cloudflare"]',
|
|
@@ -379,7 +428,11 @@ async function handleTurnstileChallenge(page, forceDebug = false) {
|
|
|
379
428
|
let turnstileFrame = null;
|
|
380
429
|
for (const selector of turnstileSelectors) {
|
|
381
430
|
try {
|
|
382
|
-
await
|
|
431
|
+
await Promise.race([
|
|
432
|
+
page.waitForSelector(selector, { timeout: 3000 }),
|
|
433
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error('Selector timeout')), 4000))
|
|
434
|
+
]);
|
|
435
|
+
|
|
383
436
|
const frames = await page.frames();
|
|
384
437
|
turnstileFrame = frames.find(frame =>
|
|
385
438
|
frame.url().includes('challenges.cloudflare.com') ||
|
|
@@ -394,7 +447,6 @@ async function handleTurnstileChallenge(page, forceDebug = false) {
|
|
|
394
447
|
if (turnstileFrame) {
|
|
395
448
|
if (forceDebug) console.log(`[debug][cloudflare] Found Turnstile iframe`);
|
|
396
449
|
|
|
397
|
-
// Wait for checkbox in iframe
|
|
398
450
|
const checkboxSelectors = [
|
|
399
451
|
'input[type="checkbox"].ctp-checkbox',
|
|
400
452
|
'input[type="checkbox"]',
|
|
@@ -404,10 +456,12 @@ async function handleTurnstileChallenge(page, forceDebug = false) {
|
|
|
404
456
|
|
|
405
457
|
for (const selector of checkboxSelectors) {
|
|
406
458
|
try {
|
|
407
|
-
await
|
|
459
|
+
await Promise.race([
|
|
460
|
+
turnstileFrame.waitForSelector(selector, { timeout: 3000 }),
|
|
461
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error('Checkbox timeout')), 4000))
|
|
462
|
+
]);
|
|
408
463
|
|
|
409
|
-
|
|
410
|
-
await waitForTimeout(page, Math.random() * 1000 + 500);
|
|
464
|
+
await waitForTimeout(page, 500);
|
|
411
465
|
await turnstileFrame.click(selector);
|
|
412
466
|
|
|
413
467
|
if (forceDebug) console.log(`[debug][cloudflare] Clicked Turnstile checkbox: ${selector}`);
|
|
@@ -417,48 +471,21 @@ async function handleTurnstileChallenge(page, forceDebug = false) {
|
|
|
417
471
|
}
|
|
418
472
|
}
|
|
419
473
|
|
|
420
|
-
// Wait for Turnstile completion
|
|
421
|
-
await
|
|
422
|
-
(
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
474
|
+
// Wait for Turnstile completion with timeout
|
|
475
|
+
await Promise.race([
|
|
476
|
+
page.waitForFunction(
|
|
477
|
+
() => {
|
|
478
|
+
const responseInput = document.querySelector('input[name="cf-turnstile-response"]');
|
|
479
|
+
return responseInput && responseInput.value && responseInput.value.length > 0;
|
|
480
|
+
},
|
|
481
|
+
{ timeout: 15000 }
|
|
482
|
+
),
|
|
483
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error('Turnstile completion timeout')), 20000))
|
|
484
|
+
]);
|
|
428
485
|
|
|
429
486
|
result.success = true;
|
|
430
487
|
} else {
|
|
431
|
-
|
|
432
|
-
const containerSelectors = [
|
|
433
|
-
'.cf-turnstile',
|
|
434
|
-
'.ctp-checkbox-container',
|
|
435
|
-
'.ctp-checkbox-label'
|
|
436
|
-
];
|
|
437
|
-
|
|
438
|
-
for (const selector of containerSelectors) {
|
|
439
|
-
try {
|
|
440
|
-
await page.waitForSelector(selector, { timeout: 5000 });
|
|
441
|
-
|
|
442
|
-
// Human-like interaction
|
|
443
|
-
await waitForTimeout(page, Math.random() * 1000 + 500);
|
|
444
|
-
await page.click(selector);
|
|
445
|
-
|
|
446
|
-
if (forceDebug) console.log(`[debug][cloudflare] Clicked Turnstile container: ${selector}`);
|
|
447
|
-
|
|
448
|
-
// Wait for completion
|
|
449
|
-
const completionCheck = await checkChallengeCompletion(page);
|
|
450
|
-
if (completionCheck.isCompleted) {
|
|
451
|
-
result.success = true;
|
|
452
|
-
break;
|
|
453
|
-
}
|
|
454
|
-
} catch (e) {
|
|
455
|
-
continue;
|
|
456
|
-
}
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
if (!result.success) {
|
|
460
|
-
result.error = 'Turnstile iframe/container not found or not interactive';
|
|
461
|
-
}
|
|
488
|
+
result.error = 'Turnstile iframe not found';
|
|
462
489
|
}
|
|
463
490
|
|
|
464
491
|
} catch (error) {
|
|
@@ -469,44 +496,7 @@ async function handleTurnstileChallenge(page, forceDebug = false) {
|
|
|
469
496
|
}
|
|
470
497
|
|
|
471
498
|
/**
|
|
472
|
-
*
|
|
473
|
-
* @param {import('puppeteer').Page} page - Puppeteer page instance
|
|
474
|
-
* @param {boolean} forceDebug - Debug mode flag
|
|
475
|
-
* @returns {Promise<object>} JS challenge result
|
|
476
|
-
*/
|
|
477
|
-
async function waitForJSChallengeCompletion(page, forceDebug = false) {
|
|
478
|
-
const result = {
|
|
479
|
-
success: false,
|
|
480
|
-
error: null
|
|
481
|
-
};
|
|
482
|
-
|
|
483
|
-
try {
|
|
484
|
-
if (forceDebug) console.log(`[debug][cloudflare] Waiting for JS challenge completion`);
|
|
485
|
-
|
|
486
|
-
// Wait for challenge to complete automatically
|
|
487
|
-
await page.waitForFunction(
|
|
488
|
-
() => {
|
|
489
|
-
return !document.body.textContent.includes('Checking your browser') &&
|
|
490
|
-
!document.body.textContent.includes('Please wait while we verify') &&
|
|
491
|
-
!document.querySelector('.cf-challenge-running') &&
|
|
492
|
-
!document.querySelector('[data-cf-challenge]');
|
|
493
|
-
},
|
|
494
|
-
{ timeout: 30000 }
|
|
495
|
-
);
|
|
496
|
-
|
|
497
|
-
result.success = true;
|
|
498
|
-
} catch (error) {
|
|
499
|
-
result.error = `JS challenge timeout: ${error.message}`;
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
return result;
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
/**
|
|
506
|
-
* Handles legacy checkbox challenges - Fallback for older implementations
|
|
507
|
-
* @param {import('puppeteer').Page} page - Puppeteer page instance
|
|
508
|
-
* @param {boolean} forceDebug - Debug mode flag
|
|
509
|
-
* @returns {Promise<object>} Legacy challenge result
|
|
499
|
+
* Handles legacy checkbox challenges with timeout protection
|
|
510
500
|
*/
|
|
511
501
|
async function handleLegacyCheckbox(page, forceDebug = false) {
|
|
512
502
|
const result = {
|
|
@@ -523,27 +513,20 @@ async function handleLegacyCheckbox(page, forceDebug = false) {
|
|
|
523
513
|
|
|
524
514
|
for (const selector of legacySelectors) {
|
|
525
515
|
try {
|
|
526
|
-
await
|
|
516
|
+
await Promise.race([
|
|
517
|
+
page.waitForSelector(selector, { timeout: 3000 }),
|
|
518
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error('Legacy selector timeout')), 4000))
|
|
519
|
+
]);
|
|
527
520
|
|
|
528
521
|
const checkbox = await page.$(selector);
|
|
529
522
|
if (checkbox) {
|
|
530
|
-
|
|
531
|
-
if (
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
await checkbox.click();
|
|
539
|
-
if (forceDebug) console.log(`[debug][cloudflare] Clicked legacy checkbox: ${selector}`);
|
|
540
|
-
|
|
541
|
-
// Wait for challenge completion
|
|
542
|
-
const completionCheck = await checkChallengeCompletion(page);
|
|
543
|
-
if (completionCheck.isCompleted) {
|
|
544
|
-
result.success = true;
|
|
545
|
-
break;
|
|
546
|
-
}
|
|
523
|
+
await checkbox.click();
|
|
524
|
+
if (forceDebug) console.log(`[debug][cloudflare] Clicked legacy checkbox: ${selector}`);
|
|
525
|
+
|
|
526
|
+
const completionCheck = await checkChallengeCompletion(page);
|
|
527
|
+
if (completionCheck.isCompleted) {
|
|
528
|
+
result.success = true;
|
|
529
|
+
break;
|
|
547
530
|
}
|
|
548
531
|
}
|
|
549
532
|
} catch (e) {
|
|
@@ -563,28 +546,24 @@ async function handleLegacyCheckbox(page, forceDebug = false) {
|
|
|
563
546
|
}
|
|
564
547
|
|
|
565
548
|
/**
|
|
566
|
-
* Checks if challenge has been completed
|
|
567
|
-
* @param {import('puppeteer').Page} page - Puppeteer page instance
|
|
568
|
-
* @returns {Promise<object>} Completion status
|
|
549
|
+
* Checks if challenge has been completed with timeout protection
|
|
569
550
|
*/
|
|
570
551
|
async function checkChallengeCompletion(page) {
|
|
571
552
|
try {
|
|
572
|
-
const isCompleted = await page
|
|
573
|
-
// Check for absence of challenge indicators
|
|
553
|
+
const isCompleted = await safePageEvaluate(page, () => {
|
|
574
554
|
const noChallengeRunning = !document.querySelector('.cf-challenge-running');
|
|
575
555
|
const noChallengeContainer = !document.querySelector('.cf-challenge-container');
|
|
576
556
|
const noChallengePage = !document.body.textContent.includes('Checking your browser') &&
|
|
577
557
|
!document.body.textContent.includes('Just a moment') &&
|
|
578
558
|
!document.body.textContent.includes('Verify you are human');
|
|
579
559
|
|
|
580
|
-
// Check for completion indicators
|
|
581
560
|
const hasClearanceCookie = document.cookie.includes('cf_clearance');
|
|
582
561
|
const hasTurnstileResponse = document.querySelector('input[name="cf-turnstile-response"]')?.value;
|
|
583
562
|
|
|
584
563
|
return (noChallengeRunning && noChallengeContainer && noChallengePage) ||
|
|
585
564
|
hasClearanceCookie ||
|
|
586
565
|
hasTurnstileResponse;
|
|
587
|
-
});
|
|
566
|
+
}, 5000);
|
|
588
567
|
|
|
589
568
|
return { isCompleted };
|
|
590
569
|
} catch (error) {
|
|
@@ -593,12 +572,7 @@ async function checkChallengeCompletion(page) {
|
|
|
593
572
|
}
|
|
594
573
|
|
|
595
574
|
/**
|
|
596
|
-
* Main function to handle all Cloudflare challenges
|
|
597
|
-
* @param {import('puppeteer').Page} page - Puppeteer page instance
|
|
598
|
-
* @param {string} currentUrl - Current URL being processed
|
|
599
|
-
* @param {object} siteConfig - Site configuration object
|
|
600
|
-
* @param {boolean} forceDebug - Debug mode flag
|
|
601
|
-
* @returns {Promise<object>} Combined result of all Cloudflare handling
|
|
575
|
+
* Main function to handle all Cloudflare challenges with comprehensive timeout protection
|
|
602
576
|
*/
|
|
603
577
|
async function handleCloudflareProtection(page, currentUrl, siteConfig, forceDebug = false) {
|
|
604
578
|
const result = {
|
|
@@ -608,6 +582,40 @@ async function handleCloudflareProtection(page, currentUrl, siteConfig, forceDeb
|
|
|
608
582
|
errors: []
|
|
609
583
|
};
|
|
610
584
|
|
|
585
|
+
try {
|
|
586
|
+
// Overall timeout for all Cloudflare operations
|
|
587
|
+
return await Promise.race([
|
|
588
|
+
performCloudflareHandling(page, currentUrl, siteConfig, forceDebug),
|
|
589
|
+
new Promise((resolve) => {
|
|
590
|
+
setTimeout(() => {
|
|
591
|
+
console.warn(`[cloudflare] Overall timeout for ${currentUrl} - continuing with scan`);
|
|
592
|
+
resolve({
|
|
593
|
+
phishingWarning: { attempted: false, success: true },
|
|
594
|
+
verificationChallenge: { attempted: false, success: true },
|
|
595
|
+
overallSuccess: true,
|
|
596
|
+
errors: ['Cloudflare handling timed out']
|
|
597
|
+
});
|
|
598
|
+
}, 45000); // 45 second overall timeout
|
|
599
|
+
})
|
|
600
|
+
]);
|
|
601
|
+
} catch (error) {
|
|
602
|
+
result.overallSuccess = false;
|
|
603
|
+
result.errors.push(`Cloudflare handling failed: ${error.message}`);
|
|
604
|
+
return result;
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
/**
|
|
609
|
+
* Performs the actual Cloudflare handling
|
|
610
|
+
*/
|
|
611
|
+
async function performCloudflareHandling(page, currentUrl, siteConfig, forceDebug = false) {
|
|
612
|
+
const result = {
|
|
613
|
+
phishingWarning: { attempted: false, success: false },
|
|
614
|
+
verificationChallenge: { attempted: false, success: false },
|
|
615
|
+
overallSuccess: true,
|
|
616
|
+
errors: []
|
|
617
|
+
};
|
|
618
|
+
|
|
611
619
|
// Handle phishing warnings if enabled
|
|
612
620
|
if (siteConfig.cloudflare_phish === true) {
|
|
613
621
|
const phishingResult = await handlePhishingWarning(page, currentUrl, forceDebug);
|
|
@@ -634,7 +642,6 @@ async function handleCloudflareProtection(page, currentUrl, siteConfig, forceDeb
|
|
|
634
642
|
}
|
|
635
643
|
}
|
|
636
644
|
|
|
637
|
-
// Log overall result
|
|
638
645
|
if (!result.overallSuccess && forceDebug) {
|
|
639
646
|
console.log(`[debug][cloudflare] Overall Cloudflare handling failed for ${currentUrl}:`);
|
|
640
647
|
result.errors.forEach(error => {
|
|
@@ -657,4 +664,4 @@ module.exports = {
|
|
|
657
664
|
waitForJSChallengeCompletion,
|
|
658
665
|
handleLegacyCheckbox,
|
|
659
666
|
checkChallengeCompletion
|
|
660
|
-
};
|
|
667
|
+
};
|
package/nwss.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// === Network scanner script (nwss.js) v1.0.
|
|
1
|
+
// === Network scanner script (nwss.js) v1.0.37 ===
|
|
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
|
|
@@ -33,7 +33,7 @@ const { navigateWithRedirectHandling, handleRedirectTimeout } = require('./lib/r
|
|
|
33
33
|
const { monitorBrowserHealth, isBrowserHealthy } = require('./lib/browserhealth');
|
|
34
34
|
|
|
35
35
|
// --- Script Configuration & Constants ---
|
|
36
|
-
const VERSION = '1.0.
|
|
36
|
+
const VERSION = '1.0.37'; // Script version
|
|
37
37
|
const MAX_CONCURRENT_SITES = 5;
|
|
38
38
|
const RESOURCE_CLEANUP_INTERVAL = 80; // Close browser and restart every N sites to free resources
|
|
39
39
|
|
|
@@ -936,7 +936,7 @@ function setupFrameHandling(page, forceDebug) {
|
|
|
936
936
|
'--disable-image-animation-resync'
|
|
937
937
|
],
|
|
938
938
|
headless: launchHeadless ? 'shell' : false,
|
|
939
|
-
protocolTimeout:
|
|
939
|
+
protocolTimeout: 60000 // 60 seconds
|
|
940
940
|
});
|
|
941
941
|
|
|
942
942
|
// Store the user data directory on the browser object for cleanup
|
|
@@ -2091,7 +2091,21 @@ function setupFrameHandling(page, forceDebug) {
|
|
|
2091
2091
|
}
|
|
2092
2092
|
|
|
2093
2093
|
} catch (err) {
|
|
2094
|
-
|
|
2094
|
+
// Enhanced error handling with rule preservation for partial matches
|
|
2095
|
+
if (err.message.includes('Runtime.callFunctionOn timed out') ||
|
|
2096
|
+
err.message.includes('Protocol error') ||
|
|
2097
|
+
err.message.includes('Target closed') ||
|
|
2098
|
+
err.message.includes('Browser has been closed')) {
|
|
2099
|
+
console.error(formatLogMessage('error', `Critical browser protocol error on ${currentUrl}: ${err.message}`));
|
|
2100
|
+
return {
|
|
2101
|
+
url: currentUrl,
|
|
2102
|
+
rules: [],
|
|
2103
|
+
success: false,
|
|
2104
|
+
needsImmediateRestart: true,
|
|
2105
|
+
error: `Critical protocol error: ${err.message}`
|
|
2106
|
+
};
|
|
2107
|
+
}
|
|
2108
|
+
|
|
2095
2109
|
if (err.message.includes('Protocol error') ||
|
|
2096
2110
|
err.message.includes('Target closed') ||
|
|
2097
2111
|
err.message.includes('Browser process was killed') ||
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fanboynz/network-scanner",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.37",
|
|
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": {
|