cuonztech-privacy-chaos-gate 0.2.0 → 0.2.1

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/LICENSE ADDED
@@ -0,0 +1,97 @@
1
+ CuonzTech Privacy Chaos Gate (CPCG) — Lizenz / License
2
+
3
+ Copyright (c) 2026 CuonzTech · Carlo Cuonz. Alle Rechte vorbehalten. / All rights reserved.
4
+
5
+ Source-available, proprietär. Der Quelltext ist einsehbar, aber nicht frei im Sinne einer
6
+ Open-Source-Lizenz. Gilt ab Version 0.2.1.
7
+ Fassung vom 2026-10-23 (Revision 2). / Revision of 2026-10-23 (revision 2).
8
+
9
+ HINWEIS ZU FRÜHEREN VERSIONEN
10
+ Die bereits auf npm veröffentlichten Versionen 0.1.0 und 0.2.0 wurden unter der MIT-Lizenz
11
+ veröffentlicht. Für Empfänger dieser Versionen bleiben sie unter MIT. Diese Lizenz gilt für
12
+ Version 0.2.1 und alle späteren Versionen.
13
+
14
+
15
+ ================================================================================
16
+ DEUTSCH
17
+ ================================================================================
18
+
19
+ 1. Was ohne Lizenzschlüssel erlaubt ist
20
+ Sie dürfen die Software herunterladen, installieren und ausführen, um Websites zu
21
+ scannen und das Konsolen-Gate (Ergebnis in der Konsole, Exit-Code, JSON-Report) in der
22
+ CI zu verwenden. Das gilt auch für kommerzielle und interne Zwecke. Ziele auf dem
23
+ eigenen Rechner (localhost, Loopback) sind immer frei. Frei ist ebenso der lokale
24
+ Auto-Fix: Patches erzeugen und im eigenen Arbeitsverzeichnis anwenden, ohne Commit und
25
+ ohne Pull Request.
26
+
27
+ 2. Was eine gültige Lizenz von CuonzTech erfordert
28
+ a) Erzeugen eines PDF-Reports sowie eines Kundenreports (HTML/Markdown mit --client),
29
+ b) Auto-Fix als Commit oder Pull Request (Code-Patches in ein Repository schreiben,
30
+ nicht das lokale Anwenden im Arbeitsverzeichnis).
31
+ Eine Lizenz erhalten Sie über cuonztech.ch (Pilot, Pro, Agency). Der Umfang der Tarife
32
+ steht auf der Produktseite und in der Lizenzmail. Die Zahl der Domains je Tarif beruht
33
+ auf Vertrauen; die Software zählt sie nicht.
34
+
35
+ 3. Was nicht erlaubt ist
36
+ - Die Software oder Teile davon zu kopieren, zu verkaufen, weiterzugeben,
37
+ unterzulizenzieren oder als eigenes Produkt oder als Dienstleistung anzubieten,
38
+ soweit dies über Ziffer 1 hinausgeht.
39
+ - Lizenzprüfungen, Lizenzschlüssel, Signaturprüfungen oder Hinweise auf diese Lizenz zu
40
+ entfernen, zu umgehen, zu deaktivieren oder zu fälschen.
41
+ - Lizenzschlüssel Dritten zugänglich zu machen oder zu veröffentlichen.
42
+ - Copyright- und Lizenzhinweise zu entfernen.
43
+ Die Einsicht in den Quelltext begründet kein Recht, ihn zu verändern oder
44
+ weiterzuverbreiten.
45
+
46
+ 4. Keine Gewährleistung, keine Rechtsberatung
47
+ Die Software wird «wie besehen» geliefert, ohne Zusicherung der Eignung für einen
48
+ bestimmten Zweck, der Vollständigkeit der Befunde oder der Konformität einer Website mit
49
+ dem revDSG, der DSGVO oder anderem Recht. Sie ist ein technisches Hilfsmittel und
50
+ ersetzt keine Rechtsberatung. Die Haftung ist im gesetzlich zulässigen Rahmen
51
+ ausgeschlossen, insbesondere für indirekte Schäden und entgangenen Gewinn.
52
+
53
+ 5. Drittkomponenten
54
+ Die Software verwendet Open-Source-Bibliotheken (zum Beispiel Playwright, Commander, Zod)
55
+ unter deren jeweiligen Lizenzen. Diese Lizenz ändert sie nicht.
56
+
57
+ 6. Beendigung
58
+ Verstösse gegen Ziffer 3 beenden die Berechtigung nach Ziffer 1 und 2 ohne weitere
59
+ Mitteilung.
60
+
61
+ 7. Anwendbares Recht
62
+ Schweizer Recht. Gerichtsstand ist der Sitz von CuonzTech, soweit zwingendes Recht nichts
63
+ anderes vorsieht.
64
+
65
+ 8. Kontakt
66
+ support@cuonztech.ch · https://cuonztech.ch
67
+
68
+
69
+ ================================================================================
70
+ ENGLISH (short version; the German text above governs in case of doubt)
71
+ ================================================================================
72
+
73
+ Copyright (c) 2026 CuonzTech · Carlo Cuonz. All rights reserved. Source-available,
74
+ proprietary, from version 0.2.1.
75
+
76
+ Versions 0.1.0 and 0.2.0 were published on npm under the MIT License and remain under MIT
77
+ for their recipients. This license applies to version 0.2.1 and later.
78
+
79
+ Without a license key you may install and run the software to scan websites and use the
80
+ console gate (console output, exit code, JSON report) in CI, including for commercial and
81
+ internal use. Local targets (localhost, loopback) are always free. Local auto-fix (creating
82
+ and applying patches in your own working directory, without a commit or pull request) is free
83
+ as well.
84
+
85
+ A valid license from CuonzTech is required for: the PDF report and customer reports (HTML,
86
+ Markdown with --client) and auto-fix as a commit or pull request (writing patches into a repository).
87
+ The number of domains per plan is based on trust and is not counted by the software.
88
+
89
+ You may not copy, sell, redistribute or sublicense the software, offer it as your own
90
+ product or service, or remove, bypass, disable or forge license checks, license keys or
91
+ license notices. Viewing the source code grants no right to modify or redistribute it.
92
+
93
+ The software is provided "as is", without warranty of any kind. It is a technical aid, not
94
+ legal advice, and does not guarantee compliance with the Swiss revFADP, the GDPR or any
95
+ other law. Liability is excluded to the extent permitted by law.
96
+
97
+ Swiss law applies. Contact: support@cuonztech.ch
package/README.md CHANGED
@@ -9,22 +9,55 @@ läuft erlaubtes Tracking nach dem Akzeptieren korrekt an?
9
9
 
