@fanboynz/network-scanner 1.0.90 → 1.0.92
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/interaction.js +40 -18
- package/nwss.js +156 -12
- package/package.json +1 -1
- package/lib/evaldocument.js +0 -193
package/lib/interaction.js
CHANGED
|
@@ -84,7 +84,7 @@ const MOUSE_MOVEMENT = {
|
|
|
84
84
|
MAX_DELAY: 25, // Maximum milliseconds between movement steps
|
|
85
85
|
DEFAULT_CURVE: 0.2, // Default curve intensity (reduced for performance)
|
|
86
86
|
DEFAULT_JITTER: 2, // Default random jitter in pixels
|
|
87
|
-
DISTANCE_STEP_RATIO:
|
|
87
|
+
DISTANCE_STEP_RATIO: 200, // CRITICAL: 4x increase to drastically reduce steps
|
|
88
88
|
CURVE_INTENSITY_RATIO: 0.01 // Multiplier for curve calculation
|
|
89
89
|
};
|
|
90
90
|
|
|
@@ -339,19 +339,19 @@ async function humanLikeMouseMove(page, fromX, fromY, toX, toY, options = {}) {
|
|
|
339
339
|
// FIXED: More aggressive step capping to prevent excessive delays
|
|
340
340
|
let actualSteps;
|
|
341
341
|
if (options.steps) {
|
|
342
|
-
//
|
|
343
|
-
actualSteps = Math.min(options.steps,
|
|
342
|
+
// CRITICAL: Much lower step cap to prevent timeouts
|
|
343
|
+
actualSteps = Math.min(options.steps, 8); // Was MAX_STEPS (30), now max 8
|
|
344
344
|
} else {
|
|
345
345
|
// Calculate steps based on distance with strict limits
|
|
346
346
|
const calculatedSteps = Math.floor(distance / MOUSE_MOVEMENT.DISTANCE_STEP_RATIO);
|
|
347
347
|
actualSteps = Math.max(
|
|
348
|
-
|
|
349
|
-
Math.min(calculatedSteps,
|
|
348
|
+
2, // Min 2 steps instead of 5
|
|
349
|
+
Math.min(calculatedSteps, 6) // Max 6 steps instead of 30
|
|
350
350
|
);
|
|
351
351
|
}
|
|
352
352
|
|
|
353
|
-
//
|
|
354
|
-
const maxTotalTime =
|
|
353
|
+
// CRITICAL: Emergency timeout - never exceed 300ms for mouse movement
|
|
354
|
+
const maxTotalTime = 300; // 300ms maximum (was 2000ms)
|
|
355
355
|
const estimatedTime = actualSteps * maxDelay;
|
|
356
356
|
if (estimatedTime > maxTotalTime) {
|
|
357
357
|
actualSteps = Math.floor(maxTotalTime / maxDelay);
|
|
@@ -787,6 +787,14 @@ async function performPageInteraction(page, currentUrl, options = {}, forceDebug
|
|
|
787
787
|
} = options;
|
|
788
788
|
|
|
789
789
|
try {
|
|
790
|
+
// CRITICAL: Emergency timeout wrapper for entire interaction
|
|
791
|
+
const MAX_INTERACTION_TIME = 15000; // 15 seconds absolute maximum
|
|
792
|
+
const interactionStartTime = Date.now();
|
|
793
|
+
|
|
794
|
+
const checkTimeout = () => {
|
|
795
|
+
return Date.now() - interactionStartTime > MAX_INTERACTION_TIME;
|
|
796
|
+
};
|
|
797
|
+
|
|
790
798
|
// Validate page state before starting interaction
|
|
791
799
|
try {
|
|
792
800
|
// Optimized timeout calculation - shorter for better performance
|
|
@@ -845,20 +853,26 @@ async function performPageInteraction(page, currentUrl, options = {}, forceDebug
|
|
|
845
853
|
return; // Exit gracefully if mouse operations fail
|
|
846
854
|
}
|
|
847
855
|
|
|
848
|
-
|
|
856
|
+
|
|
849
857
|
const totalDuration = duration * settings.pauseMultiplier;
|
|
850
|
-
|
|
858
|
+
// CRITICAL: Cap action intervals to prevent long waits
|
|
859
|
+
const baseInterval = totalDuration / (actualMovements + (includeScrolling ? settings.scrolls : 0));
|
|
860
|
+
const actionInterval = Math.min(baseInterval, 100); // Never wait more than 100ms
|
|
861
|
+
|
|
862
|
+
// Start timing ONLY the actual interaction operations
|
|
863
|
+
const actualInteractionStartTime = Date.now();
|
|
851
864
|
|
|
852
865
|
// Perform mouse movements
|
|
853
866
|
for (let i = 0; i < actualMovements; i++) {
|
|
867
|
+
if (checkTimeout()) break; // Emergency timeout check
|
|
854
868
|
const targetPos = generateRandomCoordinates(maxX, maxY, {
|
|
855
869
|
avoidCenter: i % 2 === 0,
|
|
856
870
|
preferEdges: i % 3 === 0
|
|
857
871
|
});
|
|
858
872
|
|
|
859
873
|
await humanLikeMouseMove(page, currentPos.x, currentPos.y, targetPos.x, targetPos.y, {
|
|
860
|
-
steps:
|
|
861
|
-
curve: 0.
|
|
874
|
+
steps: 3 + Math.floor(Math.random() * 4), // CRITICAL: 3-6 steps (was 8-18)
|
|
875
|
+
curve: 0.05 + Math.random() * 0.05, // CRITICAL: Minimal curve
|
|
862
876
|
jitter: 1 + Math.random() * 2
|
|
863
877
|
});
|
|
864
878
|
|
|
@@ -866,24 +880,25 @@ async function performPageInteraction(page, currentUrl, options = {}, forceDebug
|
|
|
866
880
|
|
|
867
881
|
// Occasional pause
|
|
868
882
|
if (Math.random() < PROBABILITIES.PAUSE_CHANCE) {
|
|
869
|
-
await fastTimeout(
|
|
883
|
+
await fastTimeout(25 + Math.random() * 50); // CRITICAL: Much shorter pauses
|
|
870
884
|
}
|
|
871
885
|
|
|
872
886
|
// Time-based spacing
|
|
873
|
-
await fastTimeout(actionInterval);
|
|
887
|
+
await fastTimeout(Math.min(actionInterval, 50)); // CRITICAL: Cap at 50ms
|
|
874
888
|
}
|
|
875
889
|
|
|
876
890
|
// Scrolling simulation
|
|
877
891
|
if (includeScrolling) {
|
|
878
892
|
for (let i = 0; i < settings.scrolls; i++) {
|
|
893
|
+
if (checkTimeout()) break; // Emergency timeout check
|
|
879
894
|
const direction = Math.random() < PROBABILITIES.SCROLL_DOWN_BIAS ? 'down' : 'up';
|
|
880
895
|
await simulateScrolling(page, {
|
|
881
896
|
direction,
|
|
882
|
-
amount:
|
|
883
|
-
smoothness:
|
|
897
|
+
amount: 1 + Math.floor(Math.random() * 2), // CRITICAL: Less scrolling
|
|
898
|
+
smoothness: 1 + Math.floor(Math.random() * 2) // CRITICAL: Much less smooth
|
|
884
899
|
});
|
|
885
900
|
|
|
886
|
-
await fastTimeout(actionInterval);
|
|
901
|
+
await fastTimeout(Math.min(actionInterval, 100)); // CRITICAL: Cap intervals
|
|
887
902
|
}
|
|
888
903
|
}
|
|
889
904
|
|
|
@@ -912,9 +927,16 @@ async function performPageInteraction(page, currentUrl, options = {}, forceDebug
|
|
|
912
927
|
// Silently handle hover failures - not critical
|
|
913
928
|
}
|
|
914
929
|
|
|
915
|
-
|
|
930
|
+
// End timing ONLY after actual interaction operations complete
|
|
931
|
+
const interactionElapsedTime = Date.now() - actualInteractionStartTime
|
|
932
|
+
|
|
933
|
+
// CRITICAL: Warn about slow interactions
|
|
934
|
+
if (interactionElapsedTime > 8000) {
|
|
935
|
+
console.warn(`[interaction] WARNING: Interaction took ${interactionElapsedTime}ms for ${currentUrl}`);
|
|
936
|
+
}
|
|
937
|
+
|
|
916
938
|
if (forceDebug) {
|
|
917
|
-
console.log(`[interaction] Completed interaction simulation in ${
|
|
939
|
+
console.log(`[interaction] Completed interaction simulation in ${interactionElapsedTime}ms (${actualMovements} movements, ${includeScrolling ? settings.scrolls : 0} scrolls)`);
|
|
918
940
|
}
|
|
919
941
|
|
|
920
942
|
} catch (interactionErr) {
|
package/nwss.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// === Network scanner script (nwss.js) v1.0.
|
|
1
|
+
// === Network scanner script (nwss.js) v1.0.91 ===
|
|
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
|
|
@@ -45,9 +45,6 @@ const { createGlobalHelpers, getTotalDomainsSkipped, getDetectedDomainsCount } =
|
|
|
45
45
|
const { createSmartCache } = require('./lib/smart-cache'); // Smart cache system
|
|
46
46
|
const { clearPersistentCache } = require('./lib/smart-cache');
|
|
47
47
|
|
|
48
|
-
// Evaluate on new document functionality
|
|
49
|
-
const { applyEvaluateOnNewDocument } = require('./lib/evaldocument');
|
|
50
|
-
|
|
51
48
|
// Fast setTimeout helper for Puppeteer 22.x compatibility
|
|
52
49
|
// Uses standard Promise constructor for better performance than node:timers/promises
|
|
53
50
|
function fastTimeout(ms) {
|
|
@@ -128,7 +125,7 @@ const { navigateWithRedirectHandling, handleRedirectTimeout } = require('./lib/r
|
|
|
128
125
|
const { monitorBrowserHealth, isBrowserHealthy, isQuicklyResponsive } = require('./lib/browserhealth');
|
|
129
126
|
|
|
130
127
|
// --- Script Configuration & Constants ---
|
|
131
|
-
const VERSION = '1.0.
|
|
128
|
+
const VERSION = '1.0.91'; // Script version
|
|
132
129
|
|
|
133
130
|
// get startTime
|
|
134
131
|
const startTime = Date.now();
|
|
@@ -1555,9 +1552,158 @@ function setupFrameHandling(page, forceDebug) {
|
|
|
1555
1552
|
|
|
1556
1553
|
// --- START: evaluateOnNewDocument for Fetch/XHR Interception (Moved and Fixed) ---
|
|
1557
1554
|
// This script is injected if --eval-on-doc is used or siteConfig.evaluateOnNewDocument is true.
|
|
1558
|
-
const
|
|
1559
|
-
|
|
1560
|
-
|
|
1555
|
+
const shouldInjectEvalForPage = siteConfig.evaluateOnNewDocument === true || globalEvalOnDoc;
|
|
1556
|
+
let evalOnDocSuccess = false; // Track injection success for fallback logic
|
|
1557
|
+
|
|
1558
|
+
if (shouldInjectEvalForPage) {
|
|
1559
|
+
if (forceDebug) {
|
|
1560
|
+
if (globalEvalOnDoc) {
|
|
1561
|
+
console.log(formatLogMessage('debug', `[evalOnDoc] Global Fetch/XHR interception enabled, applying to: ${currentUrl}`));
|
|
1562
|
+
} else { // siteConfig.evaluateOnNewDocument must be true
|
|
1563
|
+
console.log(formatLogMessage('debug', `[evalOnDoc] Site-specific Fetch/XHR interception enabled for: ${currentUrl}`));
|
|
1564
|
+
}
|
|
1565
|
+
}
|
|
1566
|
+
|
|
1567
|
+
// Strategy 1: Try full injection with health check
|
|
1568
|
+
let browserResponsive = false;
|
|
1569
|
+
try {
|
|
1570
|
+
await Promise.race([
|
|
1571
|
+
browserInstance.version(), // Quick responsiveness test
|
|
1572
|
+
new Promise((_, reject) =>
|
|
1573
|
+
setTimeout(() => reject(new Error('Browser health check timeout')), 3000)
|
|
1574
|
+
)
|
|
1575
|
+
]);
|
|
1576
|
+
browserResponsive = true;
|
|
1577
|
+
} catch (healthErr) {
|
|
1578
|
+
if (forceDebug) {
|
|
1579
|
+
console.log(formatLogMessage('debug', `[evalOnDoc] Browser health check failed: ${healthErr.message}`));
|
|
1580
|
+
}
|
|
1581
|
+
browserResponsive = false;
|
|
1582
|
+
}
|
|
1583
|
+
|
|
1584
|
+
// Strategy 2: Try injection with reduced complexity if browser is responsive
|
|
1585
|
+
if (browserResponsive) {
|
|
1586
|
+
try {
|
|
1587
|
+
await Promise.race([
|
|
1588
|
+
page.evaluateOnNewDocument(() => {
|
|
1589
|
+
// Prevent infinite reload loops
|
|
1590
|
+
let reloadCount = 0;
|
|
1591
|
+
const MAX_RELOADS = 2;
|
|
1592
|
+
const originalReload = window.location.reload;
|
|
1593
|
+
const originalReplace = window.location.replace;
|
|
1594
|
+
const originalAssign = window.location.assign;
|
|
1595
|
+
|
|
1596
|
+
window.location.reload = function() {
|
|
1597
|
+
if (++reloadCount > MAX_RELOADS) {
|
|
1598
|
+
console.log('[loop-protection] Blocked excessive reload attempt');
|
|
1599
|
+
return;
|
|
1600
|
+
}
|
|
1601
|
+
return originalReload.apply(this, arguments);
|
|
1602
|
+
};
|
|
1603
|
+
|
|
1604
|
+
// Also protect against location.replace/assign to same URL
|
|
1605
|
+
const currentHref = window.location.href;
|
|
1606
|
+
window.location.replace = function(url) {
|
|
1607
|
+
if (url === currentHref && ++reloadCount > MAX_RELOADS) {
|
|
1608
|
+
console.log('[loop-protection] Blocked same-page replace attempt');
|
|
1609
|
+
return;
|
|
1610
|
+
}
|
|
1611
|
+
return originalReplace.apply(this, arguments);
|
|
1612
|
+
};
|
|
1613
|
+
|
|
1614
|
+
// This script intercepts and logs Fetch and XHR requests
|
|
1615
|
+
// from within the page context at the earliest possible moment.
|
|
1616
|
+
const originalFetch = window.fetch;
|
|
1617
|
+
window.fetch = (...args) => {
|
|
1618
|
+
try {
|
|
1619
|
+
console.log('[evalOnDoc][fetch]', args[0]); // Log fetch requests
|
|
1620
|
+
const fetchPromise = originalFetch.apply(this, args);
|
|
1621
|
+
|
|
1622
|
+
// Add network error handling to prevent page errors
|
|
1623
|
+
return fetchPromise.catch(fetchErr => {
|
|
1624
|
+
console.log('[evalOnDoc][fetch-error]', args[0], fetchErr.message);
|
|
1625
|
+
throw fetchErr; // Re-throw to maintain normal error flow
|
|
1626
|
+
});
|
|
1627
|
+
} catch (fetchWrapperErr) {
|
|
1628
|
+
console.log('[evalOnDoc][fetch-wrapper-error]', fetchWrapperErr.message);
|
|
1629
|
+
return originalFetch.apply(this, args);
|
|
1630
|
+
}
|
|
1631
|
+
};
|
|
1632
|
+
|
|
1633
|
+
const originalXHROpen = XMLHttpRequest.prototype.open;
|
|
1634
|
+
XMLHttpRequest.prototype.open = function (method, xhrUrl) {
|
|
1635
|
+
try {
|
|
1636
|
+
console.log('[evalOnDoc][xhr]', xhrUrl); // Log XHR requests
|
|
1637
|
+
|
|
1638
|
+
// Add error handling for XHR
|
|
1639
|
+
this.addEventListener('error', function(event) {
|
|
1640
|
+
console.log('[evalOnDoc][xhr-error]', xhrUrl, 'Network error occurred');
|
|
1641
|
+
});
|
|
1642
|
+
|
|
1643
|
+
return originalXHROpen.apply(this, arguments);
|
|
1644
|
+
} catch (xhrOpenErr) {
|
|
1645
|
+
console.log('[evalOnDoc][xhr-open-error]', xhrOpenErr.message);
|
|
1646
|
+
return originalXHROpen.apply(this, arguments);
|
|
1647
|
+
}
|
|
1648
|
+
};
|
|
1649
|
+
}),
|
|
1650
|
+
new Promise((_, reject) =>
|
|
1651
|
+
setTimeout(() => reject(new Error('Injection timeout')), 8000)
|
|
1652
|
+
)
|
|
1653
|
+
]);
|
|
1654
|
+
evalOnDocSuccess = true;
|
|
1655
|
+
if (forceDebug) {
|
|
1656
|
+
console.log(formatLogMessage('debug', `[evalOnDoc] Full injection successful for ${currentUrl}`));
|
|
1657
|
+
}
|
|
1658
|
+
} catch (fullInjectionErr) {
|
|
1659
|
+
if (forceDebug) {
|
|
1660
|
+
console.log(formatLogMessage('debug', `[evalOnDoc] Full injection failed: ${fullInjectionErr.message}, trying simplified fallback`));
|
|
1661
|
+
}
|
|
1662
|
+
|
|
1663
|
+
// Strategy 3: Fallback - Try minimal injection (just fetch monitoring)
|
|
1664
|
+
try {
|
|
1665
|
+
await Promise.race([
|
|
1666
|
+
page.evaluateOnNewDocument(() => {
|
|
1667
|
+
// Minimal injection - just fetch monitoring
|
|
1668
|
+
if (window.fetch) {
|
|
1669
|
+
const originalFetch = window.fetch;
|
|
1670
|
+
window.fetch = (...args) => {
|
|
1671
|
+
try {
|
|
1672
|
+
console.log('[evalOnDoc][fetch-minimal]', args[0]);
|
|
1673
|
+
return originalFetch.apply(this, args);
|
|
1674
|
+
} catch (err) {
|
|
1675
|
+
return originalFetch.apply(this, args);
|
|
1676
|
+
}
|
|
1677
|
+
};
|
|
1678
|
+
}
|
|
1679
|
+
}),
|
|
1680
|
+
new Promise((_, reject) =>
|
|
1681
|
+
setTimeout(() => reject(new Error('Minimal injection timeout')), 3000)
|
|
1682
|
+
)
|
|
1683
|
+
]);
|
|
1684
|
+
evalOnDocSuccess = true;
|
|
1685
|
+
if (forceDebug) {
|
|
1686
|
+
console.log(formatLogMessage('debug', `[evalOnDoc] Minimal injection successful for ${currentUrl}`));
|
|
1687
|
+
}
|
|
1688
|
+
} catch (minimalInjectionErr) {
|
|
1689
|
+
if (forceDebug) {
|
|
1690
|
+
console.log(formatLogMessage('debug', `[evalOnDoc] Minimal injection also failed: ${minimalInjectionErr.message}`));
|
|
1691
|
+
}
|
|
1692
|
+
evalOnDocSuccess = false;
|
|
1693
|
+
}
|
|
1694
|
+
}
|
|
1695
|
+
} else {
|
|
1696
|
+
if (forceDebug) {
|
|
1697
|
+
console.log(formatLogMessage('debug', `[evalOnDoc] Browser unresponsive, skipping injection for ${currentUrl}`));
|
|
1698
|
+
}
|
|
1699
|
+
evalOnDocSuccess = false;
|
|
1700
|
+
}
|
|
1701
|
+
|
|
1702
|
+
// Final status logging
|
|
1703
|
+
if (!evalOnDocSuccess) {
|
|
1704
|
+
console.warn(formatLogMessage('warn', `[evalOnDoc] All injection strategies failed for ${currentUrl} - continuing with standard request monitoring only`));
|
|
1705
|
+
}
|
|
1706
|
+
}
|
|
1561
1707
|
// --- END: evaluateOnNewDocument for Fetch/XHR Interception ---
|
|
1562
1708
|
|
|
1563
1709
|
// --- CSS Element Blocking Setup ---
|
|
@@ -1608,7 +1754,7 @@ function setupFrameHandling(page, forceDebug) {
|
|
|
1608
1754
|
await Promise.race([
|
|
1609
1755
|
page.setRequestInterception(true),
|
|
1610
1756
|
new Promise((_, reject) =>
|
|
1611
|
-
setTimeout(() => reject(new Error('Network.enable timeout')),
|
|
1757
|
+
setTimeout(() => reject(new Error('Network.enable timeout')), 10000)
|
|
1612
1758
|
)
|
|
1613
1759
|
]);
|
|
1614
1760
|
|
|
@@ -1618,9 +1764,7 @@ function setupFrameHandling(page, forceDebug) {
|
|
|
1618
1764
|
} catch (networkErr) {
|
|
1619
1765
|
if (networkErr.message.includes('timed out') ||
|
|
1620
1766
|
networkErr.message.includes('Network.enable') ||
|
|
1621
|
-
networkErr.message.includes('timeout')
|
|
1622
|
-
networkErr.constructor.name === 'ProtocolError' ||
|
|
1623
|
-
networkErr.name === 'ProtocolError') {
|
|
1767
|
+
networkErr.message.includes('timeout')) {
|
|
1624
1768
|
console.warn(formatLogMessage('warn', `Network setup failed for ${currentUrl}: ${networkErr.message} - triggering browser restart`));
|
|
1625
1769
|
return {
|
|
1626
1770
|
url: currentUrl,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fanboynz/network-scanner",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.92",
|
|
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": {
|
package/lib/evaldocument.js
DELETED
|
@@ -1,193 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Module for handling evaluateOnNewDocument functionality
|
|
3
|
-
* Provides Fetch/XHR interception and page protection mechanisms
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Applies evaluateOnNewDocument script injection to a page
|
|
8
|
-
* @param {import('puppeteer').Page} page - Puppeteer page instance
|
|
9
|
-
* @param {string} currentUrl - Current URL being processed
|
|
10
|
-
* @param {Object} siteConfig - Site configuration
|
|
11
|
-
* @param {boolean} globalEvalOnDoc - Global eval-on-doc flag
|
|
12
|
-
* @param {boolean} forceDebug - Debug logging flag
|
|
13
|
-
* @param {Function} formatLogMessage - Log formatting function
|
|
14
|
-
* @returns {Promise<boolean>} Success status of injection
|
|
15
|
-
*/
|
|
16
|
-
async function applyEvaluateOnNewDocument(page, currentUrl, siteConfig, globalEvalOnDoc, forceDebug, formatLogMessage) {
|
|
17
|
-
const shouldInjectEvalForPage = siteConfig.evaluateOnNewDocument === true || globalEvalOnDoc;
|
|
18
|
-
let evalOnDocSuccess = false;
|
|
19
|
-
|
|
20
|
-
if (!shouldInjectEvalForPage) {
|
|
21
|
-
return false;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
if (forceDebug) {
|
|
25
|
-
if (globalEvalOnDoc) {
|
|
26
|
-
console.log(formatLogMessage('debug', `[evalOnDoc] Global Fetch/XHR interception enabled, applying to: ${currentUrl}`));
|
|
27
|
-
} else {
|
|
28
|
-
console.log(formatLogMessage('debug', `[evalOnDoc] Site-specific Fetch/XHR interception enabled for: ${currentUrl}`));
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
// Strategy 1: Try full injection with health check
|
|
33
|
-
let browserResponsive = false;
|
|
34
|
-
try {
|
|
35
|
-
await Promise.race([
|
|
36
|
-
page.browser().version(), // Quick responsiveness test
|
|
37
|
-
new Promise((_, reject) =>
|
|
38
|
-
setTimeout(() => reject(new Error('Browser health check timeout')), 3000)
|
|
39
|
-
)
|
|
40
|
-
]);
|
|
41
|
-
browserResponsive = true;
|
|
42
|
-
} catch (healthErr) {
|
|
43
|
-
if (forceDebug) {
|
|
44
|
-
console.log(formatLogMessage('debug', `[evalOnDoc] Browser health check failed: ${healthErr.message}`));
|
|
45
|
-
}
|
|
46
|
-
browserResponsive = false;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// Strategy 2: Try injection with reduced complexity if browser is responsive
|
|
50
|
-
if (browserResponsive) {
|
|
51
|
-
try {
|
|
52
|
-
await Promise.race([
|
|
53
|
-
page.evaluateOnNewDocument(createFullInterceptionScript()),
|
|
54
|
-
new Promise((_, reject) =>
|
|
55
|
-
setTimeout(() => reject(new Error('Injection timeout')), 8000)
|
|
56
|
-
)
|
|
57
|
-
]);
|
|
58
|
-
evalOnDocSuccess = true;
|
|
59
|
-
if (forceDebug) {
|
|
60
|
-
console.log(formatLogMessage('debug', `[evalOnDoc] Full injection successful for ${currentUrl}`));
|
|
61
|
-
}
|
|
62
|
-
} catch (fullInjectionErr) {
|
|
63
|
-
if (forceDebug) {
|
|
64
|
-
console.log(formatLogMessage('debug', `[evalOnDoc] Full injection failed: ${fullInjectionErr.message}, trying simplified fallback`));
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// Strategy 3: Fallback - Try minimal injection (just fetch monitoring)
|
|
68
|
-
try {
|
|
69
|
-
await Promise.race([
|
|
70
|
-
page.evaluateOnNewDocument(createMinimalInterceptionScript()),
|
|
71
|
-
new Promise((_, reject) =>
|
|
72
|
-
setTimeout(() => reject(new Error('Minimal injection timeout')), 3000)
|
|
73
|
-
)
|
|
74
|
-
]);
|
|
75
|
-
evalOnDocSuccess = true;
|
|
76
|
-
if (forceDebug) {
|
|
77
|
-
console.log(formatLogMessage('debug', `[evalOnDoc] Minimal injection successful for ${currentUrl}`));
|
|
78
|
-
}
|
|
79
|
-
} catch (minimalInjectionErr) {
|
|
80
|
-
if (forceDebug) {
|
|
81
|
-
console.log(formatLogMessage('debug', `[evalOnDoc] Minimal injection also failed: ${minimalInjectionErr.message}`));
|
|
82
|
-
}
|
|
83
|
-
evalOnDocSuccess = false;
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
} else {
|
|
87
|
-
if (forceDebug) {
|
|
88
|
-
console.log(formatLogMessage('debug', `[evalOnDoc] Browser unresponsive, skipping injection for ${currentUrl}`));
|
|
89
|
-
}
|
|
90
|
-
evalOnDocSuccess = false;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
// Final status logging
|
|
94
|
-
if (!evalOnDocSuccess) {
|
|
95
|
-
console.warn(formatLogMessage('warn', `[evalOnDoc] All injection strategies failed for ${currentUrl} - continuing with standard request monitoring only`));
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
return evalOnDocSuccess;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* Creates the full interception script with all protections
|
|
103
|
-
* @returns {Function} Script function for evaluateOnNewDocument
|
|
104
|
-
*/
|
|
105
|
-
function createFullInterceptionScript() {
|
|
106
|
-
return () => {
|
|
107
|
-
// Prevent infinite reload loops
|
|
108
|
-
let reloadCount = 0;
|
|
109
|
-
const MAX_RELOADS = 2;
|
|
110
|
-
const originalReload = window.location.reload;
|
|
111
|
-
const originalReplace = window.location.replace;
|
|
112
|
-
const originalAssign = window.location.assign;
|
|
113
|
-
|
|
114
|
-
window.location.reload = function() {
|
|
115
|
-
if (++reloadCount > MAX_RELOADS) {
|
|
116
|
-
console.log('[loop-protection] Blocked excessive reload attempt');
|
|
117
|
-
return;
|
|
118
|
-
}
|
|
119
|
-
return originalReload.apply(this, arguments);
|
|
120
|
-
};
|
|
121
|
-
|
|
122
|
-
// Also protect against location.replace/assign to same URL
|
|
123
|
-
const currentHref = window.location.href;
|
|
124
|
-
window.location.replace = function(url) {
|
|
125
|
-
if (url === currentHref && ++reloadCount > MAX_RELOADS) {
|
|
126
|
-
console.log('[loop-protection] Blocked same-page replace attempt');
|
|
127
|
-
return;
|
|
128
|
-
}
|
|
129
|
-
return originalReplace.apply(this, arguments);
|
|
130
|
-
};
|
|
131
|
-
|
|
132
|
-
// This script intercepts and logs Fetch and XHR requests
|
|
133
|
-
// from within the page context at the earliest possible moment.
|
|
134
|
-
const originalFetch = window.fetch;
|
|
135
|
-
window.fetch = (...args) => {
|
|
136
|
-
try {
|
|
137
|
-
console.log('[evalOnDoc][fetch]', args[0]); // Log fetch requests
|
|
138
|
-
const fetchPromise = originalFetch.apply(this, args);
|
|
139
|
-
|
|
140
|
-
// Add network error handling to prevent page errors
|
|
141
|
-
return fetchPromise.catch(fetchErr => {
|
|
142
|
-
console.log('[evalOnDoc][fetch-error]', args[0], fetchErr.message);
|
|
143
|
-
throw fetchErr; // Re-throw to maintain normal error flow
|
|
144
|
-
});
|
|
145
|
-
} catch (fetchWrapperErr) {
|
|
146
|
-
console.log('[evalOnDoc][fetch-wrapper-error]', fetchWrapperErr.message);
|
|
147
|
-
return originalFetch.apply(this, args);
|
|
148
|
-
}
|
|
149
|
-
};
|
|
150
|
-
|
|
151
|
-
const originalXHROpen = XMLHttpRequest.prototype.open;
|
|
152
|
-
XMLHttpRequest.prototype.open = function (method, xhrUrl) {
|
|
153
|
-
try {
|
|
154
|
-
console.log('[evalOnDoc][xhr]', xhrUrl); // Log XHR requests
|
|
155
|
-
|
|
156
|
-
// Add error handling for XHR
|
|
157
|
-
this.addEventListener('error', function(event) {
|
|
158
|
-
console.log('[evalOnDoc][xhr-error]', xhrUrl, 'Network error occurred');
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
return originalXHROpen.apply(this, arguments);
|
|
162
|
-
} catch (xhrOpenErr) {
|
|
163
|
-
console.log('[evalOnDoc][xhr-open-error]', xhrOpenErr.message);
|
|
164
|
-
return originalXHROpen.apply(this, arguments);
|
|
165
|
-
}
|
|
166
|
-
};
|
|
167
|
-
};
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
/**
|
|
171
|
-
* Creates the minimal interception script (fetch only)
|
|
172
|
-
* @returns {Function} Script function for evaluateOnNewDocument
|
|
173
|
-
*/
|
|
174
|
-
function createMinimalInterceptionScript() {
|
|
175
|
-
return () => {
|
|
176
|
-
// Minimal injection - just fetch monitoring
|
|
177
|
-
if (window.fetch) {
|
|
178
|
-
const originalFetch = window.fetch;
|
|
179
|
-
window.fetch = (...args) => {
|
|
180
|
-
try {
|
|
181
|
-
console.log('[evalOnDoc][fetch-minimal]', args[0]);
|
|
182
|
-
return originalFetch.apply(this, args);
|
|
183
|
-
} catch (err) {
|
|
184
|
-
return originalFetch.apply(this, args);
|
|
185
|
-
}
|
|
186
|
-
};
|
|
187
|
-
}
|
|
188
|
-
};
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
module.exports = {
|
|
192
|
-
applyEvaluateOnNewDocument
|
|
193
|
-
};
|