cuonztech-privacy-chaos-gate 0.3.0 → 0.3.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/action.yml +1 -1
- package/dist/{chunk-YOWEZW3W.js → chunk-BZFL6QDO.js} +61 -4
- package/dist/cli.js +2 -2
- package/dist/index.d.ts +5 -0
- package/dist/index.js +1 -1
- package/package.json +1 -1
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.1" # = 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
|
|
|
@@ -517,6 +517,7 @@ function hintViolations(violations, strict = false, jurisdictions = resolveJuris
|
|
|
517
517
|
const blocking = new Set(blockingViolations(violations, strict, jurisdictions));
|
|
518
518
|
return violations.filter((v) => !blocking.has(v));
|
|
519
519
|
}
|
|
520
|
+
var UNLISTED_EMBED_PREFIX = "Fremd-Inhalt (";
|
|
520
521
|
function hintReasonOf(v, jurisdictions) {
|
|
521
522
|
if (isAdvisory(v) && v.trackerCategory === "cookieless-analytics") {
|
|
522
523
|
return "Cookielose Reichweitenmessung (keine Cookies, kein Zugriff auf das Ger\xE4t): nach verbreiteter Aufsichtspraxis einwilligungsfrei, wenn sie nur der Reichweitenmessung dient; dokumentiert, mit --strict blockierend.";
|
|
@@ -524,6 +525,9 @@ function hintReasonOf(v, jurisdictions) {
|
|
|
524
525
|
if (isAdvisory(v) && v.tcf === "denied") {
|
|
525
526
|
return "Request nach dem Ablehnen mit IAB-TCF-String, der die Ablehnung tr\xE4gt (kein Zweck erteilt): der Anbieter muss selbst verzichten; der Request selbst \xFCbermittelt trotzdem Daten (IP, Seite). Dokumentiert, mit --strict blockierend.";
|
|
526
527
|
}
|
|
528
|
+
if (isAdvisory(v) && v.trackerName.startsWith(UNLISTED_EMBED_PREFIX)) {
|
|
529
|
+
return "Eingebetteter Fremd-Inhalt, der vor der Einwilligung l\xE4dt und nicht in der Tracker-Liste steht: Inhalt und Datenschutzerkl\xE4rung pr\xFCfen; dokumentiert, mit --strict blockierend.";
|
|
530
|
+
}
|
|
527
531
|
if (isAdvisory(v)) return "Rein funktionaler Drittanbieter (z. B. Schriften): kein Verstoss, dokumentiert.";
|
|
528
532
|
return hintReasonFor(jurisdictions, v.ruleId, v.trackerCategory);
|
|
529
533
|
}
|
|
@@ -1406,6 +1410,7 @@ var CURATED_VENDOR_DOMAINS = [
|
|
|
1406
1410
|
["gc.zgo.at", "GoatCounter", "cookieless-analytics"],
|
|
1407
1411
|
["withcabin.com", "Cabin Analytics", "cookieless-analytics"],
|
|
1408
1412
|
["counter.dev", "counter.dev", "cookieless-analytics"],
|
|
1413
|
+
["cloudflareinsights.com", "Cloudflare Web Analytics", "cookieless-analytics"],
|
|
1409
1414
|
// --- Werbung, Ad-Tech, Datenplattformen ---
|
|
1410
1415
|
["googletagservices.com", "Google Ad Manager", "marketing"],
|
|
1411
1416
|
["amazon-adsystem.com", "Amazon Ads", "marketing"],
|
|
@@ -1546,7 +1551,8 @@ var KNOWN_TRACKERS = [
|
|
|
1546
1551
|
name: "Google Analytics / Google Tag (gtag.js)",
|
|
1547
1552
|
category: "analytics",
|
|
1548
1553
|
consentModeLoader: true,
|
|
1549
|
-
|
|
1554
|
+
// `/a?id=G-…` ist der Beacon des Google-Tags (Konfiguration/Diagnose) derselben Messkennung.
|
|
1555
|
+
patterns: [at(www("googletagmanager.com"), "/gtag/js$"), at(www("googletagmanager.com"), "/a$")]
|
|
1550
1556
|
},
|
|
1551
1557
|
{
|
|
1552
1558
|
name: "Meta (Facebook) Pixel",
|
|
@@ -3646,6 +3652,9 @@ function ruleIdFor(phase) {
|
|
|
3646
3652
|
if (phase === "gpc") return "gpc-ignored";
|
|
3647
3653
|
return phase === "post-wipe" ? "tracking-without-consent" : phase === "post-reject" || phase === "post-reject-restart" || phase === "post-tamper" ? "tracking-after-deny" : phase === "post-revoke" ? "tracking-after-revoke" : "pre-consent-tracking";
|
|
3648
3654
|
}
|
|
3655
|
+
function isFirstPartyGoogleMeasurement(t) {
|
|
3656
|
+
return !!t.trackerName?.startsWith("Google Analytics (First-Party-Endpoint");
|
|
3657
|
+
}
|
|
3649
3658
|
function asScope(s) {
|
|
3650
3659
|
return typeof s === "string" ? new TargetScope(s) : s;
|
|
3651
3660
|
}
|
|
@@ -5254,6 +5263,39 @@ var ChaosScanner = class {
|
|
|
5254
5263
|
checkedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
5255
5264
|
});
|
|
5256
5265
|
}
|
|
5266
|
+
/**
|
|
5267
|
+
* Hosts eingebetteter Fremd-Frames, die vor der Freigabe Requests auslösen, ohne dass die Tracker-Liste sie kennt.
|
|
5268
|
+
* Eigene Hosts, Loopback/IP und bereits erkannte Tracker zählen nicht.
|
|
5269
|
+
*/
|
|
5270
|
+
unlistedEmbedHosts(embeds, targetUrl, timeline) {
|
|
5271
|
+
let targetHost;
|
|
5272
|
+
try {
|
|
5273
|
+
targetHost = new URL(targetUrl).hostname;
|
|
5274
|
+
} catch {
|
|
5275
|
+
return [];
|
|
5276
|
+
}
|
|
5277
|
+
const hosts = /* @__PURE__ */ new Set();
|
|
5278
|
+
for (const e of embeds) {
|
|
5279
|
+
try {
|
|
5280
|
+
const u = new URL(e.src, targetUrl);
|
|
5281
|
+
if (/^https?:$/.test(u.protocol) && !/^[\d.]+$|^\[|^localhost$/i.test(u.hostname) && !isFirstPartyHost(u.hostname, targetHost)) hosts.add(u.hostname);
|
|
5282
|
+
} catch {
|
|
5283
|
+
}
|
|
5284
|
+
}
|
|
5285
|
+
const out = [];
|
|
5286
|
+
for (const host of hosts) {
|
|
5287
|
+
const own = timeline.filter((t) => {
|
|
5288
|
+
try {
|
|
5289
|
+
const h = new URL(t.url).hostname;
|
|
5290
|
+
return !t.trackerName && (h === host || h.endsWith(`.${host}`));
|
|
5291
|
+
} catch {
|
|
5292
|
+
return false;
|
|
5293
|
+
}
|
|
5294
|
+
});
|
|
5295
|
+
if (own.length) out.push({ host, count: own.length, sample: own[0].url });
|
|
5296
|
+
}
|
|
5297
|
+
return out;
|
|
5298
|
+
}
|
|
5257
5299
|
/**
|
|
5258
5300
|
* CNAME-Cloaking nachtragen: Hosts der eigenen Seite aus der Zeitlinie, die kein Tracker sind, per DNS prüfen. Zeigt die
|
|
5259
5301
|
* CNAME-Kette auf einen Tracker-Host, wird der Request mit seiner ursprünglichen Phase als Tracker-Befund eingetragen.
|
|
@@ -6293,7 +6335,7 @@ var ChaosScanner = class {
|
|
|
6293
6335
|
googleRequests.push(t.url);
|
|
6294
6336
|
}
|
|
6295
6337
|
}
|
|
6296
|
-
const hits = timeline.filter((t) => isGoogleMeasurementRequest(t.url) || isGoogleMeasurementRequest(viaCname(t.url) ?? "")).map((t) => ({ ...t, signal: summarizeSignals(parseConsentSignals(t.url)) }));
|
|
6338
|
+
const hits = timeline.filter((t) => isGoogleMeasurementRequest(t.url) || isGoogleMeasurementRequest(viaCname(t.url) ?? "") || isFirstPartyGoogleMeasurement(t)).map((t) => ({ ...t, signal: summarizeSignals(parseConsentSignals(t.url)) }));
|
|
6297
6339
|
const signalled = hits.some((h) => h.signal !== "none");
|
|
6298
6340
|
const hasConsentMode = decided && (Object.keys(defaults).length > 0 || Object.keys(after).length > 0 || signalled);
|
|
6299
6341
|
const gaCookies = storageAfter.cookies.filter((c) => /^_ga|_gid|_gat|_gcl/i.test(c));
|
|
@@ -6453,6 +6495,21 @@ var ChaosScanner = class {
|
|
|
6453
6495
|
await page.waitForTimeout(1500);
|
|
6454
6496
|
await this.snap(page, "embed-leaks", "01-embeds", screenshots, actionTimeline, actionSequence);
|
|
6455
6497
|
const storageAfter = await this.snapshotStorage(page);
|
|
6498
|
+
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
|
+
}
|
|
6456
6513
|
if (embeds.length === 0 && embedLeakCount === 0 && embedAdvisoryCount === 0) {
|
|
6457
6514
|
const status2 = embedsEvalOk ? "not-applicable" : "not-checkable";
|
|
6458
6515
|
evidence.push({
|
|
@@ -6476,7 +6533,7 @@ var ChaosScanner = class {
|
|
|
6476
6533
|
evidence.push({
|
|
6477
6534
|
scenario: "embed-leaks",
|
|
6478
6535
|
status,
|
|
6479
|
-
note: (status === "passed" ? embedAdvisoryCount ? `${embeds.length} Embed(s) gefunden, keine marketing-/analytics-relevanten Drittanbieter-Requests vor Freigabe.` : `${embeds.length} Embed(s) gefunden, keine Drittanbieter-Requests vor Freigabe.` : `${embedLeakCount} Embed-/Widget-Request(s) vor Consent-Freigabe.`) + (embedAdvisoryCount ? ` ${embedAdvisoryCount} Hinweis(e) auf rein funktionale Einbettungen (z. B. Karten) vor Freigabe \u2014 im Report sichtbar, ohne Gate-Wirkung.` : ""),
|
|
6536
|
+
note: (status === "passed" ? embedAdvisoryCount ? `${embeds.length} Embed(s) gefunden, keine marketing-/analytics-relevanten Drittanbieter-Requests vor Freigabe.` : `${embeds.length} Embed(s) gefunden, keine Drittanbieter-Requests vor Freigabe.` : `${embedLeakCount} Embed-/Widget-Request(s) vor Consent-Freigabe.`) + (embedAdvisoryCount ? ` ${embedAdvisoryCount} Hinweis(e) auf rein funktionale Einbettungen (z. B. Karten) vor Freigabe \u2014 im Report sichtbar, ohne Gate-Wirkung.` : "") + (unlistedEmbeds.length ? ` Fremd-Inhalt ohne Eintrag in der Tracker-Liste l\xE4dt vor Freigabe: ${unlistedEmbeds.map((u) => `${u.host} (${u.count} Request(s))`).join(", ")} \u2014 Hinweis, ohne Gate-Wirkung.` : ""),
|
|
6480
6537
|
actionSequence,
|
|
6481
6538
|
actionTimeline,
|
|
6482
6539
|
requestTimeline: timeline,
|
|
@@ -10931,7 +10988,7 @@ import pc from "picocolors";
|
|
|
10931
10988
|
import { createHash } from "crypto";
|
|
10932
10989
|
import fs10 from "fs/promises";
|
|
10933
10990
|
import path10 from "path";
|
|
10934
|
-
var CPCG_VERSION = true ? "0.3.
|
|
10991
|
+
var CPCG_VERSION = true ? "0.3.1" : "dev";
|
|
10935
10992
|
function scanConfigOf(config) {
|
|
10936
10993
|
return {
|
|
10937
10994
|
targetUrl: config.targetUrl,
|
package/dist/cli.js
CHANGED
|
@@ -11,12 +11,12 @@ import {
|
|
|
11
11
|
resolveEntitlement,
|
|
12
12
|
resolveGateConfig,
|
|
13
13
|
resolveJurisdictions
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-BZFL6QDO.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.1" : "dev";
|
|
20
20
|
loadProjectEnv();
|
|
21
21
|
function finish(code) {
|
|
22
22
|
process.exitCode = code;
|
package/dist/index.d.ts
CHANGED
|
@@ -2439,6 +2439,11 @@ 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
|
+
/**
|
|
2443
|
+
* Hosts eingebetteter Fremd-Frames, die vor der Freigabe Requests auslösen, ohne dass die Tracker-Liste sie kennt.
|
|
2444
|
+
* Eigene Hosts, Loopback/IP und bereits erkannte Tracker zählen nicht.
|
|
2445
|
+
*/
|
|
2446
|
+
private unlistedEmbedHosts;
|
|
2442
2447
|
/**
|
|
2443
2448
|
* CNAME-Cloaking nachtragen: Hosts der eigenen Seite aus der Zeitlinie, die kein Tracker sind, per DNS prüfen. Zeigt die
|
|
2444
2449
|
* CNAME-Kette auf einen Tracker-Host, wird der Request mit seiner ursprünglichen Phase als Tracker-Befund eingetragen.
|
package/dist/index.js
CHANGED
package/package.json
CHANGED