10
10
  ## Installieren
11
11
 
12
+ Voraussetzung: Node.js 20 oder neuer.
13
+
12
14
  ```bash
13
- pnpm add -D @cuonztech/privacy-chaos-gate
14
- # oder direkt
15
- npx @cuonztech/privacy-chaos-gate scan --url https://staging.example.com --license CPCG-…
15
+ # 1. CLI installieren (Befehl: cpcg)
16
+ npm install -g cuonztech-privacy-chaos-gate # oder im Projekt: npm install -D cuonztech-privacy-chaos-gate
17
+
18
+ # 2. Prüf-Browser installieren (einmalig pro Rechner)
19
+ npx playwright install chromium
20
+
21
+ # 3. Ersten Scan starten (Scan und Konsolen-Gate brauchen keine Lizenz)
22
+ cpcg --url https://staging.example.com
16
23
  ```
17
24
 
18
- ## Lizenz
25
+ Ohne Installation: `npx cuonztech-privacy-chaos-gate --url https://staging.example.com`
26
+ (der Browser aus Schritt 2 muss trotzdem vorhanden sein; auf Linux-CI zusätzlich mit Systembibliotheken:
27
+ `npx playwright install --with-deps chromium`). Fehlt der Browser, meldet das CLI das und beendet sich mit Exit 3.
28
+
29
+ Das CLI hat keine Subcommands: alle Angaben sind Optionen von `cpcg`.
19
30
 
31
+ ## Lizenz und Preise
32
+
33
+ Das Paket ist proprietär und source-available (Datei `LICENSE`, ab 0.2.1; die Versionen 0.1.0 und 0.2.0 wurden unter MIT veröffentlicht und bleiben es).
20
34
  Online-Kauf → Code per Mail.
21
35
 
22
36
  ```bash
23
- export CPCG_LICENSE="CPCG-…"
24
- cpcg scan --url https://staging.example.com --jurisdiction CH
37
+ export CPCG_LICENSE="CPCG-…" # Lizenzcode (Pilot/Einmal) oder Kunden-Key CPCG-K-… (Abo)
38
+ cpcg --url https://staging.example.com --jurisdiction CH
25
39
  ```
26
40
 
27
- Ohne Lizenz: localhost/Test-Fixtures; Produktiv-URLs mit Warnung (Trial-Modus).
41
+ In CI die Lizenz als Secret setzen und per Umgebungsvariable `CPCG_LICENSE` übergeben, nicht als Argument.
42
+
43
+ **Frei, ohne Lizenz** (auch kommerziell und intern, auch gegen Produktiv-URLs): Scan, Konsolen-Gate (Exit-Code, JSON-Report, neutraler Evidence-Report ohne Kundenbezug), lokales `--auto-fix` ohne Commit/PR. Ziele auf `localhost`, `127.0.0.1` und `::1` sind immer frei.
44
+
45
+ **Nur mit gültiger Lizenz:**
46
+
47
+ | Funktion | Option |
48
+ |----------|--------|
49
+ | PDF-Report sowie Kundenreport (HTML/Markdown mit `--client`) | `--report-pdf`, `--client` |
50
+ | Auto-Fix als Commit oder Pull Request | `--git-commit`, `--create-pr` |
51
+
52
+ Ohne Lizenz werden diese Funktionen mit einem Hinweis übersprungen; der Exit-Code bleibt der des Gates. Exit 2 gibt es nur bei einer **ungültigen, abgelaufenen oder gesperrten** Lizenz, nicht bei fehlender Lizenz und nicht bei einem nicht erreichbaren Lizenzserver.
53
+
54
+ | Plan | Preis (netto, CHF) | Umfang |
55
+ |------|--------------------|--------|
56
+ | Pilot / Evidence | 890 einmalig | Kundenreport, Auto-Fix-PR, 1 Domain, 90 Tage |
57
+ | Pro | 69 je Domain und Monat | Kundenreport, Auto-Fix-PR, GitHub Action und CLI-Lizenz |
58
+ | Agency | 349 je Monat | Pro für bis zu 10 Domains |
59
+
60
+ Die Domain-Anzahl beruht auf Vertrauen: das CLI zählt Domains nicht. Scans werden nicht gezählt. Monatlich kündbar. Kauf und Fragen: `support@cuonztech.ch`.
28
61
 
29
62
  ## Was das Tool prüft
30
63
 
@@ -61,32 +94,104 @@ JustRelate, Didomi, Axeptio, Sourcepoint, Consentmanager, Borlabs, Klaro.
61
94
  Jeder Scan schreibt ein Evidence-Paket:
62
95
 
63
96
  ```
64
- report.json LeakReport (maschinenlesbar)
65
- report-evidence.md Kunden-Report (Markdown)
66
- report-evidence.html Kunden-Report (HTML, CuonzTech-Brand)
67
- evidence/ Screenshots (portabel, relative Pfade)
68
- report-deluxe.pdf Deluxe-PDF mit Deckblatt (mit --report-pdf)
97
+ reports/privacy-chaos-report.json LeakReport (maschinenlesbar), Pfad per --report-output
98
+ reports/privacy-chaos-report-evidence.md Evidence-Report (Markdown); mit Lizenz und --client als Kundenreport
99
+ reports/privacy-chaos-report-evidence.html Evidence-Report (HTML, CuonzTech-Brand); mit Lizenz und --client als Kundenreport
100
+ reports/privacy-chaos-report-deluxe.pdf Deluxe-PDF mit Deckblatt (mit --report-pdf, nur mit Lizenz)
101
+ reports/evidence/ Screenshots (portabel, relative Pfade)
69
102
  ```
