@fanboynz/network-scanner 3.0.3 → 3.1.2
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 +53 -0
- package/lib/adblock-rust.js +17 -4
- package/lib/adblock.js +92 -15
- package/lib/browserhealth.js +41 -100
- package/lib/cdp.js +68 -34
- package/lib/clear_sitedata.js +68 -20
- package/lib/compress.js +26 -58
- package/lib/curl.js +44 -22
- package/lib/domain-cache.js +8 -57
- package/lib/dry-run.js +9 -4
- package/lib/fingerprint.js +599 -129
- package/lib/fingerprint.md +94 -0
- package/lib/interaction.js +262 -26
- package/lib/nettools.js +47 -76
- package/lib/openvpn_vpn.js +116 -35
- package/lib/proxy.js +6 -2
- package/lib/searchstring.js +15 -237
- package/lib/smart-cache.js +9 -1
- package/lib/socks-relay.js +14 -9
- package/lib/validate_rules.js +285 -3
- package/lib/wireguard_vpn.js +64 -12
- package/nwss.js +557 -220
- package/package.json +1 -1
- package/regex-tool/index.html +321 -628
package/lib/wireguard_vpn.js
CHANGED
|
@@ -33,8 +33,15 @@ function getExternalIP(interfaceName) {
|
|
|
33
33
|
// Track active interfaces for cleanup
|
|
34
34
|
const activeInterfaces = new Map();
|
|
35
35
|
|
|
36
|
-
// Temp config directory for inline configs
|
|
37
|
-
|
|
36
|
+
// Temp config directory for inline configs — namespaced per process.
|
|
37
|
+
// disconnectAll() rmSync's this whole directory on exit; a fixed shared path
|
|
38
|
+
// meant two concurrent nwss processes using inline configs would clobber each
|
|
39
|
+
// other: the first to exit deleted the survivor's live .conf, so its
|
|
40
|
+
// `wg-quick down <path>` then failed (file gone) and the kernel interface
|
|
41
|
+
// leaked. Scoping to the PID keeps each process's teardown to its own files.
|
|
42
|
+
// (Stale dirs from a crashed process are left for manual cleanup — sweeping
|
|
43
|
+
// them would reintroduce the same cross-process destruction this avoids.)
|
|
44
|
+
const TEMP_CONFIG_DIR = path.join('/tmp/nwss-wireguard', String(process.pid));
|
|
38
45
|
|
|
39
46
|
/**
|
|
40
47
|
* Check if running with sufficient privileges
|
|
@@ -85,9 +92,11 @@ function resolveInterfaceName(vpnConfig) {
|
|
|
85
92
|
* @returns {string} Path to temp config file
|
|
86
93
|
*/
|
|
87
94
|
function writeInlineConfig(interfaceName, configContent) {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
95
|
+
// mkdirSync with recursive:true is a no-op when the directory exists,
|
|
96
|
+
// so the prior existsSync gate was redundant. Saves one stat() syscall
|
|
97
|
+
// per VPN bringup and follows the standard "act, don't check-then-act"
|
|
98
|
+
// idiom (also avoids a microscopic TOCTOU window).
|
|
99
|
+
fs.mkdirSync(TEMP_CONFIG_DIR, { recursive: true, mode: 0o700 });
|
|
91
100
|
|
|
92
101
|
const configPath = path.join(TEMP_CONFIG_DIR, `${interfaceName}.conf`);
|
|
93
102
|
fs.writeFileSync(configPath, configContent, { mode: 0o600 });
|
|
@@ -114,6 +123,14 @@ function interfaceUp(configPath, interfaceName, forceDebug = false) {
|
|
|
114
123
|
// spawnSync with arg array (no shell) — configPath comes from user
|
|
115
124
|
// JSON, so naive `sudo wg-quick up "${configPath}"` was vulnerable
|
|
116
125
|
// to a `";rm -rf ~;"` payload escaping the quotes.
|
|
126
|
+
//
|
|
127
|
+
// NOTE: an "already exists" failure here usually means a previous
|
|
128
|
+
// run's teardown failed and left the kernel interface alive. Recover
|
|
129
|
+
// manually with `sudo wg-quick down <name>` or `sudo ip link delete
|
|
130
|
+
// <name>`. A self-heal mechanism was tried (commit e032bde) and
|
|
131
|
+
// reverted because it raced with concurrent nwss processes sharing
|
|
132
|
+
// the same VPN config — process B's self-heal would destroy process
|
|
133
|
+
// A's live interface. Keep the manual-recovery default for safety.
|
|
117
134
|
const upRes = spawnSync('sudo', ['wg-quick', 'up', configPath], {
|
|
118
135
|
encoding: 'utf8',
|
|
119
136
|
timeout: 15000
|
|
@@ -193,10 +210,12 @@ function interfaceDown(interfaceName, forceDebug = false) {
|
|
|
193
210
|
// down failure where the kernel interface might persist briefly.
|
|
194
211
|
// Was previously only inside the try block, so failure paths
|
|
195
212
|
// leaked the temp file.
|
|
213
|
+
//
|
|
214
|
+
// No existsSync gate — unlinkSync throws ENOENT for missing files
|
|
215
|
+
// and the try/catch already swallows it. Saves one stat() and
|
|
216
|
+
// closes a microscopic TOCTOU window.
|
|
196
217
|
const tempPath = path.join(TEMP_CONFIG_DIR, `${interfaceName}.conf`);
|
|
197
|
-
|
|
198
|
-
try { fs.unlinkSync(tempPath); } catch {}
|
|
199
|
-
}
|
|
218
|
+
try { fs.unlinkSync(tempPath); } catch {}
|
|
200
219
|
}
|
|
201
220
|
}
|
|
202
221
|
|
|
@@ -310,6 +329,22 @@ function validateVpnConfig(vpnConfig) {
|
|
|
310
329
|
result.warnings.push('Both "config" and "config_inline" provided; "config" takes precedence');
|
|
311
330
|
}
|
|
312
331
|
|
|
332
|
+
// F1: Validate user-provided interface name to prevent path traversal via
|
|
333
|
+
// resolveInterfaceName → writeInlineConfig's path.join. Also enforces
|
|
334
|
+
// Linux IFNAMSIZ (15 chars max). User would need to attack their own
|
|
335
|
+
// config (same trust boundary as rest of nwss + must run as root for WG),
|
|
336
|
+
// so this is defensive rather than security-critical — but the validation
|
|
337
|
+
// catches typos that would otherwise produce confusing wg-quick errors.
|
|
338
|
+
if (vpnConfig.interface !== undefined && vpnConfig.interface !== null) {
|
|
339
|
+
if (typeof vpnConfig.interface !== 'string' || !/^[a-zA-Z0-9_-]{1,15}$/.test(vpnConfig.interface)) {
|
|
340
|
+
result.isValid = false;
|
|
341
|
+
result.errors.push(
|
|
342
|
+
`Invalid 'interface' name ${JSON.stringify(vpnConfig.interface)}: ` +
|
|
343
|
+
`must match /^[a-zA-Z0-9_-]{1,15}$/ (Linux IFNAMSIZ limit + path-safe chars)`
|
|
344
|
+
);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
313
348
|
// Validate config file exists
|
|
314
349
|
if (vpnConfig.config) {
|
|
315
350
|
const configPath = vpnConfig.config;
|
|
@@ -365,8 +400,11 @@ async function connectForSite(siteConfig, forceDebug = false) {
|
|
|
365
400
|
|
|
366
401
|
const interfaceName = resolveInterfaceName(vpnConfig);
|
|
367
402
|
|
|
368
|
-
// Resolve config path
|
|
369
|
-
|
|
403
|
+
// Resolve config path. A file-based config path is fixed up-front; an
|
|
404
|
+
// inline config is a temp file we (re)write inside the retry loop below
|
|
405
|
+
// (it can't be hoisted — see the writeInlineConfig call for why).
|
|
406
|
+
const useInlineConfig = !vpnConfig.config;
|
|
407
|
+
let configPath = null;
|
|
370
408
|
if (vpnConfig.config) {
|
|
371
409
|
configPath = vpnConfig.config;
|
|
372
410
|
// Resolve wg-quick style: if no path separators, look in /etc/wireguard/
|
|
@@ -376,8 +414,6 @@ async function connectForSite(siteConfig, forceDebug = false) {
|
|
|
376
414
|
configPath = etcPath;
|
|
377
415
|
}
|
|
378
416
|
}
|
|
379
|
-
} else {
|
|
380
|
-
configPath = writeInlineConfig(interfaceName, vpnConfig.config_inline);
|
|
381
417
|
}
|
|
382
418
|
|
|
383
419
|
const maxAttempts = vpnConfig.retry ? vpnConfig.max_retries + 1 : 1;
|
|
@@ -395,9 +431,25 @@ async function connectForSite(siteConfig, forceDebug = false) {
|
|
|
395
431
|
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
396
432
|
}
|
|
397
433
|
|
|
434
|
+
// (Re)write the inline temp config before each attempt. interfaceDown's
|
|
435
|
+
// finally unlinks the temp .conf (both the retry reset just above and any
|
|
436
|
+
// teardown from a prior run), so hoisting this above the loop meant a
|
|
437
|
+
// retry's `wg-quick up` ran against a deleted file and always failed.
|
|
438
|
+
// Idempotent on the first attempt.
|
|
439
|
+
if (useInlineConfig) {
|
|
440
|
+
configPath = writeInlineConfig(interfaceName, vpnConfig.config_inline);
|
|
441
|
+
}
|
|
442
|
+
|
|
398
443
|
const upResult = interfaceUp(configPath, interfaceName, forceDebug);
|
|
399
444
|
if (!upResult.success) {
|
|
400
445
|
if (attempt === maxAttempts) {
|
|
446
|
+
// interfaceUp never registered the interface (no activeInterfaces
|
|
447
|
+
// entry), so interfaceDown's finally won't run to clean the temp.
|
|
448
|
+
// Unlink the inline config here so a fully-failed connect doesn't
|
|
449
|
+
// leave a PrivateKey-bearing .conf in /tmp until process exit.
|
|
450
|
+
if (useInlineConfig && configPath) {
|
|
451
|
+
try { fs.unlinkSync(configPath); } catch {}
|
|
452
|
+
}
|
|
401
453
|
return upResult;
|
|
402
454
|
}
|
|
403
455
|
continue;
|