@duckduckgo/autoconsent 15.0.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 +32 -0
- package/build.sh +3 -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 +184 -686
- package/dist/addon-firefox/manifest.json +1 -1
- package/dist/addon-firefox/popup.bundle.js +196 -25
- package/dist/addon-firefox/popup.html +98 -5
- package/dist/addon-firefox/rule-index.json +1 -0
- 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 +184 -686
- package/dist/addon-mv3/manifest.json +1 -1
- package/dist/addon-mv3/popup.bundle.js +196 -25
- package/dist/addon-mv3/popup.html +98 -5
- package/dist/addon-mv3/rule-index.json +1 -0
- package/dist/addon-mv3/rules.json +1 -1
- package/dist/autoconsent.cjs.js +184 -685
- package/dist/autoconsent.esm.js +184 -685
- package/dist/autoconsent.playwright.js +185 -689
- package/dist/autoconsent.standalone.js +3625 -0
- package/dist/types/cmps/base.d.ts +3 -3
- package/dist/types/cmps/trustarc-top.d.ts +1 -1
- package/dist/types/eval-handler.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/cmps/conversant.ts +7 -4
- package/lib/cmps/trustarc-top.ts +1 -1
- package/lib/cmps/uniconsent.ts +4 -4
- package/lib/eval-handler.ts +6 -3
- package/lib/heuristics.ts +15 -15
- package/lib/types.ts +9 -21
- package/lib/utils.ts +13 -9
- 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 +4 -23
- package/rules/compact-rules.json +1 -1
- package/rules/rule-index-builder.ts +58 -0
- package/rules/rule-index.json +1 -0
- package/rules/rules.json +1 -1
- package/standalone/content.ts +101 -0
- 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 -15709
- package/dist/autoconsent.extra.esm.js +0 -15641
- 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;
|
|
@@ -5,7 +5,7 @@ export default class TrustArcTop extends AutoConsentCMPBase {
|
|
|
5
5
|
name: string;
|
|
6
6
|
prehideSelectors: string[];
|
|
7
7
|
runContext: RunContext;
|
|
8
|
-
_shortcutButton: HTMLElement;
|
|
8
|
+
_shortcutButton: HTMLElement | null;
|
|
9
9
|
_optInDone: boolean;
|
|
10
10
|
constructor(autoconsentInstance: AutoConsent);
|
|
11
11
|
get hasSelfTest(): boolean;
|
|
@@ -3,14 +3,14 @@ import { ContentScriptMessage } from './messages';
|
|
|
3
3
|
declare class Deferred<T> {
|
|
4
4
|
id: string;
|
|
5
5
|
promise: Promise<T>;
|
|
6
|
-
resolve: (value
|
|
7
|
-
reject: (reason?:
|
|
6
|
+
resolve: (value: T) => void;
|
|
7
|
+
reject: (reason?: unknown) => void;
|
|
8
8
|
timer: number;
|
|
9
9
|
constructor(id: string, timeout?: number);
|
|
10
10
|
}
|
|
11
11
|
type EvalState = {
|
|
12
12
|
pending: Map<string, Deferred<boolean>>;
|
|
13
|
-
sendContentMessage: (message: ContentScriptMessage) => void;
|
|
13
|
+
sendContentMessage: ((message: ContentScriptMessage) => void) | null;
|
|
14
14
|
};
|
|
15
15
|
export declare const evalState: EvalState;
|
|
16
16
|
export declare function requestEval(code: string, snippetId?: keyof typeof snippets): Promise<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/cmps/conversant.ts
CHANGED
|
@@ -41,12 +41,15 @@ export default class Conversant extends AutoConsentCMPBase {
|
|
|
41
41
|
(<HTMLElement>item.querySelector('.cmp-accordion-item-title')).click();
|
|
42
42
|
await waitFor(() => !!item.querySelector('.cmp-accordion-item-content.cmp-active'), 10, 50);
|
|
43
43
|
const content = item.querySelector('.cmp-accordion-item-content.cmp-active');
|
|
44
|
+
if (!content) {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
44
47
|
content
|
|
45
|
-
.querySelectorAll('.cmp-toggle-actions .cmp-toggle-deny:not(.cmp-toggle-deny--active)')
|
|
46
|
-
.forEach((e
|
|
48
|
+
.querySelectorAll<HTMLElement>('.cmp-toggle-actions .cmp-toggle-deny:not(.cmp-toggle-deny--active)')
|
|
49
|
+
.forEach((e) => e.click());
|
|
47
50
|
content
|
|
48
|
-
.querySelectorAll('.cmp-toggle-actions .cmp-toggle-checkbox:not(.cmp-toggle-checkbox--active)')
|
|
49
|
-
.forEach((e
|
|
51
|
+
.querySelectorAll<HTMLElement>('.cmp-toggle-actions .cmp-toggle-checkbox:not(.cmp-toggle-checkbox--active)')
|
|
52
|
+
.forEach((e) => e.click());
|
|
50
53
|
// await waitFor(() => !item.querySelector('.cmp-toggle-deny--active,.cmp-toggle-checkbox--active'), 5, 50); // this may take a long time
|
|
51
54
|
}
|
|
52
55
|
await this.click('.cmp-main-button:not(.cmp-main-button--primary)');
|
package/lib/cmps/trustarc-top.ts
CHANGED
package/lib/cmps/uniconsent.ts
CHANGED
|
@@ -29,8 +29,8 @@ export default class Uniconsent extends AutoConsentCMPBase {
|
|
|
29
29
|
|
|
30
30
|
async optOut() {
|
|
31
31
|
await this.waitForElement('.unic button', 1000);
|
|
32
|
-
document.querySelectorAll('.unic button').forEach((button
|
|
33
|
-
const text = button.textContent;
|
|
32
|
+
document.querySelectorAll<HTMLButtonElement>('.unic button').forEach((button) => {
|
|
33
|
+
const text = button.textContent || '';
|
|
34
34
|
if (text.includes('Manage Options') || text.includes('Optionen verwalten')) {
|
|
35
35
|
button.click();
|
|
36
36
|
}
|
|
@@ -39,14 +39,14 @@ export default class Uniconsent extends AutoConsentCMPBase {
|
|
|
39
39
|
if (await this.waitForElement('.unic input[type=checkbox]', 1000)) {
|
|
40
40
|
await this.waitForElement('.unic button', 1000);
|
|
41
41
|
|
|
42
|
-
document.querySelectorAll('.unic input[type=checkbox]').forEach((c
|
|
42
|
+
document.querySelectorAll<HTMLInputElement>('.unic input[type=checkbox]').forEach((c) => {
|
|
43
43
|
if (c.checked) {
|
|
44
44
|
c.click();
|
|
45
45
|
}
|
|
46
46
|
});
|
|
47
47
|
|
|
48
48
|
for (const b of <NodeListOf<HTMLButtonElement>>document.querySelectorAll('.unic button')) {
|
|
49
|
-
const text = b.textContent;
|
|
49
|
+
const text = b.textContent || '';
|
|
50
50
|
for (const pattern of ['Confirm Choices', 'Save Choices', 'Auswahl speichern']) {
|
|
51
51
|
if (text.includes(pattern)) {
|
|
52
52
|
b.click();
|
package/lib/eval-handler.ts
CHANGED
|
@@ -5,8 +5,8 @@ import { getRandomID } from './random';
|
|
|
5
5
|
class Deferred<T> {
|
|
6
6
|
id: string;
|
|
7
7
|
promise: Promise<T>;
|
|
8
|
-
resolve
|
|
9
|
-
reject
|
|
8
|
+
resolve!: (value: T) => void;
|
|
9
|
+
reject!: (reason?: unknown) => void;
|
|
10
10
|
timer: number;
|
|
11
11
|
|
|
12
12
|
constructor(id: string, timeout = 1000) {
|
|
@@ -23,7 +23,7 @@ class Deferred<T> {
|
|
|
23
23
|
|
|
24
24
|
type EvalState = {
|
|
25
25
|
pending: Map<string, Deferred<boolean>>;
|
|
26
|
-
sendContentMessage: (message: ContentScriptMessage) => void;
|
|
26
|
+
sendContentMessage: ((message: ContentScriptMessage) => void) | null;
|
|
27
27
|
};
|
|
28
28
|
|
|
29
29
|
export const evalState: EvalState = {
|
|
@@ -32,6 +32,9 @@ export const evalState: EvalState = {
|
|
|
32
32
|
};
|
|
33
33
|
|
|
34
34
|
export function requestEval(code: string, snippetId?: keyof typeof snippets): Promise<boolean> {
|
|
35
|
+
if (!evalState.sendContentMessage) {
|
|
36
|
+
return Promise.reject(new Error('AutoConsent is not initialized yet'));
|
|
37
|
+
}
|
|
35
38
|
const id = getRandomID();
|
|
36
39
|
evalState.sendContentMessage({
|
|
37
40
|
type: 'eval',
|
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
|
|
@@ -117,13 +115,18 @@ export function scheduleWhenIdle(callback: () => void, timeout = 500) {
|
|
|
117
115
|
}
|
|
118
116
|
}
|
|
119
117
|
|
|
118
|
+
type HighlightedHTMLElement = HTMLElement & {
|
|
119
|
+
__oldStyles?: string;
|
|
120
|
+
};
|
|
121
|
+
|
|
120
122
|
export function highlightNode(node: HTMLElement) {
|
|
123
|
+
const highlightedNode = node as HighlightedHTMLElement;
|
|
121
124
|
if (!node.style) return;
|
|
122
|
-
if (
|
|
125
|
+
if (highlightedNode.__oldStyles !== undefined) {
|
|
123
126
|
return; // already highlighted
|
|
124
127
|
}
|
|
125
128
|
if (node.hasAttribute('style')) {
|
|
126
|
-
|
|
129
|
+
highlightedNode.__oldStyles = node.style.cssText;
|
|
127
130
|
}
|
|
128
131
|
node.style.animation = 'pulsate .5s infinite';
|
|
129
132
|
node.style.outline = 'solid red';
|
|
@@ -154,10 +157,11 @@ export function highlightNode(node: HTMLElement) {
|
|
|
154
157
|
}
|
|
155
158
|
|
|
156
159
|
export function unhighlightNode(node: HTMLElement) {
|
|
160
|
+
const highlightedNode = node as HighlightedHTMLElement;
|
|
157
161
|
if (!node.style || !node.hasAttribute('style')) return;
|
|
158
|
-
if (
|
|
159
|
-
node.style.cssText =
|
|
160
|
-
delete
|
|
162
|
+
if (highlightedNode.__oldStyles !== undefined) {
|
|
163
|
+
node.style.cssText = highlightedNode.__oldStyles;
|
|
164
|
+
delete highlightedNode.__oldStyles;
|
|
161
165
|
} else {
|
|
162
166
|
node.removeAttribute('style');
|
|
163
167
|
}
|