@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,63 +0,0 @@
|
|
|
1
|
-
import fs from 'fs';
|
|
2
|
-
import path from 'path';
|
|
3
|
-
import { fileURLToPath } from 'url';
|
|
4
|
-
import { FiltersEngine } from '@ghostery/adblocker';
|
|
5
|
-
|
|
6
|
-
const rulesDir = path.join(path.dirname(fileURLToPath(import.meta.url)), '../rules');
|
|
7
|
-
|
|
8
|
-
const filterlistContent = fs.readFileSync(path.join(rulesDir, 'filterlist.txt'), 'utf-8');
|
|
9
|
-
|
|
10
|
-
const engine = FiltersEngine.parse(filterlistContent, {
|
|
11
|
-
enableMutationObserver: false, // we don't monitor DOM changes at the moment
|
|
12
|
-
loadNetworkFilters: false,
|
|
13
|
-
enableHtmlFiltering: false,
|
|
14
|
-
loadCSPFilters: false,
|
|
15
|
-
});
|
|
16
|
-
const serializedEngine = engine.serialize();
|
|
17
|
-
const properlySizedBuffer = ensure32BitAlignment(serializedEngine.buffer);
|
|
18
|
-
const engineJson = JSON.stringify(Array.from(new Uint32Array(properlySizedBuffer)));
|
|
19
|
-
const engineLength = serializedEngine.length;
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Ensures the buffer is aligned to a 32-bit boundary by padding if necessary
|
|
23
|
-
* @param {Buffer} originalBuffer
|
|
24
|
-
* @returns {Buffer} - The aligned buffer
|
|
25
|
-
*/
|
|
26
|
-
|
|
27
|
-
function ensure32BitAlignment(originalBuffer) {
|
|
28
|
-
// Calculate the necessary padding for 32-bit alignment
|
|
29
|
-
const padding = originalBuffer.byteLength % 4 === 0 ? 0 : 4 - (originalBuffer.byteLength % 4);
|
|
30
|
-
|
|
31
|
-
if (padding !== 0) {
|
|
32
|
-
// Create a new buffer with the required padding for 32-bit alignment
|
|
33
|
-
const paddedBuffer = new ArrayBuffer(originalBuffer.byteLength + padding);
|
|
34
|
-
|
|
35
|
-
// Create a Uint8Array view over the original and new buffers
|
|
36
|
-
const originalView = new Uint8Array(originalBuffer);
|
|
37
|
-
const paddedView = new Uint8Array(paddedBuffer);
|
|
38
|
-
|
|
39
|
-
// Copy the original data into the new buffer
|
|
40
|
-
paddedView.set(originalView);
|
|
41
|
-
|
|
42
|
-
// Set the padding bytes to zero
|
|
43
|
-
for (let i = originalBuffer.byteLength; i < paddedBuffer.byteLength; i++) {
|
|
44
|
-
paddedView[i] = 0;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
return paddedBuffer;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// If already aligned, return the original buffer
|
|
51
|
-
return originalBuffer;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
fs.writeFile(
|
|
55
|
-
path.join(rulesDir, '../lib/filterlist-engine.ts'),
|
|
56
|
-
`
|
|
57
|
-
const serializedEngine = /* @__PURE__ */ new Uint8Array(new Uint32Array(
|
|
58
|
-
${engineJson}
|
|
59
|
-
).buffer).slice(0, ${engineLength});
|
|
60
|
-
export default serializedEngine;
|
|
61
|
-
`,
|
|
62
|
-
() => console.log('Written filterlist-engine.ts'),
|
|
63
|
-
);
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
set -e
|
|
3
|
-
|
|
4
|
-
SCRIPT_DIR="`dirname $0`"
|
|
5
|
-
RULES_DIR="${SCRIPT_DIR}/../rules/filterlists/"
|
|
6
|
-
|
|
7
|
-
# set EASYLIST_REVISION to a specific commit hash to lock to a specific revision. By default we use the latest revision.
|
|
8
|
-
if [ "${EASYLIST_REVISION}" = "" ];
|
|
9
|
-
then
|
|
10
|
-
echo "Looking up the latest Easylist revision..."
|
|
11
|
-
EASYLIST_REVISION=`git ls-remote https://github.com/easylist/easylist.git refs/heads/master | cut -f 1`
|
|
12
|
-
fi
|
|
13
|
-
|
|
14
|
-
echo "Using Easylist revision: ${EASYLIST_REVISION}"
|
|
15
|
-
|
|
16
|
-
rm -f rules/filterlists/easylist_cookie_*
|
|
17
|
-
wget --directory-prefix="${RULES_DIR}" \
|
|
18
|
-
"https://raw.githubusercontent.com/easylist/easylist/${EASYLIST_REVISION}/easylist_cookie/easylist_cookie_general_hide.txt" \
|
|
19
|
-
"https://raw.githubusercontent.com/easylist/easylist/${EASYLIST_REVISION}/easylist_cookie/easylist_cookie_specific_hide.txt" \
|
|
20
|
-
"https://raw.githubusercontent.com/easylist/easylist/${EASYLIST_REVISION}/easylist_cookie/easylist_cookie_specific_uBO.txt" \
|
|
21
|
-
"https://raw.githubusercontent.com/easylist/easylist/${EASYLIST_REVISION}/easylist_cookie/easylist_cookie_international_specific_hide.txt" \
|
|
22
|
-
"https://raw.githubusercontent.com/easylist/easylist/${EASYLIST_REVISION}/easylist_cookie/easylist_cookie_allowlist_general_hide.txt"
|
|
23
|
-
|
|
24
|
-
echo "${EASYLIST_REVISION}" > "${RULES_DIR}/easylist_revision.txt"
|
|
25
|
-
echo "Saved the Easylist revision to ${RULES_DIR}/easylist_revision.txt"
|
|
@@ -1,216 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import fs from 'fs';
|
|
4
|
-
import path from 'path';
|
|
5
|
-
import { fileURLToPath } from 'url';
|
|
6
|
-
import { parse } from 'csv-parse/sync';
|
|
7
|
-
import { getDomain } from 'tldts-experimental';
|
|
8
|
-
import { Readable } from 'stream';
|
|
9
|
-
import { finished } from 'stream/promises';
|
|
10
|
-
|
|
11
|
-
const rulesDir = path.join(path.dirname(fileURLToPath(import.meta.url)), '../rules');
|
|
12
|
-
const dataDir = path.join(path.dirname(fileURLToPath(import.meta.url)), '../data');
|
|
13
|
-
|
|
14
|
-
const easylistRevision = fs.readFileSync(path.join(rulesDir, 'filterlists', 'easylist_revision.txt'), 'utf-8');
|
|
15
|
-
const domainSpecificLists = [
|
|
16
|
-
'easylist_cookie_specific_uBO.txt',
|
|
17
|
-
'easylist_cookie_specific_hide.txt',
|
|
18
|
-
'easylist_cookie_international_specific_hide.txt',
|
|
19
|
-
];
|
|
20
|
-
|
|
21
|
-
const MAX_DOMAIN_RANK = 100000;
|
|
22
|
-
|
|
23
|
-
/** @typedef {{ rules: Record<string, string[]>}} FilterlistJSON */
|
|
24
|
-
|
|
25
|
-
/** @type {Map<string, string>} */
|
|
26
|
-
let domainMap;
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Processes a list containing domain-specific cosmetic rules, filters
|
|
30
|
-
* out unsupported rules and removes longtail domain rules
|
|
31
|
-
* @param {String} listFileName
|
|
32
|
-
*/
|
|
33
|
-
async function processDomainSpecificFilterList(listFileName) {
|
|
34
|
-
console.log(`Processing ${listFileName}`);
|
|
35
|
-
const data = fs.readFileSync(path.join(rulesDir, 'filterlists', listFileName), 'utf-8');
|
|
36
|
-
const lines = data.split('\n');
|
|
37
|
-
/** @type {FilterlistJSON} */
|
|
38
|
-
const filterlistJSON = { rules: {} };
|
|
39
|
-
|
|
40
|
-
// Remove comments and network rules, which take a different format:
|
|
41
|
-
// ! at start of line indicates comment
|
|
42
|
-
// || at start of line indicates network rule
|
|
43
|
-
const filteredLines = lines.filter((line) => !line.startsWith('!') && !line.startsWith('||'));
|
|
44
|
-
// Dump rules into json structure for parsing
|
|
45
|
-
for (const rule of filteredLines) {
|
|
46
|
-
const splitRule = rule.split('##');
|
|
47
|
-
const target = splitRule[0];
|
|
48
|
-
const action = splitRule[1];
|
|
49
|
-
|
|
50
|
-
if (!action) {
|
|
51
|
-
continue;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
if (!filterlistJSON.rules[target]) {
|
|
55
|
-
filterlistJSON.rules[target] = [action];
|
|
56
|
-
} else {
|
|
57
|
-
filterlistJSON.rules[target].push(action);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// Remove any targets that contain a +js rule. we do it this way because
|
|
62
|
-
// when a target has a :js rule, we must also remove any css rules
|
|
63
|
-
for (const item in filterlistJSON.rules) {
|
|
64
|
-
const containsJS = filterlistJSON.rules[item].some((filter) => filter.includes('+js'));
|
|
65
|
-
if (containsJS) {
|
|
66
|
-
delete filterlistJSON.rules[item];
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
for (const item in filterlistJSON.rules) {
|
|
71
|
-
let domains;
|
|
72
|
-
|
|
73
|
-
if (item.includes(',')) {
|
|
74
|
-
domains = item.split(',');
|
|
75
|
-
} else {
|
|
76
|
-
domains = [item];
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
const filteredDomains = domains.filter((domain) => {
|
|
80
|
-
// Don't filter global rules which don't have a domain
|
|
81
|
-
if (domain === '') {
|
|
82
|
-
return true;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
const tlDomain = getDomain(domain);
|
|
86
|
-
return domainMap.has(tlDomain);
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
// If no domains meet cutoff, remove rule entirely. If one or more
|
|
90
|
-
// domains meet cutoff, remove those that don't
|
|
91
|
-
if (filteredDomains.length === 0) {
|
|
92
|
-
delete filterlistJSON.rules[item];
|
|
93
|
-
} else if (filteredDomains.length !== domains.length) {
|
|
94
|
-
const filteredDomainString = filteredDomains.join(',');
|
|
95
|
-
filterlistJSON.rules[filteredDomainString] = filterlistJSON.rules[item];
|
|
96
|
-
delete filterlistJSON.rules[item];
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
await convertAndWriteABP(filterlistJSON, listFileName);
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
/**
|
|
104
|
-
* Fetch domain popularity data from Tranco and load it into a Map for querying
|
|
105
|
-
*/
|
|
106
|
-
async function loadTrancoList() {
|
|
107
|
-
const csvLocation = path.resolve(path.join(dataDir, 'top-1m.csv'));
|
|
108
|
-
if (!fs.existsSync(csvLocation)) {
|
|
109
|
-
console.log('Fetching latest Tranco list..');
|
|
110
|
-
// Download most recent list from https://tranco-list.eu using API
|
|
111
|
-
const date = new Date();
|
|
112
|
-
let available;
|
|
113
|
-
let download;
|
|
114
|
-
|
|
115
|
-
do {
|
|
116
|
-
const formattedDate = date.toISOString().split('T')[0].replace('/', '');
|
|
117
|
-
const res = await (await fetch(`https://tranco-list.eu/api/lists/date/${formattedDate}`)).json();
|
|
118
|
-
|
|
119
|
-
available = res.available;
|
|
120
|
-
download = res.download;
|
|
121
|
-
|
|
122
|
-
// Decrement date until a daily list is available
|
|
123
|
-
date.setTime(date.getTime() - 7 * 24 * 3600000);
|
|
124
|
-
} while (!available);
|
|
125
|
-
|
|
126
|
-
const listData = await fetch(download);
|
|
127
|
-
await finished(Readable.fromWeb(listData.body).pipe(fs.createWriteStream(csvLocation)));
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
if (typeof domainMap !== 'object') {
|
|
131
|
-
const trancoCSV = fs.readFileSync(path.join(dataDir, 'top-1m.csv'), 'utf-8');
|
|
132
|
-
/** @type {string[][]} */
|
|
133
|
-
const records = parse(trancoCSV, {
|
|
134
|
-
columns: false,
|
|
135
|
-
skip_empty_lines: true,
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
domainMap = new Map();
|
|
139
|
-
|
|
140
|
-
records.slice(0, MAX_DOMAIN_RANK).forEach(([key, value]) => {
|
|
141
|
-
domainMap.set(value, key);
|
|
142
|
-
});
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
/**
|
|
147
|
-
* Convert filter list to ABP format and write to disk
|
|
148
|
-
* @param {FilterlistJSON} JSONList
|
|
149
|
-
* @param {String} fileName
|
|
150
|
-
*/
|
|
151
|
-
function convertAndWriteABP(JSONList, fileName) {
|
|
152
|
-
return new Promise((resolve, reject) => {
|
|
153
|
-
const fileLocation = path.join(rulesDir, 'filterlists', fileName);
|
|
154
|
-
const stream = fs.createWriteStream(fileLocation, { flags: 'w' });
|
|
155
|
-
|
|
156
|
-
stream.once('open', () => {
|
|
157
|
-
for (const item in JSONList.rules) {
|
|
158
|
-
JSONList.rules[item].forEach((filter) => {
|
|
159
|
-
const abpRule = item + '##' + filter;
|
|
160
|
-
stream.write(abpRule + '\n');
|
|
161
|
-
});
|
|
162
|
-
}
|
|
163
|
-
stream.end();
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
stream.on('finish', () => {
|
|
167
|
-
console.log(`ABP format list written to ${fileLocation}`);
|
|
168
|
-
resolve();
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
stream.on('error', (err) => {
|
|
172
|
-
console.error(`An error occurred while writing ${fileLocation}: ${err.message}`);
|
|
173
|
-
});
|
|
174
|
-
});
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
/**
|
|
178
|
-
* Combine all filter lists into a single filterlist.txt
|
|
179
|
-
*/
|
|
180
|
-
function combineFilterLists() {
|
|
181
|
-
// TODO: consider using python-abp (flrender) to generate filterlist properly
|
|
182
|
-
const filterlistContent = `[Adblock Plus 2.0]
|
|
183
|
-
! Title: CPM Cosmetic Filter List
|
|
184
|
-
! Based on EasyList ${easylistRevision}
|
|
185
|
-
! DO NOT EDIT MANUALLY, your changes will be lost
|
|
186
|
-
! Generated on ${new Date().toISOString()}
|
|
187
|
-
!------------------------General element hiding rules-------------------------!
|
|
188
|
-
${fs.readFileSync(path.join(rulesDir, 'filterlists', 'easylist_cookie_general_hide.txt'), 'utf-8')}
|
|
189
|
-
!------------------------Specific element hiding rules------------------------!
|
|
190
|
-
${fs.readFileSync(path.join(rulesDir, 'filterlists', 'easylist_cookie_specific_hide.txt'), 'utf-8')}
|
|
191
|
-
${fs.readFileSync(path.join(rulesDir, 'filterlists', 'easylist_cookie_specific_uBO.txt'), 'utf-8')}
|
|
192
|
-
!------------------------Rules for international sites------------------------!
|
|
193
|
-
${fs.readFileSync(path.join(rulesDir, 'filterlists', 'easylist_cookie_international_specific_hide.txt'), 'utf-8')}
|
|
194
|
-
!---------------------------------Allowlists----------------------------------!
|
|
195
|
-
${fs.readFileSync(path.join(rulesDir, 'filterlists', 'easylist_cookie_allowlist_general_hide.txt'), 'utf-8')}
|
|
196
|
-
!--------------------------------DDG overrides--------------------------------!
|
|
197
|
-
${fs.readFileSync(path.join(rulesDir, 'filterlists', 'overrides.txt'), 'utf-8')}
|
|
198
|
-
`;
|
|
199
|
-
|
|
200
|
-
fs.writeFile(path.join(rulesDir, 'filterlist.txt'), filterlistContent, () => console.log('Written filterlist.txt'));
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
/**
|
|
204
|
-
* Parse and refine lists using domain popularity, then combine them and write filterlist.txt
|
|
205
|
-
*/
|
|
206
|
-
async function rebuildFilterList() {
|
|
207
|
-
await loadTrancoList();
|
|
208
|
-
|
|
209
|
-
for (const list of domainSpecificLists) {
|
|
210
|
-
await processDomainSpecificFilterList(list);
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
combineFilterLists();
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
rebuildFilterList();
|
package/tests/oil.spec.ts
DELETED
package/tests/springer.spec.ts
DELETED