cuonztech-privacy-chaos-gate 0.2.0 → 0.2.2
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 +97 -0
- package/README.md +197 -30
- package/action.yml +187 -26
- package/dist/chunk-42D7PLVP.js +9146 -0
- package/dist/cli.js +96 -93
- package/dist/index.d.ts +710 -88
- package/dist/index.js +41 -12
- package/docs/BRAND-PILOT-EVIDENCE.md +41 -49
- package/package.json +6 -5
- package/dist/chunk-FN3QUFUZ.js +0 -36
- package/dist/chunk-FN3QUFUZ.js.map +0 -1
- package/dist/chunk-T5D6BUAC.js +0 -4514
- package/dist/chunk-T5D6BUAC.js.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/resolveGrant-6U6VG3DM.js +0 -230
- package/dist/resolveGrant-6U6VG3DM.js.map +0 -1
- package/docs/LIZENZ-KAUFFLUSS.md +0 -105
- package/docs/PREISMODELL.md +0 -63
package/dist/cli.js
CHANGED
|
@@ -1,122 +1,125 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
+
ENV_FILE_KEYS_HELP,
|
|
4
|
+
EXIT,
|
|
5
|
+
LICENSE_CONTACT,
|
|
3
6
|
PrivacyChaosGate,
|
|
7
|
+
assertReportOutputWritable,
|
|
8
|
+
isLoopbackTarget,
|
|
9
|
+
loadProjectEnv,
|
|
10
|
+
requestedLicensedFeatures,
|
|
11
|
+
resolveEntitlement,
|
|
4
12
|
resolveGateConfig,
|
|
5
13
|
resolveJurisdiction
|
|
6
|
-
} from "./chunk-
|
|
7
|
-
import {
|
|
8
|
-
defaultPublicKey,
|
|
9
|
-
verifyEd25519
|
|
10
|
-
} from "./chunk-FN3QUFUZ.js";
|
|
14
|
+
} from "./chunk-42D7PLVP.js";
|
|
11
15
|
|
|
12
16
|
// src/cli.ts
|
|
13
|
-
import { Command } from "commander";
|
|
17
|
+
import { Command, CommanderError } from "commander";
|
|
14
18
|
import pc from "picocolors";
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
function
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
}
|
|
21
|
-
function verifyLicense(token, publicKey = defaultPublicKey()) {
|
|
22
|
-
if (!token || !token.startsWith("CPCG-")) {
|
|
23
|
-
return { valid: false, reason: "Unbekanntes Lizenzformat." };
|
|
24
|
-
}
|
|
25
|
-
const body = token.slice("CPCG-".length);
|
|
26
|
-
const dot = body.lastIndexOf(".");
|
|
27
|
-
if (dot <= 0) return { valid: false, reason: "Lizenzsignatur fehlt." };
|
|
28
|
-
const payload = body.slice(0, dot);
|
|
29
|
-
const sig = body.slice(dot + 1);
|
|
30
|
-
if (!verifyEd25519("cpcg-license-v1", payload, sig, publicKey)) {
|
|
31
|
-
return { valid: false, reason: "Ung\xFCltige Lizenzsignatur." };
|
|
32
|
-
}
|
|
33
|
-
try {
|
|
34
|
-
const claims = JSON.parse(Buffer.from(payload, "base64url").toString("utf-8"));
|
|
35
|
-
const tiers = ["trial", "pro", "agency", "enterprise"];
|
|
36
|
-
if (!tiers.includes(claims.tier)) {
|
|
37
|
-
return { valid: false, reason: "Unbekannte Lizenzstufe.", claims };
|
|
38
|
-
}
|
|
39
|
-
if (typeof claims.exp !== "number" || !Number.isFinite(claims.exp)) {
|
|
40
|
-
return { valid: false, reason: "Lizenz ohne Ablaufdatum ung\xFCltig.", claims };
|
|
41
|
-
}
|
|
42
|
-
if (claims.exp < Date.now() / 1e3) {
|
|
43
|
-
return { valid: false, reason: "Lizenz abgelaufen.", claims };
|
|
44
|
-
}
|
|
45
|
-
return { valid: true, claims };
|
|
46
|
-
} catch {
|
|
47
|
-
return { valid: false, reason: "Lizenz-Payload lesbar nicht." };
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
function resolveLicenseFromEnv(cliToken) {
|
|
51
|
-
return cliToken || process.env.CPCG_LICENSE || "";
|
|
19
|
+
var VERSION = true ? "0.2.2" : "dev";
|
|
20
|
+
loadProjectEnv();
|
|
21
|
+
function finish(code) {
|
|
22
|
+
process.exitCode = code;
|
|
23
|
+
setTimeout(() => process.exit(code), 3e3).unref();
|
|
52
24
|
}
|
|
25
|
+
var EXIT_CODES_HELP = `
|
|
26
|
+
Exit-Codes:
|
|
27
|
+
0 ok (kein Leak; bei unvollst\xE4ndigem Scan nur mit --fail-on-uncheckable false)
|
|
28
|
+
1 Leaks gefunden (BLOCKED), auch wenn ein Fix nur vorgeschlagen (Commit/PR), aber nicht verifiziert ist
|
|
29
|
+
2 Lizenz ung\xFCltig, abgelaufen oder gesperrt (eine fehlende Lizenz oder ein nicht erreichbarer Lizenzserver ist KEIN Exit 2)
|
|
30
|
+
3 Fehler (Ziel unerreichbar oder HTTP 4xx/5xx, Konfiguration, Browser fehlt, falsche Argumente)
|
|
31
|
+
4 unvollst\xE4ndig (INCONCLUSIVE): Kernszenarien (Ablehnen-/Akzeptieren-Pfad) nicht pr\xFCfbar
|
|
53
32
|
|
|
54
|
-
|
|
55
|
-
|
|
33
|
+
.env im Arbeitsverzeichnis:
|
|
34
|
+
\xDCbernommen werden nur Zugangsdaten und Endpunkte: ${ENV_FILE_KEYS_HELP}. Alle Gate- und Verhaltensschalter
|
|
35
|
+
(CPCG_IGNORE, CPCG_STRICT, CPCG_FAIL_ON_*, CPCG_REPORT_OUTPUT, CPCG_DEMO, CPCG_LLM_*, Selektoren, CPCG_LICENSE_URL \u2026)
|
|
36
|
+
gelten nur aus der echten Prozessumgebung oder als CLI-Option, damit eine mitgelieferte .env das Gate nicht entsch\xE4rft.
|
|
37
|
+
|
|
38
|
+
Lizenz:
|
|
39
|
+
Scan und Konsolen-Gate sind frei, auch ohne Lizenz. Eine g\xFCltige Lizenz (Lizenzcode CPCG-\u2026 oder Kunden-Key
|
|
40
|
+
CPCG-K-\u2026) braucht es f\xFCr --report-pdf, den Kundenreport (--client), --git-commit und --create-pr; ohne Lizenz
|
|
41
|
+
werden diese Funktionen mit Hinweis \xFCbersprungen, der Exit-Code bleibt der des Gates. Loopback-Ziele
|
|
42
|
+
(localhost, 127.0.0.1, ::1) sind immer frei. Erwerb: ${LICENSE_CONTACT}
|
|
43
|
+
`;
|
|
44
|
+
var collect = (value, previous) => [...previous, value];
|
|
56
45
|
var program = new Command();
|
|
57
|
-
program.name("cpcg").description("Cuonztech Privacy Chaos Gate - CI/CD Shift-Left Consent Scanner & Auto-Remediation").version(VERSION).option("-u, --url <url>", "Staging
|
|
46
|
+
program.name("cpcg").description("Cuonztech Privacy Chaos Gate - CI/CD Shift-Left Consent Scanner & Auto-Remediation").version(VERSION).option("-u, --url <url>", "Staging- oder localhost-URL (http/https) \u2014 Pflicht").option("-k, --api-key <key>", "LLM-API-Key; wirkt nur mit --llm-remediation (Env-Fallback nur passend zum Provider)").option("--llm-remediation [boolean]", "Opt-in: Quellcode darf zur Patch-Erzeugung an den LLM-Anbieter gesendet werden (default: false)").option("-p, --llm-provider <provider>", "LLM-Provider: openai | anthropic | gemini (umgesetzt ist derzeit nur openai; sonst greift die Regel-Engine) (default: openai)").option("-m, --llm-model <model>", "LLM-Modell (default: gpt-4o)").option("--auto-fix [boolean]", "Code-Patches erzeugen und im Quellverzeichnis anwenden (default: true nur mit ausdr\xFCcklichem --source-dir, sonst false)").option("--fail-on-leak [boolean]", "Build blockieren (Exit 1), wenn Leaks bleiben (default: true)").option("--fail-on-uncheckable [boolean]", "Exit 4, wenn Kernszenarien nicht pr\xFCfbar sind; bei false Exit 0, das Ergebnis bleibt als unvollst\xE4ndig markiert (default: true)").option("-r, --report-output <path>", "Pfad des JSON-Reports (default: reports/privacy-chaos-report.json)").option("-s, --source-dir <dir>", "Quellcode-Verzeichnis f\xFCr Auto-Fix; ausdr\xFCcklich angeben, um Auto-Fix zu aktivieren").option("--git-commit [boolean]", "Fixes automatisch in den Git-Branch committen \u2014 Lizenz erforderlich (default: false)").option("--create-pr [boolean]", "Fix-Branch anlegen und GitHub-PR \xF6ffnen; hat Vorrang vor --git-commit \u2014 Lizenz erforderlich (default: false)").option("--github-token <token>", "GitHub-Token f\xFCr PR-Erstellung (besser: Env GITHUB_TOKEN)").option("--base-branch <branch>", "PR-Basisbranch (default: Default-Branch des Remotes)").option("--branch-prefix <prefix>", "Branch-Pr\xE4fix f\xFCr Fix-PRs (default: fix/cpcg-privacy)").option("--headless [boolean]", "Browser headless starten (default: true)").option("--timeout-ms <ms>", "Timeout je Szenario-Navigation und f\xFCr den Erreichbarkeits-Check in ms, positive ganze Zahl (default: 30000)").option("--strict [boolean]", "Hinweise (rein funktionale Drittanbieter wie Google Fonts) blockieren das Gate ebenfalls (default: false)").option("--ignore <liste>", "Befunde ignorieren: Tracker-Name oder Host-Muster, kommagetrennt oder mehrfach (z. B. fonts.googleapis.com,*.hotjar.com); im Report als \xABignoriert\xBB ausgewiesen; `*`/`**` und nackte TLDs (com, ch) werden immer abgelehnt (zum Abschalten der Blockierung --fail-on-leak false), ganze Zonen (*.com) ohne --allow-broad-ignore", collect, []).option("--allow-broad-ignore [boolean]", "Ganze Zonen als --ignore-Muster (*.com, *.co.uk, .ch) bewusst zulassen; sie werden in Konsole und Report als Warnung ausgewiesen; `*` und nackte TLDs bleiben abgelehnt (default: false)").option("--accept-selector <css>", "Eigener CSS-Selektor des Akzeptieren-Buttons (mehrfach oder kommagetrennt)", collect, []).option("--reject-selector <css>", "Eigener CSS-Selektor des Ablehnen-Buttons (mehrfach oder kommagetrennt)", collect, []).option("--settings-selector <css>", "Eigener CSS-Selektor des Einstellungen-Buttons (mehrfach oder kommagetrennt)", collect, []).option("--banner-frame <teilstring>", "Teilstring der URL des Banner-iframes (CMP im Frame)").option("--max-subpages <n>", "Obergrenze der Unterseiten-Stichprobe (Szenario subpage-spa), ganze Zahl 1\u201340 (default: 12; Env CPCG_MAX_SUBPAGES). Bleibt die Stichprobe unvollst\xE4ndig, weist das Gate das aus (Exit-Code unver\xE4ndert)").option("--banner-wait <sekunden>", "Wartefenster auf einen (sp\xE4t erscheinenden) Banner in Sekunden, 1\u201360 (default: 8, bei Seiten ohne Banner-Hinweis fr\xFCher beendet; mit Angabe wird das Fenster ausgesch\xF6pft, auch bei --accept-selector/--reject-selector; Env CPCG_BANNER_WAIT)").option("--license <token>", "Cuonztech CPCG Lizenzcode CPCG-\u2026 oder Kunden-Key CPCG-K-\u2026 (oder Env CPCG_LICENSE) \u2014 nach Online-Kauf per E-Mail").option(
|
|
47
|
+
"--jurisdiction <code>",
|
|
48
|
+
"Rechtsraum, der in Konsole und report.json hervorgehoben wird: CH | EU | UK | US-CA | BR | ROW (default: CH). Der Report f\xFChrt alle Rechtsr\xE4ume auf."
|
|
49
|
+
).option("--report-pdf [boolean]", "Deluxe-PDF (Deckblatt + Evidence) nach dem Scan erzeugen \u2014 Lizenz erforderlich").option("--client <name>", "Kundenname f\xFCr Kundenreport und PDF-Deckblatt \u2014 Lizenz erforderlich (PDF-default: Host der Ziel-URL)").option("--demo [boolean]", "Demo-Lauf \u2014 Report wird als synthetische Testdaten gekennzeichnet").addHelpText("after", EXIT_CODES_HELP).allowExcessArguments(false).showHelpAfterError("(cpcg --help zeigt alle Optionen)").exitOverride().action(async (options) => {
|
|
58
50
|
try {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
51
|
+
if (!options.url && !process.env.CPCG_TARGET_URL && !process.env.STAGING_URL) {
|
|
52
|
+
console.error(pc.red("\u2716 --url fehlt (Ziel-URL, http/https)."));
|
|
53
|
+
program.outputHelp({ error: true });
|
|
54
|
+
finish(EXIT.ERROR);
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
const config = resolveGateConfig(options);
|
|
58
|
+
await assertReportOutputWritable(config.reportOutput);
|
|
59
|
+
const isLoopback = isLoopbackTarget(config.targetUrl);
|
|
60
|
+
const entitlement = await resolveEntitlement({ license: options.license });
|
|
61
|
+
if (entitlement.status === "invalid") {
|
|
62
|
+
for (const [i, line] of entitlement.messages.entries()) console.error(i === 0 ? pc.red(line) : pc.dim(line));
|
|
63
|
+
finish(EXIT.LICENSE);
|
|
64
|
+
return;
|
|
66
65
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
console.error(pc.red("\u2716 Lizenz gesperrt: " + resolved.detail));
|
|
78
|
-
console.error(pc.dim(" Kontakt: support@cuonztech.ch"));
|
|
79
|
-
} else {
|
|
80
|
-
console.error(pc.red("\u2716 Lizenz nicht verf\xFCgbar: " + resolved.detail));
|
|
81
|
-
console.error(pc.dim(" \xABNicht erreichbar\xBB ist nicht \xABgesperrt\xBB \u2014 bei St\xF6rung greift die Offline-Freigabe."));
|
|
82
|
-
}
|
|
83
|
-
process.exit(2);
|
|
66
|
+
const licensedAsked = requestedLicensedFeatures(config);
|
|
67
|
+
const licensedList = licensedAsked.length > 0 ? licensedAsked.join(", ") : "PDF-Report, Kundenreport (--client), Fix-Commit und Fix-PR";
|
|
68
|
+
if (entitlement.status === "licensed") {
|
|
69
|
+
console.log(pc.green(entitlement.summary));
|
|
70
|
+
config.license = { entitled: true };
|
|
71
|
+
} else if (entitlement.status === "unreachable") {
|
|
72
|
+
config.license = { entitled: false, reason: "unreachable" };
|
|
73
|
+
if (!isLoopback) {
|
|
74
|
+
console.log(pc.yellow("\u26A0 Lizenzserver nicht erreichbar und keine g\xFCltige Offline-Freigabe."));
|
|
75
|
+
console.log(pc.dim(` Scan und Gate laufen weiter; \xFCbersprungen werden lizenzpflichtige Funktionen (${licensedList}).`));
|
|
84
76
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
const verdict = options.url ? verifyLicenseForTarget(legacyToken, options.url) : verifyLicense(legacyToken);
|
|
94
|
-
if (!verdict.valid) {
|
|
95
|
-
console.error(pc.red(`\u2716 Lizenz ung\xFCltig: ${verdict.reason}`));
|
|
96
|
-
console.error(pc.dim(" Erwerb: cuonztech.ch \u2014 Code nach Online-Kauf per E-Mail."));
|
|
97
|
-
process.exit(2);
|
|
77
|
+
} else {
|
|
78
|
+
config.license = { entitled: false, reason: "none" };
|
|
79
|
+
if (!isLoopback) {
|
|
80
|
+
console.log(
|
|
81
|
+
pc.dim(
|
|
82
|
+
licensedAsked.length > 0 ? `\u2139 Keine Lizenz: Scan und Gate sind frei. \xDCbersprungen (lizenzpflichtig): ${licensedList} \u2014 ${LICENSE_CONTACT}` : `\u2139 Keine Lizenz: Scan und Gate sind frei. Lizenzpflichtig sind ${licensedList} \u2014 ${LICENSE_CONTACT}`
|
|
83
|
+
)
|
|
84
|
+
);
|
|
98
85
|
}
|
|
99
|
-
const c = verdict.claims;
|
|
100
|
-
console.log(pc.green(`\u2714 Cuonztech-Lizenz aktiv (${c?.tier ?? "pro"} \xB7 g\xFCltig bis ${new Date((c?.exp ?? 0) * 1e3).toLocaleDateString("de-CH")})`));
|
|
101
|
-
} else if (!customerKey && !legacyToken && !isLocal) {
|
|
102
|
-
console.log(pc.yellow("\u26A0 Keine Lizenz gesetzt (Trial-Modus). Kunden-Key: CPCG_CUSTOMER_KEY=CPCG-K-\u2026"));
|
|
103
86
|
}
|
|
104
|
-
|
|
87
|
+
if (!config.llmRemediation && (options.apiKey || process.env.CPCG_LLM_API_KEY)) {
|
|
88
|
+
console.log(pc.yellow("\u26A0 LLM-API-Key gesetzt, aber --llm-remediation fehlt: Quellcode wird NICHT an einen LLM-Anbieter gesendet."));
|
|
89
|
+
}
|
|
90
|
+
const juris = resolveJurisdiction(config.jurisdiction);
|
|
105
91
|
console.log(pc.blue("\u2139 Jurisdiktion: "), pc.white(`${juris.label} (${juris.code})`));
|
|
106
92
|
console.log(pc.dim(` ${juris.frameworks.join(" \xB7 ")}`));
|
|
107
|
-
const config = resolveGateConfig(options);
|
|
108
93
|
const gate = new PrivacyChaosGate();
|
|
109
94
|
const result = await gate.run(config);
|
|
110
|
-
|
|
95
|
+
finish(result.exitCode);
|
|
111
96
|
} catch (error) {
|
|
97
|
+
if (error instanceof CommanderError) throw error;
|
|
112
98
|
console.error(pc.bold(pc.red("\n\u274C CPCG Gate Execution Failed:")));
|
|
113
99
|
if (error instanceof Error) {
|
|
114
100
|
console.error(pc.red(error.message));
|
|
115
101
|
} else {
|
|
116
102
|
console.error(pc.red(String(error)));
|
|
117
103
|
}
|
|
118
|
-
|
|
104
|
+
finish(EXIT.ERROR);
|
|
119
105
|
}
|
|
120
106
|
});
|
|
121
|
-
|
|
122
|
-
|
|
107
|
+
for (const event of ["uncaughtException", "unhandledRejection"]) {
|
|
108
|
+
process.on(event, (err) => {
|
|
109
|
+
console.error(pc.bold(pc.red("\n\u274C CPCG unerwarteter Fehler:")));
|
|
110
|
+
console.error(pc.red(err instanceof Error ? err.message : String(err)));
|
|
111
|
+
process.exit(EXIT.ERROR);
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
try {
|
|
115
|
+
await program.parseAsync(process.argv);
|
|
116
|
+
} catch (err) {
|
|
117
|
+
if (err instanceof CommanderError) {
|
|
118
|
+
if (err.exitCode !== 0 && err.code === "commander.excessArguments") {
|
|
119
|
+
console.error(pc.red("cpcg hat keine Subcommands (z. B. \xABscan\xBB); Aufruf: cpcg --url <url> [Optionen]"));
|
|
120
|
+
}
|
|
121
|
+
finish(err.exitCode !== 0 ? EXIT.ERROR : 0);
|
|
122
|
+
} else {
|
|
123
|
+
throw err;
|
|
124
|
+
}
|
|
125
|
+
}
|