@fanboynz/network-scanner 2.0.19 → 2.0.21

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/README.md CHANGED
@@ -35,8 +35,8 @@ A Puppeteer-based tool for scanning websites to find third-party (or optionally
35
35
 
36
36
  | Argument | Description |
37
37
  |:---------------------------|:------------|
38
- | `--localhost` | Output as `127.0.0.1 domain.com` |
39
- | `--localhost-0.0.0.0` | Output as `0.0.0.0 domain.com` |
38
+ | `--localhost[=IP]` | Output as `IP domain.com` (default: 127.0.0.1) |
39
+ | | Examples: `--localhost`, `--localhost=0.0.0.0`, `--localhost=192.168.1.1` |
40
40
  | `--plain` | Output just domains (no adblock formatting) |
41
41
  | `--dnsmasq` | Output as `local=/domain.com/` (dnsmasq format) |
42
42
  | `--dnsmasq-old` | Output as `server=/domain.com/` (dnsmasq old format) |
@@ -253,6 +253,7 @@ When a page redirects to a new domain, first-party/third-party detection is base
253
253
  | `source` | Boolean | `false` | Save page source HTML after load |
254
254
  | `screenshot` | Boolean | `false` | Capture screenshot on load failure |
255
255
  | `headful` | Boolean | `false` | Launch browser with GUI for this site |
256
+ | `localhost` | String | - | Force custom IP format for this site (e.g., "127.0.0.1", "0.0.0.0", "192.168.1.1") |
256
257
  | `adblock_rules` | Boolean | `false` | Generate adblock filter rules with resource types for this site |
257
258
  | `interact_duration` | Milliseconds | `2000` | Duration of interaction simulation |
258
259
  | `interact_scrolling` | Boolean | `true` | Enable scrolling simulation |
package/lib/output.js CHANGED
@@ -93,8 +93,7 @@ function extractDomainFromRule(rule) {
93
93
  * Formats a domain according to the specified output mode
94
94
  * @param {string} domain - The domain to format
95
95
  * @param {object} options - Formatting options
96
- * @param {boolean} options.localhost - Use 127.0.0.1 format
97
- * @param {boolean} options.localhostAlt - Use 0.0.0.0 format
96
+ * @param {string|null} options.localhostIP - Use custom IP format (e.g., '127.0.0.1', '0.0.0.0')
98
97
  * @param {boolean} options.plain - Use plain domain format (no adblock syntax)
99
98
  * @param {boolean} options.adblockRules - Generate adblock filter rules with resource types
100
99
  * @param {boolean} options.dnsmasq - Use dnsmasq local format
@@ -106,7 +105,7 @@ function extractDomainFromRule(rule) {
106
105
  * @returns {string} The formatted domain
107
106
  */
108
107
  function formatDomain(domain, options = {}) {
109
- const { localhost = false, localhostAlt = false, plain = false, adblockRules = false, dnsmasq = false, dnsmasqOld = false, unbound = false, privoxy = false, pihole = false, resourceType = null } = options;
108
+ const { localhostIP = null, plain = false, adblockRules = false, dnsmasq = false, dnsmasqOld = false, unbound = false, privoxy = false, pihole = false, resourceType = null } = options;
110
109
 
111
110
 
112
111
  // Validate domain length and format
@@ -132,10 +131,8 @@ function formatDomain(domain, options = {}) {
132
131
  return `server=/${domain}/`;
133
132
  } else if (unbound) {
134
133
  return `local-zone: "${domain}." always_null`;
135
- } else if (localhost) {
136
- return `127.0.0.1 ${domain}`;
137
- } else if (localhostAlt) {
138
- return `0.0.0.0 ${domain}`;
134
+ } else if (localhostIP) {
135
+ return `${localhostIP} ${domain}`;
139
136
  } else if (adblockRules && resourceType) {
140
137
  // Generate adblock filter rules with resource type modifiers
141
138
  return `||${domain}^${resourceType}`;
@@ -178,8 +175,7 @@ function mapResourceTypeToAdblockModifier(resourceType) {
178
175
  */
179
176
  function formatRules(matchedDomains, siteConfig = {}, globalOptions = {}) {
180
177
  const {
181
- localhostMode = false,
182
- localhostModeAlt = false,
178
+ localhostIP = null,
183
179
  plainOutput = false,
184
180
  adblockRulesMode = false,
185
181
  dnsmasqMode = false,
@@ -190,8 +186,7 @@ function formatRules(matchedDomains, siteConfig = {}, globalOptions = {}) {
190
186
  } = globalOptions;
191
187
 
192
188
  // Site-level overrides
193
- const siteLocalhost = siteConfig.localhost === true;
194
- const siteLocalhostAlt = siteConfig.localhost_0_0_0_0 === true;
189
+ const siteLocalhostIP = siteConfig.localhost || null;
195
190
  const sitePlainSetting = siteConfig.plain === true;
196
191
  const siteAdblockRules = siteConfig.adblock_rules === true;
197
192
  const siteDnsmasq = siteConfig.dnsmasq === true;
@@ -208,16 +203,14 @@ function formatRules(matchedDomains, siteConfig = {}, globalOptions = {}) {
208
203
  privoxyMode || sitePrivoxy,
209
204
  piholeMode || sitePihole,
210
205
  adblockRulesMode || siteAdblockRules,
211
- localhostMode || siteLocalhost,
212
- localhostModeAlt || siteLocalhostAlt,
206
+ (localhostIP || siteLocalhostIP) ? true : false,
213
207
  plainOutput || sitePlainSetting
214
208
  ].filter(Boolean).length;
215
209
 
216
210
  if (activeFormats > 1) {
217
211
  // Multiple formats specified - fall back to standard adblock format
218
212
  const formatOptions = {
219
- localhost: false,
220
- localhostAlt: false,
213
+ localhostIP: null,
221
214
  plain: false,
222
215
  adblockRules: false,
223
216
  dnsmasq: false,
@@ -240,8 +233,7 @@ function formatRules(matchedDomains, siteConfig = {}, globalOptions = {}) {
240
233
 
241
234
  // Determine final formatting options
242
235
  const formatOptions = {
243
- localhost: localhostMode || siteLocalhost,
244
- localhostAlt: localhostModeAlt || siteLocalhostAlt,
236
+ localhostIP: siteLocalhostIP || localhostIP,
245
237
  plain: plainOutput || sitePlainSetting,
246
238
  adblockRules: adblockRulesMode || siteAdblockRules,
247
239
  dnsmasq: dnsmasqMode || siteDnsmasq,
@@ -640,7 +632,7 @@ function handleOutput(results, config = {}) {
640
632
  * @returns {string} Human-readable format description
641
633
  */
642
634
  function getFormatDescription(options = {}) {
643
- const { localhost = false, localhostAlt = false, plain = false, adblockRules = false, dnsmasq = false, dnsmasqOld = false, unbound = false, privoxy = false, pihole = false } = options;
635
+ const { localhostIP = null, plain = false, adblockRules = false, dnsmasq = false, dnsmasqOld = false, unbound = false, privoxy = false, pihole = false } = options;
644
636
 
645
637
  // Plain always takes precedence
646
638
  if (plain) {
@@ -659,10 +651,8 @@ function getFormatDescription(options = {}) {
659
651
  return 'Unbound format (local-zone: "domain.com." always_null)';
660
652
  } else if (adblockRules) {
661
653
  return 'Adblock filter rules with resource type modifiers (||domain.com^$script)';
662
- } else if (localhost) {
663
- return 'Localhost format (127.0.0.1 domain.com)';
664
- } else if (localhostAlt) {
665
- return 'Localhost format (0.0.0.0 domain.com)';
654
+ } else if (localhostIP) {
655
+ return `Localhost format (${localhostIP} domain.com)`;
666
656
  } else {
667
657
  return 'Adblock format (||domain.com^)';
668
658
  }
package/nwss.1 CHANGED
@@ -33,12 +33,15 @@ Enable colored console output for status messages.
33
33
 
34
34
  .SS Output Format Options
35
35
  .TP
36
- .B \--localhost
37
- Output rules as \fB127.0.0.1 domain.com\fR format for hosts file.
36
+ .BR \--localhost [ =\fIIP\fR ]
37
+ Output rules as \fBIP domain.com\fR format for hosts file. If no IP is specified, defaults to \fB127.0.0.1\fR.
38
38
 
39
- .TP
40
- .B \--localhost-0.0.0.0
41
- Output rules as \fB0.0.0.0 domain.com\fR format for hosts file.
39
+ Examples:
40
+ .RS
41
+ \fB\--localhost\fR (uses 127.0.0.1)
42
+ \fB\--localhost=0.0.0.0\fR
43
+ \fB\--localhost=192.168.1.1\fR
44
+ .RE
42
45
 
43
46
  .TP
44
47
  .B \--plain
@@ -300,7 +303,7 @@ Number of times to reload the page (default: 1).
300
303
 
301
304
  .TP
302
305
  .B forcereload
303
- Boolean. Force an additional reload after reloads.
306
+ Boolean or Array. Force cache-clearing reload for all URLs or specific domains. Can be \fBtrue\fR/\fBfalse\fR or array of domain names like \fB["domain1.com", "domain2.com"]\fR. When set to \fBtrue\fR, applies to all URLs in the site configuration. When set to an array, applies only to URLs whose domains match the specified domains. Supports exact hostname matching and subdomain matching (e.g., "example.com" matches both "example.com" and "subdomain.example.com"). Domain matching is case-insensitive and automatically handles protocols, ports, and paths.
304
307
 
305
308
  .TP
306
309
  .B timeout
@@ -578,11 +581,9 @@ Number. Output full subdomains instead of root domains (1/0).
578
581
 
579
582
  .TP
580
583
  .B localhost
581
- Boolean. Force localhost output format (127.0.0.1) for this site.
584
+ String. Force custom IP format for this site. Examples: \fB"127.0.0.1"\fR, \fB"0.0.0.0"\fR, \fB"192.168.1.1"\fR, \fB"10.0.0.1"\fR.
582
585
 
583
- .TP
584
- .B localhost_0_0_0_0
585
- Boolean. Force localhost output format (0.0.0.0) for this site.
586
+ When set, overrides global \fB\--localhost\fR flag for this specific site.
586
587
 
587
588
  .TP
588
589
  .B dnsmasq
@@ -783,6 +784,37 @@ With default settings (\fBignore_similar_threshold: 80\fR):
783
784
  }
784
785
  .EE
785
786
 
787
+ .SS Force reload examples:
788
+ .EX
789
+ # Force reload for all URLs in a site configuration
790
+ {
791
+ "url": ["https://site1.com", "https://site2.com"],
792
+ "filterRegex": "ads|tracking",
793
+ "forcereload": true
794
+ }
795
+
796
+ # Force reload only for specific domains
797
+ {
798
+ "url": [
799
+ "https://example.com/page1",
800
+ "https://test.org/page2",
801
+ "https://demo.net/page3"
802
+ ],
803
+ "filterRegex": "\\\\.(space|website)\\\\b",
804
+ "forcereload": ["example.com", "demo.net"],
805
+ "comments": [
806
+ "Only example.com and demo.net URLs get force reload",
807
+ "test.org URLs use standard reload"
808
+ ]
809
+ }
810
+
811
+ # Mixed configuration with domain-specific force reload
812
+ {
813
+ "url": "https://cdn.example.com/content/page.html",
814
+ "forcereload": ["example.com"]
815
+ }
816
+ .EE
817
+
786
818
  .SS Configuration with documentation comments:
787
819
  .EX
788
820
  {
@@ -970,9 +1002,11 @@ Format: \fB(^|\\.)domain\\.com$\fR
970
1002
  For Pi-hole regex filters. Blocks domain and subdomains at DNS level.
971
1003
 
972
1004
  .SS Hosts File Formats
973
- Flags: \fB\--localhost\fR, \fB\--localhost-0.0.0.0\fR
1005
+ Flag: \fB\--localhost[=IP]\fR
974
1006
  .br
975
- Formats: \fB127.0.0.1 domain.com\fR, \fB0.0.0.0 domain.com\fR
1007
+ Format: \fBIP domain.com\fR (default IP: 127.0.0.1)
1008
+ .br
1009
+ Examples: \fB\--localhost\fR, \fB\--localhost=0.0.0.0\fR, \fB\--localhost=10.0.0.1\fR
976
1010
  .br
977
1011
  For system hosts files.
978
1012
 
@@ -988,6 +1022,19 @@ Format: \fBdomain.com\fR
988
1022
  .br
989
1023
  Simple domain list without formatting.
990
1024
 
1025
+ .SS Custom IP Examples
1026
+ .EX
1027
+ # Standard localhost blocking
1028
+ node nwss.js -o hosts.txt --localhost
1029
+
1030
+ # Block to null route
1031
+ node nwss.js -o hosts.txt --localhost=0.0.0.0
1032
+
1033
+ # Route to local server
1034
+ node nwss.js -o hosts.txt --localhost=192.168.1.100
1035
+ node nwss.js -o hosts.txt --localhost=10.0.0.1
1036
+ .EE
1037
+
991
1038
  .SH FILES
992
1039
 
993
1040
  .TP
@@ -1090,6 +1137,16 @@ Report bugs to the project repository or maintainer.
1090
1137
  .BR unbound (8),
1091
1138
  .BR privoxy (8)
1092
1139
 
1140
+ .SH EXAMPLES OF CONFIG WITH CUSTOM LOCALHOST IP
1141
+ .EX
1142
+ {
1143
+ "url": "https://example.com",
1144
+ "filterRegex": "ads|tracking",
1145
+ "localhost": "10.0.0.1"
1146
+ }
1147
+ .EE
1148
+ This configuration will output rules in the format \fB10.0.0.1 domain.com\fR for this specific site, overriding any global \fB\--localhost\fR flag.
1149
+
1093
1150
  .SH AUTHORS
1094
1151
  Written for malware research and network security analysis.
1095
1152
 
package/nwss.js CHANGED
@@ -1,4 +1,4 @@
1
- // === Network scanner script (nwss.js) v2.0.19 ===
1
+ // === Network scanner script (nwss.js) v2.0.21 ===
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
@@ -130,7 +130,7 @@ const { navigateWithRedirectHandling, handleRedirectTimeout } = require('./lib/r
130
130
  const { monitorBrowserHealth, isBrowserHealthy, isQuicklyResponsive, performGroupWindowCleanup, performRealtimeWindowCleanup, trackPageForRealtime, updatePageUsage, cleanupPageBeforeReload } = require('./lib/browserhealth');
131
131
 
132
132
  // --- Script Configuration & Constants ---
133
- const VERSION = '2.0.19'; // Script version
133
+ const VERSION = '2.0.21'; // Script version
134
134
 
135
135
  // get startTime
136
136
  const startTime = Date.now();
@@ -173,8 +173,12 @@ const silentMode = args.includes('--silent');
173
173
  const showTitles = args.includes('--titles');
174
174
  const dumpUrls = args.includes('--dumpurls');
175
175
  const subDomainsMode = args.includes('--sub-domains');
176
- const localhostMode = args.includes('--localhost');
177
- const localhostModeAlt = args.includes('--localhost-0.0.0.0');
176
+ // Parse --localhost with optional IP address
177
+ let localhostIP = null;
178
+ const localhostIndex = args.findIndex(arg => arg.startsWith('--localhost'));
179
+ if (localhostIndex !== -1) {
180
+ localhostIP = args[localhostIndex].includes('=') ? args[localhostIndex].split('=')[1] : '127.0.0.1';
181
+ }
178
182
  const disableInteract = args.includes('--no-interact');
179
183
  const plainOutput = args.includes('--plain');
180
184
  const enableCDP = args.includes('--cdp');
@@ -230,7 +234,7 @@ if (adblockRulesMode) {
230
234
  if (!outputFile) {
231
235
  if (forceDebug) console.log(formatLogMessage('debug', `--adblock-rules ignored: requires --output (-o) to specify an output file`));
232
236
  adblockRulesMode = false;
233
- } else if (localhostMode || localhostModeAlt || plainOutput || dnsmasqMode || dnsmasqOldMode || unboundMode || privoxyMode || piholeMode) {
237
+ } else if (localhostIP || plainOutput || dnsmasqMode || dnsmasqOldMode || unboundMode || privoxyMode || piholeMode) {
234
238
  if (forceDebug) console.log(formatLogMessage('debug', `--adblock-rules ignored: incompatible with localhost/plain output modes`));
235
239
  adblockRulesMode = false;
236
240
  }
@@ -238,7 +242,7 @@ if (adblockRulesMode) {
238
242
 
239
243
  // Validate --dnsmasq usage
240
244
  if (dnsmasqMode) {
241
- if (localhostMode || localhostModeAlt || plainOutput || adblockRulesMode || dnsmasqOldMode || unboundMode || privoxyMode || piholeMode) {
245
+ if (localhostIP || plainOutput || adblockRulesMode || dnsmasqOldMode || unboundMode || privoxyMode || piholeMode) {
242
246
  if (forceDebug) console.log(formatLogMessage('debug', `--dnsmasq-old ignored: incompatible with localhost/plain/adblock-rules/dnsmasq output modes`));
243
247
  dnsmasqMode = false;
244
248
  }
@@ -246,7 +250,7 @@ if (dnsmasqMode) {
246
250
 
247
251
  // Validate --dnsmasq-old usage
248
252
  if (dnsmasqOldMode) {
249
- if (localhostMode || localhostModeAlt || plainOutput || adblockRulesMode || dnsmasqMode || unboundMode || privoxyMode || piholeMode) {
253
+ if (localhostIP || plainOutput || adblockRulesMode || dnsmasqMode || unboundMode || privoxyMode || piholeMode) {
250
254
  if (forceDebug) console.log(formatLogMessage('debug', `--dnsmasq-old ignored: incompatible with localhost/plain/adblock-rules/dnsmasq output modes`));
251
255
  dnsmasqOldMode = false;
252
256
  }
@@ -254,7 +258,7 @@ if (dnsmasqOldMode) {
254
258
 
255
259
  // Validate --unbound usage
256
260
  if (unboundMode) {
257
- if (localhostMode || localhostModeAlt || plainOutput || adblockRulesMode || dnsmasqMode || dnsmasqOldMode || privoxyMode || piholeMode) {
261
+ if (localhostIP || plainOutput || adblockRulesMode || dnsmasqMode || dnsmasqOldMode || privoxyMode || piholeMode) {
258
262
  if (forceDebug) console.log(formatLogMessage('debug', `--unbound ignored: incompatible with localhost/plain/adblock-rules/dnsmasq output modes`));
259
263
  unboundMode = false;
260
264
  }
@@ -262,7 +266,7 @@ if (unboundMode) {
262
266
 
263
267
  // Validate --privoxy usage
264
268
  if (privoxyMode) {
265
- if (localhostMode || localhostModeAlt || plainOutput || adblockRulesMode || dnsmasqMode || dnsmasqOldMode || unboundMode || piholeMode) {
269
+ if (localhostIP || plainOutput || adblockRulesMode || dnsmasqMode || dnsmasqOldMode || unboundMode || piholeMode) {
266
270
  if (forceDebug) console.log(formatLogMessage('debug', `--privoxy ignored: incompatible with localhost/plain/adblock-rules/dnsmasq/unbound output modes`));
267
271
  privoxyMode = false;
268
272
  }
@@ -270,7 +274,7 @@ if (privoxyMode) {
270
274
 
271
275
  // Validate --pihole usage
272
276
  if (piholeMode) {
273
- if (localhostMode || localhostModeAlt || plainOutput || adblockRulesMode || dnsmasqMode || dnsmasqOldMode || unboundMode || privoxyMode) {
277
+ if (localhostIP || plainOutput || adblockRulesMode || dnsmasqMode || dnsmasqOldMode || unboundMode || privoxyMode) {
274
278
  if (forceDebug) console.log(formatLogMessage('debug', `--pihole ignored: incompatible with localhost/plain/adblock-rules/dnsmasq/unbound/privoxy output modes`));
275
279
  piholeMode = false;
276
280
  }
@@ -430,8 +434,8 @@ Options:
430
434
  --append Append new rules to output file instead of overwriting (requires -o)
431
435
 
432
436
  Output Format Options:
433
- --localhost Output as 127.0.0.1 domain.com
434
- --localhost-0.0.0.0 Output as 0.0.0.0 domain.com
437
+ --localhost[=IP] Output as IP domain.com (default: 127.0.0.1)
438
+ Examples: --localhost, --localhost=0.0.0.0, --localhost=192.168.1.1
435
439
  --plain Output just domains (no adblock formatting)
436
440
  --dnsmasq Output as local=/domain.com/ (dnsmasq format)
437
441
  --dnsmasq-old Output as server=/domain.com/ (dnsmasq old format)
@@ -512,7 +516,7 @@ Redirect Handling Options:
512
516
  interact_intensity: "low"|"medium"|"high" Interaction simulation intensity (default: medium)
513
517
  delay: <milliseconds> Delay after load (default: 4000)
514
518
  reload: <number> Reload page n times after load (default: 1)
515
- forcereload: true/false Force an additional reload after reloads
519
+ forcereload: true/false or ["domain1.com", "domain2.com"] Force cache-clearing reload for all URLs or specific domains
516
520
  clear_sitedata: true/false Clear all cookies, cache, storage before each load (default: false)
517
521
  subDomains: 1/0 Output full subdomains (default: 0)
518
522
  localhost: true/false Force localhost output (127.0.0.1)
@@ -1478,8 +1482,7 @@ function setupFrameHandling(page, forceDebug) {
1478
1482
  const allowFirstParty = siteConfig.firstParty === true || siteConfig.firstParty === 1;
1479
1483
  const allowThirdParty = siteConfig.thirdParty === undefined || siteConfig.thirdParty === true || siteConfig.thirdParty === 1;
1480
1484
  const perSiteSubDomains = siteConfig.subDomains === 1 ? true : subDomainsMode;
1481
- const siteLocalhost = siteConfig.localhost === true;
1482
- const siteLocalhostAlt = siteConfig.localhost_0_0_0_0 === true;
1485
+ const siteLocalhostIP = siteConfig.localhost || null;
1483
1486
  const cloudflarePhishBypass = siteConfig.cloudflare_phish === true;
1484
1487
  const cloudflareBypass = siteConfig.cloudflare_bypass === true;
1485
1488
  // Add redirect and same-page loop protection
@@ -3199,7 +3202,80 @@ function setupFrameHandling(page, forceDebug) {
3199
3202
  updatePageUsage(page, true);
3200
3203
 
3201
3204
  const totalReloads = (siteConfig.reload || 1) - 1; // Subtract 1 because initial load counts as first
3202
- const useForceReload = siteConfig.forcereload === true;
3205
+
3206
+ // Enhanced forcereload logic: support boolean or domain array
3207
+ let useForceReload = false;
3208
+ if (siteConfig.forcereload === true) {
3209
+ // Original behavior: force reload for all URLs
3210
+ useForceReload = true;
3211
+ } else if (Array.isArray(siteConfig.forcereload)) {
3212
+ // Input validation: filter out invalid entries
3213
+ const validDomains = siteConfig.forcereload.filter(domain => {
3214
+ if (typeof domain !== 'string') {
3215
+ if (forceDebug) {
3216
+ console.log(formatLogMessage('debug', `Invalid forcereload entry (not string): ${typeof domain} - ${JSON.stringify(domain)}`));
3217
+ }
3218
+ return false;
3219
+ }
3220
+
3221
+ if (domain.trim() === '') {
3222
+ if (forceDebug) {
3223
+ console.log(formatLogMessage('debug', `Invalid forcereload entry (empty string)`));
3224
+ }
3225
+ return false;
3226
+ }
3227
+
3228
+ return true;
3229
+ });
3230
+
3231
+ if (validDomains.length === 0) {
3232
+ if (forceDebug) {
3233
+ console.log(formatLogMessage('debug', `No valid domains in forcereload array for ${currentUrl}`));
3234
+ }
3235
+ useForceReload = false;
3236
+ } else {
3237
+ // New behavior: force reload only for matching domains
3238
+ const currentDomain = safeGetDomain(currentUrl, true); // Get full hostname
3239
+ const currentRootDomain = safeGetDomain(currentUrl, false); // Get root domain
3240
+
3241
+ useForceReload = validDomains.some(domain => {
3242
+ // Enhanced domain cleaning: handle protocols, ports, paths, and normalize case
3243
+ let cleanDomain = domain.trim();
3244
+ cleanDomain = cleanDomain.replace(/^https?:\/\//, ''); // Remove protocol
3245
+ cleanDomain = cleanDomain.replace(/:\d+$/, ''); // Remove port (e.g., :8080)
3246
+ cleanDomain = cleanDomain.replace(/\/.*$/, ''); // Remove path
3247
+ cleanDomain = cleanDomain.toLowerCase(); // Normalize case
3248
+
3249
+ // Additional validation: basic domain format check
3250
+ if (!/^[a-z0-9.-]+$/.test(cleanDomain)) {
3251
+ if (forceDebug) {
3252
+ console.log(formatLogMessage('debug', `Skipping invalid domain format in forcereload: ${domain} -> ${cleanDomain}`));
3253
+ }
3254
+ return false;
3255
+ }
3256
+
3257
+ // Check if current URL matches this domain
3258
+ // Support both exact hostname match and subdomain match
3259
+ if (currentDomain.toLowerCase() === cleanDomain || currentRootDomain.toLowerCase() === cleanDomain) {
3260
+ return true;
3261
+ }
3262
+
3263
+ // Check if current hostname ends with the domain (subdomain match)
3264
+ if (currentDomain.toLowerCase().endsWith('.' + cleanDomain)) {
3265
+ return true;
3266
+ }
3267
+
3268
+ return false;
3269
+ });
3270
+ }
3271
+
3272
+ if (forceDebug && useForceReload) {
3273
+ console.log(formatLogMessage('debug', `Force reload enabled for ${currentUrl} - matches domain in forcereload list`));
3274
+ } else if (forceDebug && validDomains.length > 0) {
3275
+ console.log(formatLogMessage('debug', `Force reload not applied for ${currentUrl} - no domain match in [${validDomains.join(', ')}]`));
3276
+ }
3277
+ }
3278
+ // If forcereload is not specified, false, or any other value, useForceReload remains false
3203
3279
 
3204
3280
  if (useForceReload && forceDebug) {
3205
3281
  console.log(formatLogMessage('debug', `Using force reload mechanism for all ${totalReloads + 1} reload(s) on ${currentUrl}`));
@@ -3392,8 +3468,7 @@ function setupFrameHandling(page, forceDebug) {
3392
3468
  } else {
3393
3469
  // Format rules using the output module
3394
3470
  const globalOptions = {
3395
- localhostMode,
3396
- localhostModeAlt,
3471
+ localhostIP,
3397
3472
  plainOutput,
3398
3473
  adblockRulesMode,
3399
3474
  dnsmasqMode,
@@ -3434,8 +3509,7 @@ function setupFrameHandling(page, forceDebug) {
3434
3509
  // For other errors, preserve any matches we found before the error
3435
3510
  if (matchedDomains && (matchedDomains.size > 0 || (matchedDomains instanceof Map && matchedDomains.size > 0))) {
3436
3511
  const globalOptions = {
3437
- localhostMode,
3438
- localhostModeAlt,
3512
+ localhostIP,
3439
3513
  plainOutput,
3440
3514
  adblockRulesMode,
3441
3515
  dnsmasqMode,
@@ -3883,8 +3957,7 @@ function setupFrameHandling(page, forceDebug) {
3883
3957
  const detectedDomainsCount = getDetectedDomainsCount();
3884
3958
  if (forceDebug) {
3885
3959
  const globalOptions = {
3886
- localhostMode,
3887
- localhostModeAlt,
3960
+ localhostIP,
3888
3961
  plainOutput,
3889
3962
  adblockRules: adblockRulesMode,
3890
3963
  dnsmasq: dnsmasqMode,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fanboynz/network-scanner",
3
- "version": "2.0.19",
3
+ "version": "2.0.21",
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": {