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/README.de.md
CHANGED
|
@@ -22,8 +22,8 @@ npx privalint https://ihre-seite.de --lang de
|
|
|
22
22
|
|
|
23
23
|
## Deutschland, Österreich, Schweiz
|
|
24
24
|
|
|
25
|
-
Mit `--region` wählen Sie
|
|
26
|
-
|
|
25
|
+
Mit `--region` wählen Sie das Referenz- und Einstufungsprofil des Tools. Das Profil bestimmt
|
|
26
|
+
nicht, welches Recht für eine Website anwendbar ist. Befunde sind keine rechtliche Beurteilung.
|
|
27
27
|
|
|
28
28
|
| Region | Cookies und Tracking | Impressum | Besonderheit |
|
|
29
29
|
| --------------- | ----------------------------------------- | ----------------------------------------------- | ----------------------------------------------------- |
|
|
@@ -69,6 +69,72 @@ Details, Hintergründe und Lösungen stehen auf Englisch in [`docs/rules`](docs/
|
|
|
69
69
|
Der Bericht erscheint auf Deutsch in der Zusammenfassung des Workflow-Laufs. Alle Eingaben und
|
|
70
70
|
die Anbindung an GitHub Code Scanning beschreibt die [englische README](README.md#github-action).
|
|
71
71
|
|
|
72
|
+
Mit `pr-comment: true` wird ein vorhandener Bot-Kommentar aktualisiert oder einmalig erstellt.
|
|
73
|
+
Dafür benötigt der Job `pull-requests: write`. Bei Fork-PRs oder einem schreibgeschützten Token
|
|
74
|
+
wird der Kommentar ohne Scanfehler ausgelassen. Befunde mit einem fehlgeschlagenen Scan-Schritt
|
|
75
|
+
verhindern den Kommentar nicht.
|
|
76
|
+
|
|
77
|
+
## Konfiguration, Berichtsversion und Fortschritt
|
|
78
|
+
|
|
79
|
+
Die CLI liest eine vorhandene `privalint.config.json`, alternativ `--config <datei>`.
|
|
80
|
+
CLI-Argumente haben Vorrang. JSON kann URL, Seitenlimit, Sprache, Länderprofil, Ladeoptionen,
|
|
81
|
+
Regel-Schweregrade und eine Anbieter-Freigabeliste enthalten. API-Aufrufer setzen `ruleSettings`
|
|
82
|
+
und `allow` ausdrücklich; `scan()` liest keine Konfigurationsdateien automatisch.
|
|
83
|
+
|
|
84
|
+
```json
|
|
85
|
+
{
|
|
86
|
+
"url": "https://example.org",
|
|
87
|
+
"maxPages": 20,
|
|
88
|
+
"rules": { "third-party-cdns": "warning", "impressum-link": "off" },
|
|
89
|
+
"allow": { "vendors": ["Google Analytics"] }
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Freigegebene Anbieter bleiben als Hinweise mit Policy-Kennzeichnung sichtbar. Eine Freigabe ist
|
|
94
|
+
kein Nachweis von Einwilligung oder rechtlicher Zulässigkeit. Details stehen in der
|
|
95
|
+
[deutschen Anleitung](docs/configuration.de.md) und der [englischen Fassung](docs/configuration.md).
|
|
96
|
+
|
|
97
|
+
Neue Berichte enthalten `schemaVersion`, `scannerVersion` und die wirksamen `options`.
|
|
98
|
+
Ohne Schemaversion handelt es sich um einen alten Bericht. `ruleEvaluations[].outcome`
|
|
99
|
+
unterscheidet „Geprüft, kein Befund“, „Befund erkannt“ und „Nicht prüfbar“ mit Begründung.
|
|
100
|
+
`onProgress` liefert tatsächliche Phasen mit verstrichener Zeit und bereinigten Seiten-URLs,
|
|
101
|
+
keinen geschätzten Prozentwert. Die Exit-Code-Policy von CLI/Action bleibt erhalten.
|
|
102
|
+
|
|
103
|
+
Schema 1.1.0 ergänzt zufällige Scan-/Seiten-IDs und die Zuordnung von Erfassungsversuchen nach
|
|
104
|
+
Redirects. Eigenständige Schemas und Konformitätsbeispiele sind enthalten. Mit
|
|
105
|
+
`--fail-on-incomplete` bzw. Action-Input `fail-on-incomplete: true` wird unvollständige nötige
|
|
106
|
+
Beobachtung optional mit Exit 3 bewertet; Befunde mit Exit 1 haben Vorrang. Bewusst deaktivierte
|
|
107
|
+
Regeln und Consent-Prüfungen sind ausgenommen. Details stehen im
|
|
108
|
+
[Berichtsvertrag](docs/report-contract.de.md) und seiner [englischen Fassung](docs/report-contract.md).
|
|
109
|
+
|
|
110
|
+
## Öffentliche Webscanner und kontrollierte Erfassung
|
|
111
|
+
|
|
112
|
+
`scan()` unterstützt einen `networkAdapter` und ein `AbortSignal`. Jeder HTTP-Hop einschließlich
|
|
113
|
+
Redirects durchläuft den Adapter. Er gibt eine Antwort zurück oder lehnt mit einem typisierten
|
|
114
|
+
`NetworkError` ab. Eine Warteschlange puffert Lastspitzen; Parallelität, Anfragemenge,
|
|
115
|
+
Antwortgrößen und Zeitlimits sind getrennt. Im Adapter-Modus gibt es keinen Browser-HTTP-Fallback.
|
|
116
|
+
|
|
117
|
+
PrivaLint erstellt und schließt Browser sowie frische Contexts selbst, auch bei Abbruch. Der
|
|
118
|
+
Aufrufer verwaltet gemeinsam genutzte Transport-Clients. Die bestehende Egress-Policy muss bei
|
|
119
|
+
jedem Hop DNS-Auflösung und tatsächliche Socket-Ziele kontrollieren und das Request-Signal beachten.
|
|
120
|
+
Die [Anleitung](docs/controlled-scanner.de.md) beschreibt den Vertrag und die Zähler. Ein
|
|
121
|
+
[typgeprüftes Beispiel](examples/controlled-scanner.ts) nutzt ausschließlich vom Betreiber
|
|
122
|
+
freigegebene Origins und festgelegte IP-Adressen.
|
|
123
|
+
|
|
124
|
+
`findings` enthält ausschließlich Regelbefunde. Technische Einschränkungen stehen getrennt unter
|
|
125
|
+
`pages[].capture.diagnostics`. Bei 48 Anfragen mit 21 blockierten oder fehlgeschlagenen Ressourcen
|
|
126
|
+
bleibt die Erfassung unvollständig, auch wenn keine Findings vorliegen. `ruleEvaluations` zeigt
|
|
127
|
+
pro Seite und Regel `executed`, `skipped` oder `not-evaluable`; `partial: true` kennzeichnet die
|
|
128
|
+
Auswertung verfügbarer positiver Evidenz bei eingeschränkter Beobachtung.
|
|
129
|
+
|
|
130
|
+
Consent-Zustände unterscheiden „kein Banner erkannt“, „keine unterstützte Ablehnen-Möglichkeit“,
|
|
131
|
+
„Interaktion fehlgeschlagen“ und „Ablehnen getestet“. Fehlende Erkennung allein ist kein
|
|
132
|
+
Datenschutzverstoß. Ein getesteter Klick sagt noch nichts über vollständige Ressourcen-Erfassung aus.
|
|
133
|
+
|
|
134
|
+
Bestehende CLI-, API- und Action-Aufrufe bleiben gültig. Startseiten-Ladefehler werfen weiterhin
|
|
135
|
+
einen Fehler; `failureMode: 'report'` liefert stattdessen einen strukturierten Bericht und ist im
|
|
136
|
+
Adapter-Modus die Vorgabe. Es werden keine Zusagen rechtlicher Konformität gemacht.
|
|
137
|
+
|
|
72
138
|
## Grenzen
|
|
73
139
|
|
|
74
140
|
- Manche Seiten zeigen ihr Cookie-Banner Headless-Browsern nicht. Dann meldet privalint „kein
|
package/README.md
CHANGED
|
@@ -77,19 +77,26 @@ jobs:
|
|
|
77
77
|
The report shows up in the job summary. With the SARIF upload, findings also appear under
|
|
78
78
|
**Security → Code scanning**.
|
|
79
79
|
|
|
80
|
+
To create/update a PR comment, set `pr-comment: true` and grant the job `pull-requests: write`.
|
|
81
|
+
The action updates its own marked bot comment. Fork PRs and read-only tokens skip commenting
|
|
82
|
+
without failing the scan. Reports are posted even when findings make the scan step fail.
|
|
83
|
+
See [configuration and report APIs](docs/configuration.md) ([Deutsch](docs/configuration.de.md)).
|
|
84
|
+
|
|
80
85
|
Scanning a preview deployment instead of production catches problems before they ship. Pass the
|
|
81
86
|
preview URL from your hosting provider's deployment event or action output as `url`.
|
|
82
87
|
|
|
83
|
-
| Input
|
|
84
|
-
|
|
|
85
|
-
| `url`
|
|
86
|
-
| `max-pages`
|
|
87
|
-
| `fail-on`
|
|
88
|
-
| `
|
|
89
|
-
| `
|
|
90
|
-
| `
|
|
91
|
-
| `
|
|
92
|
-
| `
|
|
88
|
+
| Input | Default | |
|
|
89
|
+
| -------------------- | ----------------- | ----------------------------------------------------- |
|
|
90
|
+
| `url` | | Page to scan. Required. |
|
|
91
|
+
| `max-pages` | `1` | How many pages of the same site to crawl. |
|
|
92
|
+
| `fail-on` | `error` | `error`, `warning` or `never`. |
|
|
93
|
+
| `fail-on-incomplete` | `false` | Fail separately for incomplete required observations. |
|
|
94
|
+
| `disable` | | Comma-separated rule ids to skip. |
|
|
95
|
+
| `lang` | `en` | Language of the report: `en` or `de`. |
|
|
96
|
+
| `region` | `de` | Whose law to cite: `de`, `at`, `ch`, `eu`. |
|
|
97
|
+
| `sarif-file` | `privalint.sarif` | Where to write SARIF. Empty to skip. |
|
|
98
|
+
| `job-summary` | `true` | Add the Markdown report to the job summary. |
|
|
99
|
+
| `pr-comment` | `false` | Create/update a same-repository PR report comment. |
|
|
93
100
|
|
|
94
101
|
Outputs: `exit-code`, and `sarif-file` when a report was written.
|
|
95
102
|
|
|
@@ -113,6 +120,9 @@ privalint <url> [options]
|
|
|
113
120
|
Repeatable; .sarif, .json and .md pick their format,
|
|
114
121
|
other extensions use --format
|
|
115
122
|
--fail-on <level> error | warning | never (default error)
|
|
123
|
+
--fail-on-incomplete Exit 3 for incomplete required observations (opt-in)
|
|
124
|
+
--allow-incomplete Override failOnIncomplete from JSON config
|
|
125
|
+
--config <file> Select a JSON configuration file
|
|
116
126
|
--disable <rules> Comma-separated rule ids to skip
|
|
117
127
|
--settle <ms> Wait this long after load for late tags (default 2000)
|
|
118
128
|
--timeout <ms> Navigation timeout per page (default 30000)
|
|
@@ -126,6 +136,8 @@ privalint <url> [options]
|
|
|
126
136
|
```
|
|
127
137
|
|
|
128
138
|
Exit codes: `0` nothing at or above `--fail-on`, `1` findings, `2` bad arguments or a failed scan.
|
|
139
|
+
With `--fail-on-incomplete`, exit `3` means incomplete required observation. Findings exit `1`
|
|
140
|
+
takes precedence; intentional disabled-rule/consent exclusions are ignored by this gate.
|
|
129
141
|
|
|
130
142
|
The first run needs a browser. If Playwright hasn't installed one yet:
|
|
131
143
|
|
|
@@ -135,8 +147,8 @@ npx playwright install chromium
|
|
|
135
147
|
|
|
136
148
|
## Countries and languages
|
|
137
149
|
|
|
138
|
-
|
|
139
|
-
|
|
150
|
+
`--region` selects the references and severity profile used by the tool. It does not determine
|
|
151
|
+
which law applies to a website, and findings are not legal determinations:
|
|
140
152
|
|
|
141
153
|
| Region | Cookies and tracking | Imprint | Changes |
|
|
142
154
|
| -------------- | ------------------------------------ | ------------------------------------- | -------------------------------------------------------------------------------- |
|
|
@@ -157,6 +169,20 @@ npx privalint https://ihre-seite.ch --region ch --lang de
|
|
|
157
169
|
|
|
158
170
|
## Use it from code
|
|
159
171
|
|
|
172
|
+
CLI configuration is loaded from `privalint.config.json` when present, or `--config <file>`.
|
|
173
|
+
CLI flags override JSON settings. API callers pass `ruleSettings` and `allow` explicitly;
|
|
174
|
+
the API never loads a config file implicitly. Allowed vendors remain visible as info with a policy
|
|
175
|
+
annotation; allowlisting does not establish consent or legal approval.
|
|
176
|
+
|
|
177
|
+
```json
|
|
178
|
+
{
|
|
179
|
+
"url": "https://example.org",
|
|
180
|
+
"maxPages": 20,
|
|
181
|
+
"rules": { "third-party-cdns": "warning", "impressum-link": "off" },
|
|
182
|
+
"allow": { "vendors": ["Google Analytics"] }
|
|
183
|
+
}
|
|
184
|
+
```
|
|
185
|
+
|
|
160
186
|
```ts
|
|
161
187
|
import { scan } from 'privalint';
|
|
162
188
|
|
|
@@ -166,6 +192,44 @@ for (const finding of result.findings) {
|
|
|
166
192
|
}
|
|
167
193
|
```
|
|
168
194
|
|
|
195
|
+
New reports carry `schemaVersion`, `scannerVersion` and normalized `options`. An absent
|
|
196
|
+
schema version means a legacy report, not version 1. `ruleEvaluations[].outcome` explicitly
|
|
197
|
+
distinguishes `checked-no-finding`, `finding-detected` and `not-evaluable`, always with a fixed
|
|
198
|
+
`explanation` code. `onProgress` emits real stages (`starting`, `loading-page`, `checking-consent`,
|
|
199
|
+
`evaluating-rules`, `completed`) with elapsed time and redacted page URLs; it does not invent a
|
|
200
|
+
percentage. The CLI and Action keep their existing finding-threshold exit policy.
|
|
201
|
+
|
|
202
|
+
Schema 1.1.0 adds random scan/page IDs and explicit capture-to-page progress resolution, so redacted
|
|
203
|
+
pages remain distinguishable. Standalone schemas and complete/partial/skip/policy fixtures are
|
|
204
|
+
included. The [report contract and optional CI gate](docs/report-contract.md)
|
|
205
|
+
([Deutsch](docs/report-contract.de.md)) explain validation and identity/exit-code semantics.
|
|
206
|
+
|
|
207
|
+
### Public web scanners and controlled capture
|
|
208
|
+
|
|
209
|
+
`scan()` accepts a `networkAdapter` and `AbortSignal`. The adapter receives each HTTP hop,
|
|
210
|
+
including redirects, and returns a response or throws a typed `NetworkError`. Requests wait
|
|
211
|
+
in a FIFO queue; concurrency, request count, response bytes and timeouts are independent limits.
|
|
212
|
+
There is no browser HTTP fallback in adapter mode.
|
|
213
|
+
|
|
214
|
+
The scanner owns its browser and fresh contexts and closes them on completion or cancellation.
|
|
215
|
+
The caller owns the adapter's shared clients. Keep your existing egress policy: validate and pin
|
|
216
|
+
DNS/socket destinations for **every** hop, and honour the provided per-request abort signal.
|
|
217
|
+
|
|
218
|
+
See the [controlled scanner guide](docs/controlled-scanner.md) and the type-checked
|
|
219
|
+
[pinned-transport example](examples/controlled-scanner.ts). The example denies origins absent
|
|
220
|
+
from a trusted deployment allowlist and pins socket addresses; the allowlist must not come from
|
|
221
|
+
the person submitting the scan.
|
|
222
|
+
|
|
223
|
+
Reports separate `findings` from `pages[].capture.diagnostics`. A page with 48 requests and 21
|
|
224
|
+
blocked/failed requests is marked incomplete even if no findings were produced. Per-page
|
|
225
|
+
`ruleEvaluations` distinguish `executed`, `skipped` and `not-evaluable`; positive evidence may
|
|
226
|
+
still be evaluated with `partial: true`. No banner detected is not itself a privacy finding,
|
|
227
|
+
and does not mean that the reject path was tested.
|
|
228
|
+
|
|
229
|
+
Existing CLI, API and GitHub Action calls still work. Start-page navigation failures retain the
|
|
230
|
+
legacy throw behaviour unless `failureMode: 'report'` is selected (the default with an adapter).
|
|
231
|
+
No report claims legal compliance.
|
|
232
|
+
|
|
169
233
|
## How it works
|
|
170
234
|
|
|
171
235
|
1. Every page is opened in a **fresh browser context**: no cookies and no storage, the same as a
|
|
@@ -206,7 +270,7 @@ for (const finding of result.findings) {
|
|
|
206
270
|
## Roadmap
|
|
207
271
|
|
|
208
272
|
- PR comments from the Action
|
|
209
|
-
- Config
|
|
273
|
+
- Config migration helpers and report comparison (JSON config is available now)
|
|
210
274
|
- Check the settings layer of banners and the "accept" path
|
|
211
275
|
- More trackers, embeds and consent managers (see the
|
|
212
276
|
[`good first issue`](https://github.com/jexcellence/privalint/labels/good%20first%20issue) label)
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Lang } from './i18n.js';
|
|
2
|
+
import type { Finding, PageSnapshot, Rule, RuleEvaluation } from './types.js';
|
|
3
|
+
export declare function assessPage(page: PageSnapshot, rules: readonly Rule[], siteUrl: URL, lang: Lang, isStart: boolean, consentEnabled: boolean, catalog?: readonly Rule[]): {
|
|
4
|
+
findings: Finding[];
|
|
5
|
+
evaluations: RuleEvaluation[];
|
|
6
|
+
};
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { observedRequests } from './observations.js';
|
|
2
|
+
import { defaultRules } from './rules/index.js';
|
|
3
|
+
import { isFirstParty } from './url.js';
|
|
4
|
+
const REQUIREMENTS = {
|
|
5
|
+
'pre-consent-trackers': ['network'],
|
|
6
|
+
'google-fonts': ['network'],
|
|
7
|
+
'third-party-embeds': ['network'],
|
|
8
|
+
'third-party-cdns': ['network'],
|
|
9
|
+
'impressum-link': ['document'],
|
|
10
|
+
'privacy-policy-link': ['document'],
|
|
11
|
+
'reject-option': ['consent'],
|
|
12
|
+
'trackers-after-reject': ['after-reject'],
|
|
13
|
+
};
|
|
14
|
+
export function assessPage(page, rules, siteUrl, lang, isStart, consentEnabled, catalog = rules) {
|
|
15
|
+
const findings = [];
|
|
16
|
+
const allRules = new Map([...defaultRules, ...catalog].map((rule) => [rule.id, rule]));
|
|
17
|
+
const selected = new Set(rules.map((rule) => rule.id));
|
|
18
|
+
const complete = page.capture?.beforeConsentComplete ?? page.capture?.complete ?? page.error === undefined;
|
|
19
|
+
const validPage = (page.error === undefined || page.capture?.document === true) &&
|
|
20
|
+
page.status !== null &&
|
|
21
|
+
page.status < 400 &&
|
|
22
|
+
isFirstParty(page.finalUrl, siteUrl);
|
|
23
|
+
const available = {
|
|
24
|
+
document: validPage && complete && (page.capture?.document ?? true),
|
|
25
|
+
network: validPage,
|
|
26
|
+
consent: validPage && page.consent?.bannerFound === true && page.consent.acceptLabel !== undefined,
|
|
27
|
+
'after-reject': validPage && page.consent?.rejected === true,
|
|
28
|
+
};
|
|
29
|
+
const input = {
|
|
30
|
+
...page,
|
|
31
|
+
requests: observedRequests(page.requests),
|
|
32
|
+
// A preconnect hint is not evidence of an actual connection in an offline adapter context.
|
|
33
|
+
resourceHints: complete ? page.resourceHints : [],
|
|
34
|
+
...(page.consent?.afterReject === undefined
|
|
35
|
+
? {}
|
|
36
|
+
: {
|
|
37
|
+
consent: {
|
|
38
|
+
...page.consent,
|
|
39
|
+
afterReject: {
|
|
40
|
+
...page.consent.afterReject,
|
|
41
|
+
requests: observedRequests(page.consent.afterReject.requests),
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
}),
|
|
45
|
+
};
|
|
46
|
+
const evaluations = [...allRules.values()].map((rule) => {
|
|
47
|
+
const requirements = rule.requires ?? REQUIREMENTS[rule.id] ?? ['document'];
|
|
48
|
+
const consentRule = requirements.some((kind) => kind === 'consent' || kind === 'after-reject');
|
|
49
|
+
if (!selected.has(rule.id)) {
|
|
50
|
+
return {
|
|
51
|
+
ruleId: rule.id,
|
|
52
|
+
status: 'skipped',
|
|
53
|
+
reason: 'disabled',
|
|
54
|
+
partial: false,
|
|
55
|
+
outcome: 'not-evaluable',
|
|
56
|
+
explanation: 'disabled',
|
|
57
|
+
findingCount: 0,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
if (consentRule && (!isStart || !consentEnabled)) {
|
|
61
|
+
return {
|
|
62
|
+
ruleId: rule.id,
|
|
63
|
+
status: 'skipped',
|
|
64
|
+
reason: !isStart ? 'not-start-page' : 'consent-disabled',
|
|
65
|
+
partial: false,
|
|
66
|
+
outcome: 'not-evaluable',
|
|
67
|
+
explanation: !isStart ? 'not-start-page' : 'consent-disabled',
|
|
68
|
+
findingCount: 0,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
if (!requirements.every((kind) => available[kind])) {
|
|
72
|
+
return {
|
|
73
|
+
ruleId: rule.id,
|
|
74
|
+
status: 'not-evaluable',
|
|
75
|
+
reason: 'missing-observation',
|
|
76
|
+
partial: true,
|
|
77
|
+
outcome: 'not-evaluable',
|
|
78
|
+
explanation: requirements.includes('after-reject')
|
|
79
|
+
? (page.consent?.reason ?? 'missing-reject-observation')
|
|
80
|
+
: requirements.includes('consent')
|
|
81
|
+
? (page.consent?.reason ?? 'missing-consent-observation')
|
|
82
|
+
: 'incomplete-document',
|
|
83
|
+
findingCount: 0,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
const detected = rule.check({ page: input, siteUrl, lang });
|
|
87
|
+
findings.push(...detected.map((finding) => ({
|
|
88
|
+
...finding,
|
|
89
|
+
...(page.pageId === undefined ? {} : { pageId: page.pageId }),
|
|
90
|
+
})));
|
|
91
|
+
const partial = requirements.includes('after-reject')
|
|
92
|
+
? page.consent?.afterRejectComplete === false
|
|
93
|
+
: !complete;
|
|
94
|
+
return {
|
|
95
|
+
ruleId: rule.id,
|
|
96
|
+
status: 'executed',
|
|
97
|
+
partial,
|
|
98
|
+
outcome: detected.length > 0 ? 'finding-detected' : partial ? 'not-evaluable' : 'checked-no-finding',
|
|
99
|
+
explanation: detected.length > 0
|
|
100
|
+
? 'positive-evidence'
|
|
101
|
+
: partial
|
|
102
|
+
? 'incomplete-observation'
|
|
103
|
+
: 'required-observations-available',
|
|
104
|
+
findingCount: detected.length,
|
|
105
|
+
};
|
|
106
|
+
});
|
|
107
|
+
return {
|
|
108
|
+
findings,
|
|
109
|
+
evaluations: evaluations.map((evaluation) => ({
|
|
110
|
+
...evaluation,
|
|
111
|
+
...(page.pageId === undefined ? {} : { pageId: page.pageId }),
|
|
112
|
+
})),
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
//# sourceMappingURL=assessment.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"assessment.js","sourceRoot":"","sources":["../src/assessment.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { Browser } from 'playwright';
|
|
2
|
+
import { type ScanOptions } from './scan-options.js';
|
|
3
|
+
import type { PageSnapshot } from './types.js';
|
|
4
|
+
/** Always owns and closes its context. Each crawl page starts without cookies/storage. */
|
|
5
|
+
export declare function capturePage(browser: Browser, url: string, siteUrl: URL, options: ScanOptions, checkConsent: boolean): Promise<PageSnapshot>;
|
package/dist/capture.js
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { observeConsent } from './consent.js';
|
|
2
|
+
import { classifyLoadError, Diagnostics, diagnosticUrl } from './diagnostics.js';
|
|
3
|
+
import { ControlledNetwork, networkLimits } from './network.js';
|
|
4
|
+
import { DEFAULTS } from './scan-options.js';
|
|
5
|
+
import { isFirstParty } from './url.js';
|
|
6
|
+
/** Always owns and closes its context. Each crawl page starts without cookies/storage. */
|
|
7
|
+
export async function capturePage(browser, url, siteUrl, options, checkConsent) {
|
|
8
|
+
const controlled = options.networkAdapter !== undefined;
|
|
9
|
+
const context = await browser.newContext({
|
|
10
|
+
locale: options.locale ?? DEFAULTS.locale,
|
|
11
|
+
serviceWorkers: controlled ? 'block' : 'allow',
|
|
12
|
+
// fulfill() works offline. Unhandled browser HTTP traffic has no network fallback.
|
|
13
|
+
offline: controlled,
|
|
14
|
+
});
|
|
15
|
+
const controller = new AbortController();
|
|
16
|
+
const captureMs = options.captureTimeoutMs ?? (controlled ? 60_000 : undefined);
|
|
17
|
+
const deadline = captureMs === undefined ? undefined : AbortSignal.timeout(captureMs);
|
|
18
|
+
const signal = AbortSignal.any([
|
|
19
|
+
controller.signal,
|
|
20
|
+
...(options.signal ? [options.signal] : []),
|
|
21
|
+
...(deadline ? [deadline] : []),
|
|
22
|
+
]);
|
|
23
|
+
const diagnostics = new Diagnostics(options.maxDiagnostics ?? 100, controlled ? networkLimits(options.networkLimits).maxRequests : 5_000);
|
|
24
|
+
const requests = [];
|
|
25
|
+
diagnostics.attach(context, requests);
|
|
26
|
+
const network = options.networkAdapter
|
|
27
|
+
? new ControlledNetwork(options.networkAdapter, networkLimits(options.networkLimits), diagnostics, signal)
|
|
28
|
+
: undefined;
|
|
29
|
+
let closing;
|
|
30
|
+
const closeOnAbort = () => {
|
|
31
|
+
closing ??= context.close().catch(() => { });
|
|
32
|
+
};
|
|
33
|
+
signal.addEventListener('abort', closeOnAbort, { once: true });
|
|
34
|
+
const snapshot = {
|
|
35
|
+
url,
|
|
36
|
+
finalUrl: url,
|
|
37
|
+
status: null,
|
|
38
|
+
requests: [],
|
|
39
|
+
cookies: [],
|
|
40
|
+
links: [],
|
|
41
|
+
resourceHints: [],
|
|
42
|
+
};
|
|
43
|
+
let document = false;
|
|
44
|
+
let beforeComplete = false;
|
|
45
|
+
try {
|
|
46
|
+
if (signal.aborted)
|
|
47
|
+
closeOnAbort();
|
|
48
|
+
await network?.attach(context);
|
|
49
|
+
if (options.offline) {
|
|
50
|
+
await context.route('**/*', (route) => isFirstParty(route.request().url(), siteUrl)
|
|
51
|
+
? route.continue()
|
|
52
|
+
: route.fulfill({ status: 200, body: '' }));
|
|
53
|
+
}
|
|
54
|
+
const page = await context.newPage();
|
|
55
|
+
await network?.attachPage(page);
|
|
56
|
+
const response = await page.goto(url, {
|
|
57
|
+
waitUntil: 'load',
|
|
58
|
+
timeout: options.timeoutMs ?? DEFAULTS.timeoutMs,
|
|
59
|
+
});
|
|
60
|
+
snapshot.finalUrl = page.url();
|
|
61
|
+
snapshot.status = response?.status() ?? null;
|
|
62
|
+
await page
|
|
63
|
+
.waitForLoadState('networkidle', {
|
|
64
|
+
timeout: options.timeoutMs ?? DEFAULTS.timeoutMs,
|
|
65
|
+
})
|
|
66
|
+
.catch(() => diagnostics.add('timeout', page.url()));
|
|
67
|
+
await page.waitForTimeout(options.settleMs ?? DEFAULTS.settleMs);
|
|
68
|
+
snapshot.links = await page.$$eval('a[href]', (anchors) => anchors.map((a) => ({
|
|
69
|
+
href: a.href,
|
|
70
|
+
text: [a.textContent, a.getAttribute('aria-label'), a.getAttribute('title')]
|
|
71
|
+
.filter(Boolean)
|
|
72
|
+
.join(' ')
|
|
73
|
+
.replace(/\s+/g, ' ')
|
|
74
|
+
.trim()
|
|
75
|
+
.slice(0, 200),
|
|
76
|
+
})));
|
|
77
|
+
snapshot.resourceHints = await page.$$eval('link[rel][href]', (elements) => elements.map((link) => ({
|
|
78
|
+
rel: (link.getAttribute('rel') ?? '').toLowerCase(),
|
|
79
|
+
href: link.href,
|
|
80
|
+
})));
|
|
81
|
+
// Offline adapter contexts cannot prove a connection from a preconnect hint alone.
|
|
82
|
+
if (controlled)
|
|
83
|
+
snapshot.resourceHints = [];
|
|
84
|
+
snapshot.cookies = (await context.cookies()).map(({ name, domain, path, expires }) => ({
|
|
85
|
+
name,
|
|
86
|
+
domain,
|
|
87
|
+
path,
|
|
88
|
+
expires,
|
|
89
|
+
}));
|
|
90
|
+
snapshot.requests = [...requests];
|
|
91
|
+
snapshot.requestCount = diagnostics.data.requests;
|
|
92
|
+
document = true;
|
|
93
|
+
beforeComplete = diagnostics.data.issueCount === 0 && snapshot.error === undefined;
|
|
94
|
+
if (checkConsent && options.consentCheck !== false) {
|
|
95
|
+
options.onProgress?.({
|
|
96
|
+
stage: 'checking-consent',
|
|
97
|
+
elapsedMs: 0,
|
|
98
|
+
pageUrl: diagnosticUrl(url),
|
|
99
|
+
});
|
|
100
|
+
snapshot.consent = await observeConsent(page, context, requests, {
|
|
101
|
+
settleMs: options.settleMs ?? DEFAULTS.settleMs,
|
|
102
|
+
timeoutMs: options.timeoutMs ?? DEFAULTS.timeoutMs,
|
|
103
|
+
signal,
|
|
104
|
+
onReject: () => {
|
|
105
|
+
diagnostics.phase = 'after-reject';
|
|
106
|
+
},
|
|
107
|
+
onTimeout: () => diagnostics.add('timeout', page.url()),
|
|
108
|
+
});
|
|
109
|
+
if (signal.aborted)
|
|
110
|
+
throw new DOMException('Capture ended', 'AbortError');
|
|
111
|
+
if (snapshot.consent.error && !diagnostics.hasIssues('after-reject')) {
|
|
112
|
+
diagnostics.add(classifyLoadError(snapshot.consent.error), page.url());
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
catch (error) {
|
|
117
|
+
if (snapshot.requests.length === 0)
|
|
118
|
+
snapshot.requests = [...requests];
|
|
119
|
+
snapshot.requestCount ??= diagnostics.data.requests;
|
|
120
|
+
const kind = signal.aborted
|
|
121
|
+
? options.signal?.aborted
|
|
122
|
+
? 'aborted'
|
|
123
|
+
: 'timeout'
|
|
124
|
+
: classifyLoadError(error);
|
|
125
|
+
snapshot.error = kind;
|
|
126
|
+
if (diagnostics.data.issueCount === 0)
|
|
127
|
+
diagnostics.add(kind, url);
|
|
128
|
+
if (checkConsent &&
|
|
129
|
+
options.consentCheck !== false &&
|
|
130
|
+
(snapshot.consent === undefined || signal.aborted)) {
|
|
131
|
+
snapshot.consent = {
|
|
132
|
+
...snapshot.consent,
|
|
133
|
+
bannerFound: snapshot.consent?.bannerFound ?? false,
|
|
134
|
+
rejected: false,
|
|
135
|
+
state: 'interaction-failed',
|
|
136
|
+
outcome: 'interaction-failed',
|
|
137
|
+
reason: 'browser-interaction-failed',
|
|
138
|
+
error: kind,
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
finally {
|
|
143
|
+
diagnostics.stop();
|
|
144
|
+
controller.abort();
|
|
145
|
+
signal.removeEventListener('abort', closeOnAbort);
|
|
146
|
+
await Promise.all([closing ?? context.close(), network?.close()]);
|
|
147
|
+
}
|
|
148
|
+
if (options.signal?.aborted)
|
|
149
|
+
throw new DOMException('Scan aborted', 'AbortError');
|
|
150
|
+
beforeComplete = beforeComplete && !diagnostics.hasIssues('before-consent');
|
|
151
|
+
if (snapshot.consent?.rejected) {
|
|
152
|
+
snapshot.consent.afterRejectComplete = !diagnostics.hasIssues('after-reject');
|
|
153
|
+
}
|
|
154
|
+
snapshot.capture = {
|
|
155
|
+
complete: beforeComplete && diagnostics.data.issueCount === 0,
|
|
156
|
+
beforeConsentComplete: beforeComplete,
|
|
157
|
+
document,
|
|
158
|
+
diagnostics: diagnostics.data,
|
|
159
|
+
};
|
|
160
|
+
return snapshot;
|
|
161
|
+
}
|
|
162
|
+
//# sourceMappingURL=capture.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capture.js","sourceRoot":"","sources":["../src/capture.ts"],"names":[],"mappings":"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"}
|