@fanboynz/network-scanner 3.1.2 → 3.3.0
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/CHANGELOG.md +52 -1
- package/CLAUDE.md +2 -1
- package/README.md +67 -7
- package/eslint.config.mjs +13 -1
- package/lib/browserhealth.js +25 -3
- package/lib/dns.js +238 -0
- package/lib/domain-cache.js +14 -127
- package/lib/ghost-cursor.js +29 -11
- package/lib/interaction.js +4 -0
- package/lib/nettools.js +157 -54
- package/lib/openvpn_vpn.js +8 -0
- package/lib/output.js +24 -13
- package/lib/redirect.js +4 -1
- package/lib/validate_rules.js +16 -1
- package/lib/wireguard_vpn.js +8 -0
- package/nwss.1 +84 -15
- package/nwss.js +536 -164
- package/package.json +1 -1
package/lib/redirect.js
CHANGED
|
@@ -19,7 +19,10 @@ async function navigateWithRedirectHandling(page, currentUrl, siteConfig, gotoOp
|
|
|
19
19
|
let httpStatus = null;
|
|
20
20
|
let cfRay = null;
|
|
21
21
|
const jsRedirectTimeout = siteConfig.js_redirect_timeout || 5000; // Wait 5s for JS redirects
|
|
22
|
-
|
|
22
|
+
// Use a number check, not || , so max_redirects: 0 (follow none) isn't
|
|
23
|
+
// swallowed as falsy and silently bumped to 10. Only absent/negative/non-number defaults.
|
|
24
|
+
const maxRedirects = (typeof siteConfig.max_redirects === 'number' && siteConfig.max_redirects >= 0)
|
|
25
|
+
? siteConfig.max_redirects : 10;
|
|
23
26
|
const detectJSPatterns = siteConfig.detect_js_patterns !== false; // Default to true
|
|
24
27
|
|
|
25
28
|
// Monitor frame navigations to detect redirects
|
package/lib/validate_rules.js
CHANGED
|
@@ -806,7 +806,6 @@ function cleanRulesetFile(filePath, outputPath = null, options = {}) {
|
|
|
806
806
|
} = options;
|
|
807
807
|
|
|
808
808
|
const fs = require('fs');
|
|
809
|
-
const path = require('path');
|
|
810
809
|
|
|
811
810
|
let content;
|
|
812
811
|
try {
|
|
@@ -1118,6 +1117,7 @@ const KNOWN_SITE_CONFIG_KEYS = new Set([
|
|
|
1118
1117
|
'ignore_similar_threshold', 'interact', 'interact_click_count', 'interact_clicks',
|
|
1119
1118
|
'interact_duration', 'interact_intensity', 'interact_scrolling', 'isBrave',
|
|
1120
1119
|
'js_redirect_timeout', 'localhost', 'max_redirects', 'openvpn', 'pihole',
|
|
1120
|
+
'output_regex',
|
|
1121
1121
|
'plain', 'privoxy', 'proxy', 'proxy_bypass', 'proxy_debug', 'proxy_remote_dns',
|
|
1122
1122
|
'realistic_click', 'referrer_disable', 'referrer_headers', 'regex_and',
|
|
1123
1123
|
'reload', 'resourceTypes', 'screenshot', 'searchstring', 'searchstring_and',
|
|
@@ -1307,6 +1307,21 @@ function normalizeSiteConfig(siteConfig, siteIndex = 0) {
|
|
|
1307
1307
|
}
|
|
1308
1308
|
}
|
|
1309
1309
|
|
|
1310
|
+
// 2b. output_regex must be a compilable regex. An invalid one is silently
|
|
1311
|
+
// disabled at runtime (the use-site try/catch falls back to ||host^), so
|
|
1312
|
+
// surface it here at load time where the user can fix it.
|
|
1313
|
+
if ('output_regex' in siteConfig && siteConfig.output_regex != null && siteConfig.output_regex !== '') {
|
|
1314
|
+
if (typeof siteConfig.output_regex !== 'string') {
|
|
1315
|
+
warnings.push(`${tag}: 'output_regex' should be a string regex, got ${JSON.stringify(siteConfig.output_regex)} — will be ignored`);
|
|
1316
|
+
} else {
|
|
1317
|
+
try {
|
|
1318
|
+
new RegExp(siteConfig.output_regex);
|
|
1319
|
+
} catch (e) {
|
|
1320
|
+
warnings.push(`${tag}: 'output_regex' is not a valid regex (${e.message}) — will be ignored, output falls back to ||host^`);
|
|
1321
|
+
}
|
|
1322
|
+
}
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1310
1325
|
// 3. String → single-element array coercion for fields that accept both
|
|
1311
1326
|
// forms (dig, dig-or, whois, whois-or). Downstream consumers all gate on
|
|
1312
1327
|
// Array.isArray(), so a bare string value previously silently disabled
|
package/lib/wireguard_vpn.js
CHANGED
|
@@ -388,6 +388,14 @@ function validateVpnConfig(vpnConfig) {
|
|
|
388
388
|
* @returns {Promise<Object>} { success, interface, error }
|
|
389
389
|
*/
|
|
390
390
|
async function connectForSite(siteConfig, forceDebug = false) {
|
|
391
|
+
// Platform guard: WireGuard routing here relies on the iproute2 `ip` command
|
|
392
|
+
// and wg-quick conventions, which are Linux-only. Fail with a clear message
|
|
393
|
+
// instead of a cryptic `ip: command not found` on macOS/Windows. WSL2 reports
|
|
394
|
+
// 'linux' and passes.
|
|
395
|
+
if (process.platform !== 'linux') {
|
|
396
|
+
return { success: false, error: `WireGuard routing is currently Linux-only (needs the iproute2 'ip' command + wg-quick; not available on ${process.platform}). Run on Linux/WSL2, or remove the 'vpn' option from the site config.` };
|
|
397
|
+
}
|
|
398
|
+
|
|
391
399
|
const vpnConfig = normalizeVpnConfig(siteConfig.vpn);
|
|
392
400
|
if (!vpnConfig) {
|
|
393
401
|
return { success: false, error: 'Invalid VPN configuration' };
|
package/nwss.1
CHANGED
|
@@ -72,10 +72,6 @@ Output as \fB(^|\\.)domain\\.com$\fR format for Pi-hole regex filters.
|
|
|
72
72
|
Generate adblock filter rules with resource type modifiers (requires \fB\-o\fR).
|
|
73
73
|
|
|
74
74
|
.SS General Options
|
|
75
|
-
.TP
|
|
76
|
-
.B \--verbose
|
|
77
|
-
Enable verbose output globally for all sites.
|
|
78
|
-
|
|
79
75
|
.TP
|
|
80
76
|
.B \--debug
|
|
81
77
|
Enable debug mode with detailed logging of all network requests.
|
|
@@ -104,6 +100,10 @@ Output full subdomains instead of collapsing to root domains.
|
|
|
104
100
|
.B \--no-interact
|
|
105
101
|
Disable mouse simulation and page interaction globally.
|
|
106
102
|
|
|
103
|
+
.TP
|
|
104
|
+
.B \--ghost-cursor
|
|
105
|
+
Use ghost-cursor Bezier mouse movements globally (requires \fBnpm i ghost-cursor\fR). See \fBGhost Cursor Options\fR. Equivalent to per-site \fBcursor_mode: "ghost"\fR.
|
|
106
|
+
|
|
107
107
|
.TP
|
|
108
108
|
.BR \--custom-json " \fIFILE\fR"
|
|
109
109
|
Use \fIFILE\fR instead of \fBconfig.json\fR for configuration.
|
|
@@ -136,10 +136,31 @@ Remove Chrome/Puppeteer temporary files before exit.
|
|
|
136
136
|
.BR \--max-concurrent " \fINUMBER\fR"
|
|
137
137
|
Maximum concurrent site processing (1-50, overrides config/default).
|
|
138
138
|
|
|
139
|
+
.TP
|
|
140
|
+
.BR \--dns " \fIIP\fR[,\fIIP\fR...]"
|
|
141
|
+
Nameserver(s) for the DNS pre-check AND nettools' dig \(em does not affect Chrome
|
|
142
|
+
navigation or whois. A single address pins all queries to it; several are
|
|
143
|
+
rotated per query (each leading once, the rest as failover) to spread the
|
|
144
|
+
load. Routing dig through these avoids dig timeouts on a flaky system resolver
|
|
145
|
+
silently dropping dig-gated domains. Overrides /etc/resolv.conf. Invalid
|
|
146
|
+
entries are warned and dropped.
|
|
147
|
+
|
|
139
148
|
.TP
|
|
140
149
|
.BR \--cleanup-interval " \fINUMBER\fR"
|
|
141
150
|
Browser restart interval in URLs processed (1-1000, overrides config/default).
|
|
142
151
|
|
|
152
|
+
.TP
|
|
153
|
+
.B \--show-dead-domains
|
|
154
|
+
At end of scan, list hostnames that did not resolve or were unreachable (\fBNXDOMAIN\fR/\fBENODATA\fR plus \fBERR_NAME_NOT_RESOLVED\fR/\fBERR_ADDRESS_UNREACHABLE\fR). Excludes blocks and timeouts, since those mean the domain is alive. Useful for pruning dead URLs.
|
|
155
|
+
|
|
156
|
+
.TP
|
|
157
|
+
.BI \--block-ads= FILE\fR[,\fIFILE\fR...]
|
|
158
|
+
Block ads/trackers during the scan using EasyList-format filter list(s) \(em network rules like \fB||domain^\fR, \fB/ads/*\fR, \fB||domain^$script\fR. Comma-separated for multiple lists. Cosmetic (\fB##\fR) rules are ignored; the scanned page's own top-level document is never blocked (only sub-resources).
|
|
159
|
+
|
|
160
|
+
.TP
|
|
161
|
+
.BI \--adblock-engine= js|rust
|
|
162
|
+
Matcher backend for \fB\-\-block-ads\fR (default: \fBjs\fR). \fBjs\fR is the built-in pure-JS matcher (no extra dependencies). \fBrust\fR uses Brave's \fBadblock-rs\fR \(em much faster on large lists, same rules and results, but requires \fBnpm install adblock-rs\fR (needs a Rust toolchain).
|
|
163
|
+
|
|
143
164
|
.TP
|
|
144
165
|
.BR \-h ", " \--help
|
|
145
166
|
Show help message and exit.
|
|
@@ -249,6 +270,10 @@ Regex pattern(s) to match suspicious requests.
|
|
|
249
270
|
.B regex_and
|
|
250
271
|
Boolean. Use AND logic for multiple filterRegex patterns - ALL patterns must match the same URL (default: false).
|
|
251
272
|
|
|
273
|
+
.TP
|
|
274
|
+
.B output_regex
|
|
275
|
+
String. Regex applied to each matched URL to build the rule body: capture group 1 (or the whole match) becomes \fB||<capture>\fR instead of \fB||host^\fR. For example \fB^https?://([^/]+/[^/]+/)\fR turns \fBhttps://host.com/script/abc.js\fR into \fB||host.com/script/\fR, collapsing randomized filenames under a path into one rule. The capture must include the host. If the regex does not match a URL, output falls back to \fB||host^\fR. Adblock-only; domain-based formats (dnsmasq, pihole, hosts, plain) emit the bare host.
|
|
276
|
+
|
|
252
277
|
.TP
|
|
253
278
|
.B comments
|
|
254
279
|
Documentation strings or notes - completely ignored by the scanner. Can be a single string or array of strings. Used for adding context, URLs, timestamps, or any documentation notes to configuration files.
|
|
@@ -293,6 +318,14 @@ Boolean. Simulate mouse movements and clicks.
|
|
|
293
318
|
.B interact_intensity
|
|
294
319
|
String. Interaction simulation intensity: \fB"low"\fR, \fB"medium"\fR, \fB"high"\fR (default: "medium").
|
|
295
320
|
|
|
321
|
+
.TP
|
|
322
|
+
.B interact_click_count
|
|
323
|
+
Integer. Number of random content-zone clicks per load, capped at 20 (default: 3). The default of 3 is a primary click plus two backups, since some ad SDKs suppress the first or second click as warmup.
|
|
324
|
+
|
|
325
|
+
.TP
|
|
326
|
+
.B realistic_click
|
|
327
|
+
Boolean. Higher click fidelity: denser mouse approach (15 steps), sub-pixel hand-tremor micro-moves during the press, and a small mouseup drift so the mousedown and mouseup coordinates differ. For sites that score click realism. Costs roughly 80-120ms per click (default: false).
|
|
328
|
+
|
|
296
329
|
.TP
|
|
297
330
|
.B delay
|
|
298
331
|
Milliseconds to wait after page load (default: 4000).
|
|
@@ -419,7 +452,7 @@ Object. Custom page.goto() options for Puppeteer navigation. Available options:
|
|
|
419
452
|
.IP \(bu 4
|
|
420
453
|
\fB"networkidle0"\fR - Wait until 0 network requests for 500ms
|
|
421
454
|
.IP \(bu 4
|
|
422
|
-
\fB"networkidle2"\fR - Wait until
|
|
455
|
+
\fB"networkidle2"\fR - Wait until \(<=2 network requests for 500ms
|
|
423
456
|
.RE
|
|
424
457
|
.IP \(bu 4
|
|
425
458
|
\fBtimeout\fR: Maximum navigation time in milliseconds (overrides site timeout)
|
|
@@ -479,15 +512,28 @@ Both modes wait 16 seconds before cleanup to allow final operations to complete,
|
|
|
479
512
|
|
|
480
513
|
.TP
|
|
481
514
|
|
|
482
|
-
.SS
|
|
515
|
+
.SS Popup Capture Options
|
|
516
|
+
.TP
|
|
517
|
+
.B capture_popups
|
|
518
|
+
Boolean. Capture popup windows opened during the scan and evaluate their landing URL and in-popup requests against \fBfilterRegex\fR/\fBdig\fR/\fBwhois\fR. Requires \fBinteract\fR plus interaction clicks to fire the user-gesture click that opens popups; \fBcapture_popups\fR alone registers the listener but no popups will fire (default: false).
|
|
519
|
+
|
|
520
|
+
.TP
|
|
521
|
+
.B interact_popups
|
|
522
|
+
Boolean. Mouse-click inside captured popups (content-zone clicks) so the chain cascades to its next redirect or ad. Requires \fBcapture_popups\fR. Clicks popups up to \fBcapture_popups_max_depth\fR minus 1 \(em the deepest captured popup is observed, not clicked (default: false).
|
|
523
|
+
|
|
524
|
+
.TP
|
|
525
|
+
.B capture_popups_max_depth
|
|
526
|
+
Integer. Maximum popup-chain depth to capture, e.g. \fBsite -> p1 -> p2 -> p3 -> destination\fR. Each extra level multiplies popups and time (default: 4).
|
|
483
527
|
|
|
484
528
|
.TP
|
|
485
|
-
.B
|
|
486
|
-
|
|
529
|
+
.B capture_popups_window_ms
|
|
530
|
+
Integer. Per-popup capture window in milliseconds before the popup is auto-closed (default: 5000).
|
|
531
|
+
|
|
532
|
+
.SS Redirect Handling Options
|
|
487
533
|
|
|
488
534
|
.TP
|
|
489
535
|
.B max_redirects
|
|
490
|
-
Number. Maximum number of redirects to follow (default: 10).
|
|
536
|
+
Number. Maximum number of redirects to follow (default: 10; 0 = follow none).
|
|
491
537
|
|
|
492
538
|
.TP
|
|
493
539
|
.B js_redirect_timeout
|
|
@@ -501,6 +547,29 @@ Boolean. Analyze page source for redirect patterns (default: true).
|
|
|
501
547
|
.B redirect_timeout_multiplier
|
|
502
548
|
Number. Increase timeout for redirected URLs (default: 1.5).
|
|
503
549
|
|
|
550
|
+
.SS Ghost Cursor Options
|
|
551
|
+
Optional Bezier-curve mouse engine (the \fBghost-cursor\fR npm package, install
|
|
552
|
+
with \fBnpm i ghost-cursor\fR). Falls back to the built-in mouse if not
|
|
553
|
+
installed. Enable per-site with \fBcursor_mode\fR or globally with the
|
|
554
|
+
\fB\-\-ghost-cursor\fR flag.
|
|
555
|
+
.TP
|
|
556
|
+
.B cursor_mode
|
|
557
|
+
String. Set to \fB"ghost"\fR to use ghost-cursor Bezier mouse movements for this site.
|
|
558
|
+
.TP
|
|
559
|
+
.B ghost_cursor_speed
|
|
560
|
+
Number. Movement speed multiplier (default: auto).
|
|
561
|
+
.TP
|
|
562
|
+
.B ghost_cursor_hesitate
|
|
563
|
+
Number. Delay in milliseconds before a click (default: 50).
|
|
564
|
+
.TP
|
|
565
|
+
.B ghost_cursor_overshoot
|
|
566
|
+
Number. Maximum overshoot distance in pixels before correcting back to the target (default: auto).
|
|
567
|
+
.TP
|
|
568
|
+
.B ghost_cursor_duration
|
|
569
|
+
Number. How long the Bezier movement loop runs, in milliseconds (default: \fBinteract_duration\fR or 2000). Part of this budget (up to half) is reserved for clicks.
|
|
570
|
+
.PP
|
|
571
|
+
Ghost-cursor only \fIclicks\fR when both \fBinteract\fR and \fBinteract_clicks\fR are true. With \fBrealistic_click\fR set, each press adds hand-tremor during the hold plus a mouseup drift so mousedown and mouseup coordinates differ. Ghost mode honors \fBinteract_click_count\fR (default 3, cap 20); since realistic clicks take roughly 600-700ms each, raise \fBghost_cursor_duration\fR (about \fBinteract_click_count\fR x 700 plus movement, e.g. 5000-8000) to fit all of them \(em the default 2000 fits about one click.
|
|
572
|
+
|
|
504
573
|
.SS Cloudflare Protection Options
|
|
505
574
|
|
|
506
575
|
.TP
|
|
@@ -678,15 +747,15 @@ Global and per-site boolean to enable similarity filtering against ignoreDomains
|
|
|
678
747
|
With default settings (\fBignore_similar_threshold: 80\fR):
|
|
679
748
|
.RS
|
|
680
749
|
.IP \(bu 4
|
|
681
|
-
\fBanimerco.com\fR vs \fBanimerco.org\fR
|
|
750
|
+
\fBanimerco.com\fR vs \fBanimerco.org\fR \(-> 100% similar \(-> Ignored
|
|
682
751
|
.IP \(bu 4
|
|
683
|
-
\fBgoogle.com\fR vs \fBgoogle.co.uk\fR
|
|
752
|
+
\fBgoogle.com\fR vs \fBgoogle.co.uk\fR \(-> 100% similar \(-> Ignored
|
|
684
753
|
.IP \(bu 4
|
|
685
|
-
\fBamazon.com\fR vs \fBamazon2.org\fR
|
|
754
|
+
\fBamazon.com\fR vs \fBamazon2.org\fR \(-> 89% similar \(-> Ignored
|
|
686
755
|
.IP \(bu 4
|
|
687
|
-
\fBfacebook.com\fR vs \fBfaceboook.com\fR
|
|
756
|
+
\fBfacebook.com\fR vs \fBfaceboook.com\fR \(-> 91% similar \(-> Ignored
|
|
688
757
|
.IP \(bu 4
|
|
689
|
-
\fBapple.com\fR vs \fBmicrosoft.com\fR
|
|
758
|
+
\fBapple.com\fR vs \fBmicrosoft.com\fR \(-> 0% similar \(-> Kept
|
|
690
759
|
.RE
|
|
691
760
|
|
|
692
761
|
.SH EXAMPLES
|
|
@@ -844,7 +913,7 @@ With default settings (\fBignore_similar_threshold: 80\fR):
|
|
|
844
913
|
|
|
845
914
|
.SS Run with debug mode and similarity filtering:
|
|
846
915
|
.EX
|
|
847
|
-
node nwss.js --debug --dry-run
|
|
916
|
+
node nwss.js --debug --dry-run
|
|
848
917
|
.EE
|
|
849
918
|
|
|
850
919
|
.SS Run with adblock output format:
|