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 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-…" # Lizenzcode (Pilot/Einmal) oder Kunden-Key CPCG-K-… (Abo)
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.1" # = package.json version; bei jedem Release mit anheben (tests/action-pin.test.ts prüft das)
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
- if (own.length) out.push({ host, count: own.length, sample: own[0].url });
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
- for (const u of unlistedEmbeds) {
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: "Grant vom Dienst nicht verifizierbar."
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.1" : "dev";
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-BZFL6QDO.js";
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.1" : "dev";
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
@@ -56,7 +56,7 @@ import {
56
56
  uncheckableReasons,
57
57
  uncheckableReasonsForReport,
58
58
  waitForVerifyUrl
59
- } from "./chunk-BZFL6QDO.js";
59
+ } from "./chunk-VCRSP7XL.js";
60
60
 
61
61
  // src/index.ts
62
62
  async function runGate(optionsOrConfig) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cuonztech-privacy-chaos-gate",
3
- "version": "0.3.1",
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 test && pnpm run build",
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
  },