@duckduckgo/autoconsent 15.1.0 → 16.0.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/.github/dependabot.yml +0 -3
- package/.github/workflows/release.yml +0 -4
- package/AGENTS.md +3 -3
- package/CHANGELOG.md +20 -0
- package/build.sh +0 -2
- package/dist/addon-firefox/background.bundle.js +8 -24
- package/dist/addon-firefox/compact-rules.json +1 -1
- package/dist/addon-firefox/content.bundle.js +176 -686
- package/dist/addon-firefox/manifest.json +1 -1
- package/dist/addon-firefox/popup.bundle.js +31 -27
- package/dist/addon-firefox/popup.html +21 -13
- package/dist/addon-firefox/rule-index.json +1 -1
- package/dist/addon-firefox/rules.json +1 -1
- package/dist/addon-mv3/background.bundle.js +8 -24
- package/dist/addon-mv3/compact-rules.json +1 -1
- package/dist/addon-mv3/content.bundle.js +176 -686
- package/dist/addon-mv3/manifest.json +1 -1
- package/dist/addon-mv3/popup.bundle.js +31 -27
- package/dist/addon-mv3/popup.html +21 -13
- package/dist/addon-mv3/rule-index.json +1 -1
- package/dist/addon-mv3/rules.json +1 -1
- package/dist/autoconsent.cjs.js +176 -685
- package/dist/autoconsent.esm.js +176 -685
- package/dist/autoconsent.playwright.js +177 -689
- package/dist/autoconsent.standalone.js +177 -690
- package/dist/types/cmps/base.d.ts +3 -3
- package/dist/types/heuristics.d.ts +2 -2
- package/dist/types/types.d.ts +10 -23
- package/dist/types/web.d.ts +0 -9
- package/docs/api.md +1 -8
- package/docs/puppeteer.md +1 -3
- package/docs/rule-syntax.md +0 -17
- package/eslint.config.mjs +1 -1
- package/lib/cmps/base.ts +5 -6
- package/lib/heuristics.ts +15 -15
- package/lib/types.ts +9 -21
- package/lib/utils.ts +2 -4
- package/lib/web.ts +3 -53
- package/package.json +4 -17
- package/playwright/content.ts +1 -2
- package/readme.md +3 -6
- package/rules/autoconsent/dreamlab-cmp.json +13 -0
- package/rules/build.ts +1 -24
- package/rules/compact-rules.json +1 -1
- package/rules/rule-index-builder.ts +2 -7
- package/rules/rule-index.json +1 -1
- package/rules/rules.json +1 -1
- package/standalone/content.ts +0 -2
- package/tests/dreamlab-cmp.spec.ts +3 -0
- package/tests/quantcast.spec.ts +0 -6
- package/tests-wtr/heuristics/get-actionable-popups.ts +13 -13
- package/tests-wtr/heuristics/heuristic-cmp.ts +32 -33
- package/tests-wtr/lifecycle/find-cmp.ts +5 -8
- package/tests-wtr/web/do-opt-in.test.ts +0 -1
- package/tests-wtr/web/do-opt-out.test.ts +0 -1
- package/tests-wtr/web/do-self-test.test.ts +0 -1
- package/tests-wtr/web/filter-cmps.test.ts +0 -1
- package/tests-wtr/web/receive-message-callback.test.ts +1 -2
- package/tests-wtr/web/update-state.test.ts +1 -3
- package/tsconfig.build.json +1 -1
- package/.github/workflows/update-filterlist.yml +0 -42
- package/dist/autoconsent.extra.cjs.js +0 -15717
- package/dist/autoconsent.extra.esm.js +0 -15649
- package/dist/types/cmps/consentomatic.d.ts +0 -34
- package/dist/types/consentomatic/index.d.ts +0 -2
- package/dist/types/consentomatic/tools.d.ts +0 -10
- package/dist/types/filterlist-engine.d.ts +0 -2
- package/dist/types/filterlist-utils.d.ts +0 -4
- package/dist/types/web-extra.d.ts +0 -11
- package/lib/cmps/consentomatic.ts +0 -97
- package/lib/consentomatic/index.ts +0 -252
- package/lib/consentomatic/tools.ts +0 -199
- package/lib/filterlist-engine.ts +0 -5
- package/lib/filterlist-utils.ts +0 -45
- package/lib/web-extra.ts +0 -116
- package/rules/consentomatic.json +0 -1
- package/rules/filterlist.txt +0 -18440
- package/rules/filterlists/overrides.txt +0 -4
- package/scripts/compile-filterlist.mjs +0 -63
- package/scripts/fetch-easylist.sh +0 -25
- package/scripts/rebuild-filterlist.mjs +0 -216
- package/tests/oil.spec.ts +0 -7
- package/tests/springer.spec.ts +0 -7
- package/tests/wordpressgdpr.spec.ts +0 -6
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AutoCMP, DomActionsProvider,
|
|
1
|
+
import { AutoCMP, DomActionsProvider, HeuristicLevel, PopupData } from '../types';
|
|
2
2
|
import { AutoConsentCMPRule, AutoConsentRuleStep, ElementSelector, HideMethod, RunContext, VisibilityCheck } from '../rules';
|
|
3
3
|
import AutoConsent from '../web';
|
|
4
4
|
import { snippets } from '../eval-snippets';
|
|
@@ -62,8 +62,8 @@ export declare class AutoConsentCMP extends AutoConsentCMPBase {
|
|
|
62
62
|
}
|
|
63
63
|
export declare class AutoConsentHeuristicCMP extends AutoConsentCMPBase {
|
|
64
64
|
popups: PopupData[];
|
|
65
|
-
mode:
|
|
66
|
-
constructor(autoconsentInstance: AutoConsent, mode?:
|
|
65
|
+
mode: HeuristicLevel;
|
|
66
|
+
constructor(autoconsentInstance: AutoConsent, mode?: HeuristicLevel);
|
|
67
67
|
get hasSelfTest(): boolean;
|
|
68
68
|
get isIntermediate(): boolean;
|
|
69
69
|
get isCosmetic(): boolean;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { ButtonData, ButtonRegexClassification,
|
|
1
|
+
import { ButtonData, ButtonRegexClassification, HeuristicLevel, PopupData } from './types';
|
|
2
2
|
export declare function checkHeuristicPatterns(allText: string, detectPatterns?: RegExp[]): {
|
|
3
3
|
patterns: string[];
|
|
4
4
|
snippets: string[];
|
|
5
5
|
};
|
|
6
|
-
export declare function getActionablePopups(mode?:
|
|
6
|
+
export declare function getActionablePopups(mode?: HeuristicLevel, timeout?: number): PopupData[];
|
|
7
7
|
export declare function classifyButtons(buttons: ButtonData[]): void;
|
|
8
8
|
export declare function cleanButtonText(buttonText: string): string;
|
|
9
9
|
export declare function classifyButtonTextRegex(buttonText: string): ButtonRegexClassification;
|
package/dist/types/types.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { ContentScriptMessage } from './messages';
|
|
2
|
-
import { ConsentOMaticConfig } from './cmps/consentomatic';
|
|
3
2
|
import { AutoConsentCMPRule, ElementSelector, HideMethod, RunContext, VisibilityCheck } from './rules';
|
|
4
3
|
import { CompactCMPRuleset } from './encoding';
|
|
5
4
|
export type MessageSender = (message: ContentScriptMessage) => Promise<void>;
|
|
@@ -43,9 +42,6 @@ export interface DomActionsProvider {
|
|
|
43
42
|
export type RuleBundle = {
|
|
44
43
|
autoconsent: AutoConsentCMPRule[];
|
|
45
44
|
compact?: CompactCMPRuleset;
|
|
46
|
-
consentomatic?: {
|
|
47
|
-
[name: string]: ConsentOMaticConfig;
|
|
48
|
-
};
|
|
49
45
|
};
|
|
50
46
|
export type AutoAction = 'optOut' | 'optIn' | null;
|
|
51
47
|
export type Config = {
|
|
@@ -58,9 +54,7 @@ export type Config = {
|
|
|
58
54
|
detectRetries: number;
|
|
59
55
|
isMainWorld: boolean;
|
|
60
56
|
prehideTimeout: number;
|
|
61
|
-
enableFilterList: boolean;
|
|
62
57
|
enableHeuristicDetection: boolean;
|
|
63
|
-
enableHeuristicAction: boolean;
|
|
64
58
|
enablePopupMutationObserver: boolean;
|
|
65
59
|
visualTest: boolean;
|
|
66
60
|
logs: {
|
|
@@ -74,12 +68,10 @@ export type Config = {
|
|
|
74
68
|
};
|
|
75
69
|
performanceLoggingEnabled: boolean;
|
|
76
70
|
heuristicPopupSearchTimeout: number;
|
|
77
|
-
heuristicMode:
|
|
71
|
+
heuristicMode: HeuristicLevel;
|
|
78
72
|
};
|
|
79
|
-
export type LifecycleState = 'loading' | 'initialized' | 'waitingForInitResponse' | 'started' | 'nothingDetected' | '
|
|
73
|
+
export type LifecycleState = 'loading' | 'initialized' | 'waitingForInitResponse' | 'started' | 'nothingDetected' | 'cmpDetected' | 'openPopupDetected' | 'runningOptOut' | 'runningOptIn' | 'optOutSucceeded' | 'optOutFailed' | 'optInSucceeded' | 'optInFailed' | 'done';
|
|
80
74
|
export type ConsentState = {
|
|
81
|
-
cosmeticFiltersOn: boolean;
|
|
82
|
-
filterListReported: boolean;
|
|
83
75
|
lifecycle: LifecycleState;
|
|
84
76
|
prehideOn: boolean;
|
|
85
77
|
findCmpAttempts: number;
|
|
@@ -103,19 +95,14 @@ export interface PopupData {
|
|
|
103
95
|
text: string;
|
|
104
96
|
element: HTMLElement;
|
|
105
97
|
buttons: ButtonData[];
|
|
106
|
-
regexClassification?:
|
|
98
|
+
regexClassification?: PopupClassification;
|
|
107
99
|
}
|
|
108
100
|
/**
|
|
109
|
-
*
|
|
110
|
-
* -
|
|
111
|
-
* -
|
|
112
|
-
* -
|
|
113
|
-
* -
|
|
101
|
+
* The user setting for autoconsent and heuristic detection.
|
|
102
|
+
* - off: Heuristic detection is disabled.
|
|
103
|
+
* - reject: Heuristic detection is enabled, and will click the reject button on a popup if it exists.
|
|
104
|
+
* - tier1: Heuristic detection is enabled, and will click the acknowledge button on a popup if it exists, and no Reject or Settings button exists.
|
|
105
|
+
* - tier2: Heuristic detection is enabled, and will click the accept button on a popup if it exists, and no Reject, Acknowledge, or Settings button exists.
|
|
114
106
|
*/
|
|
115
|
-
export
|
|
116
|
-
|
|
117
|
-
readonly Reject: 0;
|
|
118
|
-
readonly Tier1: 1;
|
|
119
|
-
readonly Tier2: 2;
|
|
120
|
-
};
|
|
121
|
-
export type PopupHandlingMode = (typeof PopupHandlingModes)[keyof typeof PopupHandlingModes];
|
|
107
|
+
export type HeuristicLevel = 'off' | 'reject' | 'tier1' | 'tier2';
|
|
108
|
+
export type PopupClassification = 'none' | 'reject' | 'tier1' | 'tier2';
|
package/dist/types/web.d.ts
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import { MessageSender, AutoCMP, RuleBundle, Config, ConsentState } from './types';
|
|
2
|
-
import { ConsentOMaticConfig } from './cmps/consentomatic';
|
|
3
2
|
import { AutoConsentCMPRule } from './rules';
|
|
4
3
|
import { BackgroundMessage } from './messages';
|
|
5
4
|
import { DomActions } from './dom-actions';
|
|
6
|
-
import { FiltersEngine } from '@ghostery/adblocker';
|
|
7
5
|
export { snippets as evalSnippets } from './eval-snippets';
|
|
8
6
|
export { filterCompactRules, encodeRules, decodeRules } from './encoding';
|
|
9
7
|
export type { CompactRuleStep, CompactCMPRuleStep, CompactCMPRule, CompactCMPRuleset, IndexedCMPRuleset } from './encoding';
|
|
@@ -18,21 +16,17 @@ export default class AutoConsent {
|
|
|
18
16
|
foundCmp?: AutoCMP;
|
|
19
17
|
state: ConsentState;
|
|
20
18
|
domActions: DomActions;
|
|
21
|
-
filtersEngine?: FiltersEngine;
|
|
22
19
|
sendContentMessage: MessageSender;
|
|
23
|
-
protected cosmeticStyleSheet?: CSSStyleSheet;
|
|
24
20
|
protected focusedElement?: HTMLElement;
|
|
25
21
|
constructor(sendContentMessage: MessageSender, config?: Partial<Config> | null, declarativeRules?: RuleBundle | null);
|
|
26
22
|
get config(): Config;
|
|
27
23
|
initialize(config: Partial<Config>, declarativeRules: RuleBundle | null): void;
|
|
28
|
-
initializeFilterList(): void;
|
|
29
24
|
get shouldPrehide(): boolean;
|
|
30
25
|
saveFocus(): void;
|
|
31
26
|
restoreFocus(): void;
|
|
32
27
|
addDynamicRules(): void;
|
|
33
28
|
parseDeclarativeRules(declarativeRules: RuleBundle): void;
|
|
34
29
|
addDeclarativeCMP(ruleset: AutoConsentCMPRule): void;
|
|
35
|
-
addConsentomaticCMP(name: string, config: ConsentOMaticConfig): void;
|
|
36
30
|
start(): void;
|
|
37
31
|
_start(): Promise<boolean>;
|
|
38
32
|
findCmp(retries: number): Promise<AutoCMP[]>;
|
|
@@ -52,9 +46,6 @@ export default class AutoConsent {
|
|
|
52
46
|
waitForPopup(cmp: AutoCMP, retries?: number, interval?: number): Promise<boolean>;
|
|
53
47
|
prehideElements(): boolean;
|
|
54
48
|
undoPrehide(): void;
|
|
55
|
-
undoCosmetics(): void;
|
|
56
|
-
reportFilterlist(): void;
|
|
57
|
-
filterListFallback(): boolean;
|
|
58
49
|
updateState(change: Partial<ConsentState>): void;
|
|
59
50
|
receiveMessageCallback(message: BackgroundMessage): Promise<void>;
|
|
60
51
|
measurePerformance(): {
|
package/docs/api.md
CHANGED
|
@@ -12,7 +12,6 @@ const consent = new AutoConsent( // make sure not to leak anything to the page g
|
|
|
12
12
|
disabledCmps: [],
|
|
13
13
|
enablePrehide: true,
|
|
14
14
|
enableCosmeticRules: true,
|
|
15
|
-
enableFilterList: false,
|
|
16
15
|
detectRetries: 20,
|
|
17
16
|
isMainWorld: false,
|
|
18
17
|
prehideTimeout: 2000,
|
|
@@ -27,7 +26,6 @@ const consent = new AutoConsent( // make sure not to leak anything to the page g
|
|
|
27
26
|
},
|
|
28
27
|
{ // optionally, pass JSON rules
|
|
29
28
|
autoconsent: [ ... ],
|
|
30
|
-
consentomatic: [ ... ],
|
|
31
29
|
}
|
|
32
30
|
);
|
|
33
31
|
|
|
@@ -59,14 +57,9 @@ sequenceDiagram
|
|
|
59
57
|
|
|
60
58
|
activate CS
|
|
61
59
|
Note right of CS: Parse rules and initialize autoconsent code
|
|
62
|
-
Note right of CS: apply prehideSelectors
|
|
60
|
+
Note right of CS: apply prehideSelectors (if enabled)
|
|
63
61
|
Note right of CS: wait for DOMContentLoaded
|
|
64
62
|
|
|
65
|
-
opt if filterlist enabled and filterlist selectors matched
|
|
66
|
-
CS -->> BG: cmpDetected (with cmp: "filterList")
|
|
67
|
-
CS -->> BG: popupFound (with cmp: "filterList")
|
|
68
|
-
end
|
|
69
|
-
|
|
70
63
|
Note right of CS: detect a CMP presence (not necessarily visible)
|
|
71
64
|
|
|
72
65
|
CS ->> BG: cmpDetected
|
package/docs/puppeteer.md
CHANGED
|
@@ -40,7 +40,6 @@ const autoconsentConfig = {
|
|
|
40
40
|
detectRetries: 20,
|
|
41
41
|
isMainWorld: false,
|
|
42
42
|
prehideTimeout: 2000,
|
|
43
|
-
enableFilterList: false,
|
|
44
43
|
enableHeuristicDetection: true,
|
|
45
44
|
enableHeuristicAction: true,
|
|
46
45
|
logs: {
|
|
@@ -144,13 +143,12 @@ Key options:
|
|
|
144
143
|
| `enableCosmeticRules` | boolean | — | Enable rules that hide popups via CSS |
|
|
145
144
|
| `enableGeneratedRules` | boolean | — | Include auto-generated rules |
|
|
146
145
|
| `detectRetries` | number | — | How many times to retry CMP detection |
|
|
147
|
-
| `enableFilterList` | boolean | — | Enable ABP/uBO cosmetic filter support (requires the `/extra` build) |
|
|
148
146
|
|
|
149
147
|
## Rules
|
|
150
148
|
|
|
151
149
|
The `initResp` message **must** include a `rules` object. Without rules, no CMPs will be detected. The package ships pre-built rule bundles:
|
|
152
150
|
|
|
153
|
-
- `@duckduckgo/autoconsent/rules/rules.json` — full rules (includes `autoconsent`
|
|
151
|
+
- `@duckduckgo/autoconsent/rules/rules.json` — full rules (includes `autoconsent` array)
|
|
154
152
|
- `@duckduckgo/autoconsent/rules/compact-rules.json` — compact encoding for opt-out only (smaller payload)
|
|
155
153
|
|
|
156
154
|
Load either file and pass it as the `rules` field in the `initResp` message.
|
package/docs/rule-syntax.md
CHANGED
|
@@ -225,23 +225,6 @@ Sometimes the opt-out process requires actions that span across multiple pages o
|
|
|
225
225
|
|
|
226
226
|
Some rules do not interact with the page, and only hide the cookie pop-ups with CSS. These rules are marked with the `cosmetic: true` flag. They are useful for pop-ups that do not provide a Reject button. Cosmetic rules can be disabled with the `enableCosmeticRules` config option.
|
|
227
227
|
|
|
228
|
-
### Filterlist
|
|
229
|
-
Autoconsent supports cosmetic filters in common ABP/uBO format. For performance reasons, it needs to be bundled at build time. At the moment we include cosmetic filters from [Easylist Cookie](https://github.com/easylist/easylist/tree/master/easylist_cookie).
|
|
230
|
-
Note that by default filterlist rules are not included, as this significantly increases the resulting bundle size. To use filterlist rules, you need to explicitly import the "extra" version of the library (`@duckduckgo/autoconsent/extra`), and set the `enableFilterlist` config option to `true`.
|
|
231
|
-
|
|
232
|
-
```javascript
|
|
233
|
-
// import the library version with bundled filterlist rules
|
|
234
|
-
import AutoConsent from '@duckduckgo/autoconsent/extra'
|
|
235
|
-
|
|
236
|
-
// ...
|
|
237
|
-
|
|
238
|
-
new AutoConsent({
|
|
239
|
-
enableFilterlist: true,
|
|
240
|
-
// other options
|
|
241
|
-
})
|
|
242
|
-
|
|
243
|
-
```
|
|
244
|
-
|
|
245
228
|
## Context filters
|
|
246
229
|
|
|
247
230
|
By default, rules will be executed in all top-level documents. Some rules are designed for specific contexts (e.g. only nested iframes, or only specific URLs). This can be configured in `runContext` field (see [runContext](#rule-syntax-reference) above).
|
package/eslint.config.mjs
CHANGED
package/lib/cmps/base.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AutoCMP, DomActionsProvider,
|
|
1
|
+
import { AutoCMP, DomActionsProvider, HeuristicLevel, PopupData } from '../types';
|
|
2
2
|
import { AutoConsentCMPRule, AutoConsentRuleStep, ElementSelector, HideMethod, RunContext, VisibilityCheck } from '../rules';
|
|
3
3
|
import { requestEval } from '../eval-handler';
|
|
4
4
|
import AutoConsent from '../web';
|
|
@@ -419,9 +419,9 @@ export class AutoConsentCMP extends AutoConsentCMPBase {
|
|
|
419
419
|
|
|
420
420
|
export class AutoConsentHeuristicCMP extends AutoConsentCMPBase {
|
|
421
421
|
popups: PopupData[] = [];
|
|
422
|
-
mode:
|
|
422
|
+
mode: HeuristicLevel;
|
|
423
423
|
|
|
424
|
-
constructor(autoconsentInstance: AutoConsent, mode:
|
|
424
|
+
constructor(autoconsentInstance: AutoConsent, mode: HeuristicLevel = 'reject') {
|
|
425
425
|
super(autoconsentInstance);
|
|
426
426
|
this.name = 'HEURISTIC';
|
|
427
427
|
this.runContext = {
|
|
@@ -453,7 +453,7 @@ export class AutoConsentHeuristicCMP extends AutoConsentCMPBase {
|
|
|
453
453
|
performance.measure('heuristicDetector', 'heuristicDetectorStart', 'heuristicDetectorEnd');
|
|
454
454
|
|
|
455
455
|
if (this.popups.length > 0) {
|
|
456
|
-
this.name = `HEURISTIC
|
|
456
|
+
this.name = `HEURISTIC-${this.popups[0].regexClassification?.toUpperCase()}`;
|
|
457
457
|
return Promise.resolve(true);
|
|
458
458
|
}
|
|
459
459
|
return Promise.resolve(false);
|
|
@@ -473,8 +473,7 @@ export class AutoConsentHeuristicCMP extends AutoConsentCMPBase {
|
|
|
473
473
|
const popup = this.popups[0];
|
|
474
474
|
const level = popup.regexClassification;
|
|
475
475
|
const buttons = popup.buttons;
|
|
476
|
-
const targetButtonType =
|
|
477
|
-
level === PopupHandlingModes.Reject ? 'reject' : level === PopupHandlingModes.Tier1 ? 'acknowledge' : 'accept';
|
|
476
|
+
const targetButtonType = level === 'reject' ? 'reject' : level === 'tier1' ? 'acknowledge' : 'accept';
|
|
478
477
|
return buttons.find((button) => button.regexClassification === targetButtonType);
|
|
479
478
|
}
|
|
480
479
|
|
package/lib/heuristics.ts
CHANGED
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
REJECT_PATTERNS,
|
|
7
7
|
SETTINGS_PATTERNS,
|
|
8
8
|
} from './heuristic-patterns';
|
|
9
|
-
import { ButtonData, ButtonRegexClassification,
|
|
9
|
+
import { ButtonData, ButtonRegexClassification, HeuristicLevel, PopupClassification, PopupData } from './types';
|
|
10
10
|
import { isElementVisible, isTopFrame } from './utils';
|
|
11
11
|
|
|
12
12
|
const BUTTON_LIKE_ELEMENT_SELECTOR = 'button, input[type="button"], input[type="submit"], a, [role="button"], [class*="button"]';
|
|
@@ -28,7 +28,12 @@ export function checkHeuristicPatterns(allText: string, detectPatterns = DETECT_
|
|
|
28
28
|
return { patterns, snippets };
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
export function getActionablePopups(mode:
|
|
31
|
+
export function getActionablePopups(mode: HeuristicLevel = 'reject', timeout = POPUP_SEARCH_MAX_TIME): PopupData[] {
|
|
32
|
+
const acceptedLevels: PopupClassification[] =
|
|
33
|
+
mode === 'tier2' ? ['reject', 'tier1', 'tier2'] : mode === 'tier1' ? ['reject', 'tier1'] : ['reject'];
|
|
34
|
+
if (acceptedLevels.length === 0) {
|
|
35
|
+
return [];
|
|
36
|
+
}
|
|
32
37
|
const popups = getPotentialPopups(timeout);
|
|
33
38
|
const result = popups.reduce((acc, popup) => {
|
|
34
39
|
const popupText = popup.text?.trim();
|
|
@@ -46,13 +51,8 @@ export function getActionablePopups(mode: PopupHandlingMode = PopupHandlingModes
|
|
|
46
51
|
}, [] as PopupData[]);
|
|
47
52
|
// popups filtered by mode and sorted so a popup with reject will always win.
|
|
48
53
|
return result
|
|
49
|
-
.filter(
|
|
50
|
-
|
|
51
|
-
popup.regexClassification !== undefined &&
|
|
52
|
-
popup.regexClassification !== PopupHandlingModes.None &&
|
|
53
|
-
popup.regexClassification <= mode,
|
|
54
|
-
)
|
|
55
|
-
.sort((a, b) => (a.regexClassification ?? 0) - (b.regexClassification ?? 0));
|
|
54
|
+
.filter((popup) => popup.regexClassification !== undefined && acceptedLevels.includes(popup.regexClassification))
|
|
55
|
+
.sort((a, b) => ((a.regexClassification ?? '') > (b.regexClassification ?? '') ? 1 : -1));
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
export function classifyButtons(buttons: ButtonData[]) {
|
|
@@ -61,7 +61,7 @@ export function classifyButtons(buttons: ButtonData[]) {
|
|
|
61
61
|
}
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
-
function classifyPopup(buttons: ButtonData[]):
|
|
64
|
+
function classifyPopup(buttons: ButtonData[]): PopupClassification {
|
|
65
65
|
const { reject, settings, accept, acknowledge } = buttons.reduce(
|
|
66
66
|
(acc, button) => {
|
|
67
67
|
if (button.regexClassification && button.regexClassification !== 'other') {
|
|
@@ -72,18 +72,18 @@ function classifyPopup(buttons: ButtonData[]): PopupHandlingMode {
|
|
|
72
72
|
{ reject: 0, settings: 0, accept: 0, acknowledge: 0 },
|
|
73
73
|
);
|
|
74
74
|
if (reject > 0) {
|
|
75
|
-
return
|
|
75
|
+
return 'reject';
|
|
76
76
|
}
|
|
77
77
|
if (settings > 0) {
|
|
78
|
-
return
|
|
78
|
+
return 'none';
|
|
79
79
|
}
|
|
80
80
|
if (acknowledge > 0) {
|
|
81
|
-
return
|
|
81
|
+
return 'tier1';
|
|
82
82
|
}
|
|
83
83
|
if (accept > 0) {
|
|
84
|
-
return accept === 1 ?
|
|
84
|
+
return accept === 1 ? 'tier2' : 'none';
|
|
85
85
|
}
|
|
86
|
-
return
|
|
86
|
+
return 'none';
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
/**
|
package/lib/types.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { ContentScriptMessage } from './messages';
|
|
2
|
-
import { ConsentOMaticConfig } from './cmps/consentomatic';
|
|
3
2
|
import { AutoConsentCMPRule, ElementSelector, HideMethod, RunContext, VisibilityCheck } from './rules';
|
|
4
3
|
import { CompactCMPRuleset } from './encoding';
|
|
5
4
|
|
|
@@ -47,7 +46,6 @@ export interface DomActionsProvider {
|
|
|
47
46
|
export type RuleBundle = {
|
|
48
47
|
autoconsent: AutoConsentCMPRule[];
|
|
49
48
|
compact?: CompactCMPRuleset;
|
|
50
|
-
consentomatic?: { [name: string]: ConsentOMaticConfig };
|
|
51
49
|
};
|
|
52
50
|
|
|
53
51
|
export type AutoAction = 'optOut' | 'optIn' | null;
|
|
@@ -62,9 +60,7 @@ export type Config = {
|
|
|
62
60
|
detectRetries: number;
|
|
63
61
|
isMainWorld: boolean;
|
|
64
62
|
prehideTimeout: number;
|
|
65
|
-
enableFilterList: boolean;
|
|
66
63
|
enableHeuristicDetection: boolean;
|
|
67
|
-
enableHeuristicAction: boolean;
|
|
68
64
|
enablePopupMutationObserver: boolean;
|
|
69
65
|
visualTest: boolean; // If true, the script will delay before every click action
|
|
70
66
|
logs: {
|
|
@@ -78,7 +74,7 @@ export type Config = {
|
|
|
78
74
|
};
|
|
79
75
|
performanceLoggingEnabled: boolean;
|
|
80
76
|
heuristicPopupSearchTimeout: number;
|
|
81
|
-
heuristicMode:
|
|
77
|
+
heuristicMode: HeuristicLevel; // controls the behavior of the heuristic popup detection.
|
|
82
78
|
};
|
|
83
79
|
|
|
84
80
|
export type LifecycleState =
|
|
@@ -87,7 +83,6 @@ export type LifecycleState =
|
|
|
87
83
|
| 'waitingForInitResponse'
|
|
88
84
|
| 'started'
|
|
89
85
|
| 'nothingDetected'
|
|
90
|
-
| 'cosmeticFiltersDetected'
|
|
91
86
|
| 'cmpDetected'
|
|
92
87
|
| 'openPopupDetected'
|
|
93
88
|
| 'runningOptOut'
|
|
@@ -99,8 +94,6 @@ export type LifecycleState =
|
|
|
99
94
|
| 'done';
|
|
100
95
|
|
|
101
96
|
export type ConsentState = {
|
|
102
|
-
cosmeticFiltersOn: boolean; // true if cosmetic filter rules are currently applied.
|
|
103
|
-
filterListReported: boolean; // true if the cosmetic filter list has been reported to the user.
|
|
104
97
|
lifecycle: LifecycleState; // What point in the autoconsent lifecycle this script is at.
|
|
105
98
|
prehideOn: boolean; // If the script is currently hiding preHide elements.
|
|
106
99
|
findCmpAttempts: number; // Number of times we tried to find CMPs in this frame.
|
|
@@ -127,20 +120,15 @@ export interface PopupData {
|
|
|
127
120
|
text: string;
|
|
128
121
|
element: HTMLElement;
|
|
129
122
|
buttons: ButtonData[];
|
|
130
|
-
regexClassification?:
|
|
123
|
+
regexClassification?: PopupClassification;
|
|
131
124
|
}
|
|
132
125
|
|
|
133
126
|
/**
|
|
134
|
-
*
|
|
135
|
-
* -
|
|
136
|
-
* -
|
|
137
|
-
* -
|
|
138
|
-
* -
|
|
127
|
+
* The user setting for autoconsent and heuristic detection.
|
|
128
|
+
* - off: Heuristic detection is disabled.
|
|
129
|
+
* - reject: Heuristic detection is enabled, and will click the reject button on a popup if it exists.
|
|
130
|
+
* - tier1: Heuristic detection is enabled, and will click the acknowledge button on a popup if it exists, and no Reject or Settings button exists.
|
|
131
|
+
* - tier2: Heuristic detection is enabled, and will click the accept button on a popup if it exists, and no Reject, Acknowledge, or Settings button exists.
|
|
139
132
|
*/
|
|
140
|
-
export
|
|
141
|
-
|
|
142
|
-
Reject: 0,
|
|
143
|
-
Tier1: 1,
|
|
144
|
-
Tier2: 2,
|
|
145
|
-
} as const;
|
|
146
|
-
export type PopupHandlingMode = (typeof PopupHandlingModes)[keyof typeof PopupHandlingModes];
|
|
133
|
+
export type HeuristicLevel = 'off' | 'reject' | 'tier1' | 'tier2';
|
|
134
|
+
export type PopupClassification = 'none' | 'reject' | 'tier1' | 'tier2';
|
package/lib/utils.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { HideMethod } from './rules';
|
|
2
|
-
import { Config
|
|
2
|
+
import { Config } from './types';
|
|
3
3
|
|
|
4
4
|
// get or create a style container for CSS overrides
|
|
5
5
|
export function getStyleElement(styleOverrideElementId = 'autoconsent-css-rules'): HTMLStyleElement {
|
|
@@ -77,12 +77,10 @@ export function normalizeConfig(providedConfig: any): Config {
|
|
|
77
77
|
enableCosmeticRules: true,
|
|
78
78
|
enableGeneratedRules: true,
|
|
79
79
|
enableHeuristicDetection: false,
|
|
80
|
-
enableHeuristicAction: false,
|
|
81
80
|
enablePopupMutationObserver: false,
|
|
82
81
|
detectRetries: 20,
|
|
83
82
|
isMainWorld: false,
|
|
84
83
|
prehideTimeout: 2000,
|
|
85
|
-
enableFilterList: false,
|
|
86
84
|
visualTest: false,
|
|
87
85
|
logs: {
|
|
88
86
|
lifecycle: false,
|
|
@@ -95,7 +93,7 @@ export function normalizeConfig(providedConfig: any): Config {
|
|
|
95
93
|
},
|
|
96
94
|
performanceLoggingEnabled: false,
|
|
97
95
|
heuristicPopupSearchTimeout: 100,
|
|
98
|
-
heuristicMode:
|
|
96
|
+
heuristicMode: 'off', // heuristic disabled by default
|
|
99
97
|
};
|
|
100
98
|
const updatedConfig: Config = copyObject(defaultConfig);
|
|
101
99
|
// filter out any unknown entries
|
package/lib/web.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { MessageSender, AutoCMP, RuleBundle, Config, ConsentState } from './types';
|
|
2
|
-
import { ConsentOMaticCMP, ConsentOMaticConfig } from './cmps/consentomatic';
|
|
3
2
|
import { AutoConsentCMPRule, SUPPORTED_RULE_STEP_VERSION } from './rules';
|
|
4
3
|
import { BackgroundMessage, InitMessage } from './messages';
|
|
5
4
|
import { evalState, resolveEval } from './eval-handler';
|
|
@@ -8,7 +7,6 @@ import { dynamicCMPs } from './cmps/all';
|
|
|
8
7
|
import { AutoConsentCMP, AutoConsentHeuristicCMP } from './cmps/base';
|
|
9
8
|
import { DomActions } from './dom-actions';
|
|
10
9
|
import { isTopFrame, normalizeConfig, scheduleWhenIdle } from './utils';
|
|
11
|
-
import { FiltersEngine } from '@ghostery/adblocker';
|
|
12
10
|
import { checkHeuristicPatterns } from './heuristics';
|
|
13
11
|
import { decodeRules } from './encoding';
|
|
14
12
|
|
|
@@ -36,8 +34,6 @@ export default class AutoConsent {
|
|
|
36
34
|
#config?: Config;
|
|
37
35
|
foundCmp?: AutoCMP;
|
|
38
36
|
state: ConsentState = {
|
|
39
|
-
cosmeticFiltersOn: false,
|
|
40
|
-
filterListReported: false,
|
|
41
37
|
lifecycle: 'loading',
|
|
42
38
|
prehideOn: false,
|
|
43
39
|
findCmpAttempts: 0,
|
|
@@ -51,9 +47,7 @@ export default class AutoConsent {
|
|
|
51
47
|
endTime: 0,
|
|
52
48
|
};
|
|
53
49
|
domActions: DomActions;
|
|
54
|
-
filtersEngine?: FiltersEngine;
|
|
55
50
|
sendContentMessage: MessageSender;
|
|
56
|
-
protected cosmeticStyleSheet?: CSSStyleSheet;
|
|
57
51
|
protected focusedElement?: HTMLElement;
|
|
58
52
|
|
|
59
53
|
constructor(sendContentMessage: MessageSender, config: Partial<Config> | null = null, declarativeRules: RuleBundle | null = null) {
|
|
@@ -100,9 +94,6 @@ export default class AutoConsent {
|
|
|
100
94
|
this.parseDeclarativeRules(declarativeRules);
|
|
101
95
|
}
|
|
102
96
|
|
|
103
|
-
if (config.enableFilterList) {
|
|
104
|
-
this.initializeFilterList();
|
|
105
|
-
}
|
|
106
97
|
this.rules = filterCMPs(this.rules, normalizedConfig);
|
|
107
98
|
|
|
108
99
|
if (this.shouldPrehide) {
|
|
@@ -131,10 +122,6 @@ export default class AutoConsent {
|
|
|
131
122
|
this.updateState({ lifecycle: 'initialized' });
|
|
132
123
|
}
|
|
133
124
|
|
|
134
|
-
initializeFilterList() {
|
|
135
|
-
// no-op by default
|
|
136
|
-
}
|
|
137
|
-
|
|
138
125
|
get shouldPrehide() {
|
|
139
126
|
return this.config.enablePrehide && !this.config.visualTest;
|
|
140
127
|
}
|
|
@@ -167,11 +154,6 @@ export default class AutoConsent {
|
|
|
167
154
|
parseDeclarativeRules(declarativeRules: RuleBundle) {
|
|
168
155
|
const perfEnabled = this.#config?.performanceLoggingEnabled;
|
|
169
156
|
perfEnabled && performance.mark('parseDeclarativeRulesStart');
|
|
170
|
-
if (declarativeRules.consentomatic) {
|
|
171
|
-
for (const [name, rule] of Object.entries(declarativeRules.consentomatic)) {
|
|
172
|
-
this.addConsentomaticCMP(name, rule);
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
157
|
|
|
176
158
|
if (declarativeRules.autoconsent) {
|
|
177
159
|
declarativeRules.autoconsent.forEach((ruleset) => {
|
|
@@ -198,10 +180,6 @@ export default class AutoConsent {
|
|
|
198
180
|
}
|
|
199
181
|
}
|
|
200
182
|
|
|
201
|
-
addConsentomaticCMP(name: string, config: ConsentOMaticConfig) {
|
|
202
|
-
this.rules.push(new ConsentOMaticCMP(`com_${name}`, config));
|
|
203
|
-
}
|
|
204
|
-
|
|
205
183
|
// start the detection process, possibly with a delay
|
|
206
184
|
start() {
|
|
207
185
|
scheduleWhenIdle(() => this._start());
|
|
@@ -222,7 +200,8 @@ export default class AutoConsent {
|
|
|
222
200
|
this.undoPrehide();
|
|
223
201
|
}
|
|
224
202
|
|
|
225
|
-
|
|
203
|
+
this.updateState({ lifecycle: 'nothingDetected' });
|
|
204
|
+
return false;
|
|
226
205
|
}
|
|
227
206
|
|
|
228
207
|
this.updateState({ lifecycle: 'cmpDetected' });
|
|
@@ -296,7 +275,7 @@ export default class AutoConsent {
|
|
|
296
275
|
});
|
|
297
276
|
// heuristic CMP is only run in the top frame and only if heuristic action is enabled and retries is odd
|
|
298
277
|
const heuristicRules =
|
|
299
|
-
isTop && this.config.
|
|
278
|
+
isTop && this.config.heuristicMode !== 'off' && this.state.findCmpAttempts % 2 === 0
|
|
300
279
|
? [new AutoConsentHeuristicCMP(this, this.config.heuristicMode)]
|
|
301
280
|
: [];
|
|
302
281
|
|
|
@@ -436,11 +415,6 @@ export default class AutoConsent {
|
|
|
436
415
|
// prehide might have timeouted by this time, apply it again
|
|
437
416
|
this.prehideElements();
|
|
438
417
|
}
|
|
439
|
-
if (this.state.cosmeticFiltersOn) {
|
|
440
|
-
// cancel cosmetic filters if we have a rule for this popup
|
|
441
|
-
this.undoCosmetics();
|
|
442
|
-
}
|
|
443
|
-
|
|
444
418
|
this.foundCmp = cmp;
|
|
445
419
|
|
|
446
420
|
if (this.config.autoAction === 'optOut') {
|
|
@@ -631,30 +605,6 @@ export default class AutoConsent {
|
|
|
631
605
|
this.domActions.undoPrehide();
|
|
632
606
|
}
|
|
633
607
|
|
|
634
|
-
undoCosmetics() {
|
|
635
|
-
// no-op by default
|
|
636
|
-
}
|
|
637
|
-
|
|
638
|
-
reportFilterlist() {
|
|
639
|
-
this.sendContentMessage({
|
|
640
|
-
type: 'cmpDetected',
|
|
641
|
-
url: location.href,
|
|
642
|
-
cmp: 'filterList',
|
|
643
|
-
});
|
|
644
|
-
this.sendContentMessage({
|
|
645
|
-
type: 'popupFound',
|
|
646
|
-
cmp: 'filterList',
|
|
647
|
-
url: location.href,
|
|
648
|
-
});
|
|
649
|
-
this.updateState({ filterListReported: true });
|
|
650
|
-
}
|
|
651
|
-
|
|
652
|
-
filterListFallback() {
|
|
653
|
-
// no-op by default
|
|
654
|
-
this.updateState({ lifecycle: 'nothingDetected' });
|
|
655
|
-
return false;
|
|
656
|
-
}
|
|
657
|
-
|
|
658
608
|
updateState(change: Partial<ConsentState>) {
|
|
659
609
|
Object.assign(this.state, change);
|
|
660
610
|
this.sendContentMessage({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@duckduckgo/autoconsent",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "16.0.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"types": "./dist/types/web.d.ts",
|
|
6
6
|
"exports": {
|
|
@@ -9,13 +9,7 @@
|
|
|
9
9
|
"import": "./dist/autoconsent.esm.js",
|
|
10
10
|
"require": "./dist/autoconsent.cjs.js"
|
|
11
11
|
},
|
|
12
|
-
"./extra": {
|
|
13
|
-
"types": "./dist/types/web-extra.d.ts",
|
|
14
|
-
"import": "./dist/autoconsent.extra.esm.js",
|
|
15
|
-
"require": "./dist/autoconsent.extra.cjs.js"
|
|
16
|
-
},
|
|
17
12
|
"./rules/rules.json": "./rules/rules.json",
|
|
18
|
-
"./rules/consentomatic.json": "./rules/consentomatic.json",
|
|
19
13
|
"./rules/compact-rules.json": "./rules/compact-rules.json"
|
|
20
14
|
},
|
|
21
15
|
"directories": {
|
|
@@ -27,7 +21,7 @@
|
|
|
27
21
|
"lint-fix": "eslint . --fix && prettier . --write && npm run rule-syntax-check",
|
|
28
22
|
"bundle": "./build.sh",
|
|
29
23
|
"rule-syntax-check": "node scripts/validate-json-rules.js",
|
|
30
|
-
"watch": "npm run prepublish && chokidar \"lib\" \"addon\" \"rules/autoconsent\" \"rules/generated\"
|
|
24
|
+
"watch": "npm run prepublish && chokidar \"lib\" \"addon\" \"rules/autoconsent\" \"rules/generated\" -c \"npm run prepublish\"",
|
|
31
25
|
"create-rule": "node rules/create-rule.mjs",
|
|
32
26
|
"test": "playwright test",
|
|
33
27
|
"test:webkit": "playwright test --project webkit",
|
|
@@ -35,14 +29,10 @@
|
|
|
35
29
|
"test:chrome": "playwright test --project chrome",
|
|
36
30
|
"test:lib": "web-test-runner",
|
|
37
31
|
"test:sample": "playwright test tests/_sample-test.spec.ts --project webkit",
|
|
38
|
-
"fetch-easylist": "scripts/fetch-easylist.sh",
|
|
39
|
-
"rebuild-filterlist": "node scripts/rebuild-filterlist.mjs",
|
|
40
|
-
"update-easylist": "npm run fetch-easylist && npm run rebuild-filterlist",
|
|
41
|
-
"compile-filterlist": "node scripts/compile-filterlist.mjs",
|
|
42
32
|
"build-rules": "ts-node rules/build.ts",
|
|
43
33
|
"version": "node update_version.js && git add addon/manifest.*.json",
|
|
44
34
|
"vendor-copy": "mkdir -p addon/vendor && cp node_modules/mocha/mocha.* addon/vendor/ && cp node_modules/chai/chai.js addon/vendor/",
|
|
45
|
-
"prepublish": "npm run
|
|
35
|
+
"prepublish": "npm run build-rules && npm run bundle",
|
|
46
36
|
"launch:firefox": "web-ext run -s dist/addon-firefox",
|
|
47
37
|
"release": "auto shipit",
|
|
48
38
|
"get-text-for-xpath": "ts-node scripts/get-text-for-xpath.ts",
|
|
@@ -68,7 +58,6 @@
|
|
|
68
58
|
"bulma": "^1.0.2",
|
|
69
59
|
"chai": "^6.0.1",
|
|
70
60
|
"chokidar-cli": "^3.0.0",
|
|
71
|
-
"csv-parse": "^6.0.0",
|
|
72
61
|
"esbuild": "^0.28.0",
|
|
73
62
|
"eslint": "^10.0.3",
|
|
74
63
|
"globals": "^17.4.0",
|
|
@@ -92,8 +81,6 @@
|
|
|
92
81
|
]
|
|
93
82
|
},
|
|
94
83
|
"dependencies": {
|
|
95
|
-
"
|
|
96
|
-
"@ghostery/adblocker-content": "^2.0.4",
|
|
97
|
-
"tldts-experimental": "^7.0.4"
|
|
84
|
+
"tldts-experimental": "^7.4.3"
|
|
98
85
|
}
|
|
99
86
|
}
|