privalint 0.2.0 → 0.4.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/README.de.md +68 -2
- package/README.md +77 -13
- package/dist/assessment.d.ts +6 -0
- package/dist/assessment.js +115 -0
- package/dist/assessment.js.map +1 -0
- package/dist/capture.d.ts +5 -0
- package/dist/capture.js +162 -0
- package/dist/capture.js.map +1 -0
- package/dist/cli.js +48 -14
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +21 -0
- package/dist/config.js +113 -0
- package/dist/config.js.map +1 -0
- package/dist/consent.d.ts +3 -0
- package/dist/consent.js +81 -12
- package/dist/consent.js.map +1 -1
- package/dist/coverage.d.ts +13 -0
- package/dist/coverage.js +86 -0
- package/dist/coverage.js.map +1 -0
- package/dist/data/third-parties.js +9 -0
- package/dist/data/third-parties.js.map +1 -1
- package/dist/diagnostics.d.ts +28 -0
- package/dist/diagnostics.js +202 -0
- package/dist/diagnostics.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/network.d.ts +68 -0
- package/dist/network.js +237 -0
- package/dist/network.js.map +1 -0
- package/dist/observations.d.ts +3 -0
- package/dist/observations.js +4 -0
- package/dist/observations.js.map +1 -0
- package/dist/policy.d.ts +6 -0
- package/dist/policy.js +37 -0
- package/dist/policy.js.map +1 -0
- package/dist/report-contract.d.ts +13 -0
- package/dist/report-contract.js +16 -0
- package/dist/report-contract.js.map +1 -0
- package/dist/reporters/capture.d.ts +5 -0
- package/dist/reporters/capture.js +87 -0
- package/dist/reporters/capture.js.map +1 -0
- package/dist/reporters/markdown.js +13 -1
- package/dist/reporters/markdown.js.map +1 -1
- package/dist/reporters/messages.d.ts +1 -0
- package/dist/reporters/messages.js +18 -6
- package/dist/reporters/messages.js.map +1 -1
- package/dist/reporters/sarif.js +48 -10
- package/dist/reporters/sarif.js.map +1 -1
- package/dist/reporters/shared.js +3 -2
- package/dist/reporters/shared.js.map +1 -1
- package/dist/reporters/text.js +10 -2
- package/dist/reporters/text.js.map +1 -1
- package/dist/rules/google-fonts.js +2 -1
- package/dist/rules/google-fonts.js.map +1 -1
- package/dist/rules/reject-option.js +6 -4
- package/dist/rules/reject-option.js.map +1 -1
- package/dist/rules/third-party-embeds.js +2 -2
- package/dist/rules/third-party-embeds.js.map +1 -1
- package/dist/rules/third-party-hits.js +2 -1
- package/dist/rules/third-party-hits.js.map +1 -1
- package/dist/rules/tracker-hits.js +2 -1
- package/dist/rules/tracker-hits.js.map +1 -1
- package/dist/scan-options.d.ts +45 -0
- package/dist/scan-options.js +2 -0
- package/dist/scan-options.js.map +1 -0
- package/dist/scanner.d.ts +4 -38
- package/dist/scanner.js +177 -114
- package/dist/scanner.js.map +1 -1
- package/dist/types.d.ts +119 -0
- package/package.json +7 -1
- package/schemas/report-1.0.0.schema.json +813 -0
- package/schemas/report-1.1.0.schema.json +833 -0
- package/schemas/sarif-properties-1.0.0.schema.json +785 -0
- package/schemas/sarif-properties-1.1.0.schema.json +810 -0
package/dist/cli.js
CHANGED
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
import { writeFile } from 'node:fs/promises';
|
|
3
3
|
import { extname } from 'node:path';
|
|
4
4
|
import { parseArgs } from 'node:util';
|
|
5
|
+
import { loadConfig } from './config.js';
|
|
6
|
+
import { assessCoverage } from './coverage.js';
|
|
7
|
+
import { diagnosticUrl } from './diagnostics.js';
|
|
5
8
|
import { LANGS } from './i18n.js';
|
|
6
9
|
import { REGIONS } from './legal.js';
|
|
7
10
|
import { VERSION } from './meta.js';
|
|
@@ -13,6 +16,7 @@ const CHANNELS = ['chrome', 'msedge'];
|
|
|
13
16
|
const EXIT_OK = 0;
|
|
14
17
|
const EXIT_FINDINGS = 1;
|
|
15
18
|
const EXIT_ERROR = 2;
|
|
19
|
+
const EXIT_INCOMPLETE = 3;
|
|
16
20
|
const HELP = `privalint ${VERSION}
|
|
17
21
|
Find GDPR problems on a live website: trackers before consent or after
|
|
18
22
|
"reject", Google Fonts, missing Impressum. Runs the site in a real browser.
|
|
@@ -27,13 +31,17 @@ Options
|
|
|
27
31
|
Repeatable; .sarif, .json and .md pick their format,
|
|
28
32
|
other extensions use --format
|
|
29
33
|
--fail-on <level> error | warning | never (default error)
|
|
34
|
+
--fail-on-incomplete Exit 3 if required observation coverage is incomplete
|
|
35
|
+
--allow-incomplete Override failOnIncomplete from JSON config
|
|
30
36
|
--disable <rules> Comma-separated rule ids to skip
|
|
37
|
+
--config <file> JSON config (default: ./privalint.config.json when present)
|
|
31
38
|
--settle <ms> Wait this long after load for late tags (default 2000)
|
|
32
39
|
--timeout <ms> Navigation timeout per page (default 30000)
|
|
33
40
|
--locale <locale> Browser locale (default de-DE)
|
|
34
41
|
--lang <lang> Language of the report: en | de (default en)
|
|
35
42
|
--region <region> Whose law to cite: de | at | ch | eu (default de)
|
|
36
43
|
--skip-consent Don't click "reject" on the consent banner
|
|
44
|
+
--consent Enable consent checking even if the JSON config disables it
|
|
37
45
|
--headed Open a visible browser window instead of running headless
|
|
38
46
|
--channel <name> Use an installed browser: chrome or msedge
|
|
39
47
|
--list-rules Print the available rules and exit
|
|
@@ -44,6 +52,7 @@ Exit codes
|
|
|
44
52
|
0 no findings at or above --fail-on
|
|
45
53
|
1 findings at or above --fail-on
|
|
46
54
|
2 invalid arguments or the scan failed
|
|
55
|
+
3 incomplete coverage (opt-in; findings exit 1 takes precedence)
|
|
47
56
|
`;
|
|
48
57
|
const FORMAT_BY_EXTENSION = {
|
|
49
58
|
'.sarif': 'sarif',
|
|
@@ -59,13 +68,17 @@ async function main(argv) {
|
|
|
59
68
|
format: { type: 'string', short: 'f', default: 'text' },
|
|
60
69
|
output: { type: 'string', short: 'o', multiple: true },
|
|
61
70
|
'fail-on': { type: 'string', default: 'error' },
|
|
71
|
+
'fail-on-incomplete': { type: 'boolean' },
|
|
72
|
+
'allow-incomplete': { type: 'boolean' },
|
|
62
73
|
disable: { type: 'string' },
|
|
74
|
+
config: { type: 'string' },
|
|
63
75
|
settle: { type: 'string' },
|
|
64
76
|
timeout: { type: 'string' },
|
|
65
77
|
locale: { type: 'string' },
|
|
66
|
-
lang: { type: 'string'
|
|
67
|
-
region: { type: 'string'
|
|
78
|
+
lang: { type: 'string' },
|
|
79
|
+
region: { type: 'string' },
|
|
68
80
|
'skip-consent': { type: 'boolean' },
|
|
81
|
+
consent: { type: 'boolean' },
|
|
69
82
|
headed: { type: 'boolean' },
|
|
70
83
|
channel: { type: 'string' },
|
|
71
84
|
'list-rules': { type: 'boolean' },
|
|
@@ -87,24 +100,39 @@ async function main(argv) {
|
|
|
87
100
|
}
|
|
88
101
|
return EXIT_OK;
|
|
89
102
|
}
|
|
90
|
-
const
|
|
103
|
+
const config = await loadConfig(values.config);
|
|
104
|
+
const target = parseTarget(positionals.length === 0 && config.url ? [config.url] : positionals);
|
|
91
105
|
const reportFormat = oneOf(values.format, FORMATS, '--format');
|
|
92
106
|
const failOn = oneOf(values['fail-on'], FAIL_LEVELS, '--fail-on');
|
|
93
107
|
const rules = selectRules(values.disable);
|
|
108
|
+
if (values['fail-on-incomplete'] && values['allow-incomplete']) {
|
|
109
|
+
throw new Error('Use either --fail-on-incomplete or --allow-incomplete, not both.');
|
|
110
|
+
}
|
|
111
|
+
const failOnIncomplete = values['fail-on-incomplete'] === true ||
|
|
112
|
+
(values['allow-incomplete'] !== true && config.failOnIncomplete === true);
|
|
113
|
+
if (values.consent && values['skip-consent']) {
|
|
114
|
+
throw new Error('Use either --consent or --skip-consent, not both.');
|
|
115
|
+
}
|
|
94
116
|
const result = await scan(target.href, {
|
|
95
117
|
rules,
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
118
|
+
...(config.rules === undefined ? {} : { ruleSettings: config.rules }),
|
|
119
|
+
...(config.allow === undefined ? {} : { allow: config.allow }),
|
|
120
|
+
maxPages: wholeNumber(values['max-pages'], '--max-pages', 1) ?? config.maxPages,
|
|
121
|
+
settleMs: wholeNumber(values.settle, '--settle', 0) ?? config.settleMs,
|
|
122
|
+
timeoutMs: wholeNumber(values.timeout, '--timeout', 1) ?? config.timeoutMs,
|
|
123
|
+
locale: values.locale ?? config.locale,
|
|
124
|
+
lang: oneOf(values.lang ?? config.lang ?? 'en', LANGS, '--lang'),
|
|
125
|
+
region: oneOf(values.region ?? config.region ?? 'de', REGIONS, '--region'),
|
|
126
|
+
consentCheck: values.consent === true
|
|
127
|
+
? true
|
|
128
|
+
: values['skip-consent'] === true
|
|
129
|
+
? false
|
|
130
|
+
: (config.consentCheck ?? true),
|
|
103
131
|
headed: values.headed === true,
|
|
104
132
|
channel: values.channel === undefined ? undefined : oneOf(values.channel, CHANNELS, '--channel'),
|
|
105
133
|
onPage: (page) => process.stderr.write(page.error === undefined
|
|
106
|
-
? `scanned ${page.finalUrl}\n`
|
|
107
|
-
: `skipped ${page.url}: ${page.error}\n`),
|
|
134
|
+
? `scanned ${diagnosticUrl(page.finalUrl)}\n`
|
|
135
|
+
: `skipped ${diagnosticUrl(page.url)}: ${page.error}\n`),
|
|
108
136
|
});
|
|
109
137
|
const outputs = values.output ?? [];
|
|
110
138
|
for (const file of outputs) {
|
|
@@ -121,7 +149,13 @@ async function main(argv) {
|
|
|
121
149
|
process.stdout.write('\n');
|
|
122
150
|
}
|
|
123
151
|
}
|
|
124
|
-
|
|
152
|
+
if (shouldFail(result.findings, failOn))
|
|
153
|
+
return EXIT_FINDINGS;
|
|
154
|
+
if (failOnIncomplete && !assessCoverage(result).complete) {
|
|
155
|
+
process.stderr.write('privalint: required observation coverage is incomplete (exit 3).\n');
|
|
156
|
+
return EXIT_INCOMPLETE;
|
|
157
|
+
}
|
|
158
|
+
return EXIT_OK;
|
|
125
159
|
}
|
|
126
160
|
function parseTarget(positionals) {
|
|
127
161
|
const [raw, ...rest] = positionals;
|
|
@@ -133,7 +167,7 @@ function parseTarget(positionals) {
|
|
|
133
167
|
return new URL(withScheme);
|
|
134
168
|
}
|
|
135
169
|
catch {
|
|
136
|
-
throw new Error(
|
|
170
|
+
throw new Error('Not a valid HTTP(S) URL.');
|
|
137
171
|
}
|
|
138
172
|
}
|
|
139
173
|
function oneOf(value, allowed, flag) {
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,OAAO,EAAe,MAAM,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAChF,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAGpC,MAAM,WAAW,GAAG,CAAC,OAAO,EAAE,SAAS,EAAE,OAAO,CAAU,CAAC;AAC3D,MAAM,QAAQ,GAAG,CAAC,QAAQ,EAAE,QAAQ,CAAU,CAAC;AAG/C,MAAM,OAAO,GAAG,CAAC,CAAC;AAClB,MAAM,aAAa,GAAG,CAAC,CAAC;AACxB,MAAM,UAAU,GAAG,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type Lang } from './i18n.js';
|
|
2
|
+
import { type Region } from './legal.js';
|
|
3
|
+
import type { RuleSettings } from './types.js';
|
|
4
|
+
export interface PrivalintConfig {
|
|
5
|
+
url?: string;
|
|
6
|
+
maxPages?: number;
|
|
7
|
+
settleMs?: number;
|
|
8
|
+
timeoutMs?: number;
|
|
9
|
+
locale?: string;
|
|
10
|
+
lang?: Lang;
|
|
11
|
+
region?: Region;
|
|
12
|
+
consentCheck?: boolean;
|
|
13
|
+
failOnIncomplete?: boolean;
|
|
14
|
+
rules?: RuleSettings;
|
|
15
|
+
allow?: {
|
|
16
|
+
vendors: string[];
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
/** JSON only; no execution of repository-supplied code. Reject typos instead of ignoring them. */
|
|
20
|
+
export declare function parseConfig(value: unknown): PrivalintConfig;
|
|
21
|
+
export declare function loadConfig(file?: string, cwd?: string): Promise<PrivalintConfig>;
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { open } from 'node:fs/promises';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { LANGS } from './i18n.js';
|
|
4
|
+
import { REGIONS } from './legal.js';
|
|
5
|
+
import { validateRuleSettings } from './policy.js';
|
|
6
|
+
import { defaultRules } from './rules/index.js';
|
|
7
|
+
const KEYS = [
|
|
8
|
+
'url',
|
|
9
|
+
'maxPages',
|
|
10
|
+
'settleMs',
|
|
11
|
+
'timeoutMs',
|
|
12
|
+
'locale',
|
|
13
|
+
'lang',
|
|
14
|
+
'region',
|
|
15
|
+
'consentCheck',
|
|
16
|
+
'failOnIncomplete',
|
|
17
|
+
'rules',
|
|
18
|
+
'allow',
|
|
19
|
+
];
|
|
20
|
+
function record(value) {
|
|
21
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
22
|
+
}
|
|
23
|
+
function invalid() {
|
|
24
|
+
throw new TypeError('Invalid privalint config. See docs/configuration.md for the supported JSON fields.');
|
|
25
|
+
}
|
|
26
|
+
/** JSON only; no execution of repository-supplied code. Reject typos instead of ignoring them. */
|
|
27
|
+
export function parseConfig(value) {
|
|
28
|
+
if (!record(value) || Object.keys(value).some((key) => !KEYS.includes(key)))
|
|
29
|
+
invalid();
|
|
30
|
+
for (const key of ['url', 'locale']) {
|
|
31
|
+
if (value[key] !== undefined && (typeof value[key] !== 'string' || !value[key].trim()))
|
|
32
|
+
invalid();
|
|
33
|
+
}
|
|
34
|
+
if (typeof value.url === 'string') {
|
|
35
|
+
let url;
|
|
36
|
+
try {
|
|
37
|
+
url = new URL(value.url);
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
invalid();
|
|
41
|
+
}
|
|
42
|
+
if (!['http:', 'https:'].includes(url.protocol) || url.username || url.password)
|
|
43
|
+
invalid();
|
|
44
|
+
}
|
|
45
|
+
for (const key of ['maxPages', 'settleMs', 'timeoutMs']) {
|
|
46
|
+
const number = value[key];
|
|
47
|
+
if (number !== undefined &&
|
|
48
|
+
(typeof number !== 'number' ||
|
|
49
|
+
!Number.isSafeInteger(number) ||
|
|
50
|
+
number < (key === 'settleMs' ? 0 : 1)))
|
|
51
|
+
invalid();
|
|
52
|
+
}
|
|
53
|
+
if (value.lang !== undefined && !LANGS.includes(value.lang))
|
|
54
|
+
invalid();
|
|
55
|
+
if (value.region !== undefined && !REGIONS.includes(value.region))
|
|
56
|
+
invalid();
|
|
57
|
+
if (value.consentCheck !== undefined && typeof value.consentCheck !== 'boolean')
|
|
58
|
+
invalid();
|
|
59
|
+
if (value.failOnIncomplete !== undefined && typeof value.failOnIncomplete !== 'boolean')
|
|
60
|
+
invalid();
|
|
61
|
+
if (value.rules !== undefined) {
|
|
62
|
+
if (!record(value.rules))
|
|
63
|
+
invalid();
|
|
64
|
+
validateRuleSettings(value.rules, defaultRules);
|
|
65
|
+
}
|
|
66
|
+
if (value.allow !== undefined) {
|
|
67
|
+
if (!record(value.allow) ||
|
|
68
|
+
Object.keys(value.allow).some((key) => key !== 'vendors') ||
|
|
69
|
+
!Array.isArray(value.allow.vendors) ||
|
|
70
|
+
!value.allow.vendors.every((vendor) => typeof vendor === 'string' && vendor.trim().length > 0 && vendor.length <= 200))
|
|
71
|
+
invalid();
|
|
72
|
+
}
|
|
73
|
+
// Copy only validated fields into ordinary objects, avoiding prototype-bearing input objects.
|
|
74
|
+
return JSON.parse(JSON.stringify(value));
|
|
75
|
+
}
|
|
76
|
+
export async function loadConfig(file, cwd = process.cwd()) {
|
|
77
|
+
let text;
|
|
78
|
+
try {
|
|
79
|
+
const handle = await open(file ?? join(cwd, 'privalint.config.json'), 'r');
|
|
80
|
+
try {
|
|
81
|
+
const buffer = Buffer.alloc(64 * 1024 + 1);
|
|
82
|
+
let size = 0;
|
|
83
|
+
while (size < buffer.length) {
|
|
84
|
+
const { bytesRead } = await handle.read(buffer, size, buffer.length - size, null);
|
|
85
|
+
if (bytesRead === 0)
|
|
86
|
+
break;
|
|
87
|
+
size += bytesRead;
|
|
88
|
+
}
|
|
89
|
+
if (size === buffer.length)
|
|
90
|
+
invalid();
|
|
91
|
+
text = buffer.toString('utf8', 0, size);
|
|
92
|
+
}
|
|
93
|
+
finally {
|
|
94
|
+
await handle.close();
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
if (error instanceof TypeError)
|
|
99
|
+
throw error;
|
|
100
|
+
if (file === undefined && error instanceof Error && 'code' in error && error.code === 'ENOENT')
|
|
101
|
+
return {};
|
|
102
|
+
throw new Error('Could not read privalint config file.');
|
|
103
|
+
}
|
|
104
|
+
let value;
|
|
105
|
+
try {
|
|
106
|
+
value = JSON.parse(text);
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
invalid();
|
|
110
|
+
}
|
|
111
|
+
return parseConfig(value);
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
package/dist/consent.d.ts
CHANGED
|
@@ -3,6 +3,9 @@ import type { ConsentObservation, RecordedRequest } from './types.js';
|
|
|
3
3
|
export interface ConsentCheckOptions {
|
|
4
4
|
settleMs: number;
|
|
5
5
|
timeoutMs: number;
|
|
6
|
+
signal?: AbortSignal;
|
|
7
|
+
onReject?: () => void;
|
|
8
|
+
onTimeout?: () => void;
|
|
6
9
|
}
|
|
7
10
|
/**
|
|
8
11
|
* Looks for a consent banner, clicks its reject button if there is one,
|
package/dist/consent.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { classifyLoadError } from './diagnostics.js';
|
|
1
2
|
/**
|
|
2
3
|
* Button selectors of common consent managers. Best effort: vendors change
|
|
3
4
|
* their markup, so the label heuristic below is the fallback for everything
|
|
@@ -43,6 +44,26 @@ const KNOWN_CMPS = [
|
|
|
43
44
|
accept: '#cc-main [data-role="all"]',
|
|
44
45
|
reject: '#cc-main [data-role="necessary"]',
|
|
45
46
|
},
|
|
47
|
+
{
|
|
48
|
+
name: 'CookieFirst',
|
|
49
|
+
accept: '[data-cookiefirst-action="accept"], :is(#cookiefirst-root, .cookiefirst-root) [data-testid="actionButton-accept"]',
|
|
50
|
+
reject: '[data-cookiefirst-action="reject"], :is(#cookiefirst-root, .cookiefirst-root) [data-testid="actionButton-reject"]',
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
name: 'Osano',
|
|
54
|
+
accept: '.osano-cm-accept-all',
|
|
55
|
+
reject: '.osano-cm-deny-all',
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
name: 'Termly',
|
|
59
|
+
accept: '[data-tid="banner-accept"], [class*="termly-styles-module-acceptButton-"]',
|
|
60
|
+
reject: '[data-tid="banner-decline"]',
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
name: 'Sourcepoint',
|
|
64
|
+
accept: 'button.sp_choice_type_11',
|
|
65
|
+
reject: 'button.sp_choice_type_13',
|
|
66
|
+
},
|
|
46
67
|
];
|
|
47
68
|
// Unanchored on purpose: "Alle Cookies akzeptieren" and "Accept all cookies" must both match.
|
|
48
69
|
// Labels that also match REJECT_LABEL ("Nur notwendige akzeptieren") are never treated as accept.
|
|
@@ -59,27 +80,43 @@ const CLICKABLE = 'button, [role="button"], a, input[type="button"], input[type=
|
|
|
59
80
|
* be loaded; `requests` is the live array the page's request listener writes to.
|
|
60
81
|
*/
|
|
61
82
|
export async function observeConsent(page, context, requests, options) {
|
|
83
|
+
if (options.signal?.aborted)
|
|
84
|
+
throw new DOMException('Consent check aborted', 'AbortError');
|
|
62
85
|
const banner = await findBanner(page);
|
|
63
86
|
if (banner === undefined) {
|
|
64
|
-
return {
|
|
87
|
+
return {
|
|
88
|
+
bannerFound: false,
|
|
89
|
+
rejected: false,
|
|
90
|
+
state: 'not-detected',
|
|
91
|
+
outcome: 'no-banner-detected',
|
|
92
|
+
reason: 'no-supported-banner',
|
|
93
|
+
};
|
|
65
94
|
}
|
|
66
95
|
const observation = {
|
|
67
96
|
bannerFound: true,
|
|
68
97
|
rejected: false,
|
|
98
|
+
state: 'reject-unavailable',
|
|
99
|
+
outcome: 'unsupported-banner',
|
|
100
|
+
reason: 'no-supported-reject-control',
|
|
69
101
|
...(banner.cmp === undefined ? {} : { cmp: banner.cmp }),
|
|
70
102
|
...(banner.acceptLabel === undefined ? {} : { acceptLabel: banner.acceptLabel }),
|
|
71
103
|
...(banner.rejectLabel === undefined ? {} : { rejectLabel: banner.rejectLabel }),
|
|
72
104
|
...(banner.payOrOk ? { payOrOk: true } : {}),
|
|
73
105
|
};
|
|
74
106
|
if (banner.reject === undefined) {
|
|
107
|
+
if (banner.acceptLabel === undefined)
|
|
108
|
+
observation.reason = 'unsupported-banner-markup';
|
|
75
109
|
return observation;
|
|
76
110
|
}
|
|
77
111
|
try {
|
|
78
112
|
const cookiesBefore = new Set((await context.cookies()).map(cookieKey));
|
|
79
113
|
const firstRequest = requests.length;
|
|
80
|
-
|
|
114
|
+
options.onReject?.();
|
|
115
|
+
await banner.reject.click({ timeout: Math.min(5_000, options.timeoutMs) });
|
|
81
116
|
await settle(page, options);
|
|
82
|
-
await page.reload({ waitUntil: 'load', timeout: options.timeoutMs });
|
|
117
|
+
const response = await page.reload({ waitUntil: 'load', timeout: options.timeoutMs });
|
|
118
|
+
if (response === null || response.status() >= 400)
|
|
119
|
+
throw new Error('HTTP reload failed');
|
|
83
120
|
await settle(page, options);
|
|
84
121
|
const cookies = (await context.cookies())
|
|
85
122
|
.filter((cookie) => !cookiesBefore.has(cookieKey(cookie)))
|
|
@@ -87,20 +124,30 @@ export async function observeConsent(page, context, requests, options) {
|
|
|
87
124
|
return {
|
|
88
125
|
...observation,
|
|
89
126
|
rejected: true,
|
|
127
|
+
state: 'reject-tested',
|
|
128
|
+
outcome: 'reject-succeeded',
|
|
129
|
+
reason: 'reject-clicked-and-reloaded',
|
|
90
130
|
afterReject: { requests: requests.slice(firstRequest), cookies },
|
|
91
131
|
};
|
|
92
132
|
}
|
|
93
133
|
catch (error) {
|
|
94
|
-
|
|
95
|
-
|
|
134
|
+
return {
|
|
135
|
+
...observation,
|
|
136
|
+
state: 'interaction-failed',
|
|
137
|
+
outcome: 'interaction-failed',
|
|
138
|
+
reason: 'browser-interaction-failed',
|
|
139
|
+
error: classifyLoadError(error),
|
|
140
|
+
};
|
|
96
141
|
}
|
|
97
142
|
}
|
|
98
143
|
async function findBanner(page) {
|
|
99
|
-
for (const
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
144
|
+
for (const frame of page.frames()) {
|
|
145
|
+
for (const cmp of KNOWN_CMPS) {
|
|
146
|
+
const accept = await firstVisible(frame.locator(cmp.accept));
|
|
147
|
+
if (accept !== undefined) {
|
|
148
|
+
const reject = await firstVisible(frame.locator(cmp.reject));
|
|
149
|
+
return describe(accept, reject, cmp.name);
|
|
150
|
+
}
|
|
104
151
|
}
|
|
105
152
|
}
|
|
106
153
|
// Unknown or self-built banners: go by button labels, in every frame.
|
|
@@ -116,7 +163,29 @@ async function findBanner(page) {
|
|
|
116
163
|
const accept = await firstVisible(clickable.filter({ hasText: ACCEPT_LABEL }).filter({ hasNotText: REJECT_LABEL }));
|
|
117
164
|
if (accept !== undefined) {
|
|
118
165
|
const reject = await firstVisible(clickable.filter({ hasText: REJECT_LABEL }));
|
|
119
|
-
|
|
166
|
+
const realCookieBanner = await frame
|
|
167
|
+
.locator('script')
|
|
168
|
+
.evaluateAll((elements) => elements.some((element) => /real-cookie-banner|devowl-wp-react-cookie-banner/i.test((element.getAttribute('src') ?? '') + (element.textContent ?? ''))))
|
|
169
|
+
.catch(() => false);
|
|
170
|
+
const cmp = realCookieBanner ? 'Real Cookie Banner' : undefined;
|
|
171
|
+
return describe(accept, reject, cmp);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
// Recognizable consent UI with unsupported controls is different from no banner detected.
|
|
175
|
+
for (const frame of page.frames()) {
|
|
176
|
+
const candidates = frame.locator('[role="dialog"], [aria-modal="true"], [id*="cookie-banner"], [class*="cookie-banner"]');
|
|
177
|
+
const count = Math.min(await candidates.count().catch(() => 0), 10);
|
|
178
|
+
for (let index = 0; index < count; index++) {
|
|
179
|
+
const candidate = candidates.nth(index);
|
|
180
|
+
if (!(await candidate.isVisible().catch(() => false)))
|
|
181
|
+
continue;
|
|
182
|
+
const text = await candidate.innerText({ timeout: 500 }).catch(() => '');
|
|
183
|
+
if (CONSENT_CONTEXT.test(text) &&
|
|
184
|
+
(await candidate
|
|
185
|
+
.locator(CLICKABLE)
|
|
186
|
+
.count()
|
|
187
|
+
.catch(() => 0)) > 0)
|
|
188
|
+
return {};
|
|
120
189
|
}
|
|
121
190
|
}
|
|
122
191
|
return undefined;
|
|
@@ -177,7 +246,7 @@ async function label(locator) {
|
|
|
177
246
|
}
|
|
178
247
|
async function settle(page, options) {
|
|
179
248
|
await page.waitForLoadState('networkidle', { timeout: options.timeoutMs }).catch(() => {
|
|
180
|
-
|
|
249
|
+
options.onTimeout?.();
|
|
181
250
|
});
|
|
182
251
|
await page.waitForTimeout(options.settleMs);
|
|
183
252
|
}
|
package/dist/consent.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"consent.js","sourceRoot":"","sources":["../src/consent.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"consent.js","sourceRoot":"","sources":["../src/consent.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ScanResult } from './types.js';
|
|
2
|
+
export interface CoverageIssue {
|
|
3
|
+
pageId?: string;
|
|
4
|
+
pageUrl?: string;
|
|
5
|
+
ruleId?: string;
|
|
6
|
+
reason: 'legacy-report' | 'unsupported-schema' | 'missing-pages' | 'incomplete-capture' | 'missing-evaluation' | 'not-evaluable' | 'partial-evaluation' | 'inconsistent-counters';
|
|
7
|
+
}
|
|
8
|
+
export interface CoverageAssessment {
|
|
9
|
+
complete: boolean;
|
|
10
|
+
issues: CoverageIssue[];
|
|
11
|
+
}
|
|
12
|
+
/** Opt-in observation gate. Intentional exclusions are not privacy or coverage failures. */
|
|
13
|
+
export declare function assessCoverage(result: ScanResult): CoverageAssessment;
|
package/dist/coverage.js
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { diagnosticUrl } from './diagnostics.js';
|
|
2
|
+
import { identifyReportSchema } from './report-contract.js';
|
|
3
|
+
/** Opt-in observation gate. Intentional exclusions are not privacy or coverage failures. */
|
|
4
|
+
export function assessCoverage(result) {
|
|
5
|
+
const identity = identifyReportSchema(result);
|
|
6
|
+
if (identity.kind !== 'supported') {
|
|
7
|
+
return {
|
|
8
|
+
complete: false,
|
|
9
|
+
issues: [{ reason: identity.kind === 'legacy' ? 'legacy-report' : 'unsupported-schema' }],
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
const issues = [];
|
|
13
|
+
const selected = result.options?.selectedRules ?? result.rules.map((rule) => rule.id);
|
|
14
|
+
if (result.pages.length === 0)
|
|
15
|
+
issues.push({ reason: 'missing-pages' });
|
|
16
|
+
for (const page of result.pages) {
|
|
17
|
+
const location = {
|
|
18
|
+
...(page.pageId === undefined ? {} : { pageId: page.pageId }),
|
|
19
|
+
pageUrl: diagnosticUrl(page.url),
|
|
20
|
+
};
|
|
21
|
+
if (!page.capture?.complete ||
|
|
22
|
+
!page.capture.document ||
|
|
23
|
+
page.capture.diagnostics.issueCount > 0 ||
|
|
24
|
+
page.error !== undefined ||
|
|
25
|
+
page.status === null ||
|
|
26
|
+
page.status >= 400) {
|
|
27
|
+
issues.push({ ...location, reason: 'incomplete-capture' });
|
|
28
|
+
}
|
|
29
|
+
if (page.capture && !consistentCounters(page.capture.diagnostics)) {
|
|
30
|
+
issues.push({ ...location, reason: 'inconsistent-counters' });
|
|
31
|
+
}
|
|
32
|
+
const evaluations = new Map((page.ruleEvaluations ?? []).map((evaluation) => [evaluation.ruleId, evaluation]));
|
|
33
|
+
for (const ruleId of selected) {
|
|
34
|
+
const evaluation = evaluations.get(ruleId);
|
|
35
|
+
if (evaluation === undefined) {
|
|
36
|
+
issues.push({ ...location, ruleId, reason: 'missing-evaluation' });
|
|
37
|
+
}
|
|
38
|
+
else if (evaluation.status === 'skipped' &&
|
|
39
|
+
['disabled', 'consent-disabled', 'not-start-page'].includes(evaluation.reason ?? '')) {
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
else if (evaluation.status !== 'executed' ||
|
|
43
|
+
!['checked-no-finding', 'finding-detected'].includes(evaluation.outcome ?? '')) {
|
|
44
|
+
issues.push({ ...location, ruleId, reason: 'not-evaluable' });
|
|
45
|
+
}
|
|
46
|
+
else if (evaluation.partial) {
|
|
47
|
+
issues.push({ ...location, ruleId, reason: 'partial-evaluation' });
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return { complete: issues.length === 0, issues };
|
|
52
|
+
}
|
|
53
|
+
function consistentCounters(data) {
|
|
54
|
+
const values = [
|
|
55
|
+
data.requests,
|
|
56
|
+
data.finished,
|
|
57
|
+
data.failed,
|
|
58
|
+
data.blocked,
|
|
59
|
+
data.skippedRequests,
|
|
60
|
+
data.failedRequests,
|
|
61
|
+
data.securityBlockedRequests,
|
|
62
|
+
data.responses,
|
|
63
|
+
data.httpErrors,
|
|
64
|
+
data.omittedRequests,
|
|
65
|
+
data.issueCount,
|
|
66
|
+
data.dropped,
|
|
67
|
+
data.bytes,
|
|
68
|
+
];
|
|
69
|
+
if (!values.every((value) => typeof value === 'number' && Number.isSafeInteger(value) && value >= 0))
|
|
70
|
+
return false;
|
|
71
|
+
return (data.requests ===
|
|
72
|
+
data.finished +
|
|
73
|
+
(data.skippedRequests ?? 0) +
|
|
74
|
+
(data.failedRequests ?? 0) +
|
|
75
|
+
(data.securityBlockedRequests ?? 0) &&
|
|
76
|
+
data.failed ===
|
|
77
|
+
(data.skippedRequests ?? 0) +
|
|
78
|
+
(data.failedRequests ?? 0) +
|
|
79
|
+
(data.securityBlockedRequests ?? 0) &&
|
|
80
|
+
data.blocked === data.securityBlockedRequests &&
|
|
81
|
+
data.responses <= data.requests &&
|
|
82
|
+
data.httpErrors <= data.responses &&
|
|
83
|
+
data.omittedRequests <= data.requests &&
|
|
84
|
+
data.issueCount === data.issues.length + data.dropped);
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=coverage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"coverage.js","sourceRoot":"","sources":["../src/coverage.ts"],"names":[],"mappings":"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"}
|