@fanboynz/network-scanner 2.0.54 → 2.0.55
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/browserexit.js +3 -1
- package/lib/fingerprint.js +80 -30
- package/lib/interaction.js +121 -5
- package/nwss.js +27 -2
- package/package.json +1 -1
package/lib/browserexit.js
CHANGED
|
@@ -15,7 +15,7 @@ const CHROME_TEMP_PATHS = [
|
|
|
15
15
|
];
|
|
16
16
|
|
|
17
17
|
const CHROME_TEMP_PATTERNS = [
|
|
18
|
-
'
|
|
18
|
+
'com.google.Chrome.*', // Google Chrome temp files (no leading dot)
|
|
19
19
|
'.org.chromium.Chromium.*',
|
|
20
20
|
'puppeteer-*'
|
|
21
21
|
];
|
|
@@ -39,6 +39,7 @@ async function cleanupChromeTempFiles(options = {}) {
|
|
|
39
39
|
|
|
40
40
|
// Base cleanup commands for standard temp directories
|
|
41
41
|
const cleanupCommands = [
|
|
42
|
+
'rm -rf /tmp/com.google.Chrome.* 2>/dev/null || true',
|
|
42
43
|
'rm -rf /tmp/.com.google.Chrome.* 2>/dev/null || true',
|
|
43
44
|
'rm -rf /tmp/.org.chromium.Chromium.* 2>/dev/null || true',
|
|
44
45
|
'rm -rf /tmp/puppeteer-* 2>/dev/null || true',
|
|
@@ -48,6 +49,7 @@ async function cleanupChromeTempFiles(options = {}) {
|
|
|
48
49
|
|
|
49
50
|
// Add snap-specific cleanup if requested
|
|
50
51
|
if (includeSnapTemp || comprehensive) {
|
|
52
|
+
cleanupCommands.push('rm -rf /dev/shm/com.google.Chrome.* 2>/dev/null || true');
|
|
51
53
|
cleanupCommands.push(
|
|
52
54
|
'rm -rf /tmp/snap-private-tmp/snap.chromium/tmp/.org.chromium.Chromium.* 2>/dev/null || true',
|
|
53
55
|
'rm -rf /tmp/snap-private-tmp/snap.chromium/tmp/puppeteer-* 2>/dev/null || true'
|
package/lib/fingerprint.js
CHANGED
|
@@ -24,28 +24,6 @@ function seededRandom(seed) {
|
|
|
24
24
|
const _fingerprintCache = new Map();
|
|
25
25
|
|
|
26
26
|
// Type-specific property spoofing functions for monomorphic optimization
|
|
27
|
-
function spoofNavigatorProperties(navigator, properties, options = {}) {
|
|
28
|
-
if (!navigator || typeof navigator !== 'object') return false;
|
|
29
|
-
|
|
30
|
-
for (const [prop, descriptor] of Object.entries(properties)) {
|
|
31
|
-
if (!safeDefineProperty(navigator, prop, descriptor, options)) {
|
|
32
|
-
if (options.debug) console.log(`[fingerprint] Failed to spoof navigator.${prop}`);
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
return true;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function spoofScreenProperties(screen, properties, options = {}) {
|
|
39
|
-
if (!screen || typeof screen !== 'object') return false;
|
|
40
|
-
|
|
41
|
-
for (const [prop, descriptor] of Object.entries(properties)) {
|
|
42
|
-
if (!safeDefineProperty(screen, prop, descriptor, options)) {
|
|
43
|
-
if (options.debug) console.log(`[fingerprint] Failed to spoof screen.${prop}`);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
return true;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
27
|
// Built-in properties that should not be modified
|
|
50
28
|
const BUILT_IN_PROPERTIES = new Set([
|
|
51
29
|
'href', 'origin', 'protocol', 'host', 'hostname', 'port', 'pathname', 'search', 'hash',
|
|
@@ -379,6 +357,27 @@ async function applyUserAgentSpoofing(page, siteConfig, forceDebug, currentUrl)
|
|
|
379
357
|
return false;
|
|
380
358
|
};
|
|
381
359
|
})();
|
|
360
|
+
|
|
361
|
+
// Function.prototype.toString protection — make spoofed functions appear native
|
|
362
|
+
// Must be installed BEFORE any property overrides so all spoofs are protected
|
|
363
|
+
const nativeFunctionStore = new WeakMap();
|
|
364
|
+
const originalToString = Function.prototype.toString;
|
|
365
|
+
|
|
366
|
+
function maskAsNative(fn, nativeName) {
|
|
367
|
+
if (typeof fn === 'function') {
|
|
368
|
+
nativeFunctionStore.set(fn, nativeName || fn.name || '');
|
|
369
|
+
}
|
|
370
|
+
return fn;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
Function.prototype.toString = function() {
|
|
374
|
+
if (nativeFunctionStore.has(this)) {
|
|
375
|
+
return `function ${nativeFunctionStore.get(this)}() { [native code] }`;
|
|
376
|
+
}
|
|
377
|
+
return originalToString.call(this);
|
|
378
|
+
};
|
|
379
|
+
// Protect the toString override itself
|
|
380
|
+
nativeFunctionStore.set(Function.prototype.toString, 'toString');
|
|
382
381
|
|
|
383
382
|
// Create safe property definition helper
|
|
384
383
|
function safeDefinePropertyLocal(target, property, descriptor) {
|
|
@@ -439,10 +438,14 @@ async function applyUserAgentSpoofing(page, siteConfig, forceDebug, currentUrl)
|
|
|
439
438
|
// Remove webdriver properties
|
|
440
439
|
//
|
|
441
440
|
safeExecute(() => {
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
} catch (e) {}
|
|
445
|
-
|
|
441
|
+
// In real Chrome, webdriver lives on Navigator.prototype, not the instance.
|
|
442
|
+
// Override it there so Object.getOwnPropertyDescriptor(navigator, 'webdriver') returns undefined.
|
|
443
|
+
try { delete navigator.webdriver; } catch (e) {}
|
|
444
|
+
Object.defineProperty(Navigator.prototype, 'webdriver', {
|
|
445
|
+
get: () => false,
|
|
446
|
+
configurable: true,
|
|
447
|
+
enumerable: true
|
|
448
|
+
});
|
|
446
449
|
}, 'webdriver removal');
|
|
447
450
|
|
|
448
451
|
// Remove automation properties
|
|
@@ -861,6 +864,8 @@ async function applyUserAgentSpoofing(page, siteConfig, forceDebug, currentUrl)
|
|
|
861
864
|
if (typeof originalStack === 'string') {
|
|
862
865
|
return originalStack
|
|
863
866
|
.replace(/.*puppeteer.*\n?/gi, '')
|
|
867
|
+
.replace(/.*__puppeteer_evaluation_script__.*\n?/gi, '')
|
|
868
|
+
.replace(/.*evaluateOnNewDocument.*\n?/gi, '')
|
|
864
869
|
.replace(/.*chrome-devtools.*\n?/gi, '')
|
|
865
870
|
.replace(/.*webdriver.*\n?/gi, '')
|
|
866
871
|
.replace(/.*automation.*\n?/gi, '')
|
|
@@ -1635,6 +1640,55 @@ async function applyUserAgentSpoofing(page, siteConfig, forceDebug, currentUrl)
|
|
|
1635
1640
|
});
|
|
1636
1641
|
}, 'location URL masking');
|
|
1637
1642
|
|
|
1643
|
+
// Bulk-mask all spoofed prototype methods so toString() returns "[native code]"
|
|
1644
|
+
// Must run AFTER all overrides are applied
|
|
1645
|
+
safeExecute(() => {
|
|
1646
|
+
const protoMasks = [
|
|
1647
|
+
[WebGLRenderingContext.prototype, ['getParameter', 'getExtension', 'getSupportedExtensions']],
|
|
1648
|
+
[HTMLCanvasElement.prototype, ['getContext', 'toDataURL', 'toBlob']],
|
|
1649
|
+
[CanvasRenderingContext2D.prototype, ['getImageData', 'fillText', 'strokeText', 'measureText']],
|
|
1650
|
+
[EventTarget.prototype, ['addEventListener', 'removeEventListener']],
|
|
1651
|
+
[Date.prototype, ['getTimezoneOffset']],
|
|
1652
|
+
];
|
|
1653
|
+
if (typeof WebGL2RenderingContext !== 'undefined') {
|
|
1654
|
+
protoMasks.push([WebGL2RenderingContext.prototype, ['getParameter', 'getExtension']]);
|
|
1655
|
+
}
|
|
1656
|
+
protoMasks.forEach(([proto, methods]) => {
|
|
1657
|
+
methods.forEach(name => {
|
|
1658
|
+
if (typeof proto[name] === 'function') maskAsNative(proto[name], name);
|
|
1659
|
+
});
|
|
1660
|
+
});
|
|
1661
|
+
|
|
1662
|
+
// Mask navigator/window method overrides
|
|
1663
|
+
if (typeof navigator.permissions?.query === 'function') maskAsNative(navigator.permissions.query, 'query');
|
|
1664
|
+
if (typeof navigator.getBattery === 'function') maskAsNative(navigator.getBattery, 'getBattery');
|
|
1665
|
+
if (typeof speechSynthesis?.getVoices === 'function') maskAsNative(speechSynthesis.getVoices, 'getVoices');
|
|
1666
|
+
if (typeof performance.now === 'function') maskAsNative(performance.now, 'now');
|
|
1667
|
+
if (typeof Notification?.requestPermission === 'function') maskAsNative(Notification.requestPermission, 'requestPermission');
|
|
1668
|
+
if (typeof window.RTCPeerConnection === 'function') maskAsNative(window.RTCPeerConnection, 'RTCPeerConnection');
|
|
1669
|
+
if (typeof window.Image === 'function') maskAsNative(window.Image, 'Image');
|
|
1670
|
+
if (typeof window.fetch === 'function') maskAsNative(window.fetch, 'fetch');
|
|
1671
|
+
if (typeof window.PointerEvent === 'function') maskAsNative(window.PointerEvent, 'PointerEvent');
|
|
1672
|
+
|
|
1673
|
+
// Mask property getters on navigator
|
|
1674
|
+
const navProps = ['userAgentData', 'connection', 'pdfViewerEnabled', 'webdriver',
|
|
1675
|
+
'hardwareConcurrency', 'deviceMemory', 'platform', 'maxTouchPoints'];
|
|
1676
|
+
navProps.forEach(prop => {
|
|
1677
|
+
// Check both instance and prototype (webdriver lives on prototype)
|
|
1678
|
+
const desc = Object.getOwnPropertyDescriptor(navigator, prop)
|
|
1679
|
+
|| Object.getOwnPropertyDescriptor(Navigator.prototype, prop);
|
|
1680
|
+
if (desc?.get) maskAsNative(desc.get, 'get ' + prop);
|
|
1681
|
+
});
|
|
1682
|
+
|
|
1683
|
+
// Mask window property getters
|
|
1684
|
+
['screenX', 'screenY', 'outerWidth', 'outerHeight'].forEach(prop => {
|
|
1685
|
+
const desc = Object.getOwnPropertyDescriptor(window, prop);
|
|
1686
|
+
if (desc?.get) maskAsNative(desc.get, 'get ' + prop);
|
|
1687
|
+
});
|
|
1688
|
+
|
|
1689
|
+
if (debugEnabled) console.log('[fingerprint] toString protection applied to all spoofed functions');
|
|
1690
|
+
}, 'Function.prototype.toString bulk masking');
|
|
1691
|
+
|
|
1638
1692
|
}, ua, forceDebug, selectedGpu);
|
|
1639
1693
|
} catch (stealthErr) {
|
|
1640
1694
|
if (stealthErr.message.includes('Session closed') ||
|
|
@@ -2030,9 +2084,6 @@ async function applyAllFingerprintSpoofing(page, siteConfig, forceDebug, current
|
|
|
2030
2084
|
}
|
|
2031
2085
|
|
|
2032
2086
|
// Legacy compatibility function - maintained for backwards compatibility
|
|
2033
|
-
function safeExecuteSpoofing(spoofFunction, description, forceDebug = false) {
|
|
2034
|
-
return safeSpoofingExecution(spoofFunction, description, { debug: forceDebug });
|
|
2035
|
-
}
|
|
2036
2087
|
|
|
2037
2088
|
|
|
2038
2089
|
module.exports = {
|
|
@@ -2044,7 +2095,6 @@ module.exports = {
|
|
|
2044
2095
|
applyAllFingerprintSpoofing,
|
|
2045
2096
|
simulateHumanBehavior,
|
|
2046
2097
|
safeDefineProperty,
|
|
2047
|
-
safeExecuteSpoofing, // Legacy compatibility
|
|
2048
2098
|
safeSpoofingExecution,
|
|
2049
2099
|
DEFAULT_PLATFORM,
|
|
2050
2100
|
DEFAULT_TIMEZONE
|
package/lib/interaction.js
CHANGED
|
@@ -123,6 +123,20 @@ const ELEMENT_INTERACTION = {
|
|
|
123
123
|
MISTAKE_RATE: 0.02 // Probability of typing mistakes (0.02 = 2% chance)
|
|
124
124
|
};
|
|
125
125
|
|
|
126
|
+
// === CONTENT CLICK CONSTANTS ===
|
|
127
|
+
// For triggering document-level onclick handlers (e.g., Monetag onclick_static)
|
|
128
|
+
// These clicks target the page content area, not specific UI elements
|
|
129
|
+
// NOTE: No preDelay needed — mouse movements + scrolling already provide ~1s
|
|
130
|
+
// of activity before clicks fire, which is enough for async ad script registration
|
|
131
|
+
const CONTENT_CLICK = {
|
|
132
|
+
CLICK_COUNT: 2, // Two attempts (primary + backup if first suppressed)
|
|
133
|
+
INTER_CLICK_MIN: 300, // Minimum ms between clicks (above Monetag 250ms cooldown)
|
|
134
|
+
INTER_CLICK_MAX: 500, // Maximum ms between clicks
|
|
135
|
+
PRE_CLICK_DELAY: 300, // Small buffer for late-loading async ad scripts
|
|
136
|
+
VIEWPORT_INSET: 0.2, // Avoid outer 20% of viewport (menus, overlays)
|
|
137
|
+
MOUSE_APPROACH_STEPS: 3 // Minimal steps — just enough for non-instant movement
|
|
138
|
+
};
|
|
139
|
+
|
|
126
140
|
// === INTENSITY SETTINGS ===
|
|
127
141
|
// Pre-configured intensity levels - modify these to change overall behavior
|
|
128
142
|
const INTENSITY_SETTINGS = {
|
|
@@ -606,6 +620,97 @@ async function interactWithElements(page, options = {}) {
|
|
|
606
620
|
}
|
|
607
621
|
}
|
|
608
622
|
|
|
623
|
+
/**
|
|
624
|
+
* Clicks random spots in the page content area to trigger document-level
|
|
625
|
+
* onclick handlers (Monetag onclick_static, similar popunder SDKs).
|
|
626
|
+
*
|
|
627
|
+
* WHY THIS EXISTS:
|
|
628
|
+
* Ad onclick SDKs attach a single listener on `document` (capture phase)
|
|
629
|
+
* that fires on ANY click with `isTrusted: true`. They don't care which
|
|
630
|
+
* element was clicked — just that a real input event reached the document.
|
|
631
|
+
* `interactWithElements()` hunts for <button>/<a> which may not exist or
|
|
632
|
+
* may be excluded by the SDK's own filter. This function simply clicks
|
|
633
|
+
* the content area of the page where the SDK will always accept the event.
|
|
634
|
+
*
|
|
635
|
+
* TIMING:
|
|
636
|
+
* - 300ms preDelay: small buffer after mouse/scroll activity (~1.2s) for
|
|
637
|
+
* any late-loading async ad scripts to finish registering listeners.
|
|
638
|
+
* - Spaces clicks 300-500ms apart (above Monetag's 250ms cooldown).
|
|
639
|
+
* - Total time: ~1.1s for 2 clicks (preDelay + move + pause + click + gap).
|
|
640
|
+
*
|
|
641
|
+
* TARGETING:
|
|
642
|
+
* - Clicks within the inner 60% of the viewport to avoid sticky headers,
|
|
643
|
+
* footers, sidebars, cookie banners, and overlay close buttons.
|
|
644
|
+
* - Each click gets a fresh random position with natural mouse approach.
|
|
645
|
+
*
|
|
646
|
+
* @param {import('puppeteer').Page} page
|
|
647
|
+
* @param {object} [options]
|
|
648
|
+
* @param {number} [options.clicks] Number of click attempts
|
|
649
|
+
* @param {number} [options.preDelay] Ms to wait before first click
|
|
650
|
+
* @param {number} [options.interClickMin] Min ms between clicks
|
|
651
|
+
* @param {number} [options.interClickMax] Max ms between clicks
|
|
652
|
+
* @param {boolean} [options.forceDebug] Log click coordinates
|
|
653
|
+
*/
|
|
654
|
+
async function performContentClicks(page, options = {}) {
|
|
655
|
+
const {
|
|
656
|
+
clicks = CONTENT_CLICK.CLICK_COUNT,
|
|
657
|
+
preDelay = CONTENT_CLICK.PRE_CLICK_DELAY,
|
|
658
|
+
interClickMin = CONTENT_CLICK.INTER_CLICK_MIN,
|
|
659
|
+
interClickMax = CONTENT_CLICK.INTER_CLICK_MAX,
|
|
660
|
+
forceDebug = false
|
|
661
|
+
} = options;
|
|
662
|
+
|
|
663
|
+
try {
|
|
664
|
+
if (page.isClosed()) return;
|
|
665
|
+
|
|
666
|
+
const viewport = await getCachedViewport(page);
|
|
667
|
+
const inset = CONTENT_CLICK.VIEWPORT_INSET;
|
|
668
|
+
const minX = Math.floor(viewport.width * inset);
|
|
669
|
+
const maxX = Math.floor(viewport.width * (1 - inset));
|
|
670
|
+
const minY = Math.floor(viewport.height * inset);
|
|
671
|
+
const maxY = Math.floor(viewport.height * (1 - inset));
|
|
672
|
+
|
|
673
|
+
// Wait for ad scripts to register their listeners
|
|
674
|
+
await fastTimeout(preDelay);
|
|
675
|
+
|
|
676
|
+
let lastX = minX + Math.floor(Math.random() * (maxX - minX));
|
|
677
|
+
let lastY = minY + Math.floor(Math.random() * (maxY - minY));
|
|
678
|
+
|
|
679
|
+
for (let i = 0; i < clicks; i++) {
|
|
680
|
+
if (page.isClosed()) break;
|
|
681
|
+
|
|
682
|
+
// Random position in content zone
|
|
683
|
+
const targetX = minX + Math.floor(Math.random() * (maxX - minX));
|
|
684
|
+
const targetY = minY + Math.floor(Math.random() * (maxY - minY));
|
|
685
|
+
|
|
686
|
+
// Natural mouse approach (few steps, no need for elaborate curves)
|
|
687
|
+
await humanLikeMouseMove(page, lastX, lastY, targetX, targetY, {
|
|
688
|
+
steps: CONTENT_CLICK.MOUSE_APPROACH_STEPS,
|
|
689
|
+
curve: 0.03 + Math.random() * 0.04,
|
|
690
|
+
jitter: 1
|
|
691
|
+
});
|
|
692
|
+
|
|
693
|
+
// Brief human-like pause, then click
|
|
694
|
+
await fastTimeout(TIMING.CLICK_PAUSE_MIN + Math.random() * (TIMING.CLICK_PAUSE_MAX - TIMING.CLICK_PAUSE_MIN));
|
|
695
|
+
await page.mouse.click(targetX, targetY);
|
|
696
|
+
|
|
697
|
+
if (forceDebug) {
|
|
698
|
+
console.log(`[interaction] Content click ${i + 1}/${clicks} at (${targetX}, ${targetY})`);
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
lastX = targetX;
|
|
702
|
+
lastY = targetY;
|
|
703
|
+
|
|
704
|
+
// Inter-click gap (skip after last click)
|
|
705
|
+
if (i < clicks - 1) {
|
|
706
|
+
await fastTimeout(interClickMin + Math.random() * (interClickMax - interClickMin));
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
} catch (err) {
|
|
710
|
+
// Content clicks are supplementary — never break the scan
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
|
|
609
714
|
/**
|
|
610
715
|
* Simulates realistic typing behavior with human characteristics
|
|
611
716
|
*
|
|
@@ -876,12 +981,22 @@ async function performPageInteraction(page, currentUrl, options = {}, forceDebug
|
|
|
876
981
|
}
|
|
877
982
|
}
|
|
878
983
|
|
|
879
|
-
//
|
|
984
|
+
// Click interaction — two strategies for maximum ad script coverage
|
|
985
|
+
// 1. Content area clicks: triggers document-level onclick handlers
|
|
986
|
+
// (Monetag, similar popunder SDKs that listen on document)
|
|
987
|
+
// 2. Element clicks: interacts with specific UI elements
|
|
988
|
+
// (ad scripts that attach to specific clickable elements)
|
|
880
989
|
if (includeElementClicks) {
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
990
|
+
if (checkTimeout()) return; // Emergency timeout check
|
|
991
|
+
// Primary: content area clicks for document-level onclick handlers
|
|
992
|
+
await performContentClicks(page, { forceDebug });
|
|
993
|
+
// Secondary: targeted element clicks (fast, 1 attempt only)
|
|
994
|
+
if (!checkTimeout()) {
|
|
995
|
+
await interactWithElements(page, {
|
|
996
|
+
maxAttempts: 1,
|
|
997
|
+
avoidDestructive: true
|
|
998
|
+
});
|
|
999
|
+
}
|
|
885
1000
|
}
|
|
886
1001
|
|
|
887
1002
|
// Periodic memory cleanup during interaction
|
|
@@ -1105,6 +1220,7 @@ module.exports = {
|
|
|
1105
1220
|
humanLikeMouseMove,
|
|
1106
1221
|
simulateScrolling,
|
|
1107
1222
|
interactWithElements,
|
|
1223
|
+
performContentClicks,
|
|
1108
1224
|
simulateTyping,
|
|
1109
1225
|
generateRandomCoordinates
|
|
1110
1226
|
};
|
package/nwss.js
CHANGED
|
@@ -39,7 +39,7 @@ const { processResults } = require('./lib/post-processing');
|
|
|
39
39
|
// Colorize various text when used
|
|
40
40
|
const { colorize, colors, messageColors, tags, formatLogMessage } = require('./lib/colorize');
|
|
41
41
|
// Enhanced mouse interaction and page simulation
|
|
42
|
-
const { performPageInteraction, createInteractionConfig } = require('./lib/interaction');
|
|
42
|
+
const { performPageInteraction, createInteractionConfig, performContentClicks, humanLikeMouseMove } = require('./lib/interaction');
|
|
43
43
|
// Domain detection cache for performance optimization
|
|
44
44
|
const { createGlobalHelpers, getTotalDomainsSkipped, getDetectedDomainsCount } = require('./lib/domain-cache');
|
|
45
45
|
const { createSmartCache } = require('./lib/smart-cache'); // Smart cache system
|
|
@@ -1447,7 +1447,6 @@ function setupFrameHandling(page, forceDebug) {
|
|
|
1447
1447
|
'--disable-features=SafeBrowsing',
|
|
1448
1448
|
'--disable-dev-shm-usage',
|
|
1449
1449
|
'--disable-sync',
|
|
1450
|
-
'--disable-gpu', // WebGL null-context handled by fingerprint.js Proxy mock
|
|
1451
1450
|
'--mute-audio',
|
|
1452
1451
|
'--disable-translate',
|
|
1453
1452
|
'--window-size=1920,1080',
|
|
@@ -3660,6 +3659,32 @@ function setupFrameHandling(page, forceDebug) {
|
|
|
3660
3659
|
}
|
|
3661
3660
|
}
|
|
3662
3661
|
|
|
3662
|
+
// Post-reload interaction: trigger onclick ad scripts (Monetag etc.)
|
|
3663
|
+
// Each reload gives a fresh session with a new random ad domain —
|
|
3664
|
+
// without clicks the SDK never fires and we miss those domains.
|
|
3665
|
+
if (interactEnabled && !page.isClosed()) {
|
|
3666
|
+
try {
|
|
3667
|
+
// Brief wait for ad scripts to re-register after reload
|
|
3668
|
+
await fastTimeout(800);
|
|
3669
|
+
// Quick mouse moves to build movement score (Monetag tracks this)
|
|
3670
|
+
const vp = page.viewport() || { width: 1920, height: 1080 };
|
|
3671
|
+
const startX = 200 + Math.floor(Math.random() * (vp.width - 400));
|
|
3672
|
+
const startY = 200 + Math.floor(Math.random() * (vp.height - 400));
|
|
3673
|
+
await page.mouse.move(startX, startY);
|
|
3674
|
+
for (let m = 0; m < 2; m++) {
|
|
3675
|
+
const endX = 200 + Math.floor(Math.random() * (vp.width - 400));
|
|
3676
|
+
const endY = 200 + Math.floor(Math.random() * (vp.height - 400));
|
|
3677
|
+
await humanLikeMouseMove(page, startX, startY, endX, endY, { steps: 3, curve: 0.04, jitter: 1 });
|
|
3678
|
+
}
|
|
3679
|
+
// Content clicks to trigger document-level onclick handlers
|
|
3680
|
+
await performContentClicks(page, { clicks: 2, preDelay: 200, forceDebug });
|
|
3681
|
+
if (forceDebug) console.log(formatLogMessage('debug', `Post-reload interaction completed for reload #${i}`));
|
|
3682
|
+
} catch (postReloadInteractErr) {
|
|
3683
|
+
// Non-critical — continue with remaining reloads
|
|
3684
|
+
if (forceDebug) console.log(formatLogMessage('debug', `Post-reload interaction failed: ${postReloadInteractErr.message}`));
|
|
3685
|
+
}
|
|
3686
|
+
}
|
|
3687
|
+
|
|
3663
3688
|
// Only add delay if we're continuing with more reloads
|
|
3664
3689
|
if (i < totalReloads) {
|
|
3665
3690
|
// Reduce delay for problematic sites
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fanboynz/network-scanner",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.55",
|
|
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": {
|