70
103
 
71
- Der Report führt **Befunde** (Regelverstösse) und **beobachtete Requests**
104
+ Die Dateinamen richten sich nach dem JSON-Pfad: `<name>.json` ergibt `<name>-evidence.md`, `<name>-evidence.html`
105
+ und `<name>-deluxe.pdf` im selben Ordner, die Screenshots liegen in `evidence/` daneben. Standard ist
106
+ `reports/privacy-chaos-report.json`; mit `--report-output reports/scan.json` heissen die Dateien `reports/scan-evidence.md` usw.
107
+
108
+ Der Report führt **Befunde** (Regelverstösse), **Hinweise** und **beobachtete Requests**
72
109
  (Timelines) getrennt — und ordnet jedes Ergebnis mehreren Rechtsräumen zu
73
110
  (CH, EU, UK, US-CA, BR, ROW). Keine globale Compliance-Aussage.
74
111
 
112
+ **Hinweise sind kein Blocker.** Rein funktionale Drittanbieter (etwa Google Fonts) erscheinen
113
+ als Hinweis und blockieren das Gate nicht. Mit `--strict` blockieren auch Hinweise.
114
+ Bekannte, bewusst akzeptierte Befunde lassen sich mit `--ignore` ausnehmen; sie stehen im Report weiterhin
115
+ als «ignoriert».
116
+
75
117
  ## Flags
76
118
 
77
119
  ```bash
78
- cpcg scan \
79
- --url https://staging.example.com \
80
- --license CPCG-… \
81
- --report-pdf # Deluxe-PDF (Deckblatt + Evidence) erzeugen
82
- --client "Firma AG" # Kundenname fürs Deckblatt
83
- --demo # Report als synthetische Testdaten kennzeichnen
84
- --jurisdiction CH # CH | EU | UK | US-CA | BR | ROW
85
- --auto-fix true # Auto-Fix generieren
86
- --fail-on-leak true # Exit 1 bei Leaks (CI-Gate)
120
+ cpcg --url https://staging.example.com \
121
+ --report-pdf \
122
+ --client "Firma AG" \
123
+ --jurisdiction CH \
87
124
  --report-output reports/scan.json
88
125
  ```
89
126
 
