@fanboynz/network-scanner 1.0.65 → 1.0.67

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.
@@ -292,7 +292,11 @@ async function applyUserAgentSpoofing(page, siteConfig, forceDebug, currentUrl)
292
292
  /tz check/i,
293
293
  /new window\\.Error.*<anonymous>/i,
294
294
  /Failed to load resource.*server responded with a status of 40[34]/i,
295
- /Blocked script execution in 'about:blank'.*sandboxed.*allow-scripts/i
295
+ /Blocked script execution in 'about:blank'.*sandboxed.*allow-scripts/i,
296
+ /Page JavaScript error:/i,
297
+ /^[a-zA-Z0-9_$]+\[.*\]\s+is not a function/i,
298
+ /^[a-zA-Z0-9_$]+\(.*\)\s+is not a function/i,
299
+ /^[a-zA-Z0-9_$]+\.[a-zA-Z0-9_$]+.*is not a function/i
296
300
  ];
297
301
  return patterns.some(pattern => pattern.test(String(message || '')));
298
302
  }
@@ -568,6 +572,76 @@ async function applyUserAgentSpoofing(page, siteConfig, forceDebug, currentUrl)
568
572
  }, 'battery API spoofing');
569
573
 
570
574
  // Suppress common console errors
575
+
576
+ // Enhanced Mouse/Pointer Spoofing
577
+ safeExecute(() => {
578
+ // Spoof pointer capabilities
579
+ if (navigator.maxTouchPoints !== undefined) {
580
+ safeDefinePropertyLocal(navigator, 'maxTouchPoints', {
581
+ get: () => Math.random() > 0.7 ? 0 : Math.floor(Math.random() * 5) + 1
582
+ });
583
+ }
584
+
585
+ // Spoof mouse timing patterns to prevent behavioral fingerprinting
586
+ const originalAddEventListener = EventTarget.prototype.addEventListener;
587
+ EventTarget.prototype.addEventListener = function(type, listener, options) {
588
+ if (type === 'mousemove' && typeof listener === 'function') {
589
+ const wrappedListener = function(event) {
590
+ // Add slight timing variation to prevent pattern detection
591
+ const delay = Math.random() * 2; // 0-2ms variation
592
+ setTimeout(() => listener.call(this, event), delay);
593
+ };
594
+ return originalAddEventListener.call(this, type, wrappedListener, options);
595
+ }
596
+ return originalAddEventListener.call(this, type, listener, options);
597
+ };
598
+
599
+ // Spoof PointerEvent if available
600
+ if (window.PointerEvent) {
601
+ const OriginalPointerEvent = window.PointerEvent;
602
+ window.PointerEvent = function(type, eventInitDict = {}) {
603
+ // Add realistic pointer properties
604
+ const enhancedDict = {
605
+ ...eventInitDict,
606
+ pressure: eventInitDict.pressure || (Math.random() * 0.3 + 0.2), // 0.2-0.5
607
+ tangentialPressure: eventInitDict.tangentialPressure || 0,
608
+ tiltX: eventInitDict.tiltX || (Math.random() * 10 - 5), // -5 to 5
609
+ tiltY: eventInitDict.tiltY || (Math.random() * 10 - 5),
610
+ twist: eventInitDict.twist || Math.random() * 360,
611
+ pointerType: eventInitDict.pointerType || 'mouse'
612
+ };
613
+ return new OriginalPointerEvent(type, enhancedDict);
614
+ };
615
+ Object.setPrototypeOf(window.PointerEvent, OriginalPointerEvent);
616
+ }
617
+
618
+ // Spoof touch capabilities for mobile detection evasion
619
+ if (!window.TouchEvent && Math.random() > 0.8) {
620
+ // 20% chance to add touch support to confuse fingerprinters
621
+ window.TouchEvent = function(type, eventInitDict = {}) {
622
+ return new MouseEvent(type, eventInitDict);
623
+ };
624
+
625
+ safeDefinePropertyLocal(navigator, 'maxTouchPoints', { get: () => 1 });
626
+ }
627
+
628
+ // Spoof mouse wheel behavior
629
+ const originalWheelEvent = window.WheelEvent;
630
+ if (originalWheelEvent) {
631
+ window.WheelEvent = function(type, eventInitDict = {}) {
632
+ const enhancedDict = {
633
+ ...eventInitDict,
634
+ deltaMode: eventInitDict.deltaMode || 0,
635
+ deltaX: eventInitDict.deltaX || 0,
636
+ deltaY: eventInitDict.deltaY || (Math.random() * 100 - 50),
637
+ deltaZ: eventInitDict.deltaZ || 0
638
+ };
639
+ return new originalWheelEvent(type, enhancedDict);
640
+ };
641
+ }
642
+
643
+ }, 'enhanced mouse/pointer spoofing');
644
+
571
645
  safeExecute(() => {
572
646
  const originalConsoleError = console.error;
573
647
  console.error = function(...args) {
@@ -728,35 +802,98 @@ async function applyFingerprintProtection(page, siteConfig, forceDebug, currentU
728
802
 
729
803
  /**
730
804
  * Simulate human-like behavior
805
+ * Enhanced with realistic mouse patterns and timing
731
806
  */
732
807
  async function simulateHumanBehavior(page, forceDebug) {
733
808
  try {
734
809
  await page.evaluateOnNewDocument((debugEnabled) => {
735
810
 
736
811
  try {
737
- let mouseX = Math.random() * window.innerWidth;
738
- let mouseY = Math.random() * window.innerHeight;
812
+ // Enhanced human-like mouse simulation with realistic patterns
813
+ let mouseX = Math.random() * (window.innerWidth - 100) + 50;
814
+ let mouseY = Math.random() * (window.innerHeight - 100) + 50;
815
+ let lastMoveTime = Date.now();
816
+ let moveCount = 0;
817
+
818
+ // Realistic mouse movement patterns
819
+ const movePatterns = {
820
+ linear: () => ({
821
+ deltaX: (Math.random() - 0.5) * 4,
822
+ deltaY: (Math.random() - 0.5) * 4
823
+ }),
824
+ curved: () => {
825
+ const angle = moveCount * 0.1;
826
+ return {
827
+ deltaX: Math.sin(angle) * 3 + (Math.random() - 0.5) * 2,
828
+ deltaY: Math.cos(angle) * 3 + (Math.random() - 0.5) * 2
829
+ };
830
+ },
831
+ jittery: () => ({
832
+ deltaX: (Math.random() - 0.5) * 8,
833
+ deltaY: (Math.random() - 0.5) * 8
834
+ })
835
+ };
836
+
837
+ const patterns = Object.keys(movePatterns);
838
+ let currentPattern = patterns[Math.floor(Math.random() * patterns.length)];
839
+ let patternChangeCounter = 0;
739
840
 
740
841
  const moveInterval = setInterval(() => {
741
- mouseX += (Math.random() - 0.5) * 20;
742
- mouseY += (Math.random() - 0.5) * 20;
842
+ const now = Date.now();
843
+ const timeDelta = now - lastMoveTime;
743
844
 
744
- mouseX = Math.max(0, Math.min(window.innerWidth, mouseX));
745
- mouseY = Math.max(0, Math.min(window.innerHeight, mouseY));
845
+ // Change pattern occasionally
846
+ if (patternChangeCounter++ > 20 + Math.random() * 30) {
847
+ currentPattern = patterns[Math.floor(Math.random() * patterns.length)];
848
+ patternChangeCounter = 0;
849
+ }
850
+
851
+ // Apply movement pattern
852
+ const movement = movePatterns[currentPattern]();
853
+ mouseX += movement.deltaX;
854
+ mouseY += movement.deltaY;
855
+
856
+ // Keep within bounds with padding
857
+ mouseX = Math.max(10, Math.min(window.innerWidth - 10, mouseX));
858
+ mouseY = Math.max(10, Math.min(window.innerHeight - 10, mouseY));
746
859
 
747
860
  try {
861
+ // Dispatch realistic mouse events with varying timing
748
862
  document.dispatchEvent(new MouseEvent('mousemove', {
749
863
  clientX: mouseX,
750
864
  clientY: mouseY,
751
- bubbles: true
865
+ bubbles: true,
866
+ cancelable: true,
867
+ view: window,
868
+ detail: 0,
869
+ buttons: 0,
870
+ button: 0
752
871
  }));
872
+
873
+ // Occasionally simulate clicks for more realistic behavior
874
+ if (Math.random() > 0.995) { // Very rare clicks
875
+ document.dispatchEvent(new MouseEvent('click', {
876
+ clientX: mouseX,
877
+ clientY: mouseY,
878
+ bubbles: true,
879
+ cancelable: true,
880
+ view: window
881
+ }));
882
+ }
883
+
884
+ moveCount++;
885
+ lastMoveTime = now;
886
+
753
887
  } catch (e) {}
754
- }, 1000 + Math.random() * 2000);
888
+ }, 50 + Math.random() * 100); // More frequent, realistic timing (50-150ms)
755
889
 
756
- // Stop after 30 seconds
890
+ // Stop after 45 seconds with gradual slowdown
757
891
  setTimeout(() => {
758
- try { clearInterval(moveInterval); } catch (e) {}
759
- }, 30000);
892
+ try {
893
+ clearInterval(moveInterval);
894
+ if (debugEnabled) console.log('[fingerprint] Enhanced mouse simulation completed');
895
+ } catch (e) {}
896
+ }, 45000);
760
897
 
761
898
  } catch (err) {
762
899
  if (debugEnabled) console.log(`[fingerprint] Human behavior simulation failed: ${err.message}`);
@@ -164,6 +164,38 @@ const PROBABILITIES = {
164
164
  }
165
165
  };
166
166
 
167
+ // === PERFORMANCE OPTIMIZATION VARIABLES ===
168
+ // Viewport caching to reduce repeated page.viewport() calls
169
+ let cachedViewport = null;
170
+ let lastViewportCheck = 0;
171
+ const VIEWPORT_CACHE_DURATION = 30000; // 30 seconds
172
+ let interactionMemoryCleanupCounter = 0;
173
+
174
+ /**
175
+ * Gets viewport dimensions with caching for performance
176
+ * Caches viewport for 30 seconds to avoid repeated queries
177
+ *
178
+ * @param {import('puppeteer').Page} page - Puppeteer page object
179
+ * @returns {Promise<object>} Viewport dimensions {width, height}
180
+ */
181
+ async function getCachedViewport(page) {
182
+ const now = Date.now();
183
+
184
+ // Return cached viewport if still valid
185
+ if (cachedViewport && (now - lastViewportCheck) < VIEWPORT_CACHE_DURATION) {
186
+ return cachedViewport;
187
+ }
188
+
189
+ try {
190
+ cachedViewport = await page.viewport();
191
+ lastViewportCheck = now;
192
+ return cachedViewport || { width: DEFAULT_VIEWPORT.WIDTH, height: DEFAULT_VIEWPORT.HEIGHT };
193
+ } catch (viewportErr) {
194
+ // Return defaults if viewport query fails
195
+ return { width: DEFAULT_VIEWPORT.WIDTH, height: DEFAULT_VIEWPORT.HEIGHT };
196
+ }
197
+ }
198
+
167
199
  /**
168
200
  * Generates random coordinates within viewport bounds with intelligent placement
169
201
  *
@@ -481,8 +513,8 @@ async function interactWithElements(page, options = {}) {
481
513
  return;
482
514
  }
483
515
 
484
- // Shorter timeout for element interaction since body should already exist
485
- await page.waitForSelector('body', { timeout: 2000 });
516
+ // Very short timeout since page should already be loaded
517
+ await page.waitForSelector('body', { timeout: 1000 });
486
518
  } catch (bodyWaitErr) {
487
519
  if (options.forceDebug) {
488
520
  console.log(`[interaction] Page not ready for element interaction: ${bodyWaitErr.message}`);
@@ -490,10 +522,10 @@ async function interactWithElements(page, options = {}) {
490
522
  return;
491
523
  }
492
524
 
493
- // Get viewport dimensions for coordinate bounds
494
- const viewport = await page.viewport();
495
- const maxX = viewport ? viewport.width : DEFAULT_VIEWPORT.WIDTH;
496
- const maxY = viewport ? viewport.height : DEFAULT_VIEWPORT.HEIGHT;
525
+ // Use cached viewport for better performance
526
+ const viewport = await getCachedViewport(page);
527
+ const maxX = viewport.width;
528
+ const maxY = viewport.height;
497
529
 
498
530
  for (let attempt = 0; attempt < maxAttempts; attempt++) {
499
531
  try {
@@ -638,6 +670,33 @@ async function simulateTyping(page, text, options = {}) {
638
670
  }
639
671
  }
640
672
 
673
+ /**
674
+ * Cleans up interaction-related memory and cached data
675
+ * Should be called periodically in long-running sessions
676
+ *
677
+ * @param {boolean} force - Force cleanup regardless of timing
678
+ */
679
+ function cleanupInteractionMemory(force = false) {
680
+ interactionMemoryCleanupCounter++;
681
+
682
+ // Only cleanup every 10 calls unless forced
683
+ if (!force && interactionMemoryCleanupCounter % 10 !== 0) {
684
+ return;
685
+ }
686
+
687
+ // Clear cached viewport if it's older than cache duration
688
+ const now = Date.now();
689
+ if (cachedViewport && (now - lastViewportCheck) > VIEWPORT_CACHE_DURATION) {
690
+ cachedViewport = null;
691
+ lastViewportCheck = 0;
692
+ }
693
+
694
+ // Force garbage collection if available (helps with memory pressure)
695
+ if (global.gc) {
696
+ global.gc();
697
+ }
698
+ }
699
+
641
700
  /**
642
701
  * Performs comprehensive page interaction simulation - MAIN ENTRY POINT
643
702
  *
@@ -711,10 +770,9 @@ async function performPageInteraction(page, currentUrl, options = {}, forceDebug
711
770
  try {
712
771
  // Validate page state before starting interaction
713
772
  try {
714
- // Use site-specific timeout based on configuration
715
- // Sites with longer configured timeouts get more time for body detection
716
- const siteTimeout = options.siteTimeout || 30000; // From site config
717
- const bodyTimeout = Math.min(Math.max(siteTimeout / 6, 3000), 8000); // 3-8 seconds
773
+ // Optimized timeout calculation - shorter for better performance
774
+ const siteTimeout = options.siteTimeout || 20000; // Reduced default
775
+ const bodyTimeout = Math.min(Math.max(siteTimeout / 8, 2000), 5000); // 2-5 seconds (reduced)
718
776
 
719
777
  await page.waitForSelector('body', { timeout: bodyTimeout });
720
778
 
@@ -735,9 +793,9 @@ async function performPageInteraction(page, currentUrl, options = {}, forceDebug
735
793
  }
736
794
  } catch (bodyCheckErr) {
737
795
  if (forceDebug) {
738
- console.log(`[interaction] Page not ready for interaction on ${currentUrl} (waited ${bodyTimeout}ms): ${bodyCheckErr.message}`);
796
+ console.log(`[interaction] Page not ready for interaction on ${currentUrl} (waited ${Math.min(Math.max((options.siteTimeout || 20000) / 8, 2000), 5000)}ms): ${bodyCheckErr.message}`);
739
797
  // For very slow sites, we might want to try a minimal interaction anyway
740
- if (bodyTimeout >= 6000 && !bodyCheckErr.message.includes('closed')) {
798
+ if (Math.min(Math.max((options.siteTimeout || 20000) / 8, 2000), 5000) >= 4000 && !bodyCheckErr.message.includes('closed')) {
741
799
  console.log(`[interaction] Attempting minimal mouse movement only for slow-loading ${currentUrl}`);
742
800
  return await performMinimalInteraction(page, currentUrl, options, forceDebug);
743
801
  }
@@ -745,10 +803,10 @@ async function performPageInteraction(page, currentUrl, options = {}, forceDebug
745
803
  return;
746
804
  }
747
805
 
748
- // Get viewport dimensions
749
- const viewport = await page.viewport();
750
- const maxX = viewport ? viewport.width : DEFAULT_VIEWPORT.WIDTH;
751
- const maxY = viewport ? viewport.height : DEFAULT_VIEWPORT.HEIGHT;
806
+ // Use cached viewport for better performance
807
+ const viewport = await getCachedViewport(page);
808
+ const maxX = viewport.width;
809
+ const maxY = viewport.height;
752
810
 
753
811
  if (forceDebug) {
754
812
  console.log(`[interaction] Starting enhanced interaction simulation for ${new URL(currentUrl).hostname} (${intensity} intensity)`);
@@ -760,7 +818,13 @@ async function performPageInteraction(page, currentUrl, options = {}, forceDebug
760
818
 
761
819
  // Start with random position
762
820
  let currentPos = generateRandomCoordinates(maxX, maxY, { preferEdges: true });
821
+
822
+ // Batch mouse move operations for better performance
823
+ try {
763
824
  await page.mouse.move(currentPos.x, currentPos.y);
825
+ } catch (mouseMoveErr) {
826
+ return; // Exit gracefully if mouse operations fail
827
+ }
764
828
 
765
829
  const startTime = Date.now();
766
830
  const totalDuration = duration * settings.pauseMultiplier;
@@ -812,6 +876,9 @@ async function performPageInteraction(page, currentUrl, options = {}, forceDebug
812
876
  });
813
877
  }
814
878
 
879
+ // Periodic memory cleanup during interaction
880
+ cleanupInteractionMemory();
881
+
815
882
  // Final hover position
816
883
  const finalPos = generateRandomCoordinates(maxX, maxY);
817
884
  await humanLikeMouseMove(page, currentPos.x, currentPos.y, finalPos.x, finalPos.y);
@@ -847,9 +914,9 @@ async function performMinimalInteraction(page, currentUrl, options = {}, forceDe
847
914
  try {
848
915
  if (page.isClosed()) return;
849
916
 
850
- const viewport = await page.viewport();
851
- const maxX = viewport ? viewport.width : DEFAULT_VIEWPORT.WIDTH;
852
- const maxY = viewport ? viewport.height : DEFAULT_VIEWPORT.HEIGHT;
917
+ const viewport = await getCachedViewport(page);
918
+ const maxX = viewport.width;
919
+ const maxY = viewport.height;
853
920
 
854
921
  if (forceDebug) {
855
922
  console.log(`[interaction] Performing minimal interaction for slow page: ${new URL(currentUrl).hostname}`);
@@ -860,7 +927,7 @@ async function performMinimalInteraction(page, currentUrl, options = {}, forceDe
860
927
  const endPos = generateRandomCoordinates(maxX, maxY);
861
928
 
862
929
  await page.mouse.move(startPos.x, startPos.y);
863
- await fastTimeout(500);
930
+ await fastTimeout(200);
864
931
  await humanLikeMouseMove(page, startPos.x, startPos.y, endPos.x, endPos.y);
865
932
 
866
933
  } catch (minimalErr) {
@@ -1016,7 +1083,8 @@ module.exports = {
1016
1083
  // Main interaction functions
1017
1084
  performPageInteraction,
1018
1085
  createInteractionConfig,
1019
-
1086
+ getCachedViewport,
1087
+ cleanupInteractionMemory,
1020
1088
  // Component functions for custom implementations
1021
1089
  humanLikeMouseMove,
1022
1090
  simulateScrolling,
package/nwss.js CHANGED
@@ -1,4 +1,4 @@
1
- // === Network scanner script (nwss.js) v1.0.65 ===
1
+ // === Network scanner script (nwss.js) v1.0.67 ===
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
@@ -85,7 +85,7 @@ const { navigateWithRedirectHandling, handleRedirectTimeout } = require('./lib/r
85
85
  const { monitorBrowserHealth, isBrowserHealthy } = require('./lib/browserhealth');
86
86
 
87
87
  // --- Script Configuration & Constants ---
88
- const VERSION = '1.0.65'; // Script version
88
+ const VERSION = '1.0.67'; // Script version
89
89
 
90
90
  // get startTime
91
91
  const startTime = Date.now();
@@ -1837,10 +1837,14 @@ function setupFrameHandling(page, forceDebug) {
1837
1837
  page.on('request', request => {
1838
1838
  const checkedUrl = request.url();
1839
1839
  const checkedHostname = safeGetDomain(checkedUrl, true);
1840
+ const checkedRootDomain = safeGetDomain(checkedUrl, false); // Root domain for first-party detection
1840
1841
  // Use effectiveCurrentUrl which gets updated after redirects
1841
1842
  // This ensures first-party detection uses the final redirected domain
1842
1843
  const effectiveCurrentHostname = safeGetDomain(effectiveCurrentUrl, true);
1843
- const isFirstParty = checkedHostname && effectiveCurrentHostname && checkedHostname === effectiveCurrentHostname;
1844
+ const effectiveCurrentRootDomain = safeGetDomain(effectiveCurrentUrl, false); // Root domain for comparison
1845
+
1846
+ // FIXED: Compare root domains instead of full hostnames for first-party detection
1847
+ const isFirstParty = checkedRootDomain && effectiveCurrentRootDomain && checkedRootDomain === effectiveCurrentRootDomain;
1844
1848
 
1845
1849
  // Block infinite iframe loops
1846
1850
  const frameUrl = request.frame() ? request.frame().url() : '';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fanboynz/network-scanner",
3
- "version": "1.0.65",
3
+ "version": "1.0.67",
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": {