@fanboynz/network-scanner 2.0.2 → 2.0.4
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 +9 -1
- package/lib/cloudflare.js +95 -26
- package/nwss.js +125 -18
- package/package.json +1 -1
package/lib/browserhealth.js
CHANGED
|
@@ -211,6 +211,14 @@ async function isPageSafeToClose(page, forceDebug) {
|
|
|
211
211
|
return true; // Already closed
|
|
212
212
|
}
|
|
213
213
|
|
|
214
|
+
// EXTRA SAFETY: Never close pages that might be in injection process
|
|
215
|
+
try {
|
|
216
|
+
const url = page.url();
|
|
217
|
+
if (url && url !== 'about:blank' && Date.now() - (pageCreationTracker.get(page) || 0) < 30000) {
|
|
218
|
+
return false; // Don't close recently created pages (within 30 seconds)
|
|
219
|
+
}
|
|
220
|
+
} catch (err) { /* ignore */ }
|
|
221
|
+
|
|
214
222
|
const usage = pageUsageTracker.get(page);
|
|
215
223
|
if (!usage) {
|
|
216
224
|
// No usage data - assume safe if page exists for a while
|
|
@@ -991,4 +999,4 @@ if (originalPageClose) {
|
|
|
991
999
|
}
|
|
992
1000
|
return originalPageClose.apply(this, args);
|
|
993
1001
|
};
|
|
994
|
-
}
|
|
1002
|
+
}
|
package/lib/cloudflare.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Cloudflare bypass and challenge handling module - Optimized with smart detection and adaptive timeouts
|
|
3
|
+
* Version: 2.6.1 - timeoutId is not defined & race condition fix
|
|
4
|
+
* Version: 2.6.0 - Memory leak fixes and timeout cleanup
|
|
3
5
|
* Version: 2.5.0 - Fix Frame Lifecycle issue, Timing and Race condition
|
|
4
6
|
* Version: 2.4.1 - Bump timeout values
|
|
5
7
|
* Version: 2.4.0 - Fix possible endless loops with retry logic and loop detection
|
|
@@ -16,7 +18,7 @@ const { formatLogMessage } = require('./colorize');
|
|
|
16
18
|
/**
|
|
17
19
|
* Module version information
|
|
18
20
|
*/
|
|
19
|
-
const CLOUDFLARE_MODULE_VERSION = '2.
|
|
21
|
+
const CLOUDFLARE_MODULE_VERSION = '2.6.1';
|
|
20
22
|
|
|
21
23
|
/**
|
|
22
24
|
* Timeout constants for various operations (in milliseconds)
|
|
@@ -133,6 +135,8 @@ class CloudflareDetectionCache {
|
|
|
133
135
|
this.ttl = ttl;
|
|
134
136
|
this.hits = 0;
|
|
135
137
|
this.misses = 0;
|
|
138
|
+
// Prevent memory buildup in long-running processes
|
|
139
|
+
this.cleanupInterval = setInterval(() => this.cleanupExpired(), ttl / 10);
|
|
136
140
|
}
|
|
137
141
|
|
|
138
142
|
getCacheKey(url) {
|
|
@@ -175,6 +179,20 @@ class CloudflareDetectionCache {
|
|
|
175
179
|
}
|
|
176
180
|
}
|
|
177
181
|
|
|
182
|
+
cleanupExpired() {
|
|
183
|
+
const now = Date.now();
|
|
184
|
+
for (const [key, value] of this.cache.entries()) {
|
|
185
|
+
if (now - value.timestamp >= this.ttl) {
|
|
186
|
+
this.cache.delete(key);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
destroy() {
|
|
192
|
+
if (this.cleanupInterval) clearInterval(this.cleanupInterval);
|
|
193
|
+
this.clear();
|
|
194
|
+
}
|
|
195
|
+
|
|
178
196
|
clear() {
|
|
179
197
|
this.cache.clear();
|
|
180
198
|
this.hits = 0;
|
|
@@ -302,24 +320,32 @@ async function safePageEvaluate(page, func, timeout = TIMEOUTS.PAGE_EVALUATION_S
|
|
|
302
320
|
let lastError = null;
|
|
303
321
|
|
|
304
322
|
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
323
|
+
let timeoutId = null;
|
|
324
|
+
|
|
305
325
|
try {
|
|
306
|
-
// Validate page
|
|
326
|
+
// Validate page state before evaluation
|
|
307
327
|
if (page.isClosed()) {
|
|
308
328
|
throw new Error('Page is closed');
|
|
309
329
|
}
|
|
310
330
|
|
|
311
|
-
// Check if main frame is still attached
|
|
312
|
-
const mainFrame = page.mainFrame();
|
|
313
|
-
if (mainFrame.isDetached()) {
|
|
314
|
-
throw new Error('Main frame is detached');
|
|
315
|
-
}
|
|
316
331
|
|
|
317
332
|
const result = await Promise.race([
|
|
318
|
-
page.evaluate(
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
333
|
+
page.evaluate(() => {
|
|
334
|
+
// Validate execution context inside evaluation to prevent race
|
|
335
|
+
if (typeof window === 'undefined' || !document) {
|
|
336
|
+
throw new Error('Execution context invalid');
|
|
337
|
+
}
|
|
338
|
+
return (func)();
|
|
339
|
+
}),
|
|
340
|
+
new Promise((_, reject) => {
|
|
341
|
+
timeoutId = setTimeout(() => reject(new Error('Page evaluation timeout')), timeout);
|
|
342
|
+
})
|
|
322
343
|
]);
|
|
344
|
+
|
|
345
|
+
// Clear timeout if evaluation completed first
|
|
346
|
+
if (timeoutId) {
|
|
347
|
+
clearTimeout(timeoutId);
|
|
348
|
+
}
|
|
323
349
|
|
|
324
350
|
if (forceDebug && attempt > 1) {
|
|
325
351
|
console.log(formatLogMessage('cloudflare', `Page evaluation succeeded on attempt ${attempt}`));
|
|
@@ -327,6 +353,11 @@ async function safePageEvaluate(page, func, timeout = TIMEOUTS.PAGE_EVALUATION_S
|
|
|
327
353
|
|
|
328
354
|
return result;
|
|
329
355
|
} catch (error) {
|
|
356
|
+
// Ensure timeout is cleared on any error
|
|
357
|
+
if (timeoutId) {
|
|
358
|
+
clearTimeout(timeoutId);
|
|
359
|
+
}
|
|
360
|
+
|
|
330
361
|
lastError = error;
|
|
331
362
|
const errorType = categorizeError(error);
|
|
332
363
|
|
|
@@ -396,9 +427,9 @@ async function safeClick(page, selector, timeout = TIMEOUTS.CLICK_TIMEOUT) {
|
|
|
396
427
|
try {
|
|
397
428
|
return await Promise.race([
|
|
398
429
|
page.click(selector, { timeout: timeout }),
|
|
399
|
-
new Promise((_, reject) =>
|
|
400
|
-
setTimeout(() => reject(new Error('Click timeout')), timeout + TIMEOUTS.CLICK_TIMEOUT_BUFFER)
|
|
401
|
-
)
|
|
430
|
+
new Promise((_, reject) => {
|
|
431
|
+
setTimeout(() => reject(new Error('Click timeout')), timeout + TIMEOUTS.CLICK_TIMEOUT_BUFFER);
|
|
432
|
+
})
|
|
402
433
|
]);
|
|
403
434
|
} catch (error) {
|
|
404
435
|
throw new Error(`Click failed: ${error.message}`);
|
|
@@ -412,9 +443,9 @@ async function safeWaitForNavigation(page, timeout = TIMEOUTS.NAVIGATION_TIMEOUT
|
|
|
412
443
|
try {
|
|
413
444
|
return await Promise.race([
|
|
414
445
|
page.waitForNavigation({ waitUntil: 'domcontentloaded', timeout: timeout }),
|
|
415
|
-
new Promise((_, reject) =>
|
|
416
|
-
setTimeout(() => reject(new Error('Navigation timeout')), timeout + TIMEOUTS.NAVIGATION_TIMEOUT_BUFFER)
|
|
417
|
-
)
|
|
446
|
+
new Promise((_, reject) => {
|
|
447
|
+
setTimeout(() => reject(new Error('Navigation timeout')), timeout + TIMEOUTS.NAVIGATION_TIMEOUT_BUFFER);
|
|
448
|
+
})
|
|
418
449
|
]);
|
|
419
450
|
} catch (error) {
|
|
420
451
|
console.warn(formatLogMessage('cloudflare', `Navigation wait failed: ${error.message}`));
|
|
@@ -933,16 +964,29 @@ async function attemptChallengeSolveWithTimeout(page, currentUrl, challengeInfo,
|
|
|
933
964
|
error: null,
|
|
934
965
|
method: null
|
|
935
966
|
};
|
|
967
|
+
|
|
968
|
+
let timeoutId = null;
|
|
936
969
|
|
|
937
970
|
try {
|
|
971
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
972
|
+
timeoutId = setTimeout(() => reject(new Error('Challenge solving timeout')), FAST_TIMEOUTS.CHALLENGE_SOLVING);
|
|
973
|
+
});
|
|
938
974
|
// Reduced timeout for challenge solving
|
|
939
|
-
|
|
975
|
+
const finalResult = await Promise.race([
|
|
940
976
|
attemptChallengeSolve(page, currentUrl, challengeInfo, forceDebug),
|
|
941
|
-
|
|
942
|
-
setTimeout(() => reject(new Error('Challenge solving timeout')), FAST_TIMEOUTS.CHALLENGE_SOLVING)
|
|
943
|
-
)
|
|
977
|
+
timeoutPromise
|
|
944
978
|
]);
|
|
979
|
+
// Clear timeout if operation completed first
|
|
980
|
+
if (timeoutId) {
|
|
981
|
+
clearTimeout(timeoutId);
|
|
982
|
+
}
|
|
983
|
+
return finalResult;
|
|
984
|
+
|
|
945
985
|
} catch (error) {
|
|
986
|
+
// Clear timeout on error
|
|
987
|
+
if (timeoutId) {
|
|
988
|
+
clearTimeout(timeoutId);
|
|
989
|
+
}
|
|
946
990
|
result.error = `Challenge solving timed out: ${error.message}`;
|
|
947
991
|
if (forceDebug) console.log(formatLogMessage('cloudflare', `Challenge solving timeout for ${currentUrl}`));
|
|
948
992
|
return result;
|
|
@@ -1164,8 +1208,14 @@ async function waitForJSChallengeCompletion(page, forceDebug = false) {
|
|
|
1164
1208
|
error: null
|
|
1165
1209
|
};
|
|
1166
1210
|
|
|
1211
|
+
let timeoutId = null;
|
|
1212
|
+
|
|
1167
1213
|
try {
|
|
1168
1214
|
if (forceDebug) console.log(formatLogMessage('cloudflare', `Waiting for JS challenge completion`));
|
|
1215
|
+
|
|
1216
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
1217
|
+
timeoutId = setTimeout(() => reject(new Error('JS challenge timeout')), TIMEOUTS.JS_CHALLENGE_BUFFER);
|
|
1218
|
+
});
|
|
1169
1219
|
|
|
1170
1220
|
// Reduced timeout for JS challenge completion
|
|
1171
1221
|
await Promise.race([
|
|
@@ -1178,14 +1228,22 @@ async function waitForJSChallengeCompletion(page, forceDebug = false) {
|
|
|
1178
1228
|
},
|
|
1179
1229
|
{ timeout: FAST_TIMEOUTS.JS_CHALLENGE }
|
|
1180
1230
|
),
|
|
1181
|
-
|
|
1182
|
-
setTimeout(() => reject(new Error('JS challenge timeout')), TIMEOUTS.JS_CHALLENGE_BUFFER)
|
|
1183
|
-
)
|
|
1231
|
+
timeoutPromise
|
|
1184
1232
|
]);
|
|
1233
|
+
|
|
1234
|
+
// Clear timeout if completion detected first
|
|
1235
|
+
if (timeoutId) {
|
|
1236
|
+
clearTimeout(timeoutId);
|
|
1237
|
+
}
|
|
1185
1238
|
|
|
1186
1239
|
result.success = true;
|
|
1187
1240
|
if (forceDebug) console.log(formatLogMessage('cloudflare', `JS challenge completed automatically`));
|
|
1188
1241
|
} catch (error) {
|
|
1242
|
+
// Clear timeout on error
|
|
1243
|
+
if (timeoutId) {
|
|
1244
|
+
clearTimeout(timeoutId);
|
|
1245
|
+
}
|
|
1246
|
+
|
|
1189
1247
|
result.error = `JS challenge timeout: ${error.message}`;
|
|
1190
1248
|
if (forceDebug) console.log(formatLogMessage('cloudflare', `JS challenge wait failed: ${error.message}`));
|
|
1191
1249
|
}
|
|
@@ -1752,6 +1810,15 @@ function clearDetectionCache() {
|
|
|
1752
1810
|
detectionCache.clear();
|
|
1753
1811
|
}
|
|
1754
1812
|
|
|
1813
|
+
/**
|
|
1814
|
+
* Cleanup function to prevent memory leaks in long-running processes
|
|
1815
|
+
*/
|
|
1816
|
+
function cleanup() {
|
|
1817
|
+
if (detectionCache) {
|
|
1818
|
+
detectionCache.destroy();
|
|
1819
|
+
}
|
|
1820
|
+
}
|
|
1821
|
+
|
|
1755
1822
|
module.exports = {
|
|
1756
1823
|
analyzeCloudflareChallenge,
|
|
1757
1824
|
handlePhishingWarning,
|
|
@@ -1775,5 +1842,7 @@ module.exports = {
|
|
|
1775
1842
|
ERROR_TYPES,
|
|
1776
1843
|
RETRY_CONFIG,
|
|
1777
1844
|
getRetryConfig,
|
|
1778
|
-
detectChallengeLoop
|
|
1779
|
-
|
|
1845
|
+
detectChallengeLoop,
|
|
1846
|
+
// Memory management
|
|
1847
|
+
cleanup
|
|
1848
|
+
};
|
package/nwss.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// === Network scanner script (nwss.js) v2.0.
|
|
1
|
+
// === Network scanner script (nwss.js) v2.0.4 ===
|
|
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
|
|
@@ -127,7 +127,7 @@ const { navigateWithRedirectHandling, handleRedirectTimeout } = require('./lib/r
|
|
|
127
127
|
const { monitorBrowserHealth, isBrowserHealthy, isQuicklyResponsive, performGroupWindowCleanup, performRealtimeWindowCleanup, trackPageForRealtime, updatePageUsage } = require('./lib/browserhealth');
|
|
128
128
|
|
|
129
129
|
// --- Script Configuration & Constants ---
|
|
130
|
-
const VERSION = '2.0.
|
|
130
|
+
const VERSION = '2.0.4'; // Script version
|
|
131
131
|
|
|
132
132
|
// get startTime
|
|
133
133
|
const startTime = Date.now();
|
|
@@ -1059,17 +1059,48 @@ function matchesIgnoreDomain(domain, ignorePatterns) {
|
|
|
1059
1059
|
}
|
|
1060
1060
|
|
|
1061
1061
|
function setupFrameHandling(page, forceDebug) {
|
|
1062
|
+
// Track active frames and clear on navigation to prevent detached frame access
|
|
1063
|
+
let activeFrames = new Set();
|
|
1064
|
+
|
|
1065
|
+
// Clear frame tracking on navigation to prevent stale references
|
|
1066
|
+
page.on('framenavigated', (frame) => {
|
|
1067
|
+
if (frame === page.mainFrame()) {
|
|
1068
|
+
// Main frame navigated - clear all tracked frames
|
|
1069
|
+
activeFrames.clear();
|
|
1070
|
+
}
|
|
1071
|
+
});
|
|
1072
|
+
|
|
1062
1073
|
// Handle frame creation with error suppression
|
|
1063
1074
|
page.on('frameattached', async (frame) => {
|
|
1064
|
-
// Enhanced frame handling
|
|
1075
|
+
// Enhanced frame handling with detached frame protection
|
|
1065
1076
|
try {
|
|
1066
|
-
//
|
|
1067
|
-
if (frame
|
|
1077
|
+
// Multiple checks for frame validity to prevent detached frame errors
|
|
1078
|
+
if (!frame) {
|
|
1068
1079
|
if (forceDebug) {
|
|
1069
|
-
console.log(formatLogMessage('debug', `Skipping
|
|
1080
|
+
console.log(formatLogMessage('debug', `Skipping null frame`));
|
|
1070
1081
|
}
|
|
1071
1082
|
return;
|
|
1072
1083
|
}
|
|
1084
|
+
|
|
1085
|
+
// Enhanced frame validation with multiple safety checks
|
|
1086
|
+
let frameUrl;
|
|
1087
|
+
try {
|
|
1088
|
+
// Test frame accessibility first
|
|
1089
|
+
frameUrl = frame.url();
|
|
1090
|
+
|
|
1091
|
+
// Check if frame is detached (if method exists)
|
|
1092
|
+
if (frame.isDetached && frame.isDetached()) {
|
|
1093
|
+
if (forceDebug) {
|
|
1094
|
+
console.log(formatLogMessage('debug', `Skipping detached frame`));
|
|
1095
|
+
}
|
|
1096
|
+
return;
|
|
1097
|
+
}
|
|
1098
|
+
} catch (frameAccessError) {
|
|
1099
|
+
// Frame is not accessible (likely detached)
|
|
1100
|
+
return;
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
activeFrames.add(frame);
|
|
1073
1104
|
} catch (detachError) {
|
|
1074
1105
|
// Frame state checking can throw in 23.x, handle gracefully
|
|
1075
1106
|
if (forceDebug) {
|
|
@@ -1079,9 +1110,7 @@ function setupFrameHandling(page, forceDebug) {
|
|
|
1079
1110
|
}
|
|
1080
1111
|
|
|
1081
1112
|
if (frame.parentFrame()) { // Only handle child frames, not main frame
|
|
1082
|
-
try {
|
|
1083
|
-
const frameUrl = frame.url();
|
|
1084
|
-
|
|
1113
|
+
try {
|
|
1085
1114
|
if (forceDebug) {
|
|
1086
1115
|
console.log(formatLogMessage('debug', `New frame attached: ${frameUrl || 'about:blank'}`));
|
|
1087
1116
|
}
|
|
@@ -1139,7 +1168,17 @@ function setupFrameHandling(page, forceDebug) {
|
|
|
1139
1168
|
});
|
|
1140
1169
|
// Handle frame navigations (keep this for monitoring)
|
|
1141
1170
|
page.on('framenavigated', (frame) => {
|
|
1142
|
-
|
|
1171
|
+
let frameUrl;
|
|
1172
|
+
|
|
1173
|
+
// Skip if frame is not in our active set
|
|
1174
|
+
if (!activeFrames.has(frame)) return;
|
|
1175
|
+
|
|
1176
|
+
try {
|
|
1177
|
+
frameUrl = frame.url();
|
|
1178
|
+
} catch (urlErr) {
|
|
1179
|
+
// Frame likely detached during navigation
|
|
1180
|
+
return;
|
|
1181
|
+
}
|
|
1143
1182
|
if (forceDebug &&
|
|
1144
1183
|
frameUrl &&
|
|
1145
1184
|
frameUrl !== 'about:blank' &&
|
|
@@ -1154,8 +1193,18 @@ function setupFrameHandling(page, forceDebug) {
|
|
|
1154
1193
|
|
|
1155
1194
|
// Optional: Handle frame detachment for cleanup
|
|
1156
1195
|
page.on('framedetached', (frame) => {
|
|
1196
|
+
// Remove from active tracking
|
|
1197
|
+
activeFrames.delete(frame);
|
|
1198
|
+
|
|
1199
|
+
// Skip logging if we can't access frame URL
|
|
1200
|
+
let frameUrl;
|
|
1157
1201
|
if (forceDebug) {
|
|
1158
|
-
|
|
1202
|
+
try {
|
|
1203
|
+
frameUrl = frame.url();
|
|
1204
|
+
} catch (urlErr) {
|
|
1205
|
+
// Frame already detached, can't get URL
|
|
1206
|
+
return;
|
|
1207
|
+
}
|
|
1159
1208
|
if (frameUrl &&
|
|
1160
1209
|
frameUrl !== 'about:blank' &&
|
|
1161
1210
|
frameUrl !== 'about:srcdoc' &&
|
|
@@ -1583,6 +1632,11 @@ function setupFrameHandling(page, forceDebug) {
|
|
|
1583
1632
|
const shouldInjectEvalForPage = siteConfig.evaluateOnNewDocument === true || globalEvalOnDoc;
|
|
1584
1633
|
let evalOnDocSuccess = false; // Track injection success for fallback logic
|
|
1585
1634
|
|
|
1635
|
+
// PREVENT realtime cleanup during injection to avoid "Session closed" errors
|
|
1636
|
+
if (shouldInjectEvalForPage && siteConfig.window_cleanup === "realtime") {
|
|
1637
|
+
updatePageUsage(page, true); // Mark page as actively processing BEFORE injection
|
|
1638
|
+
}
|
|
1639
|
+
|
|
1586
1640
|
if (shouldInjectEvalForPage) {
|
|
1587
1641
|
if (forceDebug) {
|
|
1588
1642
|
if (globalEvalOnDoc) {
|
|
@@ -1595,8 +1649,13 @@ function setupFrameHandling(page, forceDebug) {
|
|
|
1595
1649
|
// Strategy 1: Try full injection with health check
|
|
1596
1650
|
let browserResponsive = false;
|
|
1597
1651
|
try {
|
|
1652
|
+
// Check if browser is still connected before attempting health check
|
|
1653
|
+
if (!browserInstance.isConnected()) {
|
|
1654
|
+
throw new Error('Browser not connected');
|
|
1655
|
+
}
|
|
1656
|
+
|
|
1598
1657
|
await Promise.race([
|
|
1599
|
-
browserInstance.
|
|
1658
|
+
browserInstance.pages(), // Simple existence check that doesn't require active session
|
|
1600
1659
|
new Promise((_, reject) =>
|
|
1601
1660
|
setTimeout(() => reject(new Error('Browser health check timeout')), 3000)
|
|
1602
1661
|
)
|
|
@@ -1616,6 +1675,13 @@ function setupFrameHandling(page, forceDebug) {
|
|
|
1616
1675
|
await Promise.race([
|
|
1617
1676
|
// Main injection with all safety checks
|
|
1618
1677
|
page.evaluateOnNewDocument(() => {
|
|
1678
|
+
// Prevent duplicate injections
|
|
1679
|
+
if (window.__nwss_injection_applied) {
|
|
1680
|
+
console.log('[evalOnDoc] Already injected, skipping');
|
|
1681
|
+
return;
|
|
1682
|
+
}
|
|
1683
|
+
window.__nwss_injection_applied = true;
|
|
1684
|
+
|
|
1619
1685
|
// Wrap everything in try-catch to prevent page crashes
|
|
1620
1686
|
try {
|
|
1621
1687
|
// Add timeout check within the injection
|
|
@@ -1719,7 +1785,18 @@ function setupFrameHandling(page, forceDebug) {
|
|
|
1719
1785
|
// Strategy 3: Fallback - Try minimal injection (just fetch monitoring)
|
|
1720
1786
|
try {
|
|
1721
1787
|
await Promise.race([
|
|
1722
|
-
|
|
1788
|
+
(async () => {
|
|
1789
|
+
// Validate page state before minimal injection
|
|
1790
|
+
if (!page || page.isClosed()) {
|
|
1791
|
+
throw new Error('Page is closed');
|
|
1792
|
+
}
|
|
1793
|
+
|
|
1794
|
+
const pageUrl = await page.url().catch(() => 'about:blank');
|
|
1795
|
+
if (pageUrl === 'about:blank') {
|
|
1796
|
+
throw new Error('Cannot inject on about:blank');
|
|
1797
|
+
}
|
|
1798
|
+
|
|
1799
|
+
return page.evaluateOnNewDocument(() => {
|
|
1723
1800
|
// Minimal injection - just fetch monitoring
|
|
1724
1801
|
if (window.fetch) {
|
|
1725
1802
|
const originalFetch = window.fetch;
|
|
@@ -1732,7 +1809,8 @@ function setupFrameHandling(page, forceDebug) {
|
|
|
1732
1809
|
}
|
|
1733
1810
|
};
|
|
1734
1811
|
}
|
|
1735
|
-
|
|
1812
|
+
});
|
|
1813
|
+
})(),
|
|
1736
1814
|
new Promise((_, reject) =>
|
|
1737
1815
|
setTimeout(() => reject(new Error('Minimal injection timeout')), 3000)
|
|
1738
1816
|
)
|
|
@@ -1760,6 +1838,10 @@ function setupFrameHandling(page, forceDebug) {
|
|
|
1760
1838
|
if (!evalOnDocSuccess) {
|
|
1761
1839
|
console.warn(formatLogMessage('warn', `[evalOnDoc] All injection strategies failed for ${currentUrl} - continuing with standard request monitoring only`));
|
|
1762
1840
|
}
|
|
1841
|
+
// Allow realtime cleanup to proceed after injection completes
|
|
1842
|
+
if (shouldInjectEvalForPage && siteConfig.window_cleanup === "realtime") {
|
|
1843
|
+
updatePageUsage(page, false); // Mark page as idle after injection
|
|
1844
|
+
}
|
|
1763
1845
|
}
|
|
1764
1846
|
// --- END: evaluateOnNewDocument for Fetch/XHR Interception ---
|
|
1765
1847
|
|
|
@@ -2161,8 +2243,15 @@ function setupFrameHandling(page, forceDebug) {
|
|
|
2161
2243
|
// This prevents redirect destinations from being marked as third-party
|
|
2162
2244
|
const isFirstParty = checkedRootDomain && firstPartyDomains.has(checkedRootDomain);
|
|
2163
2245
|
|
|
2164
|
-
// Block infinite iframe loops
|
|
2165
|
-
const frameUrl =
|
|
2246
|
+
// Block infinite iframe loops - safely access frame URL
|
|
2247
|
+
const frameUrl = (() => {
|
|
2248
|
+
try {
|
|
2249
|
+
const frame = request.frame();
|
|
2250
|
+
return frame ? frame.url() : '';
|
|
2251
|
+
} catch (err) {
|
|
2252
|
+
return '';
|
|
2253
|
+
}
|
|
2254
|
+
})();
|
|
2166
2255
|
if (frameUrl && frameUrl.includes('creative.dmzjmp.com') &&
|
|
2167
2256
|
request.url().includes('go.dmzjmp.com/api/models')) {
|
|
2168
2257
|
if (forceDebug) {
|
|
@@ -2174,8 +2263,18 @@ function setupFrameHandling(page, forceDebug) {
|
|
|
2174
2263
|
|
|
2175
2264
|
// Enhanced debug logging to show which frame the request came from
|
|
2176
2265
|
if (forceDebug) {
|
|
2177
|
-
|
|
2178
|
-
|
|
2266
|
+
let frameUrl = 'unknown-frame';
|
|
2267
|
+
let isMainFrame = false;
|
|
2268
|
+
|
|
2269
|
+
try {
|
|
2270
|
+
const frame = request.frame();
|
|
2271
|
+
if (frame) {
|
|
2272
|
+
frameUrl = frame.url();
|
|
2273
|
+
isMainFrame = frame === page.mainFrame();
|
|
2274
|
+
}
|
|
2275
|
+
} catch (frameErr) {
|
|
2276
|
+
frameUrl = 'detached-frame';
|
|
2277
|
+
}
|
|
2179
2278
|
console.log(formatLogMessage('debug', `${messageColors.highlight('[req]')}[frame: ${isMainFrame ? 'main' : 'iframe'}] ${frameUrl} → ${request.url()}`));
|
|
2180
2279
|
}
|
|
2181
2280
|
|
|
@@ -2997,6 +3096,14 @@ function setupFrameHandling(page, forceDebug) {
|
|
|
2997
3096
|
}
|
|
2998
3097
|
|
|
2999
3098
|
for (let i = 1; i <= totalReloads; i++) {
|
|
3099
|
+
// Clear any stale frame references before reload
|
|
3100
|
+
try {
|
|
3101
|
+
await page.evaluate(() => {
|
|
3102
|
+
// Force cleanup of any pending frame operations
|
|
3103
|
+
if (window.requestIdleCallback) window.requestIdleCallback(() => {});
|
|
3104
|
+
});
|
|
3105
|
+
} catch (cleanupErr) { /* ignore */ }
|
|
3106
|
+
|
|
3000
3107
|
if (siteConfig.clear_sitedata === true) {
|
|
3001
3108
|
try {
|
|
3002
3109
|
let reloadClearSession = null;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fanboynz/network-scanner",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.4",
|
|
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": {
|