PrivaLint prüft Websites im echten Browser
Datenschutz-Checks als CLI, GitHub Action und Node.js-API. PrivaLint 0.10.0 mit 33 Regeln, Prüfung von Impressum und Datenschutzerklärung, HTML-Bericht und Changelog aller Versionen.Ein Consent-Banner kann korrekt aussehen, während Analytics weiter Anfragen sendet. Deshalb habe ich PrivaLint gebaut. Als CLI und GitHub Action prüft das Tool Websites mit einem echten Chromium-Browser auf mögliche Datenschutzprobleme: was beim ersten Besuch lädt und welche Tracker nach einem Klick auf „Ablehnen“ weiterhin aktiv sind.
PrivaLint nutzt Playwright und steht unter MIT-Lizenz. Die aktuelle Version 0.10.0 ist auf npm veröffentlicht. Neben CLI und GitHub Action gibt es eine Node.js-API. Quellcode und Issue-Tracker liegen auf GitHub, den Einstieg beschreibt auch die deutsche README. Was sich seit der ersten Version geändert hat, steht im Changelog am Ende.
Ohne Installation kannst du deine Website im Datenschutz-Check prüfen. Der Webscanner läuft mit PrivaLint 0.10.0 und zeigt Befunde und Handlungsempfehlungen direkt im Browser. Die zusätzlichen Durchläufe für Banner-Einstellungen, Schließen, Zustimmen und Global Privacy Control sowie die HTTPS-Regel führt er nicht aus, dafür brauchst du die CLI. Warum es ihn gibt, steht im Beitrag zum Datenschutz-Check. Für größere Crawls, eigene Regeln und CI nutze die CLI.
Voraussetzung sind Node.js ab 20.12 und Chromium. Ersetze die Beispieladresse durch eine Website, die du betreibst. Der Scan prüft bis zu zehn Seiten, lädt drei davon gleichzeitig, schreibt auf Deutsch und nutzt das Länderprofil für Deutschland.
Ein Befund enthält Belege und einen Vorschlag zur Behebung. Die Schweregrade sind Voreinstellungen des Tools, keine rechtliche Bewertung jeder Drittanbieteranfrage. Die Regeldokumentation beschreibt die einzelnen Prüfungen. Zu jeder Regel und Seite hält der Bericht fest, ob sie ohne Befund geprüft wurde, etwas gefunden hat oder nicht prüfbar war, und warum. So bleibt „nichts gefunden“ getrennt von „nicht hingeschaut“.
Der Datenschutz-Check auf dieser Website führt 28 der Regeln aus, gegliedert nach den drei Stationen eines Testbesuchs: ankommen, ohne zu klicken; beim Banner Nein sagen; Pflichtangaben und Formulare. trackers-after-settings, consent-preselected, trackers-after-close, gpc-signal und https-transport laufen nur in der CLI. Jede Regel steht im Bericht mit einem von vier Zuständen: mit Befund, ohne Befund, nicht prüfbar (mit Grund, etwa weil kein unterstützter Banner erkannt wurde) oder nicht nötig. Nicht nötig heißt: Es gab keinen Banner, und vor einer Auswahl lud nichts, das eine Einwilligung braucht. Ohne Befund ist kein Bestehen, sondern nur: in dem, was geladen wurde, nichts erkannt.
Die beiden Shop-Regeln laufen nur auf der Startseite von Websites, die nach einem Shop aussehen: ein Link zu Warenkorb oder Kasse oder Skripte eines Shopsystems. Alle anderen Websites bekommen von ihnen keinen Befund. Wer eine der neuen Regeln nicht braucht, schaltet sie mit --disable oder unter "rules" in der Konfiguration ab.
Mit --lang de erscheinen Befunde, Handlungsempfehlungen und Berichte auf Deutsch. --lang en wählt Englisch, weiterhin die Voreinstellung. --region de|at|ch|hu|eu wählt das Profil für Deutschland, Österreich, die Schweiz, Ungarn oder die EU. Jeder Befund enthält die Rechtsverweise des gewählten Profils.
Das ungarische Profil verweist für Cookies auf Eht. 155. § (4) und für das Impressum auf Ekertv. 4. §. PrivaLint erkennt dafür ungarische Banner-Buttons wie „Elfogadom“, „Elutasítom“ und „Csak a szükségesek“, Einstellungslinks wie „Süti beállítások“ sowie „Impresszum“ und „Adatkezelési tájékoztató“. Seit 0.10.0 schreibt --lang hu die Berichte auch auf Ungarisch.
Im Schweizer Profil stuft PrivaLint Tracker vor der Einwilligung, Drittanbieter-Embeds und fehlende Impressumslinks jeweils eine Stufe milder ein. Tracker, die nach einem ausdrücklichen Ablehnen weiterlaufen, bleiben als Fehler eingestuft. Für eine Schweizer Website mit EU-Zielgruppe lässt sich zusätzlich ein Scan mit --region eu ausführen.
Das Länderprofil steuert Rechtsverweise und Einstufungen im Tool. Es ermittelt weder das anwendbare Recht einer Website noch den Standort des Browsers. --locale setzt separat die Browser-Locale.
Jede Seite startet in einem frischen Browserkontext ohne Cookies und gespeicherte Daten. PrivaLint wartet auf Netzwerkruhe und eine zusätzliche Verzögerung und erfasst Anfragen, Cookies, Links und Ressourcenhinweise. Auf der Startseite sucht es nach einem Consent-Banner, klickt auf Ablehnen, lädt die Seite erneut, zeichnet einen zweiten Zustand auf und prüft, ob sich die Einstellungen wieder öffnen lassen. Erscheint ein Banner erst spät, sucht PrivaLint eine weitere Ruhephase lang; consent.bannerWaitMs hält fest, wie lange.
Vor dem Klick auf Ablehnen drückt PrivaLint bis zu 60-mal die Tab-Taste und misst, ob Ablehnen so sichtbar ist wie Zustimmen. Impressum und Datenschutzerklärung, die die Startseite verlinkt, lädt es einmal zusätzlich und liest ihren Text, dazu eine Cookie-Richtlinie, die die Datenschutzerklärung verlinkt, auch als PDF auf derselben Website. Tracker, die über die eigene Domain geleitet werden, erkennt es an Pfad und Parametern, etwa GA4 unter /g/collect. Native Scans lösen außerdem die CNAME-Einträge von Subdomains auf, um dahinter versteckte Tracker zu finden. Hat die Website weitere Hosts wie blog. oder docs., bekommt die erste gecrawlte Seite von bis zu fünf davon dieselben Start-Seiten-Prüfungen; --host-checks main schaltet das ab.
Die Erkennung nutzt die Selektoren von 20 Consent-Managern, auch in iframes: Axeptio, Borlabs Cookie, Complianz, consentmanager, Cookie Information, Cookie-Script, CookieConsent, Cookiebot, CookieFirst, CookieHub, CookieYes, Didomi, iubenda, Klaro, OneTrust, Osano, Sourcepoint, Termly, TrustArc und Usercentrics. Andere Banner findet sie über Button-Beschriftungen in sieben Sprachen: Deutsch, Englisch, Französisch, Spanisch, Italienisch, Niederländisch und Ungarisch. Braucht eine Website gar keinen Banner, weil vor einer Auswahl nichts Einwilligungspflichtiges lädt, überspringt PrivaLint die Banner-Regeln mit dem Grund no-consent-needed.
Mit --check-settings geht PrivaLint einen Schritt weiter: Es lädt die Startseite in einem frischen Browser erneut, öffnet die Banner-Einstellungen, wählt alle optionalen Kategorien ab, speichert und lädt neu. Hat ein TCF-Banner einen Reiter „Berechtigtes Interesse“, widerspricht PrivaLint dort ebenfalls. Danach liest es über __tcfapi('getTCData') aus, welche Zwecke und Anbieter der Banner noch als aktiv meldet. Was danach noch an Trackern lädt, meldet die Regel trackers-after-settings. Ohne den Schalter wird sie übersprungen, ohne den Bericht als unvollständig zu markieren.
Tracker erkennt PrivaLint an Hosts, Cookies und den Namen von localStorage- und sessionStorage-Einträgen aus einer gepflegten Liste, darunter seit 0.9.0 Adobe Analytics, etracker, INFOnline, Piwik PRO, Heap, Pendo und Optimizely. Werte liest es nie. Cookies, die dort fehlen, gleicht es mit rund 1.000 Analytics- und Marketing-Cookies aus der Open Cookie Database ab, nur anhand des Cookie-Namens.
Der Crawler folgt internen Links in Breitensuche, entfernt Query-Strings und führt Duplikate nach Weiterleitungen zusammen. Seiten, die erst ein Query-Parameter auswählt, etwa /?page_id=2, crawlt er als eigene Seiten. Was als dieselbe Website gilt, bestimmt die Public Suffix List: blog.example.de und shop.example.de gehören zusammen, zwei Seiten unter github.io nicht. Mit --concurrency lädt er mehrere Seiten gleichzeitig. Der Bericht bleibt derselbe wie mit einer Seite, weil die Seiten weiterhin in Breitensuche-Reihenfolge übernommen werden. Fehlgeschlagene Seiten stehen im Bericht, ohne den restlichen Crawl abzubrechen.
- --format wählt Text, JSON, Markdown oder SARIF. Mehrere --output-Optionen schreiben mehrere Berichte; .json, .md und .sarif bestimmen das Format.
- --fail-on error|warning|never setzt die Fehlerschwelle. Exit-Code 0 bedeutet unterhalb der Schwelle, 1 steht für Befunde und 2 für ungültige Argumente oder einen fehlgeschlagenen Scan.
- --fail-on-incomplete beendet mit Exit-Code 3, wenn benötigte Beobachtungen unvollständig sind. Befunde (Exit 1) haben Vorrang.
- privalint.config.json oder --config legt Schweregrade pro Regel und eine Allowlist für Anbieter fest. Erlaubte Anbieter bleiben als Information mit Hinweis sichtbar; die Allowlist ersetzt keine Einwilligung.
- --disable nimmt kommagetrennte Regel-IDs entgegen. --skip-consent überspringt den Klick auf Ablehnen, --check-settings prüft zusätzlich die Einstellungsebene des Banners.
- --check-accept stimmt in einem frischen Browser zu und hält fest, was dann lädt, --check-close schließt den Banner mit seinem Schließen-Button, --gpc sendet Global Privacy Control. Jede Option lädt die Startseite einmal mehr.
- --format html schreibt einen eigenständigen HTML-Bericht für hell und dunkel. --baseline report.json markiert jeden Befund als neu oder bekannt, und --fail-on zählt dann nur neue.
- --device "iPhone 15" prüft den Banner im mobilen Layout, --sitemap wählt die Unterseiten aus der Sitemap, Seiten mit Formularen zuerst.
- --settle wartet standardmäßig 2000 ms, --timeout beträgt 30000 ms pro Seite und --locale ist de-DE.
- --headed öffnet ein sichtbares Fenster, --channel chrome oder --channel msedge nutzt einen installierten Browser. --sandbox startet Chromium mit Sandbox, empfohlen für fremde Websites.
Die Action schreibt einen Markdown-Bericht in die Job-Zusammenfassung und erzeugt SARIF. Ein Upload macht die Befunde in GitHub Code Scanning sichtbar, sofern verfügbar. Mit pr-comment: true und der Berechtigung pull-requests: write legt sie einen Kommentar im Pull Request an und aktualisiert ihn bei jedem Lauf. Forks und Tokens ohne Schreibrecht überspringen den Kommentar, ohne den Scan scheitern zu lassen.
Weitere Eingaben sind url, config, max-pages, concurrency, fail-on, fail-on-incomplete, disable, lang, region, check-settings, sarif-file und job-summary, seit 0.10.0 auch baseline, check-accept, check-close, gpc, device und sitemap. Seit 0.9.0 sind max-pages, concurrency, lang, region und fail-on-incomplete standardmäßig leer, damit die Werte aus privalint.config.json gelten. Die Action liefert exit-code und bei geschriebenem Bericht sarif-file zurück. Der Runner benötigt Node.js ab 20.12. Das Beispiel prüft eine feste URL; übergib die Preview-URL deines Hosters, um die Änderungen eines Pull Requests zu testen. Live-Seiten werden auf synthetische Pfade wie privalint/your-site.de/kontakt abgebildet, ein Alert hat deshalb keine Quelldatei zum Öffnen.
Die API nimmt dieselben Optionen wie die CLI. Eine Konfigurationsdatei lädt sie nie selbst; Schweregrade und Allowlist übergibst du ausdrücklich.
Wer PrivaLint hinter ein öffentliches Formular setzen will wie im Datenschutz-Check, findet im Repository eine Anleitung zum kontrollierten Scanner: Jede Anfrage läuft über einen eigenen Netzwerkadapter mit Limits für Anzahl, Größe, Weiterleitungen und Zeit.
- Getestet werden Ablehnen und, mit --check-settings und --check-close, das Abwählen in den Einstellungen und das Schließen des Banners. --check-accept hält fest, was nach dem Zustimmen lädt, bewertet es aber nicht als Befund.
- Impressum und Datenschutzerklärung prüft PrivaLint auf einzelne Angaben: Anschrift, E-Mail, Registereintrag, Kontakt des Verantwortlichen und genannte Dienste. Ob die Texte inhaltlich stimmen und vollständig sind, bewertet es nicht.
- Die Tracker-Erkennung beruht auf einer gepflegten Anbieterliste und der Open Cookie Database. Unbekannte Anbieter und serverseitiges Tracking können unentdeckt bleiben.
- Websites können Banner vor Headless-Browsern verbergen oder Blockierseiten ausliefern. Prüfe Ziel-URL und Seitenzahl und nutze bei Bedarf einen sichtbaren Browser.
- Consent-Verhalten hängt vom Standort ab. GitHub-gehostete Runner haben häufig US-Adressen. Für EU-Verhalten braucht es einen EU-Runner oder einen passenden Proxy.
- Ohne --sandbox läuft Chromium ohne Sandbox, wie in Playwrights Voreinstellung. Scans unbekannter Websites gehören in eine isolierte Umgebung ohne Secrets.
Mit 0.10.0 liest PrivaLint zum ersten Mal Impressum und Datenschutzerklärung selbst, und --baseline vergleicht zwei Scans. Als Nächstes sind weitere Länderprofile und Berichtssprachen geplant.
Teste eine eigene Website, melde einen Fehlalarm oder steuere eine Regel über das privalint-Repository bei. Der Contribution Guide erklärt den Entwicklungsablauf.
Alle Versionen mit den wichtigsten Änderungen. Die vollständige Liste steht in der CHANGELOG.md, jede Version auch unter Releases.
- 16 neue Regeln, darunter banner-keyboard, reject-less-prominent, fingerprinting, legal-notice-content, privacy-policy-vendors, privacy-policy-content, third-party-personal-data, form-preticked-consent und mixed-content. Sie laufen standardmäßig und können bestehenden Scans neue Befunde hinzufügen; mixed-content meldet ein Formular über HTTP als Fehler.
- Impressum und Datenschutzerklärung werden einmal zusätzlich geladen und gelesen, eine verlinkte Cookie-Richtlinie auch als PDF.
- Tracker, die über die eigene Domain geleitet werden, und in nativen Scans Tracker hinter CNAME-Einträgen werden erkannt.
- Opt-in-Prüfungen --check-accept, --check-close und --gpc; dazu --device, --sitemap und Start-Seiten-Prüfungen auf bis zu fünf weiteren Hosts der Website.
- HTML-Bericht mit --format html, Vergleich mit einem früheren Bericht über --baseline, Liste aller Drittanbieter-Hosts in thirdParties und eine Zusammenfassung pro Regel in ruleSummary.
- Anleitungen zum Beheben für Cookiebot, Usercentrics, OneTrust und Complianz; erkennt PrivaLint den Consent-Manager, verlinkt der Befund den passenden Abschnitt.
- Länderprofil Ungarn jetzt auch mit ungarischen Berichten (--lang hu).
- Berichtsschema 1.5.0; 1.0.0 bis 1.4.0 bleiben unterstützt.
- Sechs neue Regeln: consent-banner-missing, banner-legal-links, form-privacy-notice, https-transport, withdrawal-button und accessibility-statement. Sie können bestehenden Scans neue Befunde hinzufügen.
- --check-settings und trackers-after-reject lesen die IAB-TCF-Schnittstelle aus. Auf TCF-Bannern widerspricht --check-settings auch dem berechtigten Interesse.
- Späte Banner werden gefunden, Tracker auch an den Namen von localStorage- und sessionStorage-Einträgen erkannt.
- Neue Tracker: Adobe Analytics, Target, Audience Manager und Experience Platform Tags, etracker, INFOnline, Piwik PRO, Heap, Pendo, Optimizely, Chartbeat und Comscore. Das Facebook SDK zählt als eigener Social-Anbieter statt als Meta Pixel.
- Neue Embeds: SoundCloud, Mapbox, Trusted Shops, Pinterest und Google Calendar. Fehlerberichte an Bugsnag, Rollbar und Datadog machen eine Erfassung nicht mehr unvollständig.
- Tracker-, Embed- und Font-Regeln zählen auch Anfragen mit HTTP-Fehler: Ein Pixel, das mit 403 antwortet, hat IP-Adresse und Referrer trotzdem bekommen.
- Action: neue Eingaben config und check-settings; leere Voreinstellungen lassen privalint.config.json gelten.
- Behoben: Zugangsdaten in der Ziel-URL erscheinen in keinem Bericht mehr. „Alle bestätigen“ gilt nicht mehr als Speichern-Button, und Angebots-Buttons im Footer gelten nicht mehr als Cookie-Banner.
- Berichtsschema 1.4.0 mit Zeitmessung, TCF-Daten und Banner-Wartezeit; 1.0.0 bis 1.3.0 bleiben unterstützt.
- Übersprungene Fehlerberichte an Sentry-Ingest-Hosts machen eine Seite nicht mehr unvollständig. Die Seite verwendet deren Antwort nie, ihre URL bleibt erfasst.
- Die Abdeckungsprüfung richtet sich nach capture.complete. Ladediagnosen, die nichts verdecken, markieren die Seite nicht mehr als unvollständig.
- trackers-after-reject und trackers-after-settings verweisen auf Art. 6 Abs. 1 lit. a DSGVO statt auf Art. 7 Abs. 3 DSGVO. reject-option nennt Art. 7 Abs. 3 DSGVO nicht mehr.
- Banner von Usercentrics v3 werden an ihren Buttons erkannt.
- Intercom wird an allen dokumentierten Hosts erkannt, auch an regionalen.
- Das Opt-out-Cookie _cs_optout von Contentsquare gilt nicht mehr als Tracker.
- Länderprofil hu für Ungarn mit Eht. 155. § (4) und Ekertv. 4. §; ungarische Banner-Buttons, Einstellungslinks, Impressum und Datenschutzhinweis werden erkannt.
- Optionale Prüfung der Einstellungsebene mit --check-settings und neue Regel trackers-after-settings.
- Rund 1.000 Analytics- und Marketing-Cookies aus der Open Cookie Database ergänzen die Tracker-Liste.
- Chat- und Bewertungs-Widgets in third-party-embeds: Intercom, Tawk.to, Zendesk Web Widget, Trustpilot und Typeform.
- First-Party-Erkennung über die Public Suffix List, auch für github.io, netlify.app und vercel.app.
- Berichtsschema 1.3.0; 1.0.0 bis 1.2.0 bleiben unterstützt.
- Bricht eine Seite eine Anfrage selbst ab, etwa einen Prefetch bei langsamem Netz, gilt die Seite nicht mehr als unvollständig. Die Anfrage erscheint als übersprungen mit cancelled-by-page, ihre URL zählt weiter für die Tracker-Regeln.
- Echte Fehler wie Adapterfehler, Timeouts, Verbindungs-, TLS- und DNS-Probleme schränken das Ergebnis weiterhin ein.
- Berichtsschema 1.2.0 mit dem neuen Grund; 1.0.0 und 1.1.0 bleiben unterstützt.
- Action-Eingabe concurrency für parallele Crawls in CI.
- Axeptio, TrustArc, CookieHub, Cookie Information und Cookie-Script werden an ihren Buttons erkannt.
- Session-Replay-Dienste FullStory, Mouseflow, Smartlook, Lucky Orange und Contentsquare in der Tracker-Liste.
- Auf Websites ohne Banner, auf denen nichts Einwilligungspflichtiges lädt, werden die Banner-Regeln mit no-consent-needed übersprungen statt als nicht prüfbar gemeldet.
- Neue Regel adobe-fonts für Schriften von Adobe Fonts.
- Neue Regel consent-settings-link: Lässt sich die Einwilligung nach dem Ablehnen wieder aufrufen?
- Criteo, Taboola, Outbrain, Adform und Quantcast in der Tracker-Liste.
- --sandbox startet Chromium mit Sandbox, --concurrency lädt mehrere Seiten gleichzeitig, auf einer Website mit sechs Seiten halbierte das die Scanzeit.
- Behoben: Der Ablehnen-Button von Osano wird wieder gefunden.
- Ein Beacon, den der Netzwerkadapter bewusst nicht sendet, macht die Seite nicht mehr unvollständig. Seine URL wird weiter von den Tracker-Regeln geprüft.
- JSON-Konfiguration mit Schweregraden pro Regel und Anbieter-Allowlist.
- Optionaler Kommentar im Pull Request durch die Action.
- Abdeckungsprüfung mit Exit-Code 3 (--fail-on-incomplete) und JSON-Schemas für das Berichtsformat.
- hCaptcha, Turnstile, OpenStreetMap, TikTok und Calendly als Embeds; CookieFirst, Osano, Termly und Sourcepoint als Consent-Manager, auch in iframes.
- Kontrollierte Erfassung über einen Netzwerkadapter pro Anfrage, mit Limits für Anzahl, Bytes, Weiterleitungen und Zeit. Das ist die Grundlage für den Webscanner.
- Ladediagnosen und Abdeckungsstatus pro Seite und Regel.
- Fehlgeschlagene oder blockierte Anfragen gelten nicht mehr als Beleg für einen geladenen Anbieter.
- --lang de für deutsche Befunde und Berichte.
- --region de|at|ch|eu mit Rechtsverweisen pro Land und milderen Schweizer Einstufungen.
- Eingaben lang und region für die Action, dazu eine deutsche README.
- --headed für Websites, die Banner vor Headless-Browsern verstecken, und --channel chrome|msedge für installierte Browser.
- Erste Version: Scanner im echten Chromium, Ablehnen-Test auf der Startseite, acht Regeln, CLI mit Text, JSON, Markdown und SARIF sowie die GitHub Action.