Repository navigation
Expand file tree
/
Copy pathAdBlockerTestLab.jsx
More file actions
4384 lines (4147 loc) · 196 KB
/
Copy pathAdBlockerTestLab.jsx
File metadata and controls
4384 lines (4147 loc) · 196 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
import React, { useState, useEffect, useRef, useMemo, useCallback } from 'react';
import {
Play,
Pause,
Square,
Search,
CheckCircle2,
Sun,
Moon,
Layers,
Terminal,
Server,
Box,
Download,
RotateCcw,
ShieldCheck,
Sliders,
Trash2,
AlertTriangle,
Copy,
Check,
ChevronDown,
ChevronUp,
Filter,
ArrowRight,
Shield,
Info,
FileText,
Zap
} from 'lucide-react';
import { calculateBenchmarkScore as calculateExtractedBenchmarkScore } from './src/lab/scoring.js';
import { generateHtmlReport as generateExtractedHtmlReport, formatCsvField as formatExtractedCsvField } from './src/lab/reporting.js';
import RealAdsStressLab from './src/lab/real-ads/RealAdsStressLab.jsx';
import NavigationRibbon from './src/components/NavigationRibbon.jsx';
import OverviewView from './src/components/OverviewView.jsx';
import ResultsWorkbenchView from './src/components/ResultsWorkbenchView.jsx';
import { normalizeTab } from './src/lab/navigation.js';
export const APP_VERSION = '4.1.0';
export const SCHEMA_VERSION = '2026.10.adblockertestlab.v4.1';
export const BRAND_NAME = 'AdBlocker Test Lab';
export const BRAND_PROMISE = 'Test your protection. See the evidence. Understand the gaps.';
export const BRAND_TAGLINE = 'Test everything. Trust the evidence.';
export const EXPECTED_REGISTRY_COUNT = 55;
export const EXECUTION_STATE = {
IDLE: 'IDLE',
RUNNING: 'RUNNING',
PAUSED: 'PAUSED',
COMPLETED: 'COMPLETED',
CANCELLED: 'CANCELLED'
};
export const RESULT_STATUS = {
BLOCKED: 'BLOCKED',
PARTIAL: 'PARTIAL',
ALLOWED: 'ALLOWED',
CONSISTENT_WITH_FILTERING: 'CONSISTENT_WITH_FILTERING',
REQUEST_FAILED_CAUSE_UNKNOWN: 'REQUEST_FAILED_CAUSE_UNKNOWN',
SIGNAL_ACTIVE: 'SIGNAL_ACTIVE',
SIGNAL_INACTIVE: 'SIGNAL_INACTIVE',
INCONCLUSIVE: 'INCONCLUSIVE',
UNSUPPORTED: 'UNSUPPORTED',
ERROR: 'ERROR',
CANCELLED: 'CANCELLED',
QUEUED: 'QUEUED',
RUNNING: 'RUNNING'
};
export const METRIC_GROUP = {
AD_TRACKER_BLOCKING: 'AD_TRACKER_BLOCKING',
PRIVACY_SIGNAL: 'PRIVACY_SIGNAL',
EXPLORATORY_PROBE: 'EXPLORATORY_PROBE'
};
export const CONFIDENCE = {
CONFIRMED: {
id: 'CONFIRMED_OBSERVATION',
label: 'Confirmed Observation',
shortLabel: 'Confirmed',
stageKind: 'OBSERVED'
},
STRONG: {
id: 'STRONG_INFERENCE',
label: 'Strong Inference',
shortLabel: 'Strong Inf.',
stageKind: 'INFERRED'
},
WEAK: {
id: 'WEAK_INFERENCE',
label: 'Weak Inference',
shortLabel: 'Weak Inf.',
stageKind: 'INFERRED'
},
USER_REPORTED: {
id: 'USER_REPORTED',
label: 'User-Reported',
shortLabel: 'User-Reported',
stageKind: 'USER_REPORTED'
},
UNKNOWN: {
id: 'UNKNOWN',
label: 'Cause Unknown / Unobservable',
shortLabel: 'Unobservable',
stageKind: 'UNOBSERVABLE'
}
};
export const VALIDITY_CLASS = {
CONTROLLED_FIXTURE: 'Controlled First-Party Fixture',
EXPLORATORY_EXTERNAL: 'Exploratory Third-Party Probe (Non-Scored)',
PRIVACY_CAPABILITY: 'Browser Privacy / Restriction Signal (Separate Index)',
DEDICATED_AD_CHAMBER: 'Dedicated Sandboxed Ad-Network Chamber'
};
export const DEFAULT_SCORING_CATEGORIES = [
{
id: 'ad_resource_blocking',
name: 'Advertising Resource Blocking',
shortCode: 'ADS-NET',
defaultWeight: 22,
description: 'Client-side or server-correlated interception of ad scripts, VAST tags, and ad-image paths.'
},
{
id: 'tracker_beacon_blocking',
name: 'Tracker & Beacon Blocking',
shortCode: 'TRK-BCN',
defaultWeight: 18,
description: 'Interception of tracking pixel paths, analytics collection endpoints, and click-attribution beacons.'
},
{
id: 'cosmetic_filtering',
name: 'DOM Cosmetic Filtering',
shortCode: 'DOM-CSS',
defaultWeight: 18,
description: 'Stylesheet suppression of standard IAB banners, native ad widgets, and promoted feed containers.'
},
{
id: 'intrusive_ad_prevention',
name: 'Intrusive Overlay & Annoyance Suppression',
shortCode: 'INT-UI',
defaultWeight: 14,
description: 'Suppression of full-screen interstitials, sticky banners, scroll-locks, and deceptive download buttons.'
},
{
id: 'script_resource_filtering',
name: 'Script, XHR & Anti-Adblock Filtering',
shortCode: 'SCR-XHR',
defaultWeight: 14,
description: 'Interception of dynamic ad <script> injections, XHR bid endpoints, and fallback ad-loader retries.'
},
{
id: 'redirect_popup_prevention',
name: 'Popup Scriptlet & Affiliate Path Prevention',
shortCode: 'POP-AFF',
defaultWeight: 8,
description: 'Interception of affiliate click-tracking paths and popup-defuser scriptlet enforcement.'
},
{
id: 'network_filtering_evidence',
name: 'First-Party Network Path Filtering',
shortCode: 'NET-PATH',
defaultWeight: 6,
description: 'Controlled network adview path interception validated against neutral same-origin controls.'
}
];
function createTestSpec({
id,
section,
scoringCategory,
metricGroup,
name,
description,
expectedBehavior,
controlResource,
testResource,
recommendedRule,
type,
selectorClasses = '',
elementId = '',
probePath = '',
targetUrl = '',
measuredBehavior,
technicalValidity,
browserObservation,
dependsOnThirdParty = false,
alternativeExplanations = ['Content-blocking extension rule', 'Browser built-in filter'],
scoringEligible = true,
limitations = 'Measures observable browser behavior; does not identify a specific vendor.'
}) {
return {
id,
version: '4.1',
section,
scoringCategory,
metricGroup,
name,
description,
expectedBehavior,
controlResource,
testResource,
recommendedRule,
type,
selectorClasses,
elementId,
probePath,
targetUrl,
audit: {
measuredBehavior,
technicalValidity,
browserObservation,
dependsOnThirdParty,
alternativeExplanations,
scoringEligible,
limitations
}
};
}
export const TEST_REGISTRY = [
// 5.1 Standard Display Advertisements (5 Tests)
createTestSpec({
id: 'DSP-01',
section: '5.1 Standard Display Ads',
scoringCategory: 'cosmetic_filtering',
metricGroup: METRIC_GROUP.AD_TRACKER_BLOCKING,
name: 'IAB Leaderboard Banner (728x90) Cosmetic Hiding',
description: 'Injects a synthetic leaderboard container (.ad-leaderboard, .adbanner, #ad-top-banner) alongside a neutral control box and measures DOM layout collapse.',
expectedBehavior: 'Cosmetic stylesheet rule sets display:none, visibility:hidden, or collapses height to 0px while neutral control remains visible.',
controlResource: 'DOM <div class="adshield-control-box"> (100x22px)',
testResource: 'DOM <div id="ad-top-banner" class="ad-leaderboard adbanner adsbox">',
recommendedRule: '##.ad-leaderboard, #ad-top-banner, .adbanner',
type: 'cosmetic_dom',
selectorClasses: 'ad-leaderboard adbanner adsbox banner_ad_728x90',
elementId: 'ad-top-banner',
measuredBehavior: 'CSS/DOM visual hiding of standard EasyList leaderboard selectors.',
technicalValidity: VALIDITY_CLASS.CONTROLLED_FIXTURE,
browserObservation: 'getComputedStyle(el).display/visibility and getBoundingClientRect().height.',
limitations: 'Cosmetic hiding proves a DOM rule matched; it does not prove network requests were blocked.'
}),
createTestSpec({
id: 'DSP-02',
section: '5.1 Standard Display Ads',
scoringCategory: 'cosmetic_filtering',
metricGroup: METRIC_GROUP.AD_TRACKER_BLOCKING,
name: 'Medium Rectangle (300x250) & DFP Sidebar Slot',
description: 'Injects a 300x250 sidebar ad unit (.ad-300x250, .dfp-ad-unit) into the isolated fixture and measures layout collapse.',
expectedBehavior: 'Cosmetic rule collapses or hides the .dfp-ad-unit container.',
controlResource: 'DOM <div class="adshield-control-box">',
testResource: 'DOM <div id="dfp-ad-sidebar-1" class="ad-300x250 sidebar-ad-slot dfp-ad-unit">',
recommendedRule: '###dfp-ad-sidebar-1, .dfp-ad-unit, .ad-300x250',
type: 'cosmetic_dom',
selectorClasses: 'ad-300x250 sidebar-ad-slot dfp-ad-unit pub_300x250',
elementId: 'dfp-ad-sidebar-1',
measuredBehavior: 'DOM element suppression for DFP/IAB medium rectangle classes.',
technicalValidity: VALIDITY_CLASS.CONTROLLED_FIXTURE,
browserObservation: 'Element offsetParent, computed display, and bounding client dimensions.',
limitations: 'DNS-only blockers cannot inspect or hide first-party HTML elements.'
}),
createTestSpec({
id: 'DSP-03',
section: '5.1 Standard Display Ads',
scoringCategory: 'cosmetic_filtering',
metricGroup: METRIC_GROUP.AD_TRACKER_BLOCKING,
name: 'Publisher Ad Tag Placeholder (<ins class="adsbygoogle">)',
description: 'Evaluates whether standard <ins class="adsbygoogle"> placeholders are suppressed in the DOM.',
expectedBehavior: 'Element is hidden via injected cosmetic stylesheet.',
controlResource: 'DOM <ins class="adshield-control-ins">',
testResource: 'DOM <ins id="ins-adsbygoogle-test" class="adsbygoogle" data-ad-client="ca-pub-0000000000000000">',
recommendedRule: '##ins.adsbygoogle',
type: 'cosmetic_dom',
selectorClasses: 'adsbygoogle google-ad-placeholder',
elementId: 'ins-adsbygoogle-test',
measuredBehavior: 'Cosmetic hiding of AdSense <ins> placeholder elements.',
technicalValidity: VALIDITY_CLASS.CONTROLLED_FIXTURE,
browserObservation: 'getComputedStyle(ins) and rendered height comparison against neutral control.',
limitations: 'Does not test external script loading (tested separately in TPA-01).'
}),
createTestSpec({
id: 'DSP-04',
section: '5.1 Standard Display Ads',
scoringCategory: 'cosmetic_filtering',
metricGroup: METRIC_GROUP.AD_TRACKER_BLOCKING,
name: 'Asynchronous In-Article Ad Container Injection',
description: 'Inserts a dynamic ad slot (.in-article-ad, .sponsored-banner-slot) on a secondary animation frame to test persistent CSS/MutationObserver filtering.',
expectedBehavior: 'Dynamically inserted ad container is collapsed immediately upon DOM insertion.',
controlResource: 'Asynchronously inserted neutral <div class="adshield-async-control">',
testResource: 'Async DOM <div id="dynamic-ad-slot-04" class="in-article-ad ad-placeholder-box text-ad">',
recommendedRule: '##.in-article-ad, .ad-placeholder-box, .sponsored-banner-slot',
type: 'dynamic_cosmetic_dom',
selectorClasses: 'in-article-ad ad-placeholder-box text-ad sponsored-banner-slot',
elementId: 'dynamic-ad-slot-04',
measuredBehavior: 'Cosmetic filtering persistence on asynchronously injected DOM nodes.',
technicalValidity: VALIDITY_CLASS.CONTROLLED_FIXTURE,
browserObservation: 'Post-mutation computed style and bounding box dimensions.',
limitations: 'Measures DOM visibility only; does not attribute to a specific extension.'
}),
createTestSpec({
id: 'DSP-05',
section: '5.1 Standard Display Ads',
scoringCategory: 'cosmetic_filtering',
metricGroup: METRIC_GROUP.AD_TRACKER_BLOCKING,
name: 'Mobile Banner & Responsive Skyscraper Slot (320x50 / 160x600)',
description: 'Tests cosmetic hiding of responsive mobile/skyscraper ad containers (.mobile-ad-banner, .ad_160x600, .sky-ad-slot).',
expectedBehavior: 'Responsive ad slot collapses to zero height while neutral control box renders normally.',
controlResource: 'DOM <div class="adshield-control-box">',
testResource: 'DOM <div id="mobile-sky-ad-05" class="mobile-ad-banner ad_160x600 sky-ad-slot adsbox">',
recommendedRule: '##.mobile-ad-banner, .ad_160x600, .sky-ad-slot',
type: 'cosmetic_dom',
selectorClasses: 'mobile-ad-banner ad_160x600 sky-ad-slot adsbox',
elementId: 'mobile-sky-ad-05',
measuredBehavior: 'Visual suppression of mobile banner and skyscraper ad class signatures.',
technicalValidity: VALIDITY_CLASS.CONTROLLED_FIXTURE,
browserObservation: 'Computed display/visibility and layout height.',
limitations: 'First-party DOM check only.'
}),
// 5.2 Third-Party Advertising Requests (5 Tests)
createTestSpec({
id: 'TPA-01',
section: '5.2 Third-Party Ad Requests',
scoringCategory: 'ad_resource_blocking',
metricGroup: METRIC_GROUP.AD_TRACKER_BLOCKING,
name: 'Controlled Ad-Script Path Pattern (/pagead/js/adsbygoogle.js)',
description: 'First verifies same-origin connectivity via a neutral control fetch, then attempts a fetch to /pagead/js/adsbygoogle.js.',
expectedBehavior: 'Neutral control succeeds while ad-script path is aborted client-side (TypeError) prior to HTTP status.',
controlResource: 'Same-origin neutral control fetch (/?adshield_ctrl=TPA-01)',
testResource: 'Fetch GET /pagead/js/adsbygoogle.js?adshield_fixture=1',
recommendedRule: '||*/pagead/js/adsbygoogle.js$script,xmlhttprequest',
type: 'url_pattern_probe',
probePath: '/pagead/js/adsbygoogle.js?adshield_fixture=1',
measuredBehavior: 'Client-side URL pattern interception validated against a live neutral control.',
technicalValidity: VALIDITY_CLASS.CONTROLLED_FIXTURE,
browserObservation: 'Neutral control HTTP response + target Fetch Promise rejection (TypeError).',
limitations: 'Evaluates path-aware browser filters; DNS-only blockers do not inspect relative paths.'
}),
createTestSpec({
id: 'TPA-02',
section: '5.2 Third-Party Ad Requests',
scoringCategory: 'ad_resource_blocking',
metricGroup: METRIC_GROUP.EXPLORATORY_PROBE,
name: 'Exploratory Ad Server Endpoint Probe (static.doubleclick.net)',
description: 'Exploratory subresource fetch to static.doubleclick.net. Reports REQUEST_FAILED_CAUSE_UNKNOWN on failure because external failures have multiple causes.',
expectedBehavior: 'Reports whether external endpoint resolved or failed (non-authoritative; excluded from core score).',
controlResource: 'Same-origin connectivity verification',
testResource: 'https://static.doubleclick.net/instream/ad_status.js',
recommendedRule: '||doubleclick.net^$third-party',
type: 'external_domain_probe',
targetUrl: 'https://static.doubleclick.net/instream/ad_status.js',
measuredBehavior: 'Reachability of external DoubleClick ad script endpoint.',
technicalValidity: VALIDITY_CLASS.EXPLORATORY_EXTERNAL,
browserObservation: 'no-cors Fetch resolution vs network rejection.',
dependsOnThirdParty: true,
alternativeExplanations: ['Adblocker extension', 'DNS sinkhole', 'Firewall/ISP block', 'CSP or outage'],
scoringEligible: false,
limitations: 'P1-6 Fix: Never reported as BLOCKED because client JS cannot distinguish an adblock rule from DNS failure or outage.'
}),
createTestSpec({
id: 'TPA-03',
section: '5.2 Third-Party Ad Requests',
scoringCategory: 'ad_resource_blocking',
metricGroup: METRIC_GROUP.EXPLORATORY_PROBE,
name: 'Exploratory Ad Syndication Probe (pagead2.googlesyndication.com)',
description: 'Exploratory subresource probe to pagead2.googlesyndication.com/pagead/show_ads.js.',
expectedBehavior: 'Reports whether external endpoint resolved or failed (REQUEST_FAILED_CAUSE_UNKNOWN).',
controlResource: 'Same-origin connectivity verification',
testResource: 'https://pagead2.googlesyndication.com/pagead/show_ads.js',
recommendedRule: '||googlesyndication.com^$third-party',
type: 'external_domain_probe',
targetUrl: 'https://pagead2.googlesyndication.com/pagead/show_ads.js',
measuredBehavior: 'Reachability of external Google Syndication script.',
technicalValidity: VALIDITY_CLASS.EXPLORATORY_EXTERNAL,
browserObservation: 'no-cors Fetch resolution vs rejection.',
dependsOnThirdParty: true,
scoringEligible: false,
limitations: 'Exploratory external probe; excluded from core score denominator.'
}),
createTestSpec({
id: 'TPA-04',
section: '5.2 Third-Party Ad Requests',
scoringCategory: 'ad_resource_blocking',
metricGroup: METRIC_GROUP.AD_TRACKER_BLOCKING,
name: 'Ad Server Iframe Container Suppression (#google_ads_frame1)',
description: 'Injects a sandboxed iframe with standard ad-server attributes (id="google_ads_frame1", class="ad-iframe-slot") and checks DOM suppression.',
expectedBehavior: 'Iframe container is hidden or collapsed by cosmetic/frame rules.',
controlResource: 'Neutral sandboxed <iframe class="adshield-control-frame">',
testResource: '<iframe id="google_ads_frame1" class="ad-iframe-slot google_ads_frame">',
recommendedRule: '###google_ads_frame1, .google_ads_frame, .ad-iframe-slot',
type: 'cosmetic_dom',
selectorClasses: 'ad-iframe-slot google_ads_frame',
elementId: 'google_ads_frame1',
measuredBehavior: 'DOM suppression of ad-server iframe containers.',
technicalValidity: VALIDITY_CLASS.CONTROLLED_FIXTURE,
browserObservation: 'Iframe computed display and bounding client height.',
limitations: 'Tests element-level frame hiding rather than external cross-origin navigation.'
}),
createTestSpec({
id: 'TPA-05',
section: '5.2 Third-Party Ad Requests',
scoringCategory: 'ad_resource_blocking',
metricGroup: METRIC_GROUP.AD_TRACKER_BLOCKING,
name: 'Controlled Ad Image Asset Path (/ads/banner_300x250.gif)',
description: 'Requests a controlled relative ad-image path (/ads/banner_300x250.gif?adunit=top) after validating same-origin control connectivity.',
expectedBehavior: 'Fetch request to /ads/banner_300x250.gif is aborted client-side prior to HTTP status response.',
controlResource: 'Same-origin neutral fetch control',
testResource: 'Fetch GET /ads/banner_300x250.gif?adunit=top&adshield=1',
recommendedRule: '||*/ads/banner_*.gif$image,xmlhttprequest',
type: 'url_pattern_probe',
probePath: '/ads/banner_300x250.gif?adunit=top&adshield=1',
measuredBehavior: 'Interception of URL paths matching /ads/banner_*.gif filter rules.',
technicalValidity: VALIDITY_CLASS.CONTROLLED_FIXTURE,
browserObservation: 'Fetch rejection vs HTTP response.',
limitations: 'Relative URL path test; evaluates path-matching rules rather than domain resolution.'
}),
// 5.3 Tracking Pixels and Beacons (5 Tests)
createTestSpec({
id: 'TRK-01',
section: '5.3 Tracking Pixels & Beacons',
scoringCategory: 'tracker_beacon_blocking',
metricGroup: METRIC_GROUP.AD_TRACKER_BLOCKING,
name: '1x1 Tracking Pixel DOM & Telemetry Path (/r/collect)',
description: 'Evaluates both DOM visibility of <img class="tracking-pixel"> and network interception of /r/collect?v=1&tid=UA-000000-1 separately.',
expectedBehavior: 'Both the /r/collect telemetry path and the .tracking-pixel image element are blocked/hidden.',
controlResource: 'Neutral 1x1 control image element + same-origin fetch control',
testResource: '<img class="tracking-pixel"> + Fetch /r/collect?v=1&tid=UA-000000-1&t=pageview',
recommendedRule: '||*/r/collect?*$xmlhttprequest,image\n##img.tracking-pixel',
type: 'pixel_probe',
probePath: '/r/collect?v=1&tid=UA-000000-1&t=pageview&cid=synthetic',
selectorClasses: 'tracking-pixel analytics-pixel beacon-img',
measuredBehavior: 'Separate measurement of 1x1 pixel cosmetic hiding and /r/collect URL interception.',
technicalValidity: VALIDITY_CLASS.CONTROLLED_FIXTURE,
browserObservation: 'Image computed style + Fetch promise outcome.',
limitations: 'Uses synthetic non-identifying parameters (UA-000000-1).'
}),
createTestSpec({
id: 'TRK-02',
section: '5.3 Tracking Pixels & Beacons',
scoringCategory: 'tracker_beacon_blocking',
metricGroup: METRIC_GROUP.EXPLORATORY_PROBE,
name: 'Exploratory Analytics Domain Probe (google-analytics.com)',
description: 'Exploratory probe to www.google-analytics.com/analytics.js.',
expectedBehavior: 'Reports REQUEST_FAILED_CAUSE_UNKNOWN when unreachable (non-scored).',
controlResource: 'Same-origin connectivity verification',
testResource: 'https://www.google-analytics.com/analytics.js',
recommendedRule: '||google-analytics.com^$third-party',
type: 'external_domain_probe',
targetUrl: 'https://www.google-analytics.com/analytics.js',
measuredBehavior: 'Reachability of external Google Analytics script.',
technicalValidity: VALIDITY_CLASS.EXPLORATORY_EXTERNAL,
browserObservation: 'no-cors Fetch resolution vs rejection.',
dependsOnThirdParty: true,
scoringEligible: false,
limitations: 'Exploratory third-party endpoint; excluded from core score denominator.'
}),
createTestSpec({
id: 'TRK-03',
section: '5.3 Tracking Pixels & Beacons',
scoringCategory: 'tracker_beacon_blocking',
metricGroup: METRIC_GROUP.EXPLORATORY_PROBE,
name: 'Navigator.sendBeacon Telemetry Correlation & Wrapper Check',
description: 'P1-4 Fix: Queries /api/fixture/beacon arrival correlation when backend is active; otherwise treats non-native sendBeacon wrappers or boolean returns as CONSISTENT_WITH_FILTERING / INCONCLUSIVE (never a confirmed block).',
expectedBehavior: 'Reports server-correlated drop if fixture server is active, or CONSISTENT_WITH_FILTERING / INCONCLUSIVE without server correlation.',
controlResource: 'First-party beacon arrival check + Function.prototype.toString inspection',
testResource: 'navigator.sendBeacon("/pixel/track?event=adshield_synthetic")',
recommendedRule: '||*/pixel/track?*$ping,beacon',
type: 'beacon_check',
measuredBehavior: 'Beacon transmission outcome and prototype wrapper inspection.',
technicalValidity: VALIDITY_CLASS.EXPLORATORY_EXTERNAL,
browserObservation: 'First-party correlation receipt or navigator.sendBeacon boolean + function signature.',
scoringEligible: false,
limitations: 'P1-4 Fix: Without an active backend arrival log, a non-native sendBeacon wrapper or boolean return is never treated as a confirmed BLOCKED pass.'
}),
createTestSpec({
id: 'TRK-04',
section: '5.3 Tracking Pixels & Beacons',
scoringCategory: 'tracker_beacon_blocking',
metricGroup: METRIC_GROUP.EXPLORATORY_PROBE,
name: 'Exploratory Session-Replay Domain Probe (static.hotjar.com)',
description: 'Exploratory probe to static.hotjar.com/c/hotjar-000000.js.',
expectedBehavior: 'Reports REQUEST_FAILED_CAUSE_UNKNOWN when unreachable.',
controlResource: 'Same-origin connectivity verification',
testResource: 'https://static.hotjar.com/c/hotjar-000000.js',
recommendedRule: '||hotjar.com^$third-party',
type: 'external_domain_probe',
targetUrl: 'https://static.hotjar.com/c/hotjar-000000.js',
measuredBehavior: 'Reachability of external Hotjar script endpoint.',
technicalValidity: VALIDITY_CLASS.EXPLORATORY_EXTERNAL,
browserObservation: 'no-cors Fetch outcome.',
dependsOnThirdParty: true,
scoringEligible: false,
limitations: 'Exploratory external check; not included in core weighted score.'
}),
createTestSpec({
id: 'TRK-05',
section: '5.3 Tracking Pixels & Beacons',
scoringCategory: 'tracker_beacon_blocking',
metricGroup: METRIC_GROUP.AD_TRACKER_BLOCKING,
name: 'Controlled Conversion Pixel Path (/tr?id=00000&ev=PageView)',
description: 'Tests URL pattern filtering against a controlled path matching standard conversion pixel query structures (/tr?id=000000&ev=PageView&noscript=1).',
expectedBehavior: 'Request is aborted client-side by EasyPrivacy/tracker path rules while neutral control succeeds.',
controlResource: 'Same-origin neutral fetch control',
testResource: 'Fetch GET /tr?id=000000000&ev=PageView&noscript=1',
recommendedRule: '||*/tr?id=*&ev=PageView$image,xmlhttprequest',
type: 'url_pattern_probe',
probePath: '/tr?id=000000000&ev=PageView&noscript=1',
measuredBehavior: 'Client-side interception of conversion-pixel URL query patterns.',
technicalValidity: VALIDITY_CLASS.CONTROLLED_FIXTURE,
browserObservation: 'Fetch rejection vs HTTP response.',
limitations: 'First-party path simulation; evaluates query-pattern filter rules.'
}),
// 5.4 Annoying Advertising Behaviors (5 Tests)
createTestSpec({
id: 'INT-01',
section: '5.4 Intrusive Ad Behaviors',
scoringCategory: 'intrusive_ad_prevention',
metricGroup: METRIC_GROUP.AD_TRACKER_BLOCKING,
name: 'Full-Screen Interstitial Overlay Suppression',
description: 'Renders a contained synthetic interstitial overlay (#ad-interstitial-modal, .interstitial-ad-overlay) inside the sandbox fixture.',
expectedBehavior: 'Cosmetic annoyance/ad filter hides or collapses the interstitial overlay.',
controlResource: 'DOM <div class="adshield-control-box">',
testResource: 'DOM <div id="ad-interstitial-modal" class="interstitial-ad-overlay ad-modal-backdrop">',
recommendedRule: '###ad-interstitial-modal, .interstitial-ad-overlay',
type: 'cosmetic_dom',
selectorClasses: 'interstitial-ad-overlay ad-modal-backdrop popup-ad-overlay',
elementId: 'ad-interstitial-modal',
measuredBehavior: 'DOM hiding of full-screen ad interstitial classes.',
technicalValidity: VALIDITY_CLASS.CONTROLLED_FIXTURE,
browserObservation: 'Computed display/visibility and bounding box dimensions.',
limitations: 'Isolated inside the sandbox container so the page UI is never obstructed.'
}),
createTestSpec({
id: 'INT-02',
section: '5.4 Intrusive Ad Behaviors',
scoringCategory: 'intrusive_ad_prevention',
metricGroup: METRIC_GROUP.AD_TRACKER_BLOCKING,
name: 'Sticky Bottom Catfish Banner (.sticky-ad-bottom)',
description: 'Tests whether sticky bottom ad containers (#fixed-ad-footer, .sticky-ad-bottom) are hidden.',
expectedBehavior: 'Sticky ad container is hidden by cosmetic rules.',
controlResource: 'DOM <div class="adshield-control-box">',
testResource: 'DOM <div id="fixed-ad-footer" class="sticky-ad-bottom bottom-ad-wrapper adsbox">',
recommendedRule: '###fixed-ad-footer, .sticky-ad-bottom',
type: 'cosmetic_dom',
selectorClasses: 'sticky-ad-bottom bottom-ad-wrapper fixed-ad-banner adsbox',
elementId: 'fixed-ad-footer',
measuredBehavior: 'DOM suppression of sticky bottom banner selectors.',
technicalValidity: VALIDITY_CLASS.CONTROLLED_FIXTURE,
browserObservation: 'Computed style and layout dimensions.',
limitations: 'Requires CSS cosmetic filtering capability.'
}),
createTestSpec({
id: 'INT-03',
section: '5.4 Intrusive Ad Behaviors',
scoringCategory: 'redirect_popup_prevention',
metricGroup: METRIC_GROUP.AD_TRACKER_BLOCKING,
name: 'Popup Scriptlet Wrapper & Popunder Trigger Suppression',
description: 'P0-2 Fix: Does NOT award credit for a self-imposed iframe sandbox restriction. Separately checks whether window.open is wrapped by a blocker scriptlet and whether a .popunder-ad-trigger anchor is hidden in the DOM.',
expectedBehavior: 'Popup trigger element is hidden and/or window.open is shimmed by an active popup-defuser scriptlet while neutral control anchor remains visible.',
controlResource: 'Neutral anchor <a class="adshield-control-link"> + native function baseline',
testResource: 'DOM <a id="popunder-trigger-03" class="popunder-ad-trigger ad-pop-link adsbox"> + window.open descriptor check',
recommendedRule: '##.popunder-ad-trigger, .ad-pop-link\n+*##+js(nowoif)',
type: 'popup_filter_check',
selectorClasses: 'popunder-ad-trigger ad-pop-link adsbox',
elementId: 'popunder-trigger-03',
measuredBehavior: 'Active popup-defuser scriptlet wrapping and DOM suppression of popunder trigger links.',
technicalValidity: VALIDITY_CLASS.CONTROLLED_FIXTURE,
browserObservation: 'window.open descriptor/toString inspection + popunder trigger element computed style.',
limitations: 'Never uses an iframe lacking allow-popups to claim an adblock pass.'
}),
createTestSpec({
id: 'INT-04',
section: '5.4 Intrusive Ad Behaviors',
scoringCategory: 'intrusive_ad_prevention',
metricGroup: METRIC_GROUP.PRIVACY_SIGNAL,
name: 'Unmuted Media Autoplay Restriction Signal',
description: 'Evaluates browser autoplay restriction posture on unmuted HTMLMediaElement playback.',
expectedBehavior: 'Reports SIGNAL_ACTIVE if unmuted play() is rejected by browser/extension policy, or INCONCLUSIVE after user gesture activation.',
controlResource: 'HTMLAudioElement API availability check',
testResource: 'In-memory silent WAV data-URI unmuted audio.play() promise',
recommendedRule: 'Browser Settings -> Site Settings -> Sound / Autoplay -> Block',
type: 'autoplay_policy_check',
measuredBehavior: 'Browser media autoplay policy enforcement.',
technicalValidity: VALIDITY_CLASS.PRIVACY_CAPABILITY,
browserObservation: 'HTMLMediaElement.play() Promise rejection (NotAllowedError) vs resolution.',
scoringEligible: false,
limitations: 'P0-3 Fix: Categorized under Browser Privacy & Hardening Signals so browser media policies never distort the Ad & Tracker Blocking Score.'
}),
createTestSpec({
id: 'INT-05',
section: '5.4 Intrusive Ad Behaviors',
scoringCategory: 'intrusive_ad_prevention',
metricGroup: METRIC_GROUP.AD_TRACKER_BLOCKING,
name: 'Scroll-Lock & Page Takeover Backdrop Mitigation (.tp-backdrop)',
description: 'Tests whether known takeover/scroll-lock overlay classes (.tp-modal, .tp-backdrop, .fc-ab-root) are hidden.',
expectedBehavior: 'Takeover backdrop elements are collapsed by annoyance rules.',
controlResource: 'DOM <div class="adshield-control-box">',
testResource: 'DOM <div id="tp-backdrop-test" class="tp-modal tp-backdrop fc-ab-root ad-takeover-overlay">',
recommendedRule: '##.tp-modal, .tp-backdrop, .fc-ab-root',
type: 'cosmetic_dom',
selectorClasses: 'tp-modal tp-backdrop fc-ab-root ad-takeover-overlay',
elementId: 'tp-backdrop-test',
measuredBehavior: 'Cosmetic hiding of scroll-lock modal backdrop classes.',
technicalValidity: VALIDITY_CLASS.CONTROLLED_FIXTURE,
browserObservation: 'Computed style and bounding client height.',
limitations: 'Tests known modal takeover class selectors.'
}),
// 5.5 Video Advertisements (4 Tests)
createTestSpec({
id: 'VID-01',
section: '5.5 Video Advertisements',
scoringCategory: 'ad_resource_blocking',
metricGroup: METRIC_GROUP.EXPLORATORY_PROBE,
name: 'Exploratory Google IMA3 Video Ad SDK Probe',
description: 'Exploratory probe to imasdk.googleapis.com/js/sdkloader/ima3.js.',
expectedBehavior: 'Reports REQUEST_FAILED_CAUSE_UNKNOWN when unreachable.',
controlResource: 'Same-origin connectivity verification',
testResource: 'https://imasdk.googleapis.com/js/sdkloader/ima3.js',
recommendedRule: '||imasdk.googleapis.com/js/sdkloader/ima3.js$script',
type: 'external_domain_probe',
targetUrl: 'https://imasdk.googleapis.com/js/sdkloader/ima3.js',
measuredBehavior: 'Reachability of external IMA3 video ad SDK.',
technicalValidity: VALIDITY_CLASS.EXPLORATORY_EXTERNAL,
browserObservation: 'no-cors Fetch outcome.',
dependsOnThirdParty: true,
scoringEligible: false,
limitations: 'Exploratory external probe; excluded from core score denominator.'
}),
createTestSpec({
id: 'VID-02',
section: '5.5 Video Advertisements',
scoringCategory: 'cosmetic_filtering',
metricGroup: METRIC_GROUP.AD_TRACKER_BLOCKING,
name: 'Video Player Pre-Roll & Companion Ad Overlay (.video-ads)',
description: 'Injects synthetic video ad overlay containers (.video-ads, .ytp-ad-module, .ima-ad-container) into the fixture.',
expectedBehavior: 'Cosmetic filter collapses the video ad overlay container.',
controlResource: 'DOM <div class="adshield-control-box">',
testResource: 'DOM <div id="video-ad-overlay-test" class="video-ads ytp-ad-module ima-ad-container">',
recommendedRule: '##.video-ads, .ytp-ad-module, .ima-ad-container',
type: 'cosmetic_dom',
selectorClasses: 'video-ads ytp-ad-module ima-ad-container vjs-ad-playing',
elementId: 'video-ad-overlay-test',
measuredBehavior: 'UI-level cosmetic hiding of HTML5 video player ad overlays.',
technicalValidity: VALIDITY_CLASS.CONTROLLED_FIXTURE,
browserObservation: 'Computed display and rendered height.',
limitations: 'Evaluates overlay visibility, not streaming manifest manipulation.'
}),
createTestSpec({
id: 'VID-03',
section: '5.5 Video Advertisements',
scoringCategory: 'ad_resource_blocking',
metricGroup: METRIC_GROUP.AD_TRACKER_BLOCKING,
name: 'Controlled VAST / VMAP XML Ad Tag Path (/ads/vast.xml)',
description: 'Tests URL interception on a controlled VAST XML request path (/ads/vast.xml?ad_rule=1&vad_type=linear).',
expectedBehavior: 'Request is aborted client-side by VAST ad-tag filter rules while neutral control succeeds.',
controlResource: 'Same-origin neutral fetch control',
testResource: 'Fetch GET /ads/vast.xml?ad_rule=1&vad_type=linear&adshield=1',
recommendedRule: '||*/ads/vast.xml*$xmlhttprequest',
type: 'url_pattern_probe',
probePath: '/ads/vast.xml?ad_rule=1&vad_type=linear&adshield=1',
measuredBehavior: 'Client-side blocking of VAST XML ad tag URL patterns.',
technicalValidity: VALIDITY_CLASS.CONTROLLED_FIXTURE,
browserObservation: 'Fetch rejection vs HTTP response.',
limitations: 'First-party path test.'
}),
createTestSpec({
id: 'VID-04',
section: '5.5 Video Advertisements',
scoringCategory: 'ad_resource_blocking',
metricGroup: METRIC_GROUP.AD_TRACKER_BLOCKING,
name: 'Mid-Roll Video Ad Quartile Ping Path (/video/ad_tracking?event=midpoint)',
description: 'Tests whether synthetic video ad quartile tracking pings (/video/ad_tracking?ad_id=99&event=midpoint) are intercepted.',
expectedBehavior: 'Video ad tracking request is aborted client-side.',
controlResource: 'Same-origin neutral fetch control',
testResource: 'Fetch GET /video/ad_tracking?ad_id=99&event=midpoint&vast=1',
recommendedRule: '||*/video/ad_tracking?*$xmlhttprequest',
type: 'url_pattern_probe',
probePath: '/video/ad_tracking?ad_id=99&event=midpoint&vast=1',
measuredBehavior: 'Interception of video ad quartile telemetry URLs.',
technicalValidity: VALIDITY_CLASS.CONTROLLED_FIXTURE,
browserObservation: 'Fetch rejection vs HTTP response.',
limitations: 'Evaluates URL pattern matching on synthetic quartile events.'
}),
// 5.6 Search, Sponsored Content & Native Advertising (4 Tests)
createTestSpec({
id: 'NAT-01',
section: '5.6 Search & Native Ads',
scoringCategory: 'cosmetic_filtering',
metricGroup: METRIC_GROUP.AD_TRACKER_BLOCKING,
name: 'Sponsored Search Result Container (#tads, .commercial-unit)',
description: 'Injects synthetic search ad containers (#tads, .commercial-unit-desktop-top) into the test fixture.',
expectedBehavior: 'Search ad container is hidden unless Acceptable Ads exceptions are enabled.',
controlResource: 'DOM <div class="adshield-control-box">',
testResource: 'DOM <div id="tads" class="commercial-unit-desktop-top ads-ad">',
recommendedRule: '###tads, .commercial-unit-desktop-top',
type: 'cosmetic_dom',
selectorClasses: 'commercial-unit-desktop-top ads-ad sponsored-search-ad',
elementId: 'tads',
measuredBehavior: 'Cosmetic suppression of search ad containers.',
technicalValidity: VALIDITY_CLASS.CONTROLLED_FIXTURE,
browserObservation: 'Computed display and rendered height.',
limitations: 'First-party native search ads served without standard selectors cannot be blocked by generic CSS.'
}),
createTestSpec({
id: 'NAT-02',
section: '5.6 Search & Native Ads',
scoringCategory: 'cosmetic_filtering',
metricGroup: METRIC_GROUP.AD_TRACKER_BLOCKING,
name: 'Native Recommendation Widget Containers (.OUTBRAIN, #taboola)',
description: 'Tests cosmetic hiding of third-party recommendation widget containers (#taboola-below-article-thumbnails, .OUTBRAIN).',
expectedBehavior: 'Recommendation widget containers are hidden in the DOM.',
controlResource: 'DOM <div class="adshield-control-box">',
testResource: 'DOM <div id="taboola-below-article-thumbnails" class="OUTBRAIN trc_rbox_container">',
recommendedRule: '###taboola-below-article-thumbnails, .OUTBRAIN, .trc_rbox_container',
type: 'cosmetic_dom',
selectorClasses: 'OUTBRAIN trc_rbox_container taboola-widget',
elementId: 'taboola-below-article-thumbnails',
measuredBehavior: 'DOM hiding of Taboola and Outbrain container selectors.',
technicalValidity: VALIDITY_CLASS.CONTROLLED_FIXTURE,
browserObservation: 'Computed display and layout height.',
limitations: 'Measures DOM container hiding separately from network requests.'
}),
createTestSpec({
id: 'NAT-03',
section: '5.6 Search & Native Ads',
scoringCategory: 'ad_resource_blocking',
metricGroup: METRIC_GROUP.EXPLORATORY_PROBE,
name: 'Exploratory Native Ad Network Probe (widgets.outbrain.com)',
description: 'Exploratory subresource probe to widgets.outbrain.com/outbrain.js.',
expectedBehavior: 'Reports REQUEST_FAILED_CAUSE_UNKNOWN when unreachable.',
controlResource: 'Same-origin connectivity verification',
testResource: 'https://widgets.outbrain.com/outbrain.js',
recommendedRule: '||outbrain.com^$third-party',
type: 'external_domain_probe',
targetUrl: 'https://widgets.outbrain.com/outbrain.js',
measuredBehavior: 'Reachability of external Outbrain widget script.',
technicalValidity: VALIDITY_CLASS.EXPLORATORY_EXTERNAL,
browserObservation: 'no-cors Fetch outcome.',
dependsOnThirdParty: true,
scoringEligible: false,
limitations: 'Exploratory external probe; excluded from core score denominator.'
}),
createTestSpec({
id: 'NAT-04',
section: '5.6 Search & Native Ads',
scoringCategory: 'cosmetic_filtering',
metricGroup: METRIC_GROUP.AD_TRACKER_BLOCKING,
name: 'Sponsored Product Card & In-Feed Native Unit (.sponsored-product-card)',
description: 'Tests cosmetic filtering of in-feed sponsored product placements (.sponsored-product-card, .native-ad-item, .promoted-listing).',
expectedBehavior: 'In-feed sponsored card is collapsed by cosmetic rules.',
controlResource: 'DOM <div class="adshield-control-box">',
testResource: 'DOM <div id="native-product-04" class="sponsored-product-card native-ad-item adsbox">',
recommendedRule: '##.sponsored-product-card, .native-ad-item, .promoted-listing',
type: 'cosmetic_dom',
selectorClasses: 'sponsored-product-card native-ad-item promoted-listing adsbox',
elementId: 'native-product-04',
measuredBehavior: 'Cosmetic hiding of sponsored product card selectors.',
technicalValidity: VALIDITY_CLASS.CONTROLLED_FIXTURE,
browserObservation: 'Computed style and rendered dimensions.',
limitations: 'First-party native ads without distinguishing classes cannot be separated from organic content by network filters.'
}),
// 5.7 Social and Embedded Advertising (3 Tests)
createTestSpec({
id: 'SOC-01',
section: '5.7 Social & Embedded Ads',
scoringCategory: 'tracker_beacon_blocking',
metricGroup: METRIC_GROUP.EXPLORATORY_PROBE,
name: 'Exploratory Social Pixel Probe (connect.facebook.net/fbevents.js)',
description: 'Exploratory subresource probe to connect.facebook.net/en_US/fbevents.js.',
expectedBehavior: 'Reports REQUEST_FAILED_CAUSE_UNKNOWN when unreachable.',
controlResource: 'Same-origin connectivity verification',
testResource: 'https://connect.facebook.net/en_US/fbevents.js',
recommendedRule: '||connect.facebook.net/*/fbevents.js$script',
type: 'external_domain_probe',
targetUrl: 'https://connect.facebook.net/en_US/fbevents.js',
measuredBehavior: 'Reachability of Meta Pixel fbevents.js endpoint.',
technicalValidity: VALIDITY_CLASS.EXPLORATORY_EXTERNAL,
browserObservation: 'no-cors Fetch outcome.',
dependsOnThirdParty: true,
scoringEligible: false,
limitations: 'Exploratory external check; excluded from core score denominator.'
}),
createTestSpec({
id: 'SOC-02',
section: '5.7 Social & Embedded Ads',
scoringCategory: 'cosmetic_filtering',
metricGroup: METRIC_GROUP.AD_TRACKER_BLOCKING,
name: 'Sponsored Social Feed Card (.promoted-tweet, .sponsored-post)',
description: 'Injects synthetic social promotional feed containers (.promoted-tweet, .fbEmuTracking, .ad-social-card).',
expectedBehavior: 'Social promotional container is hidden.',
controlResource: 'DOM <div class="adshield-control-box">',
testResource: 'DOM <div id="social-ad-card-test" class="promoted-tweet fbEmuTracking ad-social-card">',
recommendedRule: '##.promoted-tweet, .fbEmuTracking, .ad-social-card',
type: 'cosmetic_dom',
selectorClasses: 'promoted-tweet fbEmuTracking ad-social-card sponsored-post-unit',
elementId: 'social-ad-card-test',
measuredBehavior: 'DOM hiding of promoted social feed selectors.',
technicalValidity: VALIDITY_CLASS.CONTROLLED_FIXTURE,
browserObservation: 'Computed display and rendered height.',
limitations: 'Tests known social ad class selectors.'
}),
createTestSpec({
id: 'SOC-03',
section: '5.7 Social & Embedded Ads',
scoringCategory: 'tracker_beacon_blocking',
metricGroup: METRIC_GROUP.AD_TRACKER_BLOCKING,
name: 'Embedded Social Share & Tracking Widget Container (.addthis_toolbox)',
description: 'Tests cosmetic suppression of embedded social tracking toolbars (.addthis_toolbox, .sharethis-inline-share-buttons).',
expectedBehavior: 'Social tracking widget container is hidden by social annoyance/privacy lists.',
controlResource: 'DOM <div class="adshield-control-box">',
testResource: 'DOM <div id="social-widget-03" class="addthis_toolbox sharethis-inline-share-buttons">',
recommendedRule: '##.addthis_toolbox, .sharethis-inline-share-buttons',
type: 'cosmetic_dom',
selectorClasses: 'addthis_toolbox sharethis-inline-share-buttons social-tracking-widget',
elementId: 'social-widget-03',
measuredBehavior: 'DOM hiding of third-party social share widget containers.',
technicalValidity: VALIDITY_CLASS.CONTROLLED_FIXTURE,
browserObservation: 'Computed display and layout height.',
limitations: 'Only hidden when social widget filter lists are enabled.'
}),
// 5.8 Affiliate and Tracking Scenarios (4 Tests)
createTestSpec({
id: 'AFF-01',
section: '5.8 Affiliate & Attribution',
scoringCategory: 'redirect_popup_prevention',
metricGroup: METRIC_GROUP.AD_TRACKER_BLOCKING,
name: 'Controlled Affiliate Click-Tracking Path (/aff_c?offer_id=999)',
description: 'Tests whether requests to synthetic affiliate click-measurement paths (/aff_c?offer_id=999&aff_id=888) are intercepted.',
expectedBehavior: 'Request is aborted client-side by affiliate/tracking URL filter rules.',
controlResource: 'Same-origin neutral fetch control',
testResource: 'Fetch GET /aff_c?offer_id=999&aff_id=888&adshield_test=1',
recommendedRule: '||*/aff_c?offer_id=*$doc,xmlhttprequest',
type: 'url_pattern_probe',
probePath: '/aff_c?offer_id=999&aff_id=888&adshield_test=1',
measuredBehavior: 'Client-side interception of HasOffers/affiliate click-tracking path patterns.',
technicalValidity: VALIDITY_CLASS.CONTROLLED_FIXTURE,
browserObservation: 'Fetch rejection vs HTTP response.',
limitations: 'Never navigates the tab or triggers external affiliate redirects.'
}),
createTestSpec({
id: 'AFF-02',
section: '5.8 Affiliate & Attribution',
scoringCategory: 'redirect_popup_prevention',
metricGroup: METRIC_GROUP.PRIVACY_SIGNAL,
name: 'URL Tracking Parameter Hygiene Signal (fbclid / gclid / utm_*)',
description: 'Inspects current document location for residual tracking parameters. Evaluated in the Privacy Signals index.',
expectedBehavior: 'Reports whether active location URL is free of known click-tracking parameters.',
controlResource: 'window.location.search inspection',
testResource: 'Location query parameter check (fbclid, gclid, msclkid, mc_eid)',
recommendedRule: '*$removeparam=/^(fbclid|gclid|msclkid|mc_eid)$/',
type: 'query_param_check',
measuredBehavior: 'Presence of tracking query parameters on current document location.',
technicalValidity: VALIDITY_CLASS.PRIVACY_CAPABILITY,
browserObservation: 'URLSearchParams inspection of window.location.search.',
scoringEligible: false,
limitations: 'Excluded from Ad & Tracker Blocking Score because an in-page URL check cannot test top-level navigation query stripping unless navigated with parameters.'
}),
createTestSpec({
id: 'AFF-03',
section: '5.8 Affiliate & Attribution',
scoringCategory: 'redirect_popup_prevention',
metricGroup: METRIC_GROUP.PRIVACY_SIGNAL,
name: 'Cross-Origin Referrer Policy Hardening Signal',
description: 'Inspects document referrer policy configuration as part of the Browser Privacy & Hardening Signals index.',
expectedBehavior: 'Reports SIGNAL_ACTIVE if strict no-referrer/same-origin policy is enforced, or SIGNAL_INACTIVE for standard defaults.',
controlResource: 'Document meta[name="referrer"] & document.referrer inspection',
testResource: 'Document Referrer-Policy evaluation',
recommendedRule: 'Browser Privacy Config: network.http.sendRefererHeader = 0',
type: 'referrer_policy_check',
measuredBehavior: 'Document Referrer-Policy configuration inspection.',
technicalValidity: VALIDITY_CLASS.PRIVACY_CAPABILITY,
browserObservation: 'Meta referrer attribute inspection.',
scoringEligible: false,
limitations: 'P0-3 Fix: Evaluated in the separate Privacy Signals index; never inflates the Ad & Tracker Blocking Score.'
}),
createTestSpec({
id: 'AFF-04',
section: '5.8 Affiliate & Attribution',
scoringCategory: 'tracker_beacon_blocking',
metricGroup: METRIC_GROUP.AD_TRACKER_BLOCKING,
name: 'Controlled Click-Attribution Beacon Path (/clk?ad_id=100&redir=none)',
description: 'Tests client-side blocking of outbound click-measurement endpoints (/clk?ad_id=100&adurl=example).',
expectedBehavior: 'Click attribution path is intercepted prior to server response.',
controlResource: 'Same-origin neutral fetch control',
testResource: 'Fetch GET /clk?ad_id=100&adurl=https%3A%2F%2Fexample.invalid',
recommendedRule: '||*/clk?ad_id=*&adurl=*$xmlhttprequest',
type: 'url_pattern_probe',
probePath: '/clk?ad_id=100&adurl=https%3A%2F%2Fexample.invalid',
measuredBehavior: 'Interception of /clk?adurl= click attribution requests.',
technicalValidity: VALIDITY_CLASS.CONTROLLED_FIXTURE,
browserObservation: 'Fetch rejection vs HTTP response.',
limitations: 'First-party path test.'
}),
// 5.9 Anti-Adblock & Circumvention Scenarios (4 Tests)
createTestSpec({
id: 'AAB-01',
section: '5.9 Anti-Adblock & Circumvention',
scoringCategory: 'script_resource_filtering',
metricGroup: METRIC_GROUP.AD_TRACKER_BLOCKING,
name: 'Bait Element OffsetHeight & Scriptlet Surrogate Inspection',
description: 'Injects a classic .adsbox bait element and checks both whether it is hidden and whether surrogate globals (google_ad_status) are shimmed.',
expectedBehavior: 'Bait is hidden by cosmetic rules and/or scriptlet shims neutralize detection.',
controlResource: 'DOM <div class="adshield-control-box">',
testResource: 'DOM <div class="ad_unit adsbox text-ad"> + window surrogate check',
recommendedRule: '##.adsbox, .ad_unit, .text-ad',
type: 'anti_adblock_bait_check',
measuredBehavior: 'Interaction between cosmetic bait hiding and anti-adblock surrogate globals.',
technicalValidity: VALIDITY_CLASS.CONTROLLED_FIXTURE,
browserObservation: 'bait.offsetHeight and window global property checks.',
limitations: 'Many anti-adblock scriptlets only inject on specific domain names listed in filter lists.'
}),
createTestSpec({
id: 'AAB-02',
section: '5.9 Anti-Adblock & Circumvention',
scoringCategory: 'cosmetic_filtering',
metricGroup: METRIC_GROUP.AD_TRACKER_BLOCKING,
name: 'Anti-Adblock Warning Modal Suppression (#adblock-notice, .adblock-modal)',
description: 'Injects a synthetic anti-adblock nag container (#adblock-notice, .adblock-modal) to test Adblock Warning Removal rules.',
expectedBehavior: 'Anti-adblock modal container is hidden in the DOM.',
controlResource: 'DOM <div class="adshield-control-box">',
testResource: 'DOM <div id="adblock-notice" class="adblock-modal adb-overlay">',
recommendedRule: '###adblock-notice, .adblock-modal, .adb-overlay',
type: 'cosmetic_dom',
selectorClasses: 'adblock-modal adb-overlay adblocker-warning',
elementId: 'adblock-notice',
measuredBehavior: 'Cosmetic hiding of anti-adblock warning modal selectors.',
technicalValidity: VALIDITY_CLASS.CONTROLLED_FIXTURE,
browserObservation: 'Computed display and layout height.',
limitations: 'Requires anti-adblock or annoyance filter lists.'
}),
createTestSpec({
id: 'AAB-03',
section: '5.9 Anti-Adblock & Circumvention',
scoringCategory: 'script_resource_filtering',
metricGroup: METRIC_GROUP.AD_TRACKER_BLOCKING,
name: 'Ad Container Re-Insertion Resilience',
description: 'Simulates a script removing and re-inserting an ad node (.adsbox, .ad-banner) to verify continuous CSS rule enforcement.',
expectedBehavior: 'Re-inserted ad node remains hidden immediately upon re-insertion.',
controlResource: 'Re-inserted neutral control element',
testResource: 'Re-inserted <div id="recreated-ad-node-03" class="adsbox ad-banner ad-unit-recreated">',
recommendedRule: '##.adsbox, .ad-banner, .ad-unit-recreated',
type: 'dynamic_cosmetic_dom',
selectorClasses: 'adsbox ad-banner ad-unit-recreated',
elementId: 'recreated-ad-node-03',
measuredBehavior: 'Persistence of cosmetic hiding across DOM removal and re-insertion.',
technicalValidity: VALIDITY_CLASS.CONTROLLED_FIXTURE,
browserObservation: 'Computed display and bounding box after re-insertion.',
limitations: 'Validates persistent CSS rules.'
}),
createTestSpec({
id: 'AAB-04',
section: '5.9 Anti-Adblock & Circumvention',
scoringCategory: 'script_resource_filtering',
metricGroup: METRIC_GROUP.AD_TRACKER_BLOCKING,
name: 'Fallback Ad-Loader Retry Interception (/ads/fallback_loader.js)',
description: 'Simulates an anti-adblock recovery script attempting to fetch a secondary ad path (/ads/fallback_loader.js?retry=1).',
expectedBehavior: 'Secondary fallback ad request is also intercepted client-side.',
controlResource: 'Same-origin neutral fetch control',
testResource: 'Fetch GET /ads/fallback_loader.js?retry=1&ad_recover=1',
recommendedRule: '||*/ads/fallback_loader.js*$script,xmlhttprequest',
type: 'url_pattern_probe',
probePath: '/ads/fallback_loader.js?retry=1&ad_recover=1',
measuredBehavior: 'Interception of secondary/fallback /ads/* script requests.',
technicalValidity: VALIDITY_CLASS.CONTROLLED_FIXTURE,
browserObservation: 'Fetch rejection vs HTTP response.',
limitations: 'Controlled relative path probe.'
}),
// 5.10 Script & Resource Filtering (4 Tests)
createTestSpec({
id: 'SCR-01',
section: '5.10 Script & Resource Filtering',
scoringCategory: 'script_resource_filtering',
metricGroup: METRIC_GROUP.AD_TRACKER_BLOCKING,
name: 'Dynamic <script> Tag Injection Interception (/ads/adserver.js)',
description: 'Dynamically appends <script src="/ads/adserver.js?adshield=1"> and distinguishes immediate filter error from HTTP 404.',
expectedBehavior: 'Script element fires onerror AND companion fetch confirms client-side abort prior to HTTP status while neutral control succeeds.',
controlResource: 'Same-origin neutral control fetch + DOM script element check',
testResource: '<script src="/ads/adserver.js?adshield_synthetic=1">',
recommendedRule: '||*/ads/adserver.js*$script',
type: 'script_tag_probe',
probePath: '/ads/adserver.js?adshield_synthetic=1',
measuredBehavior: 'HTMLScriptElement loading interception on /ads/adserver.js.',
technicalValidity: VALIDITY_CLASS.CONTROLLED_FIXTURE,
browserObservation: 'script.onerror event + companion fetch status verification.',
limitations: 'Cross-checks fetch abortion vs HTTP 404 so missing backend routes are never misclassified as adblock passes.'
}),
createTestSpec({
id: 'SCR-02',
section: '5.10 Script & Resource Filtering',
scoringCategory: 'script_resource_filtering',
metricGroup: METRIC_GROUP.AD_TRACKER_BLOCKING,
name: 'XMLHttpRequest Ad Bid Endpoint Interception (/adserver/bid)',
description: 'Tests whether XMLHttpRequest to /adserver/bid?slot=leaderboard&ad_type=banner is aborted at status 0.',