@fanboynz/network-scanner 1.0.44 → 1.0.45
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/cloudflare.js +68 -0
- package/lib/flowproxy.js +260 -29
- package/nwss.js +2 -2
- package/package.json +1 -1
package/lib/cloudflare.js
CHANGED
|
@@ -52,6 +52,47 @@ function getModuleInfo() {
|
|
|
52
52
|
};
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
+
/**
|
|
56
|
+
* Validates if a URL should be processed by Cloudflare protection
|
|
57
|
+
* Only allows HTTP/HTTPS URLs, skips browser-internal and special protocols
|
|
58
|
+
* @param {string} url - URL to validate
|
|
59
|
+
* @param {boolean} forceDebug - Debug logging flag
|
|
60
|
+
* @returns {boolean} True if URL should be processed
|
|
61
|
+
*/
|
|
62
|
+
function shouldProcessUrl(url, forceDebug = false) {
|
|
63
|
+
if (!url || typeof url !== 'string') {
|
|
64
|
+
if (forceDebug) console.log(`[cloudflare][url-validation] Skipping invalid URL: ${url}`);
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Skip browser-internal and special protocol URLs
|
|
69
|
+
const skipPatterns = [
|
|
70
|
+
'about:', 'chrome:', 'chrome-extension:', 'chrome-error:', 'chrome-search:',
|
|
71
|
+
'devtools:', 'edge:', 'moz-extension:', 'safari-extension:', 'webkit:',
|
|
72
|
+
'data:', 'blob:', 'javascript:', 'vbscript:', 'file:', 'ftp:', 'ftps:'
|
|
73
|
+
];
|
|
74
|
+
|
|
75
|
+
const urlLower = url.toLowerCase();
|
|
76
|
+
for (const pattern of skipPatterns) {
|
|
77
|
+
if (urlLower.startsWith(pattern)) {
|
|
78
|
+
if (forceDebug) {
|
|
79
|
+
console.log(`[cloudflare][url-validation] Skipping ${pattern} URL: ${url.substring(0, 100)}${url.length > 100 ? '...' : ''}`);
|
|
80
|
+
}
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Only process HTTP/HTTPS URLs
|
|
86
|
+
if (!urlLower.startsWith('http://') && !urlLower.startsWith('https://')) {
|
|
87
|
+
if (forceDebug) {
|
|
88
|
+
console.log(`[cloudflare][url-validation] Skipping non-HTTP(S) URL: ${url.substring(0, 100)}${url.length > 100 ? '...' : ''}`);
|
|
89
|
+
}
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
95
|
+
|
|
55
96
|
/**
|
|
56
97
|
* Cross-version compatible timeout function for Puppeteer with timeout protection
|
|
57
98
|
*/
|
|
@@ -139,6 +180,18 @@ async function safeWaitForNavigation(page, timeout = TIMEOUTS.NAVIGATION_TIMEOUT
|
|
|
139
180
|
*/
|
|
140
181
|
async function quickCloudflareDetection(page, forceDebug = false) {
|
|
141
182
|
try {
|
|
183
|
+
// Get current page URL and validate it
|
|
184
|
+
const currentPageUrl = await page.url();
|
|
185
|
+
|
|
186
|
+
if (!shouldProcessUrl(currentPageUrl, forceDebug)) {
|
|
187
|
+
if (forceDebug) {
|
|
188
|
+
console.log(`[debug][cloudflare] Quick detection skipping non-HTTP(S) page: ${currentPageUrl}`);
|
|
189
|
+
}
|
|
190
|
+
return { hasIndicators: false, skippedInvalidUrl: true };
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Continue with existing detection logic only for valid HTTP(S) URLs
|
|
194
|
+
|
|
142
195
|
const quickCheck = await safePageEvaluate(page, () => {
|
|
143
196
|
const title = document.title || '';
|
|
144
197
|
const bodyText = document.body ? document.body.textContent.substring(0, 500) : '';
|
|
@@ -813,6 +866,21 @@ async function handleCloudflareProtection(page, currentUrl, siteConfig, forceDeb
|
|
|
813
866
|
if (forceDebug) {
|
|
814
867
|
console.log(`[debug][cloudflare] Using Cloudflare module v${CLOUDFLARE_MODULE_VERSION} for ${currentUrl}`);
|
|
815
868
|
}
|
|
869
|
+
|
|
870
|
+
// VALIDATE URL FIRST - Skip protection handling for non-HTTP(S) URLs
|
|
871
|
+
if (!shouldProcessUrl(currentUrl, forceDebug)) {
|
|
872
|
+
if (forceDebug) {
|
|
873
|
+
console.log(`[debug][cloudflare] Skipping protection handling for non-HTTP(S) URL: ${currentUrl}`);
|
|
874
|
+
}
|
|
875
|
+
return {
|
|
876
|
+
phishingWarning: { attempted: false, success: true },
|
|
877
|
+
verificationChallenge: { attempted: false, success: true },
|
|
878
|
+
overallSuccess: true,
|
|
879
|
+
errors: [],
|
|
880
|
+
skippedInvalidUrl: true
|
|
881
|
+
};
|
|
882
|
+
}
|
|
883
|
+
|
|
816
884
|
// Quick detection first - exit early if no Cloudflare detected and no explicit config
|
|
817
885
|
const quickDetection = await quickCloudflareDetection(page, forceDebug);
|
|
818
886
|
|
package/lib/flowproxy.js
CHANGED
|
@@ -1,46 +1,176 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* FlowProxy protection detection and handling module
|
|
3
|
+
* Version: 1.0.0 - Enhanced with comprehensive documentation and smart detection
|
|
3
4
|
* Detects flowProxy DDoS protection and handles it appropriately for security scanning
|
|
5
|
+
*
|
|
6
|
+
* FlowProxy (by Aurologic) is a DDoS protection service similar to Cloudflare that:
|
|
7
|
+
* - Implements rate limiting and browser verification
|
|
8
|
+
* - Uses JavaScript challenges to verify legitimate browsers
|
|
9
|
+
* - Can block automated tools and scrapers
|
|
10
|
+
* - Requires specific handling for security scanning tools
|
|
4
11
|
*/
|
|
5
12
|
|
|
6
13
|
/**
|
|
7
|
-
*
|
|
14
|
+
* Module version information
|
|
15
|
+
*/
|
|
16
|
+
const FLOWPROXY_MODULE_VERSION = '1.0.0';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Timeout constants for FlowProxy operations (in milliseconds)
|
|
20
|
+
*/
|
|
21
|
+
const TIMEOUTS = {
|
|
22
|
+
PAGE_LOAD_WAIT: 2000, // Initial wait for page to load
|
|
23
|
+
JS_CHALLENGE_DEFAULT: 15000, // Default JavaScript challenge timeout
|
|
24
|
+
RATE_LIMIT_DEFAULT: 30000, // Default rate limit delay
|
|
25
|
+
ADDITIONAL_DELAY_DEFAULT: 5000, // Default additional processing delay
|
|
26
|
+
PAGE_TIMEOUT_DEFAULT: 45000, // Default page timeout
|
|
27
|
+
NAVIGATION_TIMEOUT_DEFAULT: 45000, // Default navigation timeout
|
|
28
|
+
FALLBACK_TIMEOUT: 5000 // Fallback timeout for failed operations
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Gets module version information
|
|
33
|
+
* @returns {object} Version information object
|
|
34
|
+
*/
|
|
35
|
+
function getModuleInfo() {
|
|
36
|
+
return {
|
|
37
|
+
version: FLOWPROXY_MODULE_VERSION,
|
|
38
|
+
name: 'FlowProxy Protection Handler'
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Validates if a URL should be processed by FlowProxy protection
|
|
44
|
+
* Only allows HTTP/HTTPS URLs, skips browser-internal and special protocols
|
|
45
|
+
*
|
|
46
|
+
* @param {string} url - URL to validate
|
|
47
|
+
* @param {boolean} forceDebug - Debug logging flag
|
|
48
|
+
* @returns {boolean} True if URL should be processed
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* // Valid URLs that will be processed
|
|
52
|
+
* shouldProcessUrl('https://example.com') // => true
|
|
53
|
+
* shouldProcessUrl('http://test.com') // => true
|
|
54
|
+
*
|
|
55
|
+
* // Invalid URLs that will be skipped
|
|
56
|
+
* shouldProcessUrl('chrome://settings') // => false
|
|
57
|
+
* shouldProcessUrl('about:blank') // => false
|
|
58
|
+
* shouldProcessUrl('file:///local/file.html') // => false
|
|
59
|
+
*/
|
|
60
|
+
function shouldProcessUrl(url, forceDebug = false) {
|
|
61
|
+
if (!url || typeof url !== 'string') {
|
|
62
|
+
if (forceDebug) console.log(`[flowproxy][url-validation] Skipping invalid URL: ${url}`);
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Skip browser-internal and special protocol URLs
|
|
67
|
+
// These protocols are not relevant for FlowProxy protection
|
|
68
|
+
const skipPatterns = [
|
|
69
|
+
'about:', 'chrome:', 'chrome-extension:', 'chrome-error:', 'chrome-search:',
|
|
70
|
+
'devtools:', 'edge:', 'moz-extension:', 'safari-extension:', 'webkit:',
|
|
71
|
+
'data:', 'blob:', 'javascript:', 'vbscript:', 'file:', 'ftp:', 'ftps:'
|
|
72
|
+
];
|
|
73
|
+
|
|
74
|
+
const urlLower = url.toLowerCase();
|
|
75
|
+
for (const pattern of skipPatterns) {
|
|
76
|
+
if (urlLower.startsWith(pattern)) {
|
|
77
|
+
if (forceDebug) {
|
|
78
|
+
console.log(`[flowproxy][url-validation] Skipping ${pattern} URL: ${url.substring(0, 100)}${url.length > 100 ? '...' : ''}`);
|
|
79
|
+
}
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Only process HTTP/HTTPS URLs - FlowProxy only protects web traffic
|
|
85
|
+
if (!urlLower.startsWith('http://') && !urlLower.startsWith('https://')) {
|
|
86
|
+
if (forceDebug) {
|
|
87
|
+
console.log(`[flowproxy][url-validation] Skipping non-HTTP(S) URL: ${url.substring(0, 100)}${url.length > 100 ? '...' : ''}`);
|
|
88
|
+
}
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return true;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Cross-version compatible timeout function for Puppeteer with timeout protection
|
|
97
|
+
* Handles different Puppeteer versions that may have different timeout methods
|
|
98
|
+
*
|
|
8
99
|
* @param {import('puppeteer').Page} page - Puppeteer page instance
|
|
9
100
|
* @param {number} timeout - Timeout in milliseconds
|
|
10
101
|
* @returns {Promise<void>}
|
|
102
|
+
*
|
|
103
|
+
* @example
|
|
104
|
+
* // Wait for 5 seconds
|
|
105
|
+
* await waitForTimeout(page, 5000);
|
|
11
106
|
*/
|
|
12
107
|
async function waitForTimeout(page, timeout) {
|
|
13
108
|
try {
|
|
109
|
+
// Try newer Puppeteer method first (v1.4.0+)
|
|
14
110
|
if (typeof page.waitForTimeout === 'function') {
|
|
15
111
|
await page.waitForTimeout(timeout);
|
|
16
112
|
} else if (typeof page.waitFor === 'function') {
|
|
113
|
+
// Fallback for older Puppeteer versions
|
|
17
114
|
await page.waitFor(timeout);
|
|
18
115
|
} else {
|
|
116
|
+
// Final fallback - use standard setTimeout
|
|
19
117
|
await new Promise(resolve => setTimeout(resolve, timeout));
|
|
20
118
|
}
|
|
21
119
|
} catch (error) {
|
|
120
|
+
// If all else fails, use setTimeout
|
|
22
121
|
await new Promise(resolve => setTimeout(resolve, timeout));
|
|
23
122
|
}
|
|
24
123
|
}
|
|
25
124
|
|
|
26
125
|
/**
|
|
27
|
-
* Analyzes the current page to detect flowProxy protection
|
|
126
|
+
* Analyzes the current page to detect flowProxy protection with comprehensive detection logic
|
|
127
|
+
*
|
|
128
|
+
* FlowProxy protection typically manifests as:
|
|
129
|
+
* - DDoS protection pages with "Please wait" messages
|
|
130
|
+
* - Rate limiting responses (429 errors)
|
|
131
|
+
* - JavaScript challenges that must complete before access
|
|
132
|
+
* - Aurologic branding and flowproxy-specific elements
|
|
133
|
+
* - Browser verification processes
|
|
134
|
+
*
|
|
28
135
|
* @param {import('puppeteer').Page} page - Puppeteer page instance
|
|
29
|
-
* @returns {Promise<object>} Detection information object
|
|
136
|
+
* @returns {Promise<object>} Detection information object with detailed analysis
|
|
137
|
+
*
|
|
138
|
+
* @example
|
|
139
|
+
* const analysis = await analyzeFlowProxyProtection(page);
|
|
140
|
+
* if (analysis.isFlowProxyDetected) {
|
|
141
|
+
* console.log(`FlowProxy protection found: ${analysis.title}`);
|
|
142
|
+
* if (analysis.isRateLimited) {
|
|
143
|
+
* console.log('Rate limiting is active');
|
|
144
|
+
* }
|
|
145
|
+
* }
|
|
30
146
|
*/
|
|
31
147
|
async function analyzeFlowProxyProtection(page) {
|
|
32
148
|
try {
|
|
149
|
+
// Get current page URL and validate it first
|
|
150
|
+
const currentPageUrl = await page.url();
|
|
151
|
+
|
|
152
|
+
if (!shouldProcessUrl(currentPageUrl, false)) {
|
|
153
|
+
return {
|
|
154
|
+
isFlowProxyDetected: false,
|
|
155
|
+
skippedInvalidUrl: true,
|
|
156
|
+
url: currentPageUrl
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Continue with comprehensive FlowProxy detection for valid HTTP(S) URLs
|
|
33
161
|
return await page.evaluate(() => {
|
|
34
162
|
const title = document.title || '';
|
|
35
163
|
const bodyText = document.body ? document.body.textContent : '';
|
|
36
164
|
const url = window.location.href;
|
|
37
165
|
|
|
38
|
-
// Check for flowProxy/aurologic specific indicators
|
|
166
|
+
// Check for flowProxy/aurologic specific domain indicators
|
|
167
|
+
// FlowProxy services often redirect to aurologic domains or use flowproxy subdomains
|
|
39
168
|
const hasFlowProxyDomain = url.includes('aurologic') ||
|
|
40
169
|
url.includes('flowproxy') ||
|
|
41
170
|
url.includes('ddos-protection');
|
|
42
171
|
|
|
43
172
|
// Check for flowProxy challenge page indicators
|
|
173
|
+
// These are common titles and text patterns used by FlowProxy protection pages
|
|
44
174
|
const hasProtectionPage = title.includes('DDoS Protection') ||
|
|
45
175
|
title.includes('Please wait') ||
|
|
46
176
|
title.includes('Checking your browser') ||
|
|
@@ -48,18 +178,21 @@ async function analyzeFlowProxyProtection(page) {
|
|
|
48
178
|
bodyText.includes('flowProxy') ||
|
|
49
179
|
bodyText.includes('Verifying your browser');
|
|
50
180
|
|
|
51
|
-
// Check for specific flowProxy elements
|
|
181
|
+
// Check for specific flowProxy DOM elements
|
|
182
|
+
// FlowProxy typically adds custom data attributes and CSS classes
|
|
52
183
|
const hasFlowProxyElements = document.querySelector('[data-flowproxy]') !== null ||
|
|
53
184
|
document.querySelector('.flowproxy-challenge') !== null ||
|
|
54
185
|
document.querySelector('#flowproxy-container') !== null ||
|
|
55
186
|
document.querySelector('.aurologic-protection') !== null;
|
|
56
187
|
|
|
57
188
|
// Check for challenge indicators
|
|
189
|
+
// FlowProxy uses various elements to indicate active challenges
|
|
58
190
|
const hasChallengeElements = document.querySelector('.challenge-running') !== null ||
|
|
59
191
|
document.querySelector('.verification-container') !== null ||
|
|
60
192
|
document.querySelector('input[name="flowproxy-response"]') !== null;
|
|
61
193
|
|
|
62
194
|
// Check for rate limiting indicators
|
|
195
|
+
// Rate limiting is a common FlowProxy feature that shows specific messages
|
|
63
196
|
const isRateLimited = bodyText.includes('Rate limited') ||
|
|
64
197
|
bodyText.includes('Too many requests') ||
|
|
65
198
|
bodyText.includes('Please try again later') ||
|
|
@@ -67,17 +200,20 @@ async function analyzeFlowProxyProtection(page) {
|
|
|
67
200
|
title.includes('Rate Limit');
|
|
68
201
|
|
|
69
202
|
// Check for JavaScript challenge indicators
|
|
203
|
+
// FlowProxy often requires JavaScript to be enabled and uses specific scripts
|
|
70
204
|
const hasJSChallenge = document.querySelector('script[src*="flowproxy"]') !== null ||
|
|
71
205
|
document.querySelector('script[src*="aurologic"]') !== null ||
|
|
72
206
|
bodyText.includes('JavaScript is required') ||
|
|
73
207
|
bodyText.includes('Please enable JavaScript');
|
|
74
208
|
|
|
75
209
|
// Check for loading/processing indicators
|
|
210
|
+
// FlowProxy shows these while performing browser verification
|
|
76
211
|
const isProcessing = bodyText.includes('Processing') ||
|
|
77
212
|
bodyText.includes('Loading') ||
|
|
78
213
|
document.querySelector('.loading-spinner') !== null ||
|
|
79
214
|
document.querySelector('.processing-indicator') !== null;
|
|
80
215
|
|
|
216
|
+
// Main detection logic - any of these primary indicators suggest FlowProxy presence
|
|
81
217
|
const isFlowProxyDetected = hasFlowProxyDomain ||
|
|
82
218
|
hasProtectionPage ||
|
|
83
219
|
hasFlowProxyElements ||
|
|
@@ -98,6 +234,7 @@ async function analyzeFlowProxyProtection(page) {
|
|
|
98
234
|
};
|
|
99
235
|
});
|
|
100
236
|
} catch (error) {
|
|
237
|
+
// Return safe defaults if page evaluation fails
|
|
101
238
|
return {
|
|
102
239
|
isFlowProxyDetected: false,
|
|
103
240
|
hasFlowProxyDomain: false,
|
|
@@ -114,13 +251,69 @@ async function analyzeFlowProxyProtection(page) {
|
|
|
114
251
|
|
|
115
252
|
/**
|
|
116
253
|
* Handles flowProxy protection by implementing appropriate delays and retry logic
|
|
254
|
+
*
|
|
255
|
+
* FlowProxy handling strategy:
|
|
256
|
+
* 1. Detect protection type (rate limiting, JS challenge, etc.)
|
|
257
|
+
* 2. Implement appropriate delays based on protection type
|
|
258
|
+
* 3. Wait for JavaScript challenges to complete
|
|
259
|
+
* 4. Verify successful bypass before continuing
|
|
260
|
+
*
|
|
117
261
|
* @param {import('puppeteer').Page} page - Puppeteer page instance
|
|
118
262
|
* @param {string} currentUrl - Current URL being processed
|
|
119
|
-
* @param {object} siteConfig - Site configuration object
|
|
120
|
-
* @param {boolean} forceDebug - Debug mode flag
|
|
121
|
-
*
|
|
263
|
+
* @param {object} siteConfig - Site configuration object with FlowProxy settings
|
|
264
|
+
* @param {boolean} forceDebug - Debug mode flag for detailed logging
|
|
265
|
+
*
|
|
266
|
+
* @returns {Promise<object>} Result object with comprehensive handling details:
|
|
267
|
+
* {
|
|
268
|
+
* flowProxyDetection: {
|
|
269
|
+
* attempted: boolean, // Whether detection was attempted
|
|
270
|
+
* detected: boolean, // Whether FlowProxy protection was found
|
|
271
|
+
* details: object|null // Detailed detection information
|
|
272
|
+
* },
|
|
273
|
+
* handlingResult: {
|
|
274
|
+
* attempted: boolean, // Whether handling was attempted
|
|
275
|
+
* success: boolean // Whether handling succeeded
|
|
276
|
+
* },
|
|
277
|
+
* overallSuccess: boolean, // True if no critical failures occurred
|
|
278
|
+
* errors: string[], // Array of error messages
|
|
279
|
+
* warnings: string[], // Array of warning messages
|
|
280
|
+
* skippedInvalidUrl: boolean // True if URL was skipped due to invalid protocol
|
|
281
|
+
* }
|
|
282
|
+
*
|
|
283
|
+
* @example
|
|
284
|
+
* const config = {
|
|
285
|
+
* flowproxy_delay: 45000, // Rate limit delay (45 seconds)
|
|
286
|
+
* flowproxy_js_timeout: 20000, // JS challenge timeout (20 seconds)
|
|
287
|
+
* flowproxy_additional_delay: 8000 // Additional processing delay (8 seconds)
|
|
288
|
+
* };
|
|
289
|
+
*
|
|
290
|
+
* const result = await handleFlowProxyProtection(page, url, config, true);
|
|
291
|
+
* if (result.flowProxyDetection.detected) {
|
|
292
|
+
* console.log('FlowProxy protection handled');
|
|
293
|
+
* if (result.warnings.length > 0) {
|
|
294
|
+
* console.log('Warnings:', result.warnings);
|
|
295
|
+
* }
|
|
296
|
+
* }
|
|
122
297
|
*/
|
|
123
298
|
async function handleFlowProxyProtection(page, currentUrl, siteConfig, forceDebug = false) {
|
|
299
|
+
|
|
300
|
+
// VALIDATE URL FIRST - Skip protection handling for non-HTTP(S) URLs
|
|
301
|
+
// FlowProxy only protects web traffic, so other protocols should be skipped
|
|
302
|
+
if (!shouldProcessUrl(currentUrl, forceDebug)) {
|
|
303
|
+
if (forceDebug) {
|
|
304
|
+
console.log(`[debug][flowproxy] Skipping protection handling for non-HTTP(S) URL: ${currentUrl}`);
|
|
305
|
+
}
|
|
306
|
+
return {
|
|
307
|
+
flowProxyDetection: { attempted: false, detected: false },
|
|
308
|
+
handlingResult: { attempted: false, success: true },
|
|
309
|
+
overallSuccess: true,
|
|
310
|
+
errors: [],
|
|
311
|
+
warnings: [],
|
|
312
|
+
skippedInvalidUrl: true
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// Initialize result structure for tracking all handling aspects
|
|
124
317
|
const result = {
|
|
125
318
|
flowProxyDetection: { attempted: false, detected: false },
|
|
126
319
|
handlingResult: { attempted: false, success: false },
|
|
@@ -132,9 +325,11 @@ async function handleFlowProxyProtection(page, currentUrl, siteConfig, forceDebu
|
|
|
132
325
|
try {
|
|
133
326
|
if (forceDebug) console.log(`[debug][flowproxy] Checking for flowProxy protection on ${currentUrl}`);
|
|
134
327
|
|
|
135
|
-
// Wait
|
|
136
|
-
|
|
328
|
+
// Wait for initial page load before analyzing
|
|
329
|
+
// FlowProxy protection pages need time to fully render their elements
|
|
330
|
+
await waitForTimeout(page, TIMEOUTS.PAGE_LOAD_WAIT);
|
|
137
331
|
|
|
332
|
+
// Perform comprehensive FlowProxy detection
|
|
138
333
|
const detectionInfo = await analyzeFlowProxyProtection(page);
|
|
139
334
|
result.flowProxyDetection = {
|
|
140
335
|
attempted: true,
|
|
@@ -142,6 +337,7 @@ async function handleFlowProxyProtection(page, currentUrl, siteConfig, forceDebu
|
|
|
142
337
|
details: detectionInfo
|
|
143
338
|
};
|
|
144
339
|
|
|
340
|
+
// Only proceed with handling if FlowProxy protection is detected
|
|
145
341
|
if (detectionInfo.isFlowProxyDetected) {
|
|
146
342
|
result.handlingResult.attempted = true;
|
|
147
343
|
|
|
@@ -153,23 +349,28 @@ async function handleFlowProxyProtection(page, currentUrl, siteConfig, forceDebu
|
|
|
153
349
|
console.log(`[debug][flowproxy] Has Challenge Elements: ${detectionInfo.hasChallengeElements}`);
|
|
154
350
|
console.log(`[debug][flowproxy] Is Rate Limited: ${detectionInfo.isRateLimited}`);
|
|
155
351
|
console.log(`[debug][flowproxy] Has JS Challenge: ${detectionInfo.hasJSChallenge}`);
|
|
352
|
+
console.log(`[debug][flowproxy] Is Processing: ${detectionInfo.isProcessing}`);
|
|
353
|
+
console.log(`[debug][flowproxy] Body Snippet: "${detectionInfo.bodySnippet}"`);
|
|
156
354
|
}
|
|
157
355
|
|
|
158
|
-
//
|
|
356
|
+
// HANDLE RATE LIMITING - Highest priority as it blocks all requests
|
|
357
|
+
// Rate limiting requires waiting before any other actions
|
|
159
358
|
if (detectionInfo.isRateLimited) {
|
|
160
|
-
const rateLimitDelay = siteConfig.flowproxy_delay ||
|
|
359
|
+
const rateLimitDelay = siteConfig.flowproxy_delay || TIMEOUTS.RATE_LIMIT_DEFAULT;
|
|
161
360
|
result.warnings.push(`Rate limiting detected - implementing ${rateLimitDelay}ms delay`);
|
|
162
361
|
if (forceDebug) console.log(`[debug][flowproxy] Rate limiting detected, waiting ${rateLimitDelay}ms`);
|
|
163
362
|
await waitForTimeout(page, rateLimitDelay);
|
|
164
363
|
}
|
|
165
364
|
|
|
166
|
-
//
|
|
365
|
+
// HANDLE JAVASCRIPT CHALLENGES - Second priority as they must complete
|
|
366
|
+
// FlowProxy uses JS challenges to verify browser legitimacy
|
|
167
367
|
if (detectionInfo.hasJSChallenge || detectionInfo.isProcessing) {
|
|
168
|
-
const jsWaitTime = siteConfig.flowproxy_js_timeout ||
|
|
368
|
+
const jsWaitTime = siteConfig.flowproxy_js_timeout || TIMEOUTS.JS_CHALLENGE_DEFAULT;
|
|
169
369
|
if (forceDebug) console.log(`[debug][flowproxy] JavaScript challenge detected, waiting up to ${jsWaitTime}ms for completion`);
|
|
170
370
|
|
|
171
371
|
try {
|
|
172
|
-
// Wait for challenge
|
|
372
|
+
// Wait for challenge completion indicators to disappear
|
|
373
|
+
// These conditions indicate the JS challenge has finished
|
|
173
374
|
await page.waitForFunction(
|
|
174
375
|
() => {
|
|
175
376
|
const bodyText = document.body ? document.body.textContent : '';
|
|
@@ -184,41 +385,48 @@ async function handleFlowProxyProtection(page, currentUrl, siteConfig, forceDebu
|
|
|
184
385
|
|
|
185
386
|
if (forceDebug) console.log(`[debug][flowproxy] JavaScript challenge appears to have completed`);
|
|
186
387
|
} catch (timeoutErr) {
|
|
388
|
+
// Continue even if timeout occurs - some challenges may take longer
|
|
187
389
|
result.warnings.push(`JavaScript challenge timeout after ${jsWaitTime}ms`);
|
|
188
390
|
if (forceDebug) console.log(`[debug][flowproxy] JavaScript challenge timeout - continuing anyway`);
|
|
189
391
|
}
|
|
190
392
|
}
|
|
191
393
|
|
|
192
|
-
//
|
|
193
|
-
|
|
394
|
+
// IMPLEMENT ADDITIONAL DELAY - Final step to ensure all processing completes
|
|
395
|
+
// FlowProxy may need extra time even after challenges complete
|
|
396
|
+
const additionalDelay = siteConfig.flowproxy_additional_delay || TIMEOUTS.ADDITIONAL_DELAY_DEFAULT;
|
|
194
397
|
if (forceDebug) console.log(`[debug][flowproxy] Implementing additional ${additionalDelay}ms delay for flowProxy processing`);
|
|
195
398
|
await waitForTimeout(page, additionalDelay);
|
|
196
399
|
|
|
197
|
-
// Check if we're still on a protection page
|
|
400
|
+
// VERIFY SUCCESSFUL BYPASS - Check if we're still on a protection page
|
|
401
|
+
// This helps identify if our handling was successful
|
|
198
402
|
const finalCheck = await analyzeFlowProxyProtection(page);
|
|
199
403
|
if (finalCheck.isFlowProxyDetected && finalCheck.hasProtectionPage) {
|
|
200
404
|
result.warnings.push('Still on flowProxy protection page after handling attempts');
|
|
201
405
|
if (forceDebug) console.log(`[debug][flowproxy] Warning: Still appears to be on protection page`);
|
|
406
|
+
// Don't mark as failure - protection page may persist but still allow access
|
|
202
407
|
} else {
|
|
203
408
|
result.handlingResult.success = true;
|
|
204
409
|
if (forceDebug) console.log(`[debug][flowproxy] Successfully handled flowProxy protection for ${currentUrl}`);
|
|
205
410
|
}
|
|
206
411
|
|
|
207
412
|
} else {
|
|
413
|
+
// No FlowProxy protection detected - mark as successful (nothing to handle)
|
|
208
414
|
if (forceDebug) console.log(`[debug][flowproxy] No flowProxy protection detected on ${currentUrl}`);
|
|
209
|
-
result.overallSuccess = true;
|
|
415
|
+
result.overallSuccess = true;
|
|
210
416
|
}
|
|
211
417
|
|
|
212
418
|
} catch (error) {
|
|
419
|
+
// Critical error occurred during handling
|
|
213
420
|
result.errors.push(`FlowProxy handling error: ${error.message}`);
|
|
214
421
|
result.overallSuccess = false;
|
|
215
422
|
if (forceDebug) {
|
|
216
423
|
console.log(`[debug][flowproxy] FlowProxy handling failed for ${currentUrl}:`);
|
|
217
424
|
console.log(`[debug][flowproxy] Error: ${error.message}`);
|
|
425
|
+
console.log(`[debug][flowproxy] Stack: ${error.stack}`);
|
|
218
426
|
}
|
|
219
427
|
}
|
|
220
428
|
|
|
221
|
-
//
|
|
429
|
+
// LOG COMPREHENSIVE RESULTS for debugging and monitoring
|
|
222
430
|
if (result.errors.length > 0 && forceDebug) {
|
|
223
431
|
console.log(`[debug][flowproxy] FlowProxy handling completed with errors for ${currentUrl}:`);
|
|
224
432
|
result.errors.forEach(error => {
|
|
@@ -237,38 +445,61 @@ async function handleFlowProxyProtection(page, currentUrl, siteConfig, forceDebu
|
|
|
237
445
|
}
|
|
238
446
|
|
|
239
447
|
/**
|
|
240
|
-
*
|
|
448
|
+
* Quick check to determine if the current page might be behind flowProxy protection
|
|
449
|
+
* This is a lightweight alternative to full analysis for simple detection needs
|
|
450
|
+
*
|
|
241
451
|
* @param {import('puppeteer').Page} page - Puppeteer page instance
|
|
242
452
|
* @returns {Promise<boolean>} True if flowProxy protection is suspected
|
|
453
|
+
*
|
|
454
|
+
* @example
|
|
455
|
+
* if (await isFlowProxyProtected(page)) {
|
|
456
|
+
* console.log('FlowProxy protection detected - implementing handling');
|
|
457
|
+
* await handleFlowProxyProtection(page, url, config);
|
|
458
|
+
* }
|
|
243
459
|
*/
|
|
244
460
|
async function isFlowProxyProtected(page) {
|
|
245
461
|
try {
|
|
246
462
|
const detection = await analyzeFlowProxyProtection(page);
|
|
247
463
|
return detection.isFlowProxyDetected;
|
|
248
464
|
} catch (error) {
|
|
465
|
+
// Return false if detection fails - assume no protection
|
|
249
466
|
return false;
|
|
250
467
|
}
|
|
251
468
|
}
|
|
252
469
|
|
|
253
470
|
/**
|
|
254
471
|
* Gets recommended timeout values for flowProxy protected sites
|
|
255
|
-
*
|
|
256
|
-
*
|
|
472
|
+
* Provides sensible defaults while allowing site-specific customization
|
|
473
|
+
*
|
|
474
|
+
* @param {object} siteConfig - Site configuration object with optional FlowProxy settings
|
|
475
|
+
* @returns {object} Recommended timeout values for FlowProxy handling
|
|
476
|
+
*
|
|
477
|
+
* @example
|
|
478
|
+
* const timeouts = getFlowProxyTimeouts({
|
|
479
|
+
* flowproxy_delay: 60000, // Custom rate limit delay
|
|
480
|
+
* flowproxy_js_timeout: 25000 // Custom JS challenge timeout
|
|
481
|
+
* });
|
|
482
|
+
*
|
|
483
|
+
* // Use timeouts in page operations
|
|
484
|
+
* await page.goto(url, { timeout: timeouts.pageTimeout });
|
|
257
485
|
*/
|
|
258
486
|
function getFlowProxyTimeouts(siteConfig) {
|
|
259
487
|
return {
|
|
260
|
-
pageTimeout: siteConfig.flowproxy_page_timeout ||
|
|
261
|
-
navigationTimeout: siteConfig.flowproxy_nav_timeout ||
|
|
262
|
-
challengeTimeout: siteConfig.flowproxy_js_timeout ||
|
|
263
|
-
rateLimit: siteConfig.flowproxy_delay ||
|
|
264
|
-
additionalDelay: siteConfig.flowproxy_additional_delay ||
|
|
488
|
+
pageTimeout: siteConfig.flowproxy_page_timeout || TIMEOUTS.PAGE_TIMEOUT_DEFAULT,
|
|
489
|
+
navigationTimeout: siteConfig.flowproxy_nav_timeout || TIMEOUTS.NAVIGATION_TIMEOUT_DEFAULT,
|
|
490
|
+
challengeTimeout: siteConfig.flowproxy_js_timeout || TIMEOUTS.JS_CHALLENGE_DEFAULT,
|
|
491
|
+
rateLimit: siteConfig.flowproxy_delay || TIMEOUTS.RATE_LIMIT_DEFAULT,
|
|
492
|
+
additionalDelay: siteConfig.flowproxy_additional_delay || TIMEOUTS.ADDITIONAL_DELAY_DEFAULT
|
|
265
493
|
};
|
|
266
494
|
}
|
|
267
495
|
|
|
496
|
+
// Export all public functions for use in other modules
|
|
268
497
|
module.exports = {
|
|
269
498
|
analyzeFlowProxyProtection,
|
|
270
499
|
handleFlowProxyProtection,
|
|
271
500
|
isFlowProxyProtected,
|
|
272
501
|
getFlowProxyTimeouts,
|
|
273
|
-
waitForTimeout
|
|
502
|
+
waitForTimeout,
|
|
503
|
+
getModuleInfo,
|
|
504
|
+
FLOWPROXY_MODULE_VERSION
|
|
274
505
|
};
|
package/nwss.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// === Network scanner script (nwss.js) v1.0.
|
|
1
|
+
// === Network scanner script (nwss.js) v1.0.45 ===
|
|
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
|
|
@@ -35,7 +35,7 @@ const { navigateWithRedirectHandling, handleRedirectTimeout } = require('./lib/r
|
|
|
35
35
|
const { monitorBrowserHealth, isBrowserHealthy } = require('./lib/browserhealth');
|
|
36
36
|
|
|
37
37
|
// --- Script Configuration & Constants ---
|
|
38
|
-
const VERSION = '1.0.
|
|
38
|
+
const VERSION = '1.0.45'; // Script version
|
|
39
39
|
|
|
40
40
|
// get startTime
|
|
41
41
|
const startTime = Date.now();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fanboynz/network-scanner",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.45",
|
|
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": {
|