cuonztech-privacy-chaos-gate 0.3.1 → 0.3.3
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.md +1 -1
- package/action.yml +1 -1
- package/dist/{chunk-BZFL6QDO.js → chunk-VCRSP7XL.js} +39 -18
- package/dist/cli.js +7 -2
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -35,7 +35,7 @@ Das Paket ist proprietär und source-available (Datei `LICENSE`, ab 0.2.1; die V
|
|
|
35
35
|
Online-Kauf → Code per Mail.
|
|
36
36
|
|
|
37
37
|
```bash
|
|
38
|
-
export CPCG_LICENSE="CPCG-…" #
|
|
38
|
+
export CPCG_LICENSE="CPCG-…" # Kunden-Key CPCG-K-… (Pilot und Abo; ältere Offline-Lizenzcodes CPCG-… gelten weiter)
|
|
39
39
|
cpcg --url https://staging.example.com --jurisdiction CH
|
|
40
40
|
```
|
|
41
41
|
|
package/action.yml
CHANGED
|
@@ -188,7 +188,7 @@ runs:
|
|
|
188
188
|
shell: bash
|
|
189
189
|
env:
|
|
190
190
|
ACTION_PATH: ${{ github.action_path }}
|
|
191
|
-
CPCG_PINNED_VERSION: "0.3.
|
|
191
|
+
CPCG_PINNED_VERSION: "0.3.3" # = package.json version; bei jedem Release mit anheben (tests/action-pin.test.ts prüft das)
|
|
192
192
|
run: |
|
|
193
193
|
set -euo pipefail
|
|
194
194
|
|
|
@@ -5263,6 +5263,21 @@ var ChaosScanner = class {
|
|
|
5263
5263
|
checkedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
5264
5264
|
});
|
|
5265
5265
|
}
|
|
5266
|
+
/** Hinweise (nie blockierend) auf Fremd-Iframes ohne Eintrag in der Tracker-Liste. */
|
|
5267
|
+
unlistedEmbedHints(scenario, unlisted) {
|
|
5268
|
+
return unlisted.map((u) => ({
|
|
5269
|
+
id: `${scenario}-unlisted-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`,
|
|
5270
|
+
ruleId: "pre-consent-embed",
|
|
5271
|
+
scenario,
|
|
5272
|
+
trackerName: `${UNLISTED_EMBED_PREFIX}${u.host})`,
|
|
5273
|
+
trackerCategory: "functional",
|
|
5274
|
+
requestUrl: u.sample,
|
|
5275
|
+
method: "GET",
|
|
5276
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5277
|
+
description: `Eingebetteter Inhalt von ${u.host} l\xE4dt vor Consent-Freigabe ${u.count} Request(s); der Anbieter steht nicht in der Tracker-Liste.`,
|
|
5278
|
+
advisory: true
|
|
5279
|
+
}));
|
|
5280
|
+
}
|
|
5266
5281
|
/**
|
|
5267
5282
|
* Hosts eingebetteter Fremd-Frames, die vor der Freigabe Requests auslösen, ohne dass die Tracker-Liste sie kennt.
|
|
5268
5283
|
* Eigene Hosts, Loopback/IP und bereits erkannte Tracker zählen nicht.
|
|
@@ -5292,7 +5307,15 @@ var ChaosScanner = class {
|
|
|
5292
5307
|
return false;
|
|
5293
5308
|
}
|
|
5294
5309
|
});
|
|
5295
|
-
|
|
5310
|
+
const known = timeline.some((t) => {
|
|
5311
|
+
try {
|
|
5312
|
+
const h = new URL(t.url).hostname;
|
|
5313
|
+
return !!t.trackerName && (h === host || h.endsWith(`.${host}`));
|
|
5314
|
+
} catch {
|
|
5315
|
+
return false;
|
|
5316
|
+
}
|
|
5317
|
+
});
|
|
5318
|
+
if (own.length && !known) out.push({ host, count: own.length, sample: own[0].url });
|
|
5296
5319
|
}
|
|
5297
5320
|
return out;
|
|
5298
5321
|
}
|
|
@@ -6496,20 +6519,7 @@ var ChaosScanner = class {
|
|
|
6496
6519
|
await this.snap(page, "embed-leaks", "01-embeds", screenshots, actionTimeline, actionSequence);
|
|
6497
6520
|
const storageAfter = await this.snapshotStorage(page);
|
|
6498
6521
|
const unlistedEmbeds = this.unlistedEmbedHosts(embeds, config.targetUrl, timeline);
|
|
6499
|
-
|
|
6500
|
-
violations.push({
|
|
6501
|
-
id: `embed-leaks-unlisted-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`,
|
|
6502
|
-
ruleId: "pre-consent-embed",
|
|
6503
|
-
scenario: "embed-leaks",
|
|
6504
|
-
trackerName: `${UNLISTED_EMBED_PREFIX}${u.host})`,
|
|
6505
|
-
trackerCategory: "functional",
|
|
6506
|
-
requestUrl: u.sample,
|
|
6507
|
-
method: "GET",
|
|
6508
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6509
|
-
description: `Eingebetteter Inhalt von ${u.host} l\xE4dt vor Consent-Freigabe ${u.count} Request(s); der Anbieter steht nicht in der Tracker-Liste.`,
|
|
6510
|
-
advisory: true
|
|
6511
|
-
});
|
|
6512
|
-
}
|
|
6522
|
+
violations.push(...this.unlistedEmbedHints("embed-leaks", unlistedEmbeds));
|
|
6513
6523
|
if (embeds.length === 0 && embedLeakCount === 0 && embedAdvisoryCount === 0) {
|
|
6514
6524
|
const status2 = embedsEvalOk ? "not-applicable" : "not-checkable";
|
|
6515
6525
|
evidence.push({
|
|
@@ -6701,8 +6711,13 @@ var ChaosScanner = class {
|
|
|
6701
6711
|
const actionTimeline = [];
|
|
6702
6712
|
const screenshots = [];
|
|
6703
6713
|
let armed = false;
|
|
6714
|
+
const frameSrcs = [];
|
|
6704
6715
|
onTargetRequest(page, scope, (req) => {
|
|
6705
6716
|
if (!armed) return;
|
|
6717
|
+
try {
|
|
6718
|
+
if (req.isNavigationRequest() && req.frame().parentFrame() === page.mainFrame()) frameSrcs.push({ src: req.url() });
|
|
6719
|
+
} catch {
|
|
6720
|
+
}
|
|
6706
6721
|
const hit = identifyTracker(req.url());
|
|
6707
6722
|
timeline.push({ at: (/* @__PURE__ */ new Date()).toISOString(), url: req.url(), trackerName: hit?.name, trackerCategory: hit?.category, phase: "pre-consent" });
|
|
6708
6723
|
if (hit && this.trackerVerdict(hit) !== "skip") {
|
|
@@ -6807,6 +6822,9 @@ var ChaosScanner = class {
|
|
|
6807
6822
|
await this.snap(page, "subpage-spa", "02-after-spa", screenshots, actionTimeline, actionSequence);
|
|
6808
6823
|
const storageAfter = await this.snapshotStorage(page);
|
|
6809
6824
|
await this.applyCnameCloaking("subpage-spa", config, timeline, violations, /* @__PURE__ */ new Map());
|
|
6825
|
+
const unlistedFrames = this.unlistedEmbedHosts(frameSrcs, config.targetUrl, timeline);
|
|
6826
|
+
violations.push(...this.unlistedEmbedHints("subpage-spa", unlistedFrames));
|
|
6827
|
+
const frameNote = unlistedFrames.length ? ` Fremd-Inhalt ohne Eintrag in der Tracker-Liste l\xE4dt auf Unterseiten vor Freigabe: ${unlistedFrames.map((u) => `${u.host} (${u.count} Request(s))`).join(", ")} \u2014 Hinweis, ohne Gate-Wirkung.` : "";
|
|
6810
6828
|
const leaks = timeline.filter((e) => this.isBlocking(e) && e.phase === "pre-consent");
|
|
6811
6829
|
const status = leaks.length > 0 ? "suspicious" : reached.length === 0 ? "not-checkable" : "passed";
|
|
6812
6830
|
const sampling = deep ? deepSamplingText(deep, reached.length, visited.length, deepSaved, budgetExhausted) : sample.total > reached.length ? `Stichprobe: ${reached.length} von ${sample.total} verlinkten Unterseiten gepr\xFCft \u2014 keine vollst\xE4ndige Pr\xFCfung der Website; nicht gepr\xFCfte Routen k\xF6nnen Verst\xF6sse enthalten (--max-subpages erh\xF6hen).` : `${reached.length} von ${sample.total} verlinkten Unterseiten der Startseite gepr\xFCft (Unterseiten ohne Link auf der Startseite bleiben ungepr\xFCft).`;
|
|
@@ -6826,7 +6844,7 @@ var ChaosScanner = class {
|
|
|
6826
6844
|
evidence.push({
|
|
6827
6845
|
scenario: "subpage-spa",
|
|
6828
6846
|
status,
|
|
6829
|
-
note: status === "passed" ? `Kein Tracking ohne Consent auf ${reached.length} verlinkter Unterseite(n)${spaNavigations > 0 ? ` und bei ${spaNavigations} clientseitiger SPA-Navigation(en)` : " (keine clientseitige SPA-Navigation beobachtet \u2014 Links laden die Seite neu)"}.${spaSkipped ? ` ${spaSkipped} SPA-Durchgang/-g\xE4nge ausgelassen (Seite nicht stabil ladbar).` : ""} ${sampling}` : status === "suspicious" ? `${leaks.length} Tracker auf Unterseite/SPA ohne Consent. ${sampling}` : links.length === 0 ? deep ? `Weder Sitemap noch Startseite lieferten eine Unterseite im Zielbereich \u2014 Unterseiten/SPA nicht pr\xFCfbar. ${sampling}` : "Keine verlinkte Same-Origin-Unterseite auf der Startseite gefunden \u2014 Unterseiten/SPA nicht pr\xFCfbar." : `Keine der verlinkten Unterseiten antwortete mit HTTP-Status < 400 \u2014 Unterseiten/SPA nicht pr\xFCfbar.${deep ? ` ${sampling}` : ""}`,
|
|
6847
|
+
note: status === "passed" ? `Kein Tracking ohne Consent auf ${reached.length} verlinkter Unterseite(n)${spaNavigations > 0 ? ` und bei ${spaNavigations} clientseitiger SPA-Navigation(en)` : " (keine clientseitige SPA-Navigation beobachtet \u2014 Links laden die Seite neu)"}.${spaSkipped ? ` ${spaSkipped} SPA-Durchgang/-g\xE4nge ausgelassen (Seite nicht stabil ladbar).` : ""} ${sampling}${frameNote}` : status === "suspicious" ? `${leaks.length} Tracker auf Unterseite/SPA ohne Consent. ${sampling}${frameNote}` : links.length === 0 ? deep ? `Weder Sitemap noch Startseite lieferten eine Unterseite im Zielbereich \u2014 Unterseiten/SPA nicht pr\xFCfbar. ${sampling}` : "Keine verlinkte Same-Origin-Unterseite auf der Startseite gefunden \u2014 Unterseiten/SPA nicht pr\xFCfbar." : `Keine der verlinkten Unterseiten antwortete mit HTTP-Status < 400 \u2014 Unterseiten/SPA nicht pr\xFCfbar.${deep ? ` ${sampling}` : ""}`,
|
|
6830
6848
|
actionSequence,
|
|
6831
6849
|
actionTimeline,
|
|
6832
6850
|
requestTimeline: timeline,
|
|
@@ -10878,7 +10896,7 @@ async function resolveLicenseGrant(opts) {
|
|
|
10878
10896
|
return {
|
|
10879
10897
|
ok: false,
|
|
10880
10898
|
reason: "invalid",
|
|
10881
|
-
detail:
|
|
10899
|
+
detail: `Grant vom Dienst nicht verifizierbar${fresh.detail ? ` (${fresh.detail.replace(/\.$/, "")})` : ""}.`
|
|
10882
10900
|
};
|
|
10883
10901
|
}
|
|
10884
10902
|
if (outcome.kind === "rejected") {
|
|
@@ -10957,6 +10975,9 @@ async function resolveEntitlement(input = {}) {
|
|
|
10957
10975
|
return { status: "unreachable", detail: resolved.detail };
|
|
10958
10976
|
}
|
|
10959
10977
|
if (resolved.reason === "blocked") {
|
|
10978
|
+
if (/^Laufzeit abgelaufen/i.test(resolved.detail)) {
|
|
10979
|
+
return { status: "invalid", messages: ["\u2716 Lizenz abgelaufen: " + resolved.detail] };
|
|
10980
|
+
}
|
|
10960
10981
|
return { status: "invalid", messages: ["\u2716 Lizenz gesperrt: " + resolved.detail, " Kontakt: support@cuonztech.ch"] };
|
|
10961
10982
|
}
|
|
10962
10983
|
return { status: "invalid", messages: ["\u2716 Lizenz nicht verf\xFCgbar: " + resolved.detail] };
|
|
@@ -10988,7 +11009,7 @@ import pc from "picocolors";
|
|
|
10988
11009
|
import { createHash } from "crypto";
|
|
10989
11010
|
import fs10 from "fs/promises";
|
|
10990
11011
|
import path10 from "path";
|
|
10991
|
-
var CPCG_VERSION = true ? "0.3.
|
|
11012
|
+
var CPCG_VERSION = true ? "0.3.3" : "dev";
|
|
10992
11013
|
function scanConfigOf(config) {
|
|
10993
11014
|
return {
|
|
10994
11015
|
targetUrl: config.targetUrl,
|
package/dist/cli.js
CHANGED
|
@@ -11,12 +11,17 @@ import {
|
|
|
11
11
|
resolveEntitlement,
|
|
12
12
|
resolveGateConfig,
|
|
13
13
|
resolveJurisdictions
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-VCRSP7XL.js";
|
|
15
15
|
|
|
16
16
|
// src/cli.ts
|
|
17
17
|
import { Command, CommanderError } from "commander";
|
|
18
18
|
import pc from "picocolors";
|
|
19
|
-
var VERSION = true ? "0.3.
|
|
19
|
+
var VERSION = true ? "0.3.3" : "dev";
|
|
20
|
+
for (const stream of [process.stdout, process.stderr]) {
|
|
21
|
+
stream.on("error", (err) => {
|
|
22
|
+
if (err.code !== "EPIPE") throw err;
|
|
23
|
+
});
|
|
24
|
+
}
|
|
20
25
|
loadProjectEnv();
|
|
21
26
|
function finish(code) {
|
|
22
27
|
process.exitCode = code;
|
package/dist/index.d.ts
CHANGED
|
@@ -2439,6 +2439,8 @@ declare class ChaosScanner implements ChaosScannerInterface {
|
|
|
2439
2439
|
* Ergebnis: passed nur bei ausgeführter Aktion und null Illegal-Funden.
|
|
2440
2440
|
*/
|
|
2441
2441
|
private runScenario;
|
|
2442
|
+
/** Hinweise (nie blockierend) auf Fremd-Iframes ohne Eintrag in der Tracker-Liste. */
|
|
2443
|
+
private unlistedEmbedHints;
|
|
2442
2444
|
/**
|
|
2443
2445
|
* Hosts eingebetteter Fremd-Frames, die vor der Freigabe Requests auslösen, ohne dass die Tracker-Liste sie kennt.
|
|
2444
2446
|
* Eigene Hosts, Loopback/IP und bereits erkannte Tracker zählen nicht.
|
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cuonztech-privacy-chaos-gate",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.3",
|
|
4
4
|
"description": "CuonzTech Privacy Chaos Gate — CI/CD Consent-Scanner, Chaos-Szenarien und Auto-Fix-PR",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"test:watch": "vitest",
|
|
40
40
|
"typecheck": "tsc --noEmit",
|
|
41
41
|
"typecheck:all": "tsc -p tsconfig.test.json --noEmit",
|
|
42
|
-
"prepublishOnly": "pnpm run typecheck && pnpm run
|
|
42
|
+
"prepublishOnly": "pnpm run typecheck && pnpm run build && pnpm run test",
|
|
43
43
|
"demo": "tsx scripts/scan-demo-portal.mts && tsx scripts/make-deluxe-report.mts && tsx scripts/package-delivery.mts",
|
|
44
44
|
"demo:report": "tsx scripts/make-deluxe-report.mts"
|
|
45
45
|
},
|