@fanboynz/network-scanner 2.0.66 → 3.0.1
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/.github/workflows/npm-publish.yml +140 -11
- package/CHANGELOG.md +164 -0
- package/CLAUDE.md +40 -7
- package/README.md +29 -4
- package/lib/adblock-rust.js +23 -18
- package/lib/adblock.js +127 -82
- package/lib/browserexit.js +213 -203
- package/lib/browserhealth.js +85 -61
- package/lib/cdp.js +103 -81
- package/lib/clear_sitedata.js +61 -159
- package/lib/cloudflare.js +579 -409
- package/lib/colorize.js +29 -12
- package/lib/compare.js +16 -8
- package/lib/compress.js +2 -1
- package/lib/curl.js +287 -220
- package/lib/domain-cache.js +87 -40
- package/lib/dry-run.js +137 -194
- package/lib/fingerprint.js +341 -176
- package/lib/flowproxy.js +391 -188
- package/lib/ghost-cursor.js +8 -7
- package/lib/grep.js +248 -171
- package/lib/ignore_similar.js +70 -124
- package/lib/interaction.js +132 -235
- package/lib/nettools.js +309 -87
- package/lib/openvpn_vpn.js +12 -11
- package/lib/output.js +92 -59
- package/lib/post-processing.js +216 -162
- package/lib/proxy.js +9 -2
- package/lib/redirect.js +47 -31
- package/lib/referrer.js +158 -165
- package/lib/searchstring.js +290 -381
- package/lib/smart-cache.js +141 -91
- package/lib/socks-relay.js +21 -7
- package/lib/spawn-async.js +137 -0
- package/lib/validate_rules.js +188 -176
- package/lib/wireguard_vpn.js +111 -117
- package/nwss.js +743 -159
- package/package.json +4 -4
- package/scripts/test-stealth.js +281 -0
package/lib/adblock.js
CHANGED
|
@@ -4,6 +4,10 @@
|
|
|
4
4
|
|
|
5
5
|
const fs = require('fs');
|
|
6
6
|
const psl = require('psl');
|
|
7
|
+
const { formatLogMessage, messageColors } = require('./colorize');
|
|
8
|
+
// Subsystem tag matches the project convention (other modules use
|
|
9
|
+
// flowproxy/cloudflare/curl/grep/etc. precomputed tags).
|
|
10
|
+
const ADBLOCK_TAG = messageColors.processing('[adblock]');
|
|
7
11
|
|
|
8
12
|
// Hoisted constants — avoid recreating per rule (~80K times for EasyList)
|
|
9
13
|
const COSMETIC_OPTIONS = new Set(['generichide', 'elemhide', 'specifichide', 'genericblock']);
|
|
@@ -15,32 +19,41 @@ const PARSE_TYPE_MAP = {
|
|
|
15
19
|
};
|
|
16
20
|
|
|
17
21
|
/**
|
|
18
|
-
*
|
|
19
|
-
*
|
|
22
|
+
* FIFO cache with a hard size cap. Evicts the OLDEST-inserted entry on
|
|
23
|
+
* overflow (not the least-recently-used) — this is FIFO, not LRU, despite
|
|
24
|
+
* the original "Simple LRU cache" naming. Proper LRU would require
|
|
25
|
+
* deleting-and-reinserting on every .get() hit to bubble accessed keys
|
|
26
|
+
* to the end, which adds per-hit Map.delete + Map.set overhead. In hot
|
|
27
|
+
* paths (32k-entry result cache hit thousands of times per scan), that
|
|
28
|
+
* overhead is real; FIFO with a generous cap is the simpler trade.
|
|
29
|
+
*
|
|
30
|
+
* If access-pattern shifts become a real problem (a few "always hot"
|
|
31
|
+
* URLs evicted while never-used entries survive), implement move-on-get
|
|
32
|
+
* here — the API stays compatible.
|
|
20
33
|
*/
|
|
21
|
-
class
|
|
34
|
+
class FIFOCache {
|
|
22
35
|
constructor(maxSize = 1000) {
|
|
23
36
|
this.cache = new Map();
|
|
24
37
|
this.maxSize = maxSize;
|
|
25
38
|
}
|
|
26
|
-
|
|
39
|
+
|
|
27
40
|
get(url) {
|
|
28
41
|
return this.cache.get(url);
|
|
29
42
|
}
|
|
30
|
-
|
|
43
|
+
|
|
31
44
|
set(url, value) {
|
|
32
|
-
//
|
|
45
|
+
// FIFO eviction: drop the oldest-inserted entry when full.
|
|
33
46
|
if (this.cache.size >= this.maxSize) {
|
|
34
47
|
const firstKey = this.cache.keys().next().value;
|
|
35
48
|
this.cache.delete(firstKey);
|
|
36
49
|
}
|
|
37
50
|
this.cache.set(url, value);
|
|
38
51
|
}
|
|
39
|
-
|
|
52
|
+
|
|
40
53
|
clear() {
|
|
41
54
|
this.cache.clear();
|
|
42
55
|
}
|
|
43
|
-
|
|
56
|
+
|
|
44
57
|
getStats() {
|
|
45
58
|
return {
|
|
46
59
|
size: this.cache.size,
|
|
@@ -56,10 +69,12 @@ class URLCache {
|
|
|
56
69
|
* @returns {Object} Rule matcher with matching functions
|
|
57
70
|
*/
|
|
58
71
|
function parseAdblockRules(filePath, options = {}) {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
72
|
+
// caseSensitive option removed — it was destructured here and threaded
|
|
73
|
+
// through to createMatcher, then never actually read. All hostname/
|
|
74
|
+
// pattern comparisons are case-insensitive (lowercased explicitly or
|
|
75
|
+
// via /i regex flag), so the option would have been a substantial
|
|
76
|
+
// refactor to honor. Removed rather than left as a documented lie.
|
|
77
|
+
const { enableLogging = false } = options;
|
|
63
78
|
|
|
64
79
|
let fileContent;
|
|
65
80
|
try {
|
|
@@ -175,27 +190,27 @@ function parseAdblockRules(filePath, options = {}) {
|
|
|
175
190
|
} catch (err) {
|
|
176
191
|
rules.stats.invalid++;
|
|
177
192
|
if (enableLogging) {
|
|
178
|
-
console.log(
|
|
193
|
+
console.log(formatLogMessage('warn', `${ADBLOCK_TAG} Failed to parse rule: ${line} - ${err.message}`));
|
|
179
194
|
}
|
|
180
195
|
}
|
|
181
196
|
}
|
|
182
197
|
|
|
183
198
|
if (enableLogging) {
|
|
184
|
-
console.log(
|
|
185
|
-
console.log(` - Domain rules: ${rules.stats.domain}`);
|
|
186
|
-
console.log(` • Exact matches (Map): ${rules.stats.domainMapEntries}`);
|
|
187
|
-
console.log(` • Wildcard patterns (Array): ${rules.domainRules.length}`);
|
|
188
|
-
console.log(` - Third-party rules: ${rules.stats.thirdParty}`);
|
|
189
|
-
console.log(` - First-party rules: ${rules.stats.firstParty}`);
|
|
190
|
-
console.log(` - Path rules: ${rules.stats.path}`);
|
|
191
|
-
console.log(` - Script rules: ${rules.stats.script}`);
|
|
192
|
-
console.log(` - Regex rules: ${rules.stats.regex}`);
|
|
193
|
-
console.log(` - Whitelist rules: ${rules.stats.whitelist}`);
|
|
194
|
-
console.log(` - Comments/Element hiding: ${rules.stats.comments + rules.stats.elementHiding}`);
|
|
195
|
-
console.log(` - Invalid rules: ${rules.stats.invalid}`);
|
|
199
|
+
console.log(formatLogMessage('debug', `${ADBLOCK_TAG} Loaded ${rules.stats.total} rules:`));
|
|
200
|
+
console.log(formatLogMessage('debug', ` - Domain rules: ${rules.stats.domain}`));
|
|
201
|
+
console.log(formatLogMessage('debug', ` • Exact matches (Map): ${rules.stats.domainMapEntries}`));
|
|
202
|
+
console.log(formatLogMessage('debug', ` • Wildcard patterns (Array): ${rules.domainRules.length}`));
|
|
203
|
+
console.log(formatLogMessage('debug', ` - Third-party rules: ${rules.stats.thirdParty}`));
|
|
204
|
+
console.log(formatLogMessage('debug', ` - First-party rules: ${rules.stats.firstParty}`));
|
|
205
|
+
console.log(formatLogMessage('debug', ` - Path rules: ${rules.stats.path}`));
|
|
206
|
+
console.log(formatLogMessage('debug', ` - Script rules: ${rules.stats.script}`));
|
|
207
|
+
console.log(formatLogMessage('debug', ` - Regex rules: ${rules.stats.regex}`));
|
|
208
|
+
console.log(formatLogMessage('debug', ` - Whitelist rules: ${rules.stats.whitelist}`));
|
|
209
|
+
console.log(formatLogMessage('debug', ` - Comments/Element hiding: ${rules.stats.comments + rules.stats.elementHiding}`));
|
|
210
|
+
console.log(formatLogMessage('debug', ` - Invalid rules: ${rules.stats.invalid}`));
|
|
196
211
|
}
|
|
197
212
|
|
|
198
|
-
return createMatcher(rules, { enableLogging
|
|
213
|
+
return createMatcher(rules, { enableLogging });
|
|
199
214
|
}
|
|
200
215
|
|
|
201
216
|
/**
|
|
@@ -229,12 +244,13 @@ function parseRule(rule, isWhitelist, enableLogging = false) {
|
|
|
229
244
|
const options = optionsStr.split(',');
|
|
230
245
|
const parsedOptions = {};
|
|
231
246
|
|
|
247
|
+
// No cosmetic-option guard here — the line-level filter at lines
|
|
248
|
+
// ~126-135 already drops any rule containing $generichide/$elemhide/
|
|
249
|
+
// etc. before parseRule is ever called. The previous in-loop
|
|
250
|
+
// COSMETIC_OPTIONS.has() guard was unreachable.
|
|
232
251
|
for (const opt of options) {
|
|
233
252
|
const [key, value] = opt.split('=');
|
|
234
|
-
|
|
235
|
-
if (!COSMETIC_OPTIONS.has(trimmedKey)) {
|
|
236
|
-
parsedOptions[trimmedKey] = value ? value.trim() : true;
|
|
237
|
-
}
|
|
253
|
+
parsedOptions[key.trim()] = value ? value.trim() : true;
|
|
238
254
|
}
|
|
239
255
|
|
|
240
256
|
// Check for third-party option
|
|
@@ -359,10 +375,12 @@ function createDomainMatcher(domain) {
|
|
|
359
375
|
}
|
|
360
376
|
|
|
361
377
|
/**
|
|
362
|
-
* Shared regex cache — deduplicates identical compiled patterns across rules
|
|
363
|
-
* Large lists (EasyList ~80K rules) often have thousands of duplicate
|
|
378
|
+
* Shared regex cache — deduplicates identical compiled patterns across rules.
|
|
379
|
+
* Large lists (EasyList ~80K rules) often have thousands of duplicate
|
|
380
|
+
* patterns. Bounded via FIFOCache (was a bare Map with no size cap; for
|
|
381
|
+
* giant lists with many unique patterns it could grow unbounded).
|
|
364
382
|
*/
|
|
365
|
-
const _regexCache = new
|
|
383
|
+
const _regexCache = new FIFOCache(20000);
|
|
366
384
|
|
|
367
385
|
/**
|
|
368
386
|
* Creates a pattern matcher for path/wildcard rules
|
|
@@ -370,8 +388,12 @@ const _regexCache = new Map();
|
|
|
370
388
|
* @returns {Function} Matcher function
|
|
371
389
|
*/
|
|
372
390
|
function createPatternMatcher(pattern) {
|
|
373
|
-
//
|
|
374
|
-
|
|
391
|
+
// Capture the ORIGINAL pattern as the cache key — the local `pattern`
|
|
392
|
+
// var gets mutated by the anchor-strip below, so previously the get()
|
|
393
|
+
// used the pre-strip key but set() stored under the post-strip key,
|
|
394
|
+
// making every anchored pattern miss its own cache on the next lookup.
|
|
395
|
+
const cacheKey = pattern;
|
|
396
|
+
const cached = _regexCache.get(cacheKey);
|
|
375
397
|
if (cached) return cached;
|
|
376
398
|
|
|
377
399
|
// Convert adblock pattern to regex
|
|
@@ -401,7 +423,7 @@ function createPatternMatcher(pattern) {
|
|
|
401
423
|
|
|
402
424
|
const regex = new RegExp(regexPattern, 'i');
|
|
403
425
|
const matcher = (url) => regex.test(url);
|
|
404
|
-
_regexCache.set(
|
|
426
|
+
_regexCache.set(cacheKey, matcher);
|
|
405
427
|
return matcher;
|
|
406
428
|
}
|
|
407
429
|
|
|
@@ -412,15 +434,21 @@ function createPatternMatcher(pattern) {
|
|
|
412
434
|
* @returns {Object} Matcher with shouldBlock function
|
|
413
435
|
*/
|
|
414
436
|
function createMatcher(rules, options = {}) {
|
|
415
|
-
const { enableLogging = false
|
|
437
|
+
const { enableLogging = false } = options;
|
|
416
438
|
|
|
417
|
-
const urlCache = new
|
|
418
|
-
|
|
419
|
-
|
|
439
|
+
const urlCache = new FIFOCache(16000);
|
|
440
|
+
// Per-cache counters split out. Previously ONE counter pair received
|
|
441
|
+
// hits/misses from THREE caches: the result cache, the request-URL
|
|
442
|
+
// parse cache, AND the source-URL parse cache — getStats().hitRate
|
|
443
|
+
// was the average of three different cache behaviors and not
|
|
444
|
+
// interpretable for diagnosing performance.
|
|
445
|
+
let resultCacheHits = 0, resultCacheMisses = 0;
|
|
446
|
+
let urlCacheHits = 0, urlCacheMisses = 0;
|
|
447
|
+
let sourceCacheHits = 0, sourceCacheMisses = 0;
|
|
420
448
|
const hasPartyRules = rules.thirdPartyRules.length > 0 || rules.firstPartyRules.length > 0;
|
|
421
|
-
// Result cache
|
|
422
|
-
// instead of clearing everything
|
|
423
|
-
const resultCache = new
|
|
449
|
+
// Result cache uses FIFO eviction (see FIFOCache class comment) —
|
|
450
|
+
// evicts oldest entries one at a time instead of clearing everything.
|
|
451
|
+
const resultCache = new FIFOCache(32000);
|
|
424
452
|
|
|
425
453
|
function resultCacheGet(url, sourceUrl, resourceType) {
|
|
426
454
|
return resultCache.get(url + '\0' + sourceUrl + '\0' + resourceType);
|
|
@@ -445,30 +473,30 @@ function createMatcher(rules, options = {}) {
|
|
|
445
473
|
// Check result cache — same URL+source+type always produces same result
|
|
446
474
|
const cachedResult = resultCacheGet(url, sourceUrl, resourceType);
|
|
447
475
|
if (cachedResult) {
|
|
448
|
-
|
|
476
|
+
resultCacheHits++;
|
|
449
477
|
return cachedResult;
|
|
450
478
|
}
|
|
451
|
-
|
|
479
|
+
resultCacheMisses++;
|
|
452
480
|
|
|
453
481
|
// OPTIMIZATION: Check cache first for URL parsing (60% faster)
|
|
454
482
|
let cachedData = urlCache.get(url);
|
|
455
483
|
let hostname, lowerHostname;
|
|
456
|
-
|
|
484
|
+
|
|
457
485
|
if (cachedData) {
|
|
458
486
|
hostname = cachedData.hostname;
|
|
459
487
|
lowerHostname = cachedData.lowerHostname;
|
|
460
|
-
|
|
488
|
+
urlCacheHits++;
|
|
461
489
|
} else {
|
|
462
490
|
// Parse URL and cache result
|
|
463
491
|
const urlObj = new URL(url);
|
|
464
492
|
hostname = urlObj.hostname;
|
|
465
493
|
lowerHostname = hostname.toLowerCase();
|
|
466
|
-
|
|
494
|
+
|
|
467
495
|
urlCache.set(url, {
|
|
468
496
|
hostname,
|
|
469
497
|
lowerHostname
|
|
470
498
|
});
|
|
471
|
-
|
|
499
|
+
urlCacheMisses++;
|
|
472
500
|
}
|
|
473
501
|
|
|
474
502
|
// Lazy parent domain computation — only built when exact Map lookup misses
|
|
@@ -491,19 +519,19 @@ function createMatcher(rules, options = {}) {
|
|
|
491
519
|
|
|
492
520
|
if (cachedSourceData) {
|
|
493
521
|
sourceDomain = cachedSourceData.lowerHostname;
|
|
494
|
-
|
|
522
|
+
sourceCacheHits++;
|
|
495
523
|
} else {
|
|
496
524
|
// Parse and cache sourceUrl
|
|
497
525
|
try {
|
|
498
526
|
const sourceUrlObj = new URL(sourceUrl);
|
|
499
527
|
sourceDomain = sourceUrlObj.hostname.toLowerCase();
|
|
500
|
-
|
|
528
|
+
|
|
501
529
|
// Cache sourceUrl parsing result (same as request URLs)
|
|
502
530
|
urlCache.set(sourceUrl, {
|
|
503
531
|
hostname: sourceUrlObj.hostname,
|
|
504
532
|
lowerHostname: sourceDomain
|
|
505
533
|
});
|
|
506
|
-
|
|
534
|
+
sourceCacheMisses++;
|
|
507
535
|
} catch (err) {
|
|
508
536
|
// Invalid sourceUrl, leave as null
|
|
509
537
|
}
|
|
@@ -522,7 +550,7 @@ function createMatcher(rules, options = {}) {
|
|
|
522
550
|
if (rule) {
|
|
523
551
|
if (matchesRule(rule, url, hostname, isThirdParty, resourceType, sourceDomain)) {
|
|
524
552
|
if (enableLogging) {
|
|
525
|
-
console.log(
|
|
553
|
+
console.log(formatLogMessage('debug', `${ADBLOCK_TAG} Whitelisted: ${url} (${rule.raw || rule.pattern})`));
|
|
526
554
|
}
|
|
527
555
|
const r = { blocked: false, rule: rule.raw || rule.pattern, reason: 'whitelisted' };
|
|
528
556
|
resultCacheSet(url, sourceUrl, resourceType, r);
|
|
@@ -537,7 +565,7 @@ function createMatcher(rules, options = {}) {
|
|
|
537
565
|
if (rule) {
|
|
538
566
|
if (matchesRule(rule, url, hostname, isThirdParty, resourceType, sourceDomain)) {
|
|
539
567
|
if (enableLogging) {
|
|
540
|
-
console.log(
|
|
568
|
+
console.log(formatLogMessage('debug', `${ADBLOCK_TAG} Whitelisted: ${url} (${rule.raw || rule.pattern})`));
|
|
541
569
|
}
|
|
542
570
|
const r = { blocked: false, rule: rule.raw || rule.pattern, reason: 'whitelisted' };
|
|
543
571
|
resultCacheSet(url, sourceUrl, resourceType, r);
|
|
@@ -552,7 +580,7 @@ function createMatcher(rules, options = {}) {
|
|
|
552
580
|
const rule = rules.whitelist[i];
|
|
553
581
|
if (matchesRule(rule, url, hostname, isThirdParty, resourceType, sourceDomain)) {
|
|
554
582
|
if (enableLogging) {
|
|
555
|
-
console.log(
|
|
583
|
+
console.log(formatLogMessage('debug', `${ADBLOCK_TAG} Whitelisted: ${url} (${rule.raw || rule.pattern})`));
|
|
556
584
|
}
|
|
557
585
|
const r = { blocked: false, rule: rule.raw || rule.pattern, reason: 'whitelisted' };
|
|
558
586
|
resultCacheSet(url, sourceUrl, resourceType, r);
|
|
@@ -567,7 +595,7 @@ function createMatcher(rules, options = {}) {
|
|
|
567
595
|
if (rule) {
|
|
568
596
|
if (matchesRule(rule, url, hostname, isThirdParty, resourceType, sourceDomain)) {
|
|
569
597
|
if (enableLogging) {
|
|
570
|
-
console.log(
|
|
598
|
+
console.log(formatLogMessage('debug', `${ADBLOCK_TAG} Blocked domain: ${url} (${rule.raw || rule.pattern})`));
|
|
571
599
|
}
|
|
572
600
|
const r = { blocked: true, rule: rule.raw || rule.pattern, reason: 'domain_rule' };
|
|
573
601
|
resultCacheSet(url, sourceUrl, resourceType, r);
|
|
@@ -581,7 +609,7 @@ function createMatcher(rules, options = {}) {
|
|
|
581
609
|
if (rule) {
|
|
582
610
|
if (matchesRule(rule, url, hostname, isThirdParty, resourceType, sourceDomain)) {
|
|
583
611
|
if (enableLogging) {
|
|
584
|
-
console.log(
|
|
612
|
+
console.log(formatLogMessage('debug', `${ADBLOCK_TAG} Blocked domain: ${url} (${rule.raw || rule.pattern})`));
|
|
585
613
|
}
|
|
586
614
|
const r = { blocked: true, rule: rule.raw || rule.pattern, reason: 'domain_rule' };
|
|
587
615
|
resultCacheSet(url, sourceUrl, resourceType, r);
|
|
@@ -596,7 +624,7 @@ function createMatcher(rules, options = {}) {
|
|
|
596
624
|
const rule = rules.domainRules[i];
|
|
597
625
|
if (matchesRule(rule, url, hostname, isThirdParty, resourceType, sourceDomain)) {
|
|
598
626
|
if (enableLogging) {
|
|
599
|
-
console.log(
|
|
627
|
+
console.log(formatLogMessage('debug', `${ADBLOCK_TAG} Blocked domain: ${url} (${rule.raw || rule.pattern})`));
|
|
600
628
|
}
|
|
601
629
|
const r = { blocked: true, rule: rule.raw || rule.pattern, reason: 'domain_rule' };
|
|
602
630
|
resultCacheSet(url, sourceUrl, resourceType, r);
|
|
@@ -611,7 +639,7 @@ function createMatcher(rules, options = {}) {
|
|
|
611
639
|
const rule = rules.thirdPartyRules[i];
|
|
612
640
|
if (matchesRule(rule, url, hostname, isThirdParty, resourceType, sourceDomain)) {
|
|
613
641
|
if (enableLogging) {
|
|
614
|
-
console.log(
|
|
642
|
+
console.log(formatLogMessage('debug', `${ADBLOCK_TAG} Blocked third-party: ${url} (${rule.raw || rule.pattern})`));
|
|
615
643
|
}
|
|
616
644
|
const r = { blocked: true, rule: rule.raw || rule.pattern, reason: 'third_party_rule' };
|
|
617
645
|
resultCacheSet(url, sourceUrl, resourceType, r);
|
|
@@ -627,7 +655,7 @@ function createMatcher(rules, options = {}) {
|
|
|
627
655
|
const rule = rules.firstPartyRules[i];
|
|
628
656
|
if (matchesRule(rule, url, hostname, isThirdParty, resourceType, sourceDomain)) {
|
|
629
657
|
if (enableLogging) {
|
|
630
|
-
console.log(
|
|
658
|
+
console.log(formatLogMessage('debug', `${ADBLOCK_TAG} Blocked first-party: ${url} (${rule.raw || rule.pattern})`));
|
|
631
659
|
}
|
|
632
660
|
const r = { blocked: true, rule: rule.raw || rule.pattern, reason: 'first_party_rule' };
|
|
633
661
|
resultCacheSet(url, sourceUrl, resourceType, r);
|
|
@@ -643,7 +671,7 @@ function createMatcher(rules, options = {}) {
|
|
|
643
671
|
const rule = rules.scriptRules[i];
|
|
644
672
|
if (matchesRule(rule, url, hostname, isThirdParty, resourceType, sourceDomain)) {
|
|
645
673
|
if (enableLogging) {
|
|
646
|
-
console.log(
|
|
674
|
+
console.log(formatLogMessage('debug', `${ADBLOCK_TAG} Blocked script: ${url} (${rule.raw || rule.pattern})`));
|
|
647
675
|
}
|
|
648
676
|
const r = { blocked: true, rule: rule.raw || rule.pattern, reason: 'script_rule' };
|
|
649
677
|
resultCacheSet(url, sourceUrl, resourceType, r);
|
|
@@ -658,7 +686,7 @@ function createMatcher(rules, options = {}) {
|
|
|
658
686
|
const rule = rules.pathRules[i];
|
|
659
687
|
if (matchesRule(rule, url, hostname, isThirdParty, resourceType, sourceDomain)) {
|
|
660
688
|
if (enableLogging) {
|
|
661
|
-
console.log(
|
|
689
|
+
console.log(formatLogMessage('debug', `${ADBLOCK_TAG} Blocked path: ${url} (${rule.raw || rule.pattern})`));
|
|
662
690
|
}
|
|
663
691
|
const r = { blocked: true, rule: rule.raw || rule.pattern, reason: 'path_rule' };
|
|
664
692
|
resultCacheSet(url, sourceUrl, resourceType, r);
|
|
@@ -672,7 +700,7 @@ function createMatcher(rules, options = {}) {
|
|
|
672
700
|
const rule = rules.regexRules[i];
|
|
673
701
|
if (matchesRule(rule, url, hostname, isThirdParty, resourceType, sourceDomain)) {
|
|
674
702
|
if (enableLogging) {
|
|
675
|
-
console.log(
|
|
703
|
+
console.log(formatLogMessage('debug', `${ADBLOCK_TAG} Blocked regex: ${url} (${rule.raw || rule.pattern})`));
|
|
676
704
|
}
|
|
677
705
|
const r = { blocked: true, rule: rule.raw || rule.pattern, reason: 'regex_rule' };
|
|
678
706
|
resultCacheSet(url, sourceUrl, resourceType, r);
|
|
@@ -687,7 +715,7 @@ function createMatcher(rules, options = {}) {
|
|
|
687
715
|
|
|
688
716
|
} catch (err) {
|
|
689
717
|
if (enableLogging) {
|
|
690
|
-
console.log(
|
|
718
|
+
console.log(formatLogMessage('warn', `${ADBLOCK_TAG} Error checking ${url}: ${err.message}`));
|
|
691
719
|
}
|
|
692
720
|
// On error, allow request
|
|
693
721
|
return {
|
|
@@ -703,19 +731,34 @@ function createMatcher(rules, options = {}) {
|
|
|
703
731
|
* @returns {Object} Statistics object
|
|
704
732
|
*/
|
|
705
733
|
getStats() {
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
734
|
+
// Per-cache hit rates instead of one lumped figure. The previous
|
|
735
|
+
// single `hitRate` averaged three different cache behaviors and
|
|
736
|
+
// wasn't actionable for diagnosing performance.
|
|
737
|
+
const rate = (hits, misses) =>
|
|
738
|
+
hits + misses > 0 ? ((hits / (hits + misses)) * 100).toFixed(1) + '%' : '0%';
|
|
739
|
+
|
|
740
|
+
return {
|
|
711
741
|
...rules.stats,
|
|
712
742
|
cache: {
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
743
|
+
result: {
|
|
744
|
+
hits: resultCacheHits,
|
|
745
|
+
misses: resultCacheMisses,
|
|
746
|
+
hitRate: rate(resultCacheHits, resultCacheMisses),
|
|
747
|
+
size: resultCache.cache.size,
|
|
748
|
+
maxSize: resultCache.maxSize
|
|
749
|
+
},
|
|
750
|
+
url: {
|
|
751
|
+
hits: urlCacheHits,
|
|
752
|
+
misses: urlCacheMisses,
|
|
753
|
+
hitRate: rate(urlCacheHits, urlCacheMisses),
|
|
754
|
+
size: urlCache.cache.size,
|
|
755
|
+
maxSize: urlCache.maxSize
|
|
756
|
+
},
|
|
757
|
+
source: {
|
|
758
|
+
hits: sourceCacheHits,
|
|
759
|
+
misses: sourceCacheMisses,
|
|
760
|
+
hitRate: rate(sourceCacheHits, sourceCacheMisses)
|
|
761
|
+
}
|
|
719
762
|
}
|
|
720
763
|
};
|
|
721
764
|
}
|
|
@@ -849,24 +892,26 @@ function matchesRule(rule, url, hostname, isThirdParty, resourceType, sourceDoma
|
|
|
849
892
|
}
|
|
850
893
|
|
|
851
894
|
/**
|
|
852
|
-
* Extract base domain from hostname using Public Suffix List
|
|
853
|
-
* Correctly handles multi-part TLDs like .co.uk, .com.au, .com.br
|
|
895
|
+
* Extract base domain from hostname using Public Suffix List.
|
|
896
|
+
* Correctly handles multi-part TLDs like .co.uk, .com.au, .com.br.
|
|
854
897
|
* @param {string} hostname - Full hostname
|
|
855
898
|
* @returns {string} Base domain
|
|
856
899
|
*/
|
|
857
|
-
|
|
900
|
+
// FIFOCache (not the previous clear-all-when-full Map). The old code
|
|
901
|
+
// nuked all 10000 cached entries on overflow, forcing the next 10000
|
|
902
|
+
// hosts to re-pay the psl.parse cost. FIFO evicts one entry at a time.
|
|
903
|
+
const _baseDomainCache = new FIFOCache(10000);
|
|
858
904
|
function getBaseDomain(hostname) {
|
|
859
905
|
const cached = _baseDomainCache.get(hostname);
|
|
860
906
|
if (cached) return cached;
|
|
861
907
|
const parsed = psl.parse(hostname);
|
|
862
908
|
const result = (parsed && parsed.domain) ? parsed.domain : hostname;
|
|
863
|
-
// Cap cache size
|
|
864
|
-
if (_baseDomainCache.size > 10000) _baseDomainCache.clear();
|
|
865
909
|
_baseDomainCache.set(hostname, result);
|
|
866
910
|
return result;
|
|
867
911
|
}
|
|
868
912
|
|
|
913
|
+
// Public surface. getBaseDomain stays internal — used by createMatcher's
|
|
914
|
+
// third-party check but no external caller ever imported it.
|
|
869
915
|
module.exports = {
|
|
870
|
-
parseAdblockRules
|
|
871
|
-
getBaseDomain
|
|
916
|
+
parseAdblockRules
|
|
872
917
|
};
|