@duckduckgo/autoconsent 16.11.0 → 16.13.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/CHANGELOG.md +29 -0
- package/dist/addon-firefox/compact-rules.json +1 -1
- package/dist/addon-firefox/content.bundle.js +32 -8
- package/dist/addon-firefox/manifest.json +1 -1
- package/dist/addon-firefox/rule-index.json +1 -1
- package/dist/addon-firefox/rules.json +1 -1
- package/dist/addon-mv3/compact-rules.json +1 -1
- package/dist/addon-mv3/content.bundle.js +32 -8
- package/dist/addon-mv3/manifest.json +1 -1
- package/dist/addon-mv3/rule-index.json +1 -1
- package/dist/addon-mv3/rules.json +1 -1
- package/dist/autoconsent.cjs.js +32 -8
- package/dist/autoconsent.esm.js +32 -8
- package/dist/autoconsent.playwright.js +32 -8
- package/dist/autoconsent.standalone.js +33 -9
- package/dist/types/heuristic-patterns.d.ts +2 -1
- package/dist/types/heuristics.d.ts +1 -0
- package/lib/heuristic-patterns.ts +21 -4
- package/lib/heuristics.ts +17 -5
- package/package.json +1 -1
- package/rules/autoconsent/ourworldindata.json +2 -2
- package/rules/autoconsent/reddit.json +1 -1
- package/rules/autoconsent/unive-nl.json +40 -0
- package/rules/compact-rules.json +1 -1
- package/rules/rule-index.json +1 -1
- package/rules/rules.json +1 -1
- package/tests/ourworldindata.spec.ts +4 -1
- package/tests/unive-nl.spec.ts +3 -0
- package/tests-wtr/heuristics/get-actionable-popups.html +10 -0
- package/tests-wtr/heuristics/get-actionable-popups.ts +8 -0
- package/tests-wtr/heuristics/heuristics-utils.test.ts +38 -0
|
@@ -3,7 +3,8 @@ export declare const DETECT_PATTERNS: RegExp[];
|
|
|
3
3
|
* @type {Array<string|RegExp>}
|
|
4
4
|
*/
|
|
5
5
|
export declare const REJECT_PATTERNS: (string | RegExp)[];
|
|
6
|
-
export declare const
|
|
6
|
+
export declare const BUTTON_NEVER_MATCH_PATTERNS: RegExp[];
|
|
7
|
+
export declare const DETECT_NEVER_MATCH_PATTERNS: RegExp[];
|
|
7
8
|
export declare const SETTINGS_PATTERNS: (string | RegExp)[];
|
|
8
9
|
export declare const ACCEPT_PATTERNS: (string | RegExp)[];
|
|
9
10
|
export declare const ACKNOWLEDGE_PATTERNS: (string | RegExp)[];
|
|
@@ -5,6 +5,7 @@ export declare function checkHeuristicPatterns(allText: string, detectPatterns?:
|
|
|
5
5
|
};
|
|
6
6
|
export declare function getActionablePopups(mode?: HeuristicLevel, timeout?: number): PopupData[];
|
|
7
7
|
export declare function classifyButtons(buttons: ButtonData[]): void;
|
|
8
|
+
export declare function isExcludedPopup(popupText: string, excludePatterns?: RegExp[]): boolean;
|
|
8
9
|
export declare function cleanButtonText(buttonText: string): string;
|
|
9
10
|
export declare function classifyButtonTextRegex(buttonText: string): ButtonRegexClassification;
|
|
10
11
|
export declare function isDialogLikeElement(node: HTMLElement): boolean;
|
|
@@ -160,7 +160,7 @@ export const DETECT_PATTERNS = [
|
|
|
160
160
|
const REJECT_PATTERNS_ENGLISH = [
|
|
161
161
|
// e.g. "reject", "reject all", "reject all cookies", "deny all", "refuse cookies", "decline",
|
|
162
162
|
// "reject non-essential cookies", "reject unnecessary cookies", "reject all but necessary", "reject all and close"
|
|
163
|
-
// note that "reject and subscribe" and "reject and pay" are excluded via
|
|
163
|
+
// note that "reject and subscribe" and "reject and pay" are excluded via BUTTON_NEVER_MATCH_PATTERNS
|
|
164
164
|
/^\s*(no,?\s*)?(i\s+)?(reject|deny|refuse|decline|disable)\s*(all)?\s*(but|except)?\s*(non[- ]?essential|un(necessary|required)|optional|additional|targeting|analytics|marketing|non[- ]?necessary|extra|tracking|advertising|necessary|essential)?\s*(cookies)?\s*(and\s+close)?\s*$/is,
|
|
165
165
|
|
|
166
166
|
// e.g. "i do not accept", "do not accept cookies"
|
|
@@ -224,7 +224,7 @@ const REJECT_PATTERNS_DUTCH = [
|
|
|
224
224
|
|
|
225
225
|
const REJECT_PATTERNS_FRENCH = [
|
|
226
226
|
// refuser / rejeter / interdire / décliner (reject verbs, any position)
|
|
227
|
-
// "refuser et s'abonner" / "refuser et payer" are excluded via
|
|
227
|
+
// "refuser et s'abonner" / "refuser et payer" are excluded via BUTTON_NEVER_MATCH_PATTERNS
|
|
228
228
|
/(^|\s)(refus|rejet|rejeter|interdire|interdis|déclin|declin)/is,
|
|
229
229
|
|
|
230
230
|
// only necessary / essential / technical / functional
|
|
@@ -298,7 +298,7 @@ const REJECT_PATTERNS_BRAZILIAN_PORTUGUESE = [
|
|
|
298
298
|
|
|
299
299
|
const REJECT_PATTERNS_SPANISH = [
|
|
300
300
|
// rechazar / denegar / declinar / negar (reject verbs, any position)
|
|
301
|
-
// "rechazar y pagar" / "rechazar y suscribirse" are excluded via
|
|
301
|
+
// "rechazar y pagar" / "rechazar y suscribirse" are excluded via BUTTON_NEVER_MATCH_PATTERNS
|
|
302
302
|
/(^|\s)(rechaz|recház|deneg|negar|declin)/is,
|
|
303
303
|
|
|
304
304
|
// accept/allow/use (only) necessary / essential / technical / functional / own
|
|
@@ -394,7 +394,7 @@ export const REJECT_PATTERNS = [
|
|
|
394
394
|
...REJECT_PATTERNS_INDONESIAN,
|
|
395
395
|
];
|
|
396
396
|
|
|
397
|
-
export const
|
|
397
|
+
export const BUTTON_NEVER_MATCH_PATTERNS = [
|
|
398
398
|
/pay|subscribe/is,
|
|
399
399
|
/abonneer/is,
|
|
400
400
|
/abonnier/is,
|
|
@@ -418,6 +418,23 @@ export const NEVER_MATCH_PATTERNS = [
|
|
|
418
418
|
/subskrybuj/,
|
|
419
419
|
];
|
|
420
420
|
|
|
421
|
+
// Popup body-text patterns that suppress heuristic detection. Currently targets age gates and adult-content disclaimers whose "reject" button leads to a dead end.
|
|
422
|
+
// Applied only to popup-scoped text (via getActionablePopups); NOT applied to full-document detection, where these terms produce too many false positives.
|
|
423
|
+
export const DETECT_NEVER_MATCH_PATTERNS = [
|
|
424
|
+
// e.g. "age verification", "age confirmation", "age check", "age gate", "age restriction"
|
|
425
|
+
/age\s+(?:verification|confirmation|check|gate|restriction)/i,
|
|
426
|
+
// e.g. "over 18 years", "at least 21 years", "older than 21", "18+"
|
|
427
|
+
/(?:over|above|at\s*least|minimum|older\s+than)\s*(?:18|21)\s*(?:years|yo|y\.?o\.?|\+)?/i,
|
|
428
|
+
// e.g. "18 years of age", "18+ years old", "21 years or older"
|
|
429
|
+
/(?:18|21)\s*(?:\+|years?)\s*(?:of\s*age|or\s*older|or\s*above)/i,
|
|
430
|
+
// e.g. "I am 18+", "I am over 18", "I'm 21 or older"
|
|
431
|
+
/(?:i'?m|i\s*am)\s*(?:over|above|at\s*least)?\s*(?:18|21)(?:\+|\s*(?:or\s*older|years))?/i,
|
|
432
|
+
// e.g. "you must be 18", "users must be at least 21", "visitors must be over 18"
|
|
433
|
+
/(?:you|users?|visitors?)\s+must\s+be\s+(?:over|above|at\s*least)?\s*(?:18|21)/i,
|
|
434
|
+
// e.g. "adult oriented material", "adult content", "adult-only website"
|
|
435
|
+
/adult[\s-]+(?:oriented|only|content|material|websites?)/i,
|
|
436
|
+
];
|
|
437
|
+
|
|
421
438
|
export const SETTINGS_PATTERNS = [
|
|
422
439
|
// Multilingual "open customization" patterns: a customization verb next to a
|
|
423
440
|
// cookie/preference/settings/options/details/purposes noun (both word orders).
|
package/lib/heuristics.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ACCEPT_PATTERNS,
|
|
3
3
|
ACKNOWLEDGE_PATTERNS,
|
|
4
|
+
BUTTON_NEVER_MATCH_PATTERNS,
|
|
5
|
+
DETECT_NEVER_MATCH_PATTERNS,
|
|
4
6
|
DETECT_PATTERNS,
|
|
5
|
-
NEVER_MATCH_PATTERNS,
|
|
6
7
|
REJECT_PATTERNS,
|
|
7
8
|
SETTINGS_PATTERNS,
|
|
8
9
|
} from './heuristic-patterns';
|
|
@@ -44,6 +45,9 @@ export function getActionablePopups(mode: HeuristicLevel = 'reject', timeout = P
|
|
|
44
45
|
const result = popups.reduce((acc, popup) => {
|
|
45
46
|
const popupText = popup.text?.trim();
|
|
46
47
|
if (popupText) {
|
|
48
|
+
if (isExcludedPopup(popupText)) {
|
|
49
|
+
return acc;
|
|
50
|
+
}
|
|
47
51
|
const { patterns } = checkHeuristicPatterns(popupText);
|
|
48
52
|
if (patterns.length > 0) {
|
|
49
53
|
classifyButtons(popup.buttons);
|
|
@@ -67,6 +71,14 @@ export function classifyButtons(buttons: ButtonData[]) {
|
|
|
67
71
|
}
|
|
68
72
|
}
|
|
69
73
|
|
|
74
|
+
export function isExcludedPopup(popupText: string, excludePatterns = DETECT_NEVER_MATCH_PATTERNS): boolean {
|
|
75
|
+
if (!popupText) {
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
const truncated = popupText.slice(0, TEXT_LIMIT);
|
|
79
|
+
return excludePatterns.some((p) => p.test(truncated));
|
|
80
|
+
}
|
|
81
|
+
|
|
70
82
|
function classifyPopup(buttons: ButtonData[]): PopupClassification {
|
|
71
83
|
const { reject, settings, accept, acknowledge } = buttons.reduce(
|
|
72
84
|
(acc, button) => {
|
|
@@ -129,16 +141,16 @@ export function cleanButtonText(buttonText: string): string {
|
|
|
129
141
|
}
|
|
130
142
|
|
|
131
143
|
export function classifyButtonTextRegex(buttonText: string): ButtonRegexClassification {
|
|
132
|
-
if (testButtonMatches(buttonText, REJECT_PATTERNS,
|
|
144
|
+
if (testButtonMatches(buttonText, REJECT_PATTERNS, BUTTON_NEVER_MATCH_PATTERNS)) {
|
|
133
145
|
return 'reject';
|
|
134
146
|
}
|
|
135
|
-
if (testButtonMatches(buttonText, SETTINGS_PATTERNS,
|
|
147
|
+
if (testButtonMatches(buttonText, SETTINGS_PATTERNS, BUTTON_NEVER_MATCH_PATTERNS)) {
|
|
136
148
|
return 'settings';
|
|
137
149
|
}
|
|
138
|
-
if (testButtonMatches(buttonText, ACCEPT_PATTERNS,
|
|
150
|
+
if (testButtonMatches(buttonText, ACCEPT_PATTERNS, BUTTON_NEVER_MATCH_PATTERNS)) {
|
|
139
151
|
return 'accept';
|
|
140
152
|
}
|
|
141
|
-
if (testButtonMatches(buttonText, ACKNOWLEDGE_PATTERNS,
|
|
153
|
+
if (testButtonMatches(buttonText, ACKNOWLEDGE_PATTERNS, BUTTON_NEVER_MATCH_PATTERNS)) {
|
|
142
154
|
return 'acknowledge';
|
|
143
155
|
}
|
|
144
156
|
return 'other';
|
package/package.json
CHANGED
|
@@ -17,12 +17,12 @@
|
|
|
17
17
|
],
|
|
18
18
|
"optIn": [
|
|
19
19
|
{
|
|
20
|
-
"waitForThenClick": ".cookie-notice
|
|
20
|
+
"waitForThenClick": ".cookie-notice button.owid-btn--solid-dark-blue.cookie-notice__button"
|
|
21
21
|
}
|
|
22
22
|
],
|
|
23
23
|
"optOut": [
|
|
24
24
|
{
|
|
25
|
-
"waitForThenClick": ".cookie-notice
|
|
25
|
+
"waitForThenClick": ".cookie-notice button.owid-btn--outline-dark-blue.cookie-notice__button"
|
|
26
26
|
}
|
|
27
27
|
]
|
|
28
28
|
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "unive-nl",
|
|
3
|
+
"_metadata": {
|
|
4
|
+
"vendorUrl": "https://www.unive.nl/"
|
|
5
|
+
},
|
|
6
|
+
"cosmetic": false,
|
|
7
|
+
"runContext": {
|
|
8
|
+
"main": true,
|
|
9
|
+
"frame": false,
|
|
10
|
+
"urlPattern": "^https?://(?:[^/]+\\.)?unive\\.nl/"
|
|
11
|
+
},
|
|
12
|
+
"prehideSelectors": ["#__tealiumGDPRcpPrefs #consent-container"],
|
|
13
|
+
"detectCmp": [
|
|
14
|
+
{
|
|
15
|
+
"exists": "#__tealiumGDPRcpPrefs #consent-container #consent-modal-settings"
|
|
16
|
+
}
|
|
17
|
+
],
|
|
18
|
+
"detectPopup": [
|
|
19
|
+
{
|
|
20
|
+
"visible": "#__tealiumGDPRcpPrefs #consent-container"
|
|
21
|
+
}
|
|
22
|
+
],
|
|
23
|
+
"optIn": [
|
|
24
|
+
{
|
|
25
|
+
"waitForThenClick": "#consent-popup-close-modal"
|
|
26
|
+
}
|
|
27
|
+
],
|
|
28
|
+
"optOut": [
|
|
29
|
+
{
|
|
30
|
+
"waitForThenClick": "#no-consent-popup-close-modal"
|
|
31
|
+
}
|
|
32
|
+
],
|
|
33
|
+
"test": [
|
|
34
|
+
{
|
|
35
|
+
"waitForVisible": "#__tealiumGDPRcpPrefs #consent-container",
|
|
36
|
+
"check": "none",
|
|
37
|
+
"timeout": 2000
|
|
38
|
+
}
|
|
39
|
+
]
|
|
40
|
+
}
|