127
+ | Option | Wirkung |
128
+ |--------|---------|
129
+ | `--url <url>` | Ziel (Pflicht) |
130
+ | `--license <code>` | Lizenzcode oder Kunden-Key; besser per Env `CPCG_LICENSE` |
131
+ | `--jurisdiction <code>` | `CH`, `EU`, `UK`, `US-CA`, `BR` oder `ROW` |
132
+ | `--report-output <pfad>` | JSON-Report (weitere Dateien daneben) |
133
+ | `--report-pdf [boolean]` | Deluxe-PDF (Deckblatt + Evidence) — Lizenz erforderlich |
134
+ | `--client <name>` | Kundenname für Kundenreport und Deckblatt — Lizenz erforderlich |
135
+ | `--demo [boolean]` | Report als synthetische Testdaten kennzeichnen |
136
+ | `--fail-on-leak [boolean]` | Exit 1 bei Leaks (CI-Gate, Standard `true`) |
137
+ | `--fail-on-uncheckable [boolean]` | Exit 4, wenn Kernszenarien nicht prüfbar sind (Standard `true`); bei `false` Exit 0, das Ergebnis bleibt als unvollständig markiert |
138
+ | `--strict [boolean]` | Auch Hinweise (z. B. Google Fonts) blockieren das Gate (Standard `false`) |
139
+ | `--ignore <liste>` | Befunde ausnehmen: Tracker-Name oder Host-Muster, kommagetrennt oder mehrfach (`fonts.googleapis.com,*.hotjar.com`); im Report als «ignoriert». `*`/`**` und nackte TLDs (`com`, `ch`) werden **immer** abgelehnt, auch mit `--allow-broad-ignore` (zum Abschalten der Blockierung dient `--fail-on-leak false`); ganze Zonen (`*.com`, `.ch`) nur mit `--allow-broad-ignore`. Die Konsole nennt, wie viele Befunde ausgenommen wurden; ein Szenario, dessen Befunde alle ausgenommen sind, steht als «bestanden (Befunde ignoriert)» |
140
+ | `--allow-broad-ignore [boolean]` | Ganze Zonen als `--ignore`-Muster (`*.com`, `*.co.uk`, `.ch`) bewusst zulassen; sie erscheinen in Konsole und Report als Warnung. `*` und nackte TLDs bleiben abgelehnt (Standard `false`) |
141
+ | `--accept-selector <css>` | Eigener Selektor des Akzeptieren-Buttons, wenn die Erkennung ihn nicht findet (mehrfach oder kommagetrennt) |
142
+ | `--reject-selector <css>` | Eigener Selektor des Ablehnen-Buttons |
143
+ | `--settings-selector <css>` | Eigener Selektor des Einstellungen-Buttons |
144
+ | `--banner-frame <teilstring>` | Teil der URL des iframes, in dem der Banner liegt (CMP im Frame); findet der Scan keinen solchen Frame, warnt er |
145
+ | `--banner-wait <sekunden>` | Wartefenster auf einen spät erscheinenden Banner, 1 bis 60 (Standard 8; Umgebung `CPCG_BANNER_WAIT`). Mit Angabe wird das Fenster ausgeschöpft, ebenso bei `--accept-selector`/`--reject-selector`; die Schlussprüfung wartet zusätzlich eine Nachfrist |
146
+ | `--max-subpages <n>` | Obergrenze der Unterseiten-Stichprobe (Szenario `subpage-spa`), 1 bis 40 (Standard 12; Umgebung `CPCG_MAX_SUBPAGES`); das Zeitbudget wächst moderat mit (45 s bis 12, 90 s bis 24, sonst 120 s) |
147
+ | `--auto-fix [boolean]` | Patches erzeugen und im Quellverzeichnis anwenden (Standard `true` nur mit ausdrücklichem `--source-dir`) |
148
+ | `--source-dir <dir>` | Quellcode für Auto-Fix; ein nicht vorhandenes Verzeichnis ist ein Fehler (Exit 3). Für `--git-commit`/`--create-pr` gilt das Git-Repository, in dem es liegt (nicht das Arbeitsverzeichnis) |
149
+ | `--git-commit [boolean]` | Fix in den Git-Branch committen — Lizenz erforderlich |
150
+ | `--create-pr [boolean]` | Fix als Pull Request öffnen (Token in Env `GITHUB_TOKEN`, ersatzweise `--github-token`) — Lizenz erforderlich; hat Vorrang vor `--git-commit` |
151
+ | `--base-branch <branch>`, `--branch-prefix <prefix>` | Basisbranch und Branch-Präfix des Fix-PRs |
152
+ | `--llm-remediation [boolean]` | Opt-in: Quellcode darf an den LLM-Anbieter gesendet werden (Standard `false`) |
153
+ | `--llm-provider`, `--llm-model`, `--api-key` | Nur `openai` ist umgesetzt; andere Anbieter fallen auf die Regel-Engine zurück |
154
+ | `--headless [boolean]`, `--timeout-ms <ms>` | Browser-Modus und Timeout je Szenario-Navigation |
155
+
156
+ Alle Optionen und Standardwerte: `cpcg --help`.
157
+
158
+ **Exit-Codes** (für CI): `0` ok · `1` Leaks gefunden (auch wenn ein Fix nur vorgeschlagen, aber nicht verifiziert ist) ·
159
+ `2` Lizenz **ungültig, abgelaufen oder gesperrt** (eine fehlende Lizenz oder ein nicht erreichbarer Lizenzserver ist kein Exit 2) ·
160
+ `3` Fehler (Ziel unerreichbar oder HTTP 4xx/5xx, Konfiguration, Browser fehlt, falsche Argumente) ·
161
+ `4` unvollständig, Kernszenarien nicht prüfbar.
162
+ Ein Lauf, der nichts abschliessend prüfen konnte, ist nie grün: `--fail-on-uncheckable` (Standard) macht daraus Exit 4.
163
+
164
+ ### Unterseiten-Stichprobe
165
+
166
+ Das Szenario `subpage-spa` prüft höchstens `--max-subpages` verlinkte Unterseiten (verschiedene erste Pfadsegmente zuerst, Impressum und Datenschutz zuletzt). Sind mehr Unterseiten verlinkt als geprüft wurden, steht das nicht nur im Report: Die Konsole meldet «Unterseiten: X von Y geprüft — nicht geprüfte Routen können Verstösse enthalten; --max-subpages erhöhen», `report.json` führt es unter `gate.sampling`, die Step-Summary und der Report-Kopf zeigen «PASS, aber Stichprobe», und `gate.incomplete` ist `true`. Der Exit-Code bleibt bei PASS `0` (nicht `4`).
167
+
168
+ ### Banner nicht gefunden: Selektoren und Frames
169
+
170
+ Findet die Erkennung den Banner oder einen Button nicht, wird das Szenario «nicht prüfbar» (nie grün). Dann helfen die Overrides:
171
+
172
+ ```bash
173
+ cpcg --url https://staging.example.com \
174
+ --accept-selector "#meine-zustimmung" \
175
+ --reject-selector "button[data-consent=reject]" \
176
+ --settings-selector ".cookie-einstellungen" \
177
+ --banner-frame "consent.mein-cmp.example"
178
+ ```
179
+
180
+ Erscheint der Banner erst spät, verlängert `--banner-wait <sekunden>` das Wartefenster (Standard 8 s). Der Scan behauptet «kein Banner» nur, wenn die Seite stabil lud und die Schlussprüfung das Fenster samt Nachfrist ohne Banner-Hinweis durchlief. Sonst steht eine eigene Meldung: «Banner erschien nach dem Wartefenster — mit --banner-wait erhöhen», «Banner vorhanden, aber nicht über Ablehnen/Akzeptieren bedienbar» (zum Beispiel nur ein Schliessen-Kreuz) oder «Seite instabil (Reload-Schleife) — Banner nicht auswertbar».
181
+
182
+ ## Auto-Fix: Grenzen und Consent-Anbindung
183
+
184
+ `--auto-fix` verschiebt Tracker-Skripte hinter die Einwilligung. Das Ergebnis ist ein Vorschlag, den die Review-Stufe prüft;
185
+ ein nicht verifizierter Fix macht das Gate nicht grün.
186
+
187
+ - **OneTrust und Cookiebot** werden automatisch gelesen (Kategorien Statistik und Marketing); ein Hook ist nicht nötig. OneTrust wird auch ohne CDN-Marker erkannt, etwa bei selbst gehostetem SDK (`Optanon…`-Symbole, `OptanonConsent`-Cookie).
188
+ - **Eigene Banner** melden die Einwilligung an die eingefügte Runtime, zum Beispiel `window.cpcgGrantConsent("analytics")` (Statistik) oder `window.cpcgGrantConsent("marketing")`, ohne Argument alle Kategorien. Der Report nennt die passenden Hooks. Ohne dieses Signal startet kein Tracker; «Ablehnen» ruft nichts auf.
189
+ - **Andere Consent-Plattformen** (zum Beispiel Usercentrics, Sourcepoint, Didomi, iubenda) sind **nicht automatisch behebbar**: der Auto-Fix kann ihre Kategorie-Einwilligung nicht sicher lesen und erzeugt keinen Patch, sondern eine Anleitung zur manuellen Behebung.
190
+ - **Tracker, die nicht im Quelltext stehen** (über Tag Manager, Server, CMS oder Drittanbieter geladen) bleiben «manuell zu beheben».
191
+ - **LLM-gestützte Patches** sind ein Opt-in (`--llm-remediation`), nur mit OpenAI umgesetzt, und senden den Quellcode an den Anbieter. Ein API-Key allein genügt nicht und sendet nichts.
192
+ - **Commit und Pull Request** (`--git-commit`, `--create-pr`) brauchen eine Lizenz; das lokale Anwenden der Patches nicht. Git läuft im Repository, in dem `--source-dir` liegt; liegt es in keinem, bleibt der Fix lokal und der Lauf meldet das ausdrücklich.
193
+ - **Teilfix:** Bleiben gemeldete Tracker offen, zählt das Gate nur diese als «ungepatcht»; Score und «Passed» des Review-Agenten gelten dann ausdrücklich nur für den gepatchten Teil.
194
+
90
195
  ## Demo-Portal
91
196
 
92
197
  Reproduzierbare Testseite mit fünf absichtlichen Consent-Leaks
@@ -94,16 +199,19 @@ Reproduzierbare Testseite mit fünf absichtlichen Consent-Leaks
94
199
 
95
200
  ```bash
96
201
  node demo-portal/server.mjs # http://127.0.0.1:8787
97
- pnpm exec tsx scripts/scan-demo-portal.mts # voller Scan + Report
98
- pnpm exec tsx scripts/make-deluxe-report.mts # Deluxe-PDF daraus
202
+ npx tsx scripts/scan-demo-portal.mts # voller Scan + Report
203
+ npx tsx scripts/make-deluxe-report.mts # Deluxe-PDF daraus
99
204
  ```
100
205
 
206
+ Die Skripte in `scripts/` sind TypeScript: `node scripts/x.mts` scheitert an den Importen, deshalb `npx tsx`.
207
+
101
208
  Erwartet: Gate **BLOCKED**, Szenarien «auffällig» — exakt dieselben Zahlen bei jedem Lauf.
102
209
 
103
210
  ## GitHub Action
104
211
 
105
212
  ```yaml
106
213
  - uses: cuonztech/privacy-chaos-gate@v1
214
+ id: cpcg
107
215
  with:
108
216
  staging-url: ${{ vars.STAGING_URL }}
109
217
  license: ${{ secrets.CPCG_LICENSE }}
@@ -111,7 +219,57 @@ Erwartet: Gate **BLOCKED**, Szenarien «auffällig» — exakt dieselben Zahlen
111
219
  client-name: "Firma AG"
112
220
  ```
113
221
 
114
- Die Action nutzt `dist/cli.js` aus dem Action-Path; erst bei fehlendem Build greift `npx`.
222
+ Die Action installiert das veröffentlichte Paket `cuonztech-privacy-chaos-gate` in der Version, die in `action.yml`
223
+ fest eingetragen ist (`npm install --ignore-scripts`, ohne Secrets in der Umgebung dieses Schritts), samt dem passenden
224
+ Chromium (`playwright install --with-deps chromium`). Nur wenn im Action-Pfad ein eigener Build (`dist/cli.js`) liegt,
225
+ etwa im CI dieses Repos, wird dieser verwendet. Kennt die installierte CLI eine angeforderte Option nicht, gibt die Action
226
+ eine Warnung aus, statt sie still wegzulassen.
227
+
228
+ Scan und Konsolen-Gate laufen ohne Lizenz. `report-pdf`, `client-name`, `git-commit` und `create-pr` brauchen eine gültige Lizenz;
229
+ ohne sie werden sie mit Hinweis übersprungen.
230
+
231
+ | Input | Standard | Bedeutung |
232
+ |-------|----------|-----------|
233
+ | `staging-url` | Pflicht | Zu prüfende URL |
234
+ | `license` | leer | Lizenzcode oder Kunden-Key; wird nur per Umgebungsvariable `CPCG_LICENSE` übergeben |
235
+ | `fail-on-leak` | `true` | Build blockieren (Exit 1), wenn Leaks bleiben |
236
+ | `fail-on-uncheckable` | `true` | Build blockieren (Exit 4), wenn Kernszenarien nicht prüfbar sind |
237
+ | `strict` | `false` | Auch Hinweise (z. B. Google Fonts) blockieren das Gate |
238
+ | `ignore` | leer | Befunde ausnehmen (Tracker-Name oder Host-Muster, eine Angabe je Zeile oder kommagetrennt) |
239
+ | `accept-selector`, `reject-selector`, `settings-selector` | leer | Eigene CSS-Selektoren der Banner-Buttons (eine Angabe je Zeile) |
240
+ | `banner-frame` | leer | Teil der URL des iframes mit dem Banner |
241
+ | `auto-fix` | `false` | Patches auf dem Runner erzeugen und anwenden; ein Gate ändert Dateien nicht stillschweigend |
242
+ | `create-pr` | `false` | Fix als Pull Request öffnen (braucht `auto-fix: true`, `github-token` und eine Lizenz) |
243
+ | `github-token` | leer | Token für `create-pr` (`contents: write`, `pull-requests: write`) |
244
+ | `git-commit` | `false` | Fix in den ausgecheckten Branch committen (braucht `auto-fix: true` und eine Lizenz) |
245
+ | `target-source-dir` | `.` | Quellcode-Verzeichnis für Auto-Fix |
246
+ | `report-output` | `reports/privacy-chaos-report.json` | Pfad des JSON-Reports |
247
+ | `report-pdf`, `client-name` | `false`, leer | Deluxe-PDF und Kundenname (Lizenz erforderlich) |
248
+ | `jurisdiction` | `CH` | Rechtsraum |
249
+ | `llm-remediation`, `api-key`, `llm-provider`, `llm-model` | `false`, leer, `openai`, `gpt-4o` | Optional: LLM-gestützte Patches. Nur OpenAI ist umgesetzt; der Quellcode wird dann an den Anbieter gesendet; ohne `llm-remediation: true` wirkt der Key nicht |
250
+
251
+ `--max-subpages` und `--banner-wait` sind noch keine Inputs der Action. Bis dahin lassen sie sich im Job über `env:` setzen (`CPCG_MAX_SUBPAGES`, `CPCG_BANNER_WAIT`); als Inputs wären die Namen `max-subpages` und `banner-wait` (Werte wie beim CLI) vorgesehen.
252
+
253
+ Beispiel mit Pull Request als Ergebnis:
254
+
255
+ ```yaml
256
+ permissions:
257
+ contents: write
258
+ pull-requests: write
259
+ steps:
260
+ - uses: actions/checkout@v4
261
+ - uses: cuonztech/privacy-chaos-gate@v1
262
+ with:
263
+ staging-url: ${{ vars.STAGING_URL }}
264
+ license: ${{ secrets.CPCG_LICENSE }}
265
+ auto-fix: true
266
+ create-pr: true
267
+ github-token: ${{ secrets.GITHUB_TOKEN }}
268
+ ```
269
+
270
+ Outputs: `leaks-found`, `leaks-count`, `patch-applied`, `report-path` sowie `gate_state`, `incomplete`, `uncheckable_count`,
271
+ `leak_requests` und `exit_code` (geschrieben vom CLI nach `GITHUB_OUTPUT`; leer, wenn die installierte CLI sie nicht kennt).
272
+ Beispiel: `if: steps.cpcg.outputs.gate_state == 'blocked'`.
115
273
 
116
274
  ## Jurisdiktion
117
275
 
@@ -131,13 +289,17 @@ Die Action nutzt `dist/cli.js` aus dem Action-Path; erst bei fehlendem Build gre
131
289
 
132
290
  ```bash
133
291
  pnpm install
134
- pnpm test # 136 Tests
292
+ pnpm test # Vitest-Suite (inkl. PHP-Lizenzdienst, falls Docker läuft; action.yml-Pin und Optionsweitergabe)
135
293
  pnpm build
136
- pnpm exec tsx scripts/e2e-cli-deluxe.mts # E2E: echte CLI gegen Demo-Portal
294
+ pnpm typecheck:all # src + tests + scripts
295
+ npx tsx scripts/e2e-cli-deluxe.mts # E2E: echte CLI gegen Demo-Portal
137
296
  ```
138
297
 
298
+ Der Lizenzdienst liegt in `server/license-api/` (eigene README mit Deploy-Checkliste und Betrieb)
299
+ und gehört nicht zum veröffentlichten Paket.
300
+
139
301
  Secrets (Stripe, Lizenz-Secret) nur über Env/KeePass — nie im Repo.
140
302
 
141
303
  ---
142
304
 
143
- © CuonzTech · Carlo Cuonz · [cuonztech.ch](https://cuonztech.ch)
305
+ © CuonzTech · Carlo Cuonz · [cuonztech.ch](https://cuonztech.ch) · Lizenz: siehe [LICENSE](LICENSE)
package/action.yml CHANGED
@@ -10,11 +10,15 @@ inputs:
10
10
  description: "The URL of the staging or localhost web environment to stress-test"
11
11
  required: true
12
12
  api-key:
13
- description: "API key for the auto-remediation LLM (e.g. OpenAI, Anthropic, Gemini)"
13
+ description: "API key for the optional LLM patch generation. Only OpenAI is implemented (llm-provider openai); other providers fall back to the built-in rules. Has no effect without llm-remediation: true. Passed via the CPCG_LLM_API_KEY environment variable."
14
14
  required: false
15
15
  default: ""
16
+ llm-remediation:
17
+ description: "Opt-in: allow the source code to be sent to the LLM provider (OpenAI only) to generate patches (needs api-key). Off by default; only passed on if the installed CLI supports it."
18
+ required: false
19
+ default: "false"
16
20
  llm-provider:
17
- description: "LLM Provider to use for auto-remediation ('openai', 'anthropic', 'gemini')"
21
+ description: "LLM provider for auto-remediation. Only 'openai' is implemented; 'anthropic' and 'gemini' fall back to the built-in rules."
18
22
  required: false
19
23
  default: "openai"
20
24
  llm-model:
@@ -22,13 +26,49 @@ inputs:
22
26
  required: false
23
27
  default: "gpt-4o"
24
28
  auto-fix:
25
- description: "Automatically generate and apply code patches when tracker leaks are detected"
29
+ description: "Generate and apply code patches on the runner when tracker leaks are detected. Off by default: a gate should not silently change files; combine with create-pr to receive the fix as a pull request."
26
30
  required: false
27
- default: "true"
31
+ default: "false"
28
32
  fail-on-leak:
29
33
  description: "Block the build (exit code 1) if unpatched critical leaks remain"
30
34
  required: false
31
35
  default: "true"
36
+ fail-on-uncheckable:
37
+ description: "Block the build (exit code 4) if core scenarios could not be checked conclusively (e.g. consent banner not found). Only passed on if the installed CLI supports it; otherwise a warning is printed."
38
+ required: false
39
+ default: "true"
40
+ strict:
41
+ description: "Hints (purely functional third parties such as Google Fonts) also block the gate. Off by default: they are reported as hints, not as blockers. Only passed on if the installed CLI supports it."
42
+ required: false
43
+ default: "false"
44
+ ignore:
45
+ description: "Findings to ignore: tracker name or host pattern, one per line or comma-separated (e.g. fonts.googleapis.com or *.hotjar.com). Ignored findings are listed as ignored in the report."
46
+ required: false
47
+ default: ""
48
+ accept-selector:
49
+ description: "Own CSS selector of the accept button, one per line (when the built-in CMP detection does not find it)"
50
+ required: false
51
+ default: ""
52
+ reject-selector:
53
+ description: "Own CSS selector of the reject button, one per line"
54
+ required: false
55
+ default: ""
56
+ settings-selector:
57
+ description: "Own CSS selector of the settings button, one per line"
58
+ required: false
59
+ default: ""
60
+ banner-frame:
61
+ description: "Part of the URL of the iframe that contains the consent banner (CMP inside a frame)"
62
+ required: false
63
+ default: ""
64
+ max-subpages:
65
+ description: "Maximum number of linked subpages checked for trackers (default 12, max 40; larger values take longer)"
66
+ required: false
67
+ default: ""
68
+ banner-wait:
69
+ description: "Seconds to wait for a late consent banner (1-60; default as in the CLI)"
70
+ required: false
71
+ default: ""
32
72
  report-output:
33
73
  description: "File path where the JSON LeakReport will be written"
34
74
  required: false
@@ -38,23 +78,31 @@ inputs:
38
78
  required: false
39
79
  default: "."
40
80
  git-commit:
41
- description: "Automatically commit generated privacy fixes into the branch"
81
+ description: "Commit generated privacy fixes into the checked-out branch (requires auto-fix and a valid license; skipped without one)"
82
+ required: false
83
+ default: "false"
84
+ create-pr:
85
+ description: "Create a fix branch and open a GitHub Pull Request with the generated fix (requires auto-fix, github-token and a valid license; takes precedence over git-commit)"
42
86
  required: false
43
87
  default: "false"
88
+ github-token:
89
+ description: "GitHub token for create-pr (contents: write and pull-requests: write). Passed via the GITHUB_TOKEN environment variable, never on the command line."
90
+ required: false
91
+ default: ""
44
92
  license:
45
- description: "CuonzTech CPCG Lizenzcode (CPCG-…) — aus Secrets, nach Online-Kauf"
93
+ description: "CuonzTech CPCG license (CPCG-… or customer key CPCG-K-…) — from secrets, after online purchase. Not needed for scan and console gate; required for customer report/PDF, git-commit and create-pr. Passed via the CPCG_LICENSE environment variable."
46
94
  required: false
47
95
  default: ""
48
96
  report-pdf:
49
- description: "Deluxe-PDF (Deckblatt + Evidence) nach dem Scan erzeugen"
97
+ description: "Create the deluxe PDF (cover page + evidence) after the scan. Requires a valid license; skipped without one."
50
98
  required: false
51
99
  default: "false"
52
100
  client-name:
53
- description: "Kundenname für das PDF-Deckblatt"
101
+ description: "Client name for the customer report and the PDF cover page. Requires a valid license."
54
102
  required: false
55
103
  default: ""
56
104
  jurisdiction:
57
- description: "Rechtsraum für Bewertung: CH | EU | UK | US-CA | BR | ROW"
105
+ description: "Jurisdiction highlighted in the console and report: CH | EU | UK | US-CA | BR | ROW"
58
106
  required: false
59
107
  default: "CH"
60
108
 
@@ -71,6 +119,21 @@ outputs:
71
119
  report-path:
72
120
  description: "Absolute or relative path to the generated JSON report"
73
121
  value: ${{ steps.cpcg-run.outputs.report_path }}
122
+ gate_state:
123
+ description: "Gate result as written by the CLI (for example blocked, fix-proposed, inconclusive, passed). Empty if the installed CLI predates this output."
124
+ value: ${{ steps.cpcg-run.outputs.gate_state }}
125
+ incomplete:
126
+ description: "'true' if core scenarios could not be checked conclusively"
127
+ value: ${{ steps.cpcg-run.outputs.incomplete }}
128
+ uncheckable_count:
129
+ description: "Number of scenarios that could not be checked"
130
+ value: ${{ steps.cpcg-run.outputs.uncheckable_count }}
131
+ leak_requests:
132
+ description: "Number of tracker requests observed before consent"
133
+ value: ${{ steps.cpcg-run.outputs.leak_requests }}
134
+ exit_code:
135
+ description: "Exit code of the CLI (0 ok, 1 leaks, 2 license invalid or revoked, 3 error, 4 incomplete)"
136
+ value: ${{ steps.cpcg-run.outputs.exit_code }}
74
137
 
75
138
  runs:
76
139
  using: "composite"
@@ -80,30 +143,128 @@ runs:
80
143
  with:
81
144
  node-version: "20"
82
145
 
146
+ # Installationsschritt: bewusst ohne Secrets (Lizenz, Token, API-Key) in der Umgebung. Nur hier läuft npm.
147
+ - name: Install Privacy Chaos Gate
148
+ id: cpcg-install
149
+ shell: bash
150
+ env:
151
+ ACTION_PATH: ${{ github.action_path }}
152
+ CPCG_PINNED_VERSION: "0.2.1" # = package.json version; bei jedem Release mit anheben (tests/action-pin.test.ts prüft das)
153
+ run: |
154
+ set -euo pipefail
155
+
156
+ CLI_PATH="$ACTION_PATH/dist/cli.js"
157
+ if [ -f "$CLI_PATH" ]; then
158
+ # Eigener Build im Action-Pfad (Repo-CI): Abhängigkeiten und Browser bringt der Workflow mit.
159
+ echo "cli=$CLI_PATH" >> "$GITHUB_OUTPUT"
160
+ exit 0
161
+ fi
162
+
163
+ # Veröffentlichtes Paket in der gepinnten Version. Lifecycle-Skripte der Abhängigkeiten laufen nicht
164
+ # (--ignore-scripts); der Playwright-Browser wird unten ausdrücklich installiert.
165
+ TOOL_DIR="$(mktemp -d "${RUNNER_TEMP:-${TMPDIR:-/tmp}}/cpcg-XXXXXX")"
166
+ npm install --prefix "$TOOL_DIR" --ignore-scripts --no-audit --no-fund --loglevel=error "cuonztech-privacy-chaos-gate@${CPCG_PINNED_VERSION}"
167
+ PW_CLI="$TOOL_DIR/node_modules/playwright/cli.js"
168
+ if [ -f "$PW_CLI" ]; then
169
+ node "$PW_CLI" install --with-deps chromium
170
+ else
171
+ npx --yes playwright install --with-deps chromium
172
+ fi
173
+ echo "cli=$TOOL_DIR/node_modules/cuonztech-privacy-chaos-gate/dist/cli.js" >> "$GITHUB_OUTPUT"
174
+
83
175
  - name: Run Privacy Chaos Gate
84
176
  id: cpcg-run
85
177
  shell: bash
178
+ # Alle Inputs gelangen nur über env in das Skript (keine ${{ }}-Interpolation im run-Block:
179
+ # sonst könnten Input-Werte als Shell-Code ausgeführt werden). Die Lizenz steht nie in der Kommandozeile.
86
180
  env:
181
+ CPCG_CLI: ${{ steps.cpcg-install.outputs.cli }}
87
182
  CPCG_LLM_API_KEY: ${{ inputs.api-key }}
88
183
  CPCG_LLM_PROVIDER: ${{ inputs.llm-provider }}
89
184
  CPCG_LLM_MODEL: ${{ inputs.llm-model }}
90
185
  CPCG_LICENSE: ${{ inputs.license }}
186
+ GITHUB_TOKEN: ${{ inputs.github-token }}
187
+ IN_STAGING_URL: ${{ inputs.staging-url }}
188
+ IN_LLM_REMEDIATION: ${{ inputs.llm-remediation }}
189
+ IN_AUTO_FIX: ${{ inputs.auto-fix }}
190
+ IN_FAIL_ON_LEAK: ${{ inputs.fail-on-leak }}
191
+ IN_FAIL_ON_UNCHECKABLE: ${{ inputs.fail-on-uncheckable }}
192
+ IN_STRICT: ${{ inputs.strict }}
193
+ IN_IGNORE: ${{ inputs.ignore }}
194
+ IN_ACCEPT_SELECTOR: ${{ inputs.accept-selector }}
195
+ IN_REJECT_SELECTOR: ${{ inputs.reject-selector }}
196
+ IN_SETTINGS_SELECTOR: ${{ inputs.settings-selector }}
197
+ IN_BANNER_FRAME: ${{ inputs.banner-frame }}
198
+ IN_MAX_SUBPAGES: ${{ inputs.max-subpages }}
199
+ IN_BANNER_WAIT: ${{ inputs.banner-wait }}
200
+ IN_REPORT_OUTPUT: ${{ inputs.report-output }}
201
+ IN_SOURCE_DIR: ${{ inputs.target-source-dir }}
202
+ IN_GIT_COMMIT: ${{ inputs.git-commit }}
203
+ IN_CREATE_PR: ${{ inputs.create-pr }}
204
+ IN_REPORT_PDF: ${{ inputs.report-pdf }}
205
+ IN_CLIENT: ${{ inputs.client-name }}
206
+ IN_JURISDICTION: ${{ inputs.jurisdiction }}
91
207
  run: |
92
- CLI_PATH="${{ github.action_path }}/dist/cli.js"
93
- if [ -f "$CLI_PATH" ]; then
94
- RUNNER="node $CLI_PATH"
95
- else
96
- RUNNER="npx @cuonztech/privacy-chaos-gate"
208
+ set -euo pipefail
209
+
210
+ RUNNER=(node "$CPCG_CLI")
211
+
212
+ # Optionen immer als --name=wert: ein Wert, der mit "-" beginnt, wird so nie als eigene Option gelesen.
213
+ ARGS=(
214
+ "--url=$IN_STAGING_URL"
215
+ "--auto-fix=$IN_AUTO_FIX"
216
+ "--fail-on-leak=$IN_FAIL_ON_LEAK"
217
+ "--report-output=$IN_REPORT_OUTPUT"
218
+ "--source-dir=$IN_SOURCE_DIR"
219
+ "--git-commit=$IN_GIT_COMMIT"
220
+ "--create-pr=$IN_CREATE_PR"
221
+ "--report-pdf=$IN_REPORT_PDF"
222
+ "--jurisdiction=$IN_JURISDICTION"
223
+ )
224
+ if [ -n "$IN_CLIENT" ]; then
225
+ ARGS+=("--client=$IN_CLIENT")
97
226
  fi
98
227
 
99
- $RUNNER \
100
- --url "${{ inputs.staging-url }}" \
101
- --auto-fix "${{ inputs.auto-fix }}" \
102
- --fail-on-leak "${{ inputs.fail-on-leak }}" \
103
- --report-output "${{ inputs.report-output }}" \
104
- --source-dir "${{ inputs.target-source-dir }}" \
105
- --git-commit "${{ inputs.git-commit }}" \
106
- --license "${{ inputs.license }}" \
107
- --report-pdf "${{ inputs.report-pdf }}" \
108
- --client "${{ inputs.client-name }}" \
109
- --jurisdiction "${{ inputs.jurisdiction }}"
228
+ # Neuere Optionen nur weitergeben, wenn die installierte CLI sie kennt (ältere Versionen würden sonst mit
229
+ # "unknown option" abbrechen). Kennt sie eine gewünschte Option nicht, wird das gemeldet, nicht still übergangen.
230
+ HELP="$("${RUNNER[@]}" --help 2>&1 || true)"
231
+ supports() { printf '%s\n' "$HELP" | grep -q -- "$1"; }
232
+ warn_unsupported() {
233
+ echo "::warning title=CPCG::Die installierte CLI kennt --$1 nicht; der Input wird ignoriert. Paket- und Action-Version angleichen."
234
+ }
235
+ # Boolesche Option: übergeben, wenn bekannt; unbekannt und angefordert ("true") → Warnung.
236
+ opt_bool() {
237
+ if supports "--$1"; then
238
+ ARGS+=("--$1=$2")
239
+ elif [ "$2" = "true" ]; then
240
+ warn_unsupported "$1"
241
+ fi
242
+ return 0
243
+ }
244
+ # Wert-Option (mehrzeilig erlaubt: je nichtleere Zeile eine Option): nur wenn gesetzt.
245
+ opt_lines() {
246
+ local flag="$1" value="$2" line
247
+ [ -n "${value//[[:space:]]/}" ] || return 0
248
+ if ! supports "--$flag"; then
249
+ warn_unsupported "$flag"
250
+ return 0
251
+ fi
252
+ while IFS= read -r line; do
253
+ line="${line#"${line%%[![:space:]]*}"}"
254
+ line="${line%"${line##*[![:space:]]}"}"
255
+ if [ -n "$line" ]; then ARGS+=("--$flag=$line"); fi
256
+ done <<< "$value"
257
+ return 0
258
+ }
259
+ opt_bool fail-on-uncheckable "$IN_FAIL_ON_UNCHECKABLE"
260
+ opt_bool llm-remediation "$IN_LLM_REMEDIATION"
261
+ opt_bool strict "$IN_STRICT"
262
+ opt_lines ignore "$IN_IGNORE"
263
+ opt_lines accept-selector "$IN_ACCEPT_SELECTOR"
264
+ opt_lines reject-selector "$IN_REJECT_SELECTOR"
265
+ opt_lines settings-selector "$IN_SETTINGS_SELECTOR"
266
+ opt_lines banner-frame "$IN_BANNER_FRAME"
267
+ opt_lines max-subpages "$IN_MAX_SUBPAGES"
268
+ opt_lines banner-wait "$IN_BANNER_WAIT"
269
+
270
+ "${RUNNER[@]}" "${ARGS[@]}"