Commits vergleichen
6 Commits
8d923ba962
...
master
| Autor | SHA1 | Datum | |
|---|---|---|---|
| 904787c115 | |||
| 1f10389016 | |||
| 3a174128e0 | |||
| e19eef67b2 | |||
| d68735ffc4 | |||
| 0defb37e63 |
@@ -0,0 +1,148 @@
|
|||||||
|
# Background Message Map
|
||||||
|
|
||||||
|
Stand: aus der aktuellen Implementierung abgeleitet. Diese Datei beschreibt den beobachteten Ist-Zustand der Background-Ladefolge, Message-Routen und dauerhaften Datenänderungen.
|
||||||
|
|
||||||
|
## 1. Background-Lade- und Initialisierungspfad
|
||||||
|
|
||||||
|
Die Background-Scripts werden laut `manifest.json` in der folgenden Reihenfolge geladen. Die Dateien laufen nicht als ES-Module, sondern teilen sich den globalen Background-Kontext.
|
||||||
|
|
||||||
|
| Reihenfolge | Dateipfad | Aufgabe | Bereitgestellte globale Funktionen / Werte | Unmittelbar erkennbare Abhängigkeiten |
|
||||||
|
| --- | --- | --- | --- | --- |
|
||||||
|
| 1 | `src/core/module-registry.js` | Registriert lokale Moduldefinitionen. | `globalThis.VGCoreModuleRegistry.registerModule`, `getModules`, `getModuleById` | keine Projekt-Abhängigkeit |
|
||||||
|
| 2 | `src/modules/vg-observe/module.js` | Definiert und registriert das VG-Observe-Modul. | `globalThis.VGObserveModule` | `VGCoreModuleRegistry` |
|
||||||
|
| 3 | `src/background/db/db-constants.js` | Definiert DB-Name, DB-Version und Store-Namen. | `VENDORGET_DB_NAME`, `VENDORGET_DB_VERSION`, `VENDORGET_STORE_NAMES`, `VENDORGET_EVIDENCE_STORE_NAMES` | keine Projekt-Abhängigkeit |
|
||||||
|
| 4 | `src/background/db/db-core.js` | Öffnet, migriert und löscht die IndexedDB. | `openVendorGetDb`, `closeVendorGetDb`, `deleteVendorGetDatabase`, `ensureGvlStores`, `ensureGvlRelationshipStores` | DB-Konstanten, `indexedDB` |
|
||||||
|
| 5 | `src/core/evidence-export-json.js` | Exportiert alle Evidence-Stores als JSON-Container. | `exportVendorGetEvidenceJson` | `openVendorGetDb`, `VENDORGET_EVIDENCE_STORE_NAMES` |
|
||||||
|
| 6 | `src/core/gvl-evidence-json.js` | Export, Verifikation, Import und Provenienzmarkierung von GVL-Evidence. | `exportVendorGetGvlEvidenceJson`, `exportVendorGetGvlRevisionEvidenceJson`, `verifyVendorGetGvlRevisionEvidenceJson`, `importVendorGetGvlRevisionEvidenceJson`, `markVendorGetGvlRevisionEvidenceVaultCopy`, `getGvlEvidenceProvenanceState`, `importVendorGetGvlEvidenceJson`, weitere GVL-Hilfsfunktionen | DB-Konstanten, `openVendorGetDb`, `sha256Hex` erst zur Laufzeit benötigt, `stableStringify`, `VendorGetGvlService` bei Verifikation |
|
||||||
|
| 7 | `src/background/gvl/gvl-vendor-normalizer.js` | Normalisiert Vendors aus einem GVL-Snapshot. | `globalThis.normalizeGvlVendorsFromSnapshot`, `globalThis.normalizeLatestGvlSnapshotVendors` | `openVendorGetDb`, `VENDORGET_STORE_NAMES` |
|
||||||
|
| 8 | `src/background/gvl/gvl-vendor-relationship-normalizer.js` | Normalisiert Vendor-Beziehungen zu Purposes, Features und Special Purposes/Features. | `globalThis.normalizeGvlVendorRelationshipsFromSnapshot`, `globalThis.normalizeLatestGvlVendorRelationships` | `openVendorGetDb`, `VENDORGET_STORE_NAMES` |
|
||||||
|
| 9 | `src/background/gvl/gvl-catalog-normalizer.js` | Normalisiert GVL-Kataloge wie Purposes und Features. | `globalThis.normalizeGvlCatalogsFromSnapshot`, `globalThis.normalizeLatestGvlCatalogs` | `openVendorGetDb`, `VENDORGET_STORE_NAMES` |
|
||||||
|
| 10 | `src/background/db/db-retention.js` | Zählt Evidence-Daten und löscht ungeschützte, nicht gesperrte Evidence-Records. | `countEvidenceRecords`, `countLockedEvidenceRecords`, `getEvidenceStoreCounts`, `purgeUnlockedEvidenceRecords` | `VENDORGET_EVIDENCE_STORE_NAMES`, `VENDORGET_STORE_NAMES`, `getGvlEvidenceProvenanceState` |
|
||||||
|
| 11 | `src/background/db/db-record-locks.js` | Setzt oder entfernt Record-Locks auf allen Evidence-Stores. | `lockAllEvidenceRecords`, `unlockAllEvidenceRecords`, `updateAllEvidenceRecords` | `openVendorGetDb`, `VENDORGET_EVIDENCE_STORE_NAMES` |
|
||||||
|
| 12 | `src/core/stable-serialize.js` | Stabile JSON-Serialisierung mit sortierten Objekt-Keys. | `stableStringify`, `sortObjectKeys` | keine Projekt-Abhängigkeit |
|
||||||
|
| 13 | `src/background/utils.js` | SHA-256-Hilfsfunktion. | `sha256Hex` | `crypto.subtle`, `TextEncoder` |
|
||||||
|
| 14 | `src/core/settings-storage.js` | Generische Storage-Helfer. | `getSettingsFromStorage`, `setSettingInStorage` | `browser.storage.local` |
|
||||||
|
| 15 | `src/background/settings.js` | VG-Observe-Schalter für Consent-Capture und Request-Monitoring. | `getVendorGetSettings`, `setVendorGetSetting`, `isConsentCaptureEnabled`, `isRequestMonitoringEnabled` | Settings-Storage-Helfer |
|
||||||
|
| 16 | `src/core/maintenance-guard.js` | Kurzlebige Maintenance-Session zum Suspendieren von Evidence-Schreibzugriffen. | `startEvidenceMaintenanceSession`, `refreshEvidenceMaintenanceSession`, `endEvidenceMaintenanceSession`, `isEvidenceWriteSuspended`, `getEvidenceMaintenanceStatus` | Zeitfunktionen |
|
||||||
|
| 17 | `src/background/consent-memory.js` | Hält zuletzt beobachtete TCF-Pings und Consent-States im Speicher. | `rememberLatestTcfPing`, `getLatestTcfPing`, `rememberLatestConsentState`, `getLatestConsentStateForRequest` | keine persistente Abhängigkeit |
|
||||||
|
| 18 | `src/background/request-fingerprint.js` | Baut stabile Fingerprint-Quellen für beobachtete Requests. | `buildObservedRequestFingerprintSource`, `buildObservedRequestFingerprint` | `URL`, `sha256Hex`, `stableStringify` |
|
||||||
|
| 19 | `src/background/request-observer.js` | Beobachtet consentbezogene Requests über `webRequest`. | `handleObservedRequest` | Settings, Maintenance-Guard, Request-Fingerprint, Consent-Memory, `persistObservedRequest` aus `src/background.js` |
|
||||||
|
| 20 | `src/background/gvl-service.js` | Hashing, Speicherung und Event-Erzeugung für GVL-Rohdaten und Snapshots. | `globalThis.VendorGetGvlService` mit `calculateGvlSnapshotSha256`, `calculateRawGvlSha256`, `storeGvlRawEvidenceIfNew`, `buildGvlSnapshotRecord`, `storeGvlSnapshotIfNew`, `recordGvlSnapshotEvent`, `ingestGvlSnapshot` | `stableStringify`, GVL-Provenienzfunktionen aus `src/core/gvl-evidence-json.js` |
|
||||||
|
| 21 | `src/core/binary-utils.js` | Bit-Helfer für Base64URL- und TC-String-Decoding. | `base64UrlToBits`, `bitsToInt`, `bitsToBoolean` | `atob` |
|
||||||
|
| 22 | `src/core/tcf-core-metadata-decoder.js` | Decodiert Core-Metadaten aus TC-Strings. | `decodeTcStringCoreMetadata`, `bitsToString` | Binary-Utils |
|
||||||
|
| 23 | `src/background.js` | Zentraler Background-Router, Message-Handler, Consent-Persistenz, Request-Persistenz und GVL-Sync. | `handleVendorGetMessage`, Handlerfunktionen, Query-Helfer, `persistConsentState`, `persistObservedRequest` | Alle zuvor geladenen Komponenten, `browser.runtime.onMessage`, `browser.webRequest.onBeforeRequest`, `fetch` |
|
||||||
|
|
||||||
|
Bei Initialisierung registriert `src/background.js` `browser.runtime.onMessage` auf `handleVendorGetMessage` und `browser.webRequest.onBeforeRequest` auf `handleObservedRequest`. Der automatische GVL-Update-Pfad ist im aktuellen Code nicht aktiv gestartet; es wird lediglich geloggt: `GVL auto update disabled; use manual sync`.
|
||||||
|
|
||||||
|
## 2. Message-Katalog
|
||||||
|
|
||||||
|
| Message-Name | Zuständiger Handler | Zweck | Art des Zugriffs | Fachbereiche |
|
||||||
|
| --- | --- | --- | --- | --- |
|
||||||
|
| `export_gvl_evidence_json` | `handleExportGvlEvidenceJsonMessage` | Exportiert alle GVL-Evidence-Stores. | Lesen | GVL, Export |
|
||||||
|
| `export_gvl_revision_evidence_json` | `handleExportGvlRevisionEvidenceJsonMessage` | Exportiert eine einzelne GVL-Revision inklusive Raw Evidence, Snapshot und normalisierten Records. | Lesen | GVL, Export |
|
||||||
|
| `verify_gvl_revision_evidence_json` | `handleVerifyGvlRevisionEvidenceJsonMessage` | Prüft ein GVL-Revision-Evidence-Paket gegen Hashes, Metadaten und Konsistenzregeln. | Lesen | GVL, Import, Export |
|
||||||
|
| `import_gvl_revision_evidence_json` | `handleImportGvlRevisionEvidenceJsonMessage` | Importiert eine verifizierte GVL-Revision aus einem Evidence-Paket. | Lesen und Schreiben | GVL, Import |
|
||||||
|
| `mark_gvl_revision_evidence_vault_copy` | `handleMarkGvlRevisionEvidenceVaultCopyMessage` | Markiert Snapshot und Raw-GVL einer Revision als Vault-Kopie verfügbar. | Lesen und Schreiben | GVL, Export |
|
||||||
|
| `import_gvl_evidence_json` | `handleImportGvlEvidenceJsonMessage` | Importiert einen älteren/gesamten GVL-Evidence-Exportcontainer. | Lesen und Schreiben | GVL, Import |
|
||||||
|
| `gvl_import_json` | `handleGvlImportJsonMessage` | Importiert eine lokale GVL-JSON als Snapshot über die GVL-Service-Pipeline. | Lesen und Schreiben | GVL, Import |
|
||||||
|
| `fetch_official_gvl` | `handleFetchOfficialGvlMessage` | Ruft die offizielle IAB-GVL ab, speichert Raw Evidence/Snapshot und normalisiert Daten. | Lesen und Schreiben | GVL |
|
||||||
|
| `export_evidence_json` | `handleExportEvidenceJsonMessage` | Exportiert alle Evidence-Stores. | Lesen | Consent, Requests, GVL, Export |
|
||||||
|
| `start_evidence_maintenance_session` | `startEvidenceMaintenanceSession` | Startet eine kurzlebige In-Memory-Maintenance-Session. | Schreiben | Settings, Purge |
|
||||||
|
| `refresh_evidence_maintenance_session` | `refreshEvidenceMaintenanceSession` | Erneuert die Maintenance-Session. | Schreiben | Settings, Purge |
|
||||||
|
| `end_evidence_maintenance_session` | `endEvidenceMaintenanceSession` | Beendet die Maintenance-Session für die angegebene Quelle. | Schreiben | Settings, Purge |
|
||||||
|
| `get_evidence_maintenance_status` | `getEvidenceMaintenanceStatus` | Meldet, ob Evidence-Schreibzugriffe suspendiert sind. | Lesen | Settings, Dashboard |
|
||||||
|
| `get_evidence_retention_status` | `handleGetEvidenceRetentionStatusMessage` | Zählt Evidence-Records, gesperrte Records und Store-Bestände. | Lesen | Consent, Requests, GVL, Purge, Dashboard |
|
||||||
|
| `get_latest_gvl_update_status` | `handleGetLatestGvlUpdateStatusMessage` | Liefert den letzten bekannten GVL-Update-Status und lokale Snapshot-Infos. | Lesen | GVL, Dashboard |
|
||||||
|
| `list_gvl_snapshots` | `handleListGvlSnapshotsMessage` | Listet aktuelle GVL-Snapshots mit Event- und Provenienzstatus. | Lesen | GVL, Dashboard |
|
||||||
|
| `get_gvl_snapshot_summary` | `handleGetGvlSnapshotSummaryMessage` | Liefert Summary, Zählungen und Diagnostik für einen GVL-Snapshot. | Lesen | GVL, Dashboard |
|
||||||
|
| `rebuild_gvl_snapshot_normalized_data` | `handleRebuildGvlSnapshotNormalizedDataMessage` | Baut normalisierte GVL-Daten für einen vorhandenen Snapshot neu auf. | Lesen und Schreiben | GVL |
|
||||||
|
| `list_gvl_vendors_for_snapshot` | `handleListGvlVendorsForSnapshotMessage` | Listet normalisierte Vendoren eines Snapshots. | Lesen | GVL, Dashboard |
|
||||||
|
| `get_gvl_vendor_detail` | `handleGetGvlVendorDetailMessage` | Liefert Vendor-Detaildaten inklusive Beziehungen, Katalogtexten, Snapshot und Raw-Evidence-Summary. | Lesen | GVL, Dashboard |
|
||||||
|
| `get_latest_consent_state` | `handleGetLatestConsentStateMessage` | Liefert den zuletzt gesehenen Consent-State. | Lesen | Consent, Dashboard |
|
||||||
|
| `list_recent_consent_states` | `handleListRecentConsentStatesMessage` | Listet die letzten Consent-States. | Lesen | Consent, Dashboard |
|
||||||
|
| `list_recent_observed_requests` | `handleListRecentObservedRequestsMessage` | Listet zuletzt beobachtete Requests. | Lesen | Requests, Dashboard |
|
||||||
|
| `purge_unlocked_evidence_records` | `handlePurgeUnlockedEvidenceRecordsMessage` | Löscht nicht gesperrte und nicht workspace-geschützte Evidence-Records. | Lesen und Schreiben | Consent, Requests, GVL, Purge |
|
||||||
|
| `delete_all_evidence_database` | `handleDeleteAllEvidenceDatabaseMessage` | Löscht die komplette IndexedDB. | Schreiben | Consent, Requests, GVL, Purge |
|
||||||
|
| `lock_all_evidence_records` | `lockAllEvidenceRecords` | Setzt Lock-Metadaten auf allen Evidence-Records. | Lesen und Schreiben | Consent, Requests, GVL, Purge |
|
||||||
|
| `unlock_all_evidence_records` | `unlockAllEvidenceRecords` | Entfernt Lock-Metadaten auf allen Evidence-Records. | Lesen und Schreiben | Consent, Requests, GVL, Purge |
|
||||||
|
| `vendorget_capture` mit `eventName: "tcf_ping"` | `handleVendorGetMessage` | Speichert den letzten TCF-Ping pro Tab nur im Background-Speicher. | Schreiben | Consent |
|
||||||
|
| `vendorget_capture` mit `eventName: "consent_capture"` | `handleVendorGetMessage` und `persistConsentState` | Baut, fingerprintet und persistiert einen Consent-State sowie ein Consent-Event. | Lesen und Schreiben | Consent |
|
||||||
|
|
||||||
|
Zusätzlich existiert kein Runtime-Message-Typ für `setVendorGetSetting`; die Popup-UI ruft diese Funktion wegen gemeinsamer Script-Einbindung direkt aus dem Popup-Kontext auf.
|
||||||
|
|
||||||
|
## 3. Dauerhafte Datenänderungen
|
||||||
|
|
||||||
|
| Funktion | Betroffene IndexedDB-Stores / Storage | Zweck der Änderung | Bezug zu Evidence-Daten | Besondere Sensibilität |
|
||||||
|
| --- | --- | --- | --- | --- |
|
||||||
|
| `openVendorGetDb` / `ensureGvlStores` / `ensureGvlRelationshipStores` | alle IndexedDB-Stores bei Migration/Upgrade | Additive Anlage von Object Stores und Indizes. | Legt die Speicherstruktur für Evidence und GVL-Daten fest. | Sehr hoch: DB-Version, KeyPaths und Indizes bestimmen Lesbarkeit und Migration vorhandener Evidence. |
|
||||||
|
| `deleteVendorGetDatabase` | gesamte IndexedDB `vendorget_iv` | Löscht die komplette lokale Datenbank. | Entfernt alle lokalen Evidence- und GVL-Daten. | Extrem hoch: vollständiger Datenverlust. |
|
||||||
|
| `persistConsentState` | `consent_states`, `consent_events` | Fügt neue Consent-States hinzu oder aktualisiert bestehende Duplikate; schreibt immer ein Consent-Event. | Kern-Evidence für beobachtete Consent-Zustände. | Sehr hoch: Fingerprint, Zeitstempel, Raw-TC-Daten und Eventhistorie beeinflussen Nachvollziehbarkeit. |
|
||||||
|
| `persistObservedRequest` | `observed_requests` | Fügt beobachtete consentbezogene Requests hinzu oder aktualisiert Wiederholungen. | Request-Evidence und Korrelation zu Consent-States. | Hoch: Fingerprint-Quelle, Seen-Zählung und Korrelation beeinflussen Rekonstruktion. |
|
||||||
|
| `VendorGetGvlService.storeGvlRawEvidenceIfNew` | `gvl_raw_evidence` | Speichert Raw-GVL oder markiert vorhandene Raw-GVL mit Web-Provenienz. | Raw Evidence für GVL-Inhalte. | Sehr hoch: Raw Body und Hashes sind Grundlage späterer Verifikation. |
|
||||||
|
| `VendorGetGvlService.storeGvlSnapshotIfNew` | `gvl_snapshots` | Speichert Snapshot oder ergänzt Web-Provenienz an vorhandenem Snapshot. | GVL-Snapshot-Evidence. | Sehr hoch: Snapshot-Hash, Vendorlisten-Version und Provenienz steuern Export/Purge. |
|
||||||
|
| `VendorGetGvlService.recordGvlSnapshotEvent` | `gvl_snapshot_events` | Schreibt Ereignisse zu GVL-Ingest, Auto-Update, Fehlern oder Status. | Event-Evidence zum lokalen GVL-Verlauf. | Hoch: Ereignishistorie erklärt Entstehungskontext. |
|
||||||
|
| `VendorGetGvlService.ingestGvlSnapshot` | `gvl_snapshots`, `gvl_snapshot_events` | Speichert Snapshot und erzeugt Event `gvl_snapshot_ingested` oder `gvl_snapshot_already_known`. | GVL-Evidence und lokale Beobachtungshistorie. | Sehr hoch: Erzeugt zusätzliche Evidence zum Ingest-Pfad. |
|
||||||
|
| `importVendorGetGvlRevisionEvidenceJson` / `importGvlRevisionEvidenceStores` | `gvl_raw_evidence`, `gvl_snapshots`, normalisierte GVL-Stores | Importiert verifizierte GVL-Revisionsdaten; bestehende Raw/Snapshot-Records können Provenienz per `put` erhalten. | Rekonstruiert GVL-Evidence aus Vault-Paketen. | Sehr hoch: Import validiert und entscheidet zwischen Insert, Skip und Provenienzmerge. |
|
||||||
|
| `importVendorGetGvlEvidenceJson` / `importGvlEvidenceStores` | `gvl_raw_evidence`, `gvl_snapshots`, normalisierte GVL-Stores | Importiert GVL-Evidence-Store-Arrays und überspringt vorhandene Keys. | GVL-Evidence-Import. | Hoch: Key-Ermittlung und Skip-Logik bestimmen Dublettenverhalten. |
|
||||||
|
| `markVendorGetGvlRevisionEvidenceVaultCopy` / `markGvlRevisionEvidenceVaultCopyAvailable` / `updateGvlRevisionEvidenceRecords` | `gvl_snapshots`, `gvl_raw_evidence` | Markiert Vault-Kopie und Workspace-Löschbarkeit für Snapshot und Raw-GVL. | Verändert Evidence-Provenienz und Schutzstatus. | Sehr hoch: beeinflusst Purge-Schutz und Audit-Aussage zur Vault-Verfügbarkeit. |
|
||||||
|
| `markGvlRevisionEvidenceWebSource` / `markGvlRevisionEvidenceProvenance` | `gvl_snapshots`, `gvl_raw_evidence` | Ergänzt Web-Provenienz an GVL-Revisionsrecords. | Verändert GVL-Provenienz. | Hoch: Provenienz beeinflusst Schutzstatus und Interpretation der Herkunft. |
|
||||||
|
| `putGvlVendorRecords` | `gvl_vendors` | Schreibt normalisierte Vendor-Records per `put`. | Normalisierte Ableitung aus GVL-Snapshot. | Hoch: überschreibt normalisierte Sicht auf Vendor-Daten einer Revision. |
|
||||||
|
| `putGvlVendorRelationshipRecords` | `gvl_vendor_relationships` | Schreibt normalisierte Vendor-Beziehungsrecords per `put`. | Normalisierte Ableitung aus GVL-Snapshot. | Hoch: beeinflusst Vendor-Detail- und Purpose/Feature-Nachvollziehbarkeit. |
|
||||||
|
| `putGvlCatalogRecords` | `gvl_purposes`, `gvl_special_purposes`, `gvl_features`, `gvl_special_features` | Schreibt normalisierte Katalogrecords per `put`. | Normalisierte GVL-Katalogableitung. | Hoch: Katalogtexte und IDs sind Grundlage der Darstellung. |
|
||||||
|
| `normalizeGvlSnapshotWithExistingPipeline` | normalisierte GVL-Stores | Führt Vendor-, Katalog- und Relationship-Normalisierung aus. | Erstellt/aktualisiert normalisierte Evidence-Ableitungen. | Hoch: gemeinsamer Einstieg für Rebuild und GVL-Fetch. |
|
||||||
|
| `purgeUnlockedEvidenceRecords` | alle Stores in `VENDORGET_EVIDENCE_STORE_NAMES` | Löscht nicht gelockte und nicht workspace-geschützte Records per Cursor. | Entfernt lokale Evidence-Records selektiv. | Extrem hoch: Löschlogik hängt von Locks und GVL-Provenienzschutz ab. |
|
||||||
|
| `lockAllEvidenceRecords` / `updateAllEvidenceRecords` | alle Stores in `VENDORGET_EVIDENCE_STORE_NAMES` | Setzt `bolRecordLock` und Lock-Metadaten auf allen Evidence-Records. | Schützt Evidence vor Purge. | Sehr hoch: Vollständiger Record-Durchlauf; Schutzstatus wird verändert. |
|
||||||
|
| `unlockAllEvidenceRecords` / `updateAllEvidenceRecords` | alle Stores in `VENDORGET_EVIDENCE_STORE_NAMES` | Entfernt Lock-Metadaten und setzt Unlock-Zeit. | Macht Evidence wieder purgefähig, sofern kein anderer Schutz greift. | Sehr hoch: verändert Schutzstatus aller Evidence-Records. |
|
||||||
|
| `storeAutoGvlCheckThrottleState` | `browser.storage.local` unter `vendorgetAutoGvlUpdateStatus` | Speichert Auto-GVL-Throttle-Status. | Kein IndexedDB-Evidence-Record, aber Status zur GVL-Automatik. | Mittel: beeinflusst Statusmeldungen und Auto-Check-Verhalten, aktuell nicht gestartet. |
|
||||||
|
| `setSettingInStorage` / `setVendorGetSetting` | `browser.storage.local` unter `vendorgetSettings` | Speichert Consent-Capture- und Request-Monitoring-Schalter. | Steuert, ob neue Evidence geschrieben wird. | Hoch: Schalter bestimmen Beobachtungsumfang. |
|
||||||
|
| `startEvidenceMaintenanceSession` / `refreshEvidenceMaintenanceSession` / `endEvidenceMaintenanceSession` | nur In-Memory | Setzt/erneuert/beendet temporäre Schreibsperre. | Verhindert während Maintenance neue Evidence-Schreibzugriffe. | Mittel bis hoch: nicht persistent, aber direkt wirksam auf Capture/Request-Writes. |
|
||||||
|
|
||||||
|
Hinweis zu Store-Abdeckung: `VENDORGET_EVIDENCE_STORE_NAMES` enthält `consent_states`, `consent_events`, `observed_requests`, `gvl_snapshots`, `gvl_snapshot_events`, `gvl_vendors`, `gvl_purposes`, `gvl_special_purposes`, `gvl_features`, `gvl_special_features`, `gvl_data_categories` und `gvl_vendor_relationships`. `gvl_raw_evidence` ist nicht in dieser allgemeinen Evidence-Store-Liste enthalten, wird aber in den GVL-spezifischen Evidence-Import/Export-Pfaden verwendet.
|
||||||
|
|
||||||
|
## 4. Sensible Bereiche
|
||||||
|
|
||||||
|
| Bereich | Betroffene Komponenten | Grund der Sensibilität | Mögliche Auswirkungen von Änderungen |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| IndexedDB-Schema und Store-Konstanten | `db-constants.js`, `db-core.js`, alle DB-Nutzer | KeyPaths, Store-Namen, Indizes und DB-Version sind Grundlage aller Evidence-Zugriffe. | Bestehende Evidence kann unlesbar, nicht migrierbar oder falsch gezählt werden. |
|
||||||
|
| Consent-Capture-Persistenz | `background.js`, `tcf-core-metadata-decoder.js`, `stable-serialize.js`, `utils.js`, `consent-memory.js` | `stateFingerprint`, `rawTcData`, Zeitstempel und Eventtypen bilden die technische Consent-Evidence. | Duplikaterkennung, Rekonstruktion und Audit-Trail können verfälscht werden. |
|
||||||
|
| Request-Beobachtung und Korrelation | `request-observer.js`, `request-fingerprint.js`, `consent-memory.js`, `background.js` | Requests werden dedupliziert, fingerprinted und optional mit dem letzten Consent-State korreliert. | Request-Evidence kann fehlen, zusammenfallen oder falschen Consent-Zuständen zugeordnet werden. |
|
||||||
|
| GVL-Raw/Snapshot-Hashing | `gvl-service.js`, `gvl-evidence-json.js`, `stable-serialize.js` | Hashes sind Grundlage für Snapshot-Identität, Raw-Evidence-Verifikation und Exportpakete. | GVL-Revisionen könnten nicht mehr vergleichbar oder verifizierbar sein. |
|
||||||
|
| GVL-Provenienz und Vault-Markierung | `gvl-evidence-json.js`, `db-retention.js`, `gvl-explorer.js` | `gvlEvidenceProvenance`, `vaultCopyAvailable` und `evidenceWorkspaceDeleteAllowed` steuern Schutzstatus und Exportstatus. | Purge kann zu viel oder zu wenig löschen; Vault-Aussagen können unzuverlässig werden. |
|
||||||
|
| GVL-Normalisierung | `gvl-vendor-normalizer.js`, `gvl-vendor-relationship-normalizer.js`, `gvl-catalog-normalizer.js`, `background.js` | Normalisierte Stores sind abgeleitete Sicht auf eine konkrete GVL-Revision. | Vendor-Details, Katalogtexte und Zählungen können von Raw-Snapshot abweichen. |
|
||||||
|
| Purge- und Lock-Logik | `db-retention.js`, `db-record-locks.js`, `background.js`, Popup/Data-Maintenance | Diese Pfade löschen oder schützen Evidence über viele Stores hinweg. | Datenverlust, ungewollte Schutzumgehung oder falsche Retention-Summaries. |
|
||||||
|
| Export/Import-Pakete | `evidence-export-json.js`, `gvl-evidence-json.js`, Popup/GVL-Explorer | Exportformate bilden auditierbare Übergabecontainer. | Spätere Reimporte, Payload-Hashes und Nachvollziehbarkeit können brechen. |
|
||||||
|
| Maintenance-Guard | `maintenance-guard.js`, `background.js`, `request-observer.js` | Temporäre Schreibsperre verhindert parallele Evidence-Writes während Wartung. | Während Wartung können Daten fehlen oder trotz Wartung geschrieben werden. |
|
||||||
|
| Settings | `settings-storage.js`, `settings.js`, `popup.js` | Browser-Storage-Schalter steuern Consent-Capture und Request-Monitoring. | Beobachtungsumfang und Evidence-Erzeugung ändern sich ohne DB-Schemaänderung. |
|
||||||
|
|
||||||
|
## 5. Überwiegend lesende Bereiche
|
||||||
|
|
||||||
|
| Komponente | Aufgabe | Verwendete Datenquellen | Art des Zugriffs |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `src/core/evidence-export-json.js` | Baut einen vollständigen Evidence-Exportcontainer. | `VENDORGET_EVIDENCE_STORE_NAMES` über IndexedDB-Cursor | Lesen |
|
||||||
|
| `exportVendorGetGvlEvidenceJson` / `readGvlEvidenceStores` in `src/core/gvl-evidence-json.js` | Exportiert GVL-Evidence-Stores. | GVL-Evidence-Stores über IndexedDB-Cursor | Lesen |
|
||||||
|
| `exportVendorGetGvlRevisionEvidenceJson` in `src/core/gvl-evidence-json.js` | Exportiert eine einzelne GVL-Revision. | `gvl_snapshots`, `gvl_raw_evidence`, normalisierte GVL-Stores | Lesen |
|
||||||
|
| `verifyVendorGetGvlRevisionEvidenceJson` in `src/core/gvl-evidence-json.js` | Prüft einen übergebenen Exportcontainer. | Import-/Exportobjekt im Speicher, Hashfunktionen | Lesen |
|
||||||
|
| `handleGetEvidenceRetentionStatusMessage` und Zählfunktionen in `db-retention.js` | Liefert Gesamt-, Lock- und Store-Zählungen. | Evidence-Stores | Lesen |
|
||||||
|
| `handleGetLatestGvlUpdateStatusMessage` | Liefert lokalen GVL-Status und letzten In-Memory-Update-Status. | `gvl_snapshots`, Browser-Storage-Throttle, In-Memory-Status | Lesen |
|
||||||
|
| `handleListGvlSnapshotsMessage` | Listet GVL-Snapshots mit Event- und Provenienzstatus. | `gvl_snapshots`, `gvl_snapshot_events` | Lesen |
|
||||||
|
| `handleGetGvlSnapshotSummaryMessage` | Baut Snapshot-Summary mit normalisierten Zählungen. | `gvl_snapshots`, `gvl_snapshot_events`, normalisierte GVL-Stores | Lesen |
|
||||||
|
| `handleListGvlVendorsForSnapshotMessage` | Listet Vendoren einer Revision. | `gvl_vendors`, `gvl_snapshots` | Lesen |
|
||||||
|
| `handleGetGvlVendorDetailMessage` | Baut Vendor-Detailansicht. | `gvl_vendors`, `gvl_vendor_relationships`, Katalogstores, `gvl_snapshots`, `gvl_raw_evidence` | Lesen |
|
||||||
|
| `handleGetLatestConsentStateMessage` | Liefert neuesten Consent-State. | `consent_states` über `lastSeenAt` | Lesen |
|
||||||
|
| `handleListRecentConsentStatesMessage` | Listet aktuelle Consent-States. | `consent_states` über `lastSeenAt` | Lesen |
|
||||||
|
| `handleListRecentObservedRequestsMessage` | Listet aktuelle beobachtete Requests. | `observed_requests` über `lastSeenAt` | Lesen |
|
||||||
|
| `src/dashboard/dashboard.js` | Zeigt Evidence-Bestand und GVL-Status. | Runtime-Messages `get_evidence_retention_status`, `get_latest_gvl_update_status` | Lesen |
|
||||||
|
| `src/analysis-dashboard/analysis-dashboard.js` | Zeigt zusammenfassende Bestände und Status. | Runtime-Messages `get_evidence_retention_status`, `get_latest_gvl_update_status` | Lesen |
|
||||||
|
| `src/consent-explorer/consent-explorer.js` | Stellt Consent-States dar. | Runtime-Message `list_recent_consent_states` | Lesen |
|
||||||
|
| `src/request-explorer/request-explorer.js` | Stellt beobachtete Requests dar. | Runtime-Message `list_recent_observed_requests` | Lesen |
|
||||||
|
| `src/gvl-explorer/gvl-explorer.js` | Stellt GVL-Snapshots, Vendoren und Revisionsexporte dar; enthält zusätzlich schreibende Aktionen für Fetch, Import, Rebuild und Vault-Markierung. | GVL-Runtime-Messages | Überwiegend Lesen, einzelne Schreiben |
|
||||||
|
|
||||||
|
## 6. Audit-Hinweis
|
||||||
|
|
||||||
|
Besonders kritisch sind DB-Schema, Consent-Persistenz, Request-Fingerprints, GVL-Hashing/Provenienz, Import/Export, Purge und Record-Locks.
|
||||||
|
|
||||||
|
Überwiegend lesend arbeiten Export-, Dashboard-, Explorer- und Summary-Pfade, solange sie keine Import-, Fetch-, Rebuild-, Markierungs- oder Purge-Aktion auslösen.
|
||||||
|
|
||||||
|
Bei späteren Änderungen verdienen `src/background.js`, `src/background/db/db-core.js`, `src/background/db/db-retention.js`, `src/background/db/db-record-locks.js`, `src/core/gvl-evidence-json.js` und `src/background/gvl-service.js` besondere Aufmerksamkeit.
|
||||||
|
|
||||||
|
Änderungen an Settings und Maintenance-Guard sind ebenfalls auditrelevant, weil sie steuern, ob Evidence überhaupt geschrieben wird.
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
# VG-Environment Codex Workflow
|
# VG-Environment Codex Workflow
|
||||||
|
|
||||||
|
Vor Beginn fachlicher Arbeiten ist `docs/architecture/project-philosophy.md` als maßgebliche Beschreibung des Projektkerns zu berücksichtigen.
|
||||||
|
|
||||||
## Grundprinzip
|
## Grundprinzip
|
||||||
|
|
||||||
VG-Environment wird schrittweise modularisiert.
|
VG-Environment wird schrittweise modularisiert.
|
||||||
|
|||||||
@@ -0,0 +1,103 @@
|
|||||||
|
# VG-Environment – Wesen und Kern des Projekts
|
||||||
|
|
||||||
|
VG-Environment ist kein GVL-Explorer.
|
||||||
|
|
||||||
|
VG-Environment ist kein Dashboard.
|
||||||
|
|
||||||
|
VG-Environment ist keine Analyse-Engine.
|
||||||
|
|
||||||
|
VG-Environment ist ein lokales, evidenzorientiertes Rekonstruktionswerkzeug zur Untersuchung der technisch beobachtbaren Wirkungen von Consent-Entscheidungen.
|
||||||
|
|
||||||
|
Die zentrale Frage des Projekts lautet:
|
||||||
|
|
||||||
|
> Was ist tatsächlich geschehen, nachdem ich dieser konkreten Entscheidung zugestimmt habe?
|
||||||
|
|
||||||
|
Zur Beantwortung dieser Frage trennt VG-Environment bewusst drei unterschiedliche Wirklichkeitsebenen.
|
||||||
|
|
||||||
|
## 1. Die Provider-Erzählung
|
||||||
|
|
||||||
|
Ein CMP präsentiert dem Nutzer eine Erklärung.
|
||||||
|
|
||||||
|
Der Nutzer trifft eine Entscheidung.
|
||||||
|
|
||||||
|
Aus dieser Entscheidung entsteht ein tcString.
|
||||||
|
|
||||||
|
Der tcString dokumentiert nicht die technische Realität. Er dokumentiert die Behauptung des Systems darüber, welche Zustimmungen, Ablehnungen und Berechtigungen vorliegen sollen.
|
||||||
|
|
||||||
|
Er ist die digitale Selbstbeschreibung des Consent-Vorgangs.
|
||||||
|
|
||||||
|
VG-Environment behandelt diese Beschreibung als Evidenzobjekt – nicht als Wahrheit.
|
||||||
|
|
||||||
|
## 2. Die technische Realität
|
||||||
|
|
||||||
|
Unabhängig von jeder Erklärung werden technische Folgehandlungen beobachtet.
|
||||||
|
|
||||||
|
Requests finden statt oder finden nicht statt.
|
||||||
|
|
||||||
|
Empfänger werden kontaktiert oder werden nicht kontaktiert.
|
||||||
|
|
||||||
|
Parameter werden übertragen oder werden nicht übertragen.
|
||||||
|
|
||||||
|
Diese Beobachtungen stellen die stärkste Form der Primärevidence des Projekts dar.
|
||||||
|
|
||||||
|
Sie benötigen keine GVL und keine Interpretation, um zu existieren.
|
||||||
|
|
||||||
|
Sie sind die dokumentierte technische Realität.
|
||||||
|
|
||||||
|
## 3. Das Regelwerk
|
||||||
|
|
||||||
|
Die GVL beschreibt nicht die technische Realität.
|
||||||
|
|
||||||
|
Sie erklärt auch nicht die Requests.
|
||||||
|
|
||||||
|
Sie beschreibt vielmehr, wie die Behauptungen des Providers innerhalb des TCF-Regelwerks einzuordnen gewesen wären.
|
||||||
|
|
||||||
|
Sie beantwortet Fragen wie:
|
||||||
|
|
||||||
|
- Welche Zwecke hatte ein Vendor angegeben?
|
||||||
|
- Welche Eigenschaften waren einem Vendor zugeordnet?
|
||||||
|
- Welche Beziehungen waren innerhalb des Regelwerks vorgesehen?
|
||||||
|
|
||||||
|
Die GVL ist daher Referenz- und Analysematerial.
|
||||||
|
|
||||||
|
Sie ist ein historisches Wörterbuch zur Bewertung der Provider-Erzählung.
|
||||||
|
|
||||||
|
Sie ist nicht Bestandteil des beobachteten Vorgangs selbst.
|
||||||
|
|
||||||
|
## Die eigentliche Rekonstruktion
|
||||||
|
|
||||||
|
VG-Environment bringt diese drei Ebenen nicht durcheinander.
|
||||||
|
|
||||||
|
Es hält sie bewusst getrennt.
|
||||||
|
|
||||||
|
Erst aus ihrer Gegenüberstellung entstehen die entscheidenden Fragen:
|
||||||
|
|
||||||
|
- Was wurde behauptet?
|
||||||
|
- Was ist tatsächlich geschehen?
|
||||||
|
- Entspricht die Behauptung dem eigenen Regelwerk?
|
||||||
|
|
||||||
|
Die Antworten darauf entstehen nicht durch versteckte Bewertungen, sondern durch nachvollziehbare, auditierbare Evidence-Ketten.
|
||||||
|
|
||||||
|
## Das Leitprinzip
|
||||||
|
|
||||||
|
VG-Environment entscheidet nicht, was richtig oder falsch ist.
|
||||||
|
|
||||||
|
VG-Environment dokumentiert.
|
||||||
|
|
||||||
|
Es rekonstruiert.
|
||||||
|
|
||||||
|
Es macht technische Realität sichtbar.
|
||||||
|
|
||||||
|
Es zeigt Herkunft, Zusammenhänge und Widersprüche transparent auf.
|
||||||
|
|
||||||
|
Die Bewertung bleibt dem Nutzer, Forschenden, Journalisten, Aufsichtsbehörden oder Gerichten überlassen.
|
||||||
|
|
||||||
|
## Der Kern des Projekts
|
||||||
|
|
||||||
|
> VG-Environment ist ein Werkzeug zur Rekonstruktion technischer Realität im Spannungsfeld zwischen Provider-Erzählung, beobachtbarer Handlung und Regelwerk.
|
||||||
|
|
||||||
|
Oder kürzer:
|
||||||
|
|
||||||
|
> Es zeigt nicht, was hätte geschehen sollen.
|
||||||
|
>
|
||||||
|
> Es zeigt, was behauptet wurde, was tatsächlich geschah und auf welcher Grundlage beides beurteilt werden kann.
|
||||||
@@ -1,5 +1,8 @@
|
|||||||
# VG-Environment — Project Principles
|
# VG-Environment — Project Principles
|
||||||
|
|
||||||
|
Hinweis:
|
||||||
|
Die grundlegende Zieldefinition und das Erkenntnisinteresse von VG-Environment sind in `docs/architecture/project-philosophy.md` beschrieben und bei allen Architektur-, Implementierungs- und Analyseentscheidungen vorrangig zu berücksichtigen.
|
||||||
|
|
||||||
## Core purpose
|
## Core purpose
|
||||||
|
|
||||||
VG-Environment is a local evidence and runtime reconstruction environment for consent-related browser activity.
|
VG-Environment is a local evidence and runtime reconstruction environment for consent-related browser activity.
|
||||||
|
|||||||
@@ -40,6 +40,7 @@
|
|||||||
"src/background/gvl-service.js",
|
"src/background/gvl-service.js",
|
||||||
"src/core/binary-utils.js",
|
"src/core/binary-utils.js",
|
||||||
"src/core/tcf-core-metadata-decoder.js",
|
"src/core/tcf-core-metadata-decoder.js",
|
||||||
|
"src/core/consent-diff.js",
|
||||||
"src/background.js"
|
"src/background.js"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
Datei-Diff unterdrückt, da er zu groß ist
Diff laden
@@ -27,6 +27,7 @@ body {
|
|||||||
h1,
|
h1,
|
||||||
h2,
|
h2,
|
||||||
h3,
|
h3,
|
||||||
|
h4,
|
||||||
p {
|
p {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
@@ -49,6 +50,11 @@ h3 {
|
|||||||
color: #cbd5e1;
|
color: #cbd5e1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #cbd5e1;
|
||||||
|
}
|
||||||
|
|
||||||
p {
|
p {
|
||||||
max-width: 760px;
|
max-width: 760px;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
@@ -139,6 +145,165 @@ th {
|
|||||||
margin-top: 18px;
|
margin-top: 18px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.finding-overview {
|
||||||
|
display: grid;
|
||||||
|
gap: 10px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.finding-note {
|
||||||
|
max-width: 900px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.finding-list {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
gap: 1px;
|
||||||
|
margin: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
border: 1px solid #334155;
|
||||||
|
border-radius: 4px;
|
||||||
|
background: #334155;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.finding-list div {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: minmax(160px, 220px) 1fr;
|
||||||
|
gap: 10px;
|
||||||
|
min-width: 0;
|
||||||
|
padding: 10px 12px;
|
||||||
|
background: #1f2937;
|
||||||
|
}
|
||||||
|
|
||||||
|
.finding-list dt {
|
||||||
|
color: #cbd5e1;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.finding-list dd {
|
||||||
|
margin: 0;
|
||||||
|
overflow-wrap: anywhere;
|
||||||
|
color: #e5edf5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.consent-diff {
|
||||||
|
display: grid;
|
||||||
|
gap: 10px;
|
||||||
|
margin-top: 10px;
|
||||||
|
padding: 10px;
|
||||||
|
border: 1px solid #334155;
|
||||||
|
border-radius: 4px;
|
||||||
|
background: #172033;
|
||||||
|
}
|
||||||
|
|
||||||
|
.compact-definition-list {
|
||||||
|
display: grid;
|
||||||
|
gap: 6px;
|
||||||
|
margin: 0;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.compact-definition-list div {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: minmax(150px, 220px) 1fr;
|
||||||
|
gap: 10px;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.compact-definition-list dt {
|
||||||
|
color: #cbd5e1;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.compact-definition-list dd {
|
||||||
|
margin: 0;
|
||||||
|
overflow-wrap: anywhere;
|
||||||
|
}
|
||||||
|
|
||||||
|
.diff-note {
|
||||||
|
max-width: none;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #cbd5e1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.consent-diff-primary {
|
||||||
|
display: grid;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.consent-diff-findings {
|
||||||
|
display: grid;
|
||||||
|
gap: 6px;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.consent-diff-findings li {
|
||||||
|
padding: 9px 10px;
|
||||||
|
border: 1px solid #334155;
|
||||||
|
border-radius: 4px;
|
||||||
|
color: #e5edf5;
|
||||||
|
background: #1f2937;
|
||||||
|
overflow-wrap: anywhere;
|
||||||
|
}
|
||||||
|
|
||||||
|
.consent-diff-finding-details summary {
|
||||||
|
cursor: pointer;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.consent-diff-finding-body {
|
||||||
|
display: grid;
|
||||||
|
gap: 8px;
|
||||||
|
margin-top: 10px;
|
||||||
|
padding-top: 10px;
|
||||||
|
border-top: 1px solid #334155;
|
||||||
|
}
|
||||||
|
|
||||||
|
.consent-change-categories,
|
||||||
|
.consent-change-category-body {
|
||||||
|
display: grid;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.consent-change-category {
|
||||||
|
padding: 8px 10px;
|
||||||
|
border: 1px solid #334155;
|
||||||
|
border-radius: 4px;
|
||||||
|
background: #172033;
|
||||||
|
}
|
||||||
|
|
||||||
|
.consent-change-category summary,
|
||||||
|
.consent-change-collapsed-list summary {
|
||||||
|
cursor: pointer;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.consent-change-list-block {
|
||||||
|
display: grid;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inline-id-list {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 6px;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inline-id-list li {
|
||||||
|
padding: 3px 6px;
|
||||||
|
border: 1px solid #334155;
|
||||||
|
border-radius: 3px;
|
||||||
|
background: #0f172a;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #e5edf5;
|
||||||
|
}
|
||||||
|
|
||||||
.inspector-table th,
|
.inspector-table th,
|
||||||
.inspector-table td {
|
.inspector-table td {
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
@@ -227,4 +392,12 @@ th {
|
|||||||
.inspector-table .inspector-explanation {
|
.inspector-table .inspector-explanation {
|
||||||
width: auto;
|
width: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.finding-list {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.finding-list div {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,6 +47,7 @@
|
|||||||
<section class="consent-detail" aria-labelledby="consent-detail-title">
|
<section class="consent-detail" aria-labelledby="consent-detail-title">
|
||||||
<h2 id="consent-detail-title">Ausgewählter Consent-Zustand</h2>
|
<h2 id="consent-detail-title">Ausgewählter Consent-Zustand</h2>
|
||||||
<div id="consent-detail-observation"></div>
|
<div id="consent-detail-observation"></div>
|
||||||
|
<div id="consent-detail-diff"></div>
|
||||||
<div id="consent-detail-basics"></div>
|
<div id="consent-detail-basics"></div>
|
||||||
<div id="consent-detail-summary"></div>
|
<div id="consent-detail-summary"></div>
|
||||||
<div id="consent-detail-publisher"></div>
|
<div id="consent-detail-publisher"></div>
|
||||||
|
|||||||
Datei-Diff unterdrückt, da er zu groß ist
Diff laden
@@ -1,9 +1,87 @@
|
|||||||
console.log("VendorGet content listener loaded:", window.location.href);
|
console.log("VendorGet content listener loaded:", window.location.href);
|
||||||
|
|
||||||
|
let latestObservedPreConsentCapture = null;
|
||||||
|
let latestObservedConsentCompleted = false;
|
||||||
|
|
||||||
|
browser.runtime.onMessage.addListener((message) => {
|
||||||
|
if (message?.type !== "probe_tcf_state") {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return probeTcfState();
|
||||||
|
});
|
||||||
|
|
||||||
|
function probeTcfState() {
|
||||||
|
if (latestObservedConsentCompleted) {
|
||||||
|
return Promise.resolve({
|
||||||
|
success: true,
|
||||||
|
capture: null,
|
||||||
|
source: "content_completed"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (latestObservedPreConsentCapture && !latestObservedConsentCompleted) {
|
||||||
|
return Promise.resolve({
|
||||||
|
success: true,
|
||||||
|
capture: latestObservedPreConsentCapture,
|
||||||
|
source: "content_memory"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
const requestId = [
|
||||||
|
"tcf-probe",
|
||||||
|
Date.now().toString(36),
|
||||||
|
Math.random().toString(36).slice(2)
|
||||||
|
].join("-");
|
||||||
|
const timeoutId = setTimeout(() => {
|
||||||
|
window.removeEventListener("VendorGetTcfProbeResponse", handleResponse);
|
||||||
|
resolve({
|
||||||
|
success: false,
|
||||||
|
capture: null,
|
||||||
|
error: "tcf_probe_timeout"
|
||||||
|
});
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
|
function handleResponse(event) {
|
||||||
|
if (event?.detail?.requestId !== requestId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
clearTimeout(timeoutId);
|
||||||
|
window.removeEventListener("VendorGetTcfProbeResponse", handleResponse);
|
||||||
|
resolve({
|
||||||
|
success: event.detail.success === true,
|
||||||
|
capture: event.detail.capture ?? null,
|
||||||
|
source: "tcf_get_tc_data"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener("VendorGetTcfProbeResponse", handleResponse);
|
||||||
|
window.dispatchEvent(new CustomEvent("VendorGetTcfProbeRequest", {
|
||||||
|
detail: {
|
||||||
|
requestId
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
window.addEventListener("VendorGetFromPage", async (event) => {
|
window.addEventListener("VendorGetFromPage", async (event) => {
|
||||||
|
|
||||||
console.log("VendorGet message from page:", event.detail);
|
console.log("VendorGet message from page:", event.detail);
|
||||||
|
|
||||||
|
if (
|
||||||
|
event.detail?.eventName === "tcf_pre_consent_event" &&
|
||||||
|
!latestObservedConsentCompleted
|
||||||
|
) {
|
||||||
|
latestObservedPreConsentCapture = event.detail.payload ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.detail?.eventName === "consent_capture") {
|
||||||
|
latestObservedConsentCompleted = true;
|
||||||
|
latestObservedPreConsentCapture = null;
|
||||||
|
}
|
||||||
|
|
||||||
await browser.runtime.sendMessage({
|
await browser.runtime.sendMessage({
|
||||||
type: "vendorget_capture",
|
type: "vendorget_capture",
|
||||||
payload: event.detail
|
payload: event.detail
|
||||||
|
|||||||
@@ -0,0 +1,374 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
var CONSENT_DIFF_RULE_ID = "consent_diff";
|
||||||
|
var CONSENT_DIFF_RULE_VERSION = 1;
|
||||||
|
|
||||||
|
const CONSENT_DIFF_SCALAR_AXES = [
|
||||||
|
"tcString",
|
||||||
|
"addtlConsent",
|
||||||
|
"vendorListVersion",
|
||||||
|
"cmpId",
|
||||||
|
"cmpVersion",
|
||||||
|
"tcfPolicyVersion",
|
||||||
|
"gdprApplies",
|
||||||
|
"cmpStatus"
|
||||||
|
];
|
||||||
|
|
||||||
|
const CONSENT_DIFF_OBJECT_AXES = [
|
||||||
|
"purpose.consents",
|
||||||
|
"vendor.consents",
|
||||||
|
"specialFeatureOptins"
|
||||||
|
];
|
||||||
|
|
||||||
|
function buildConsentDiff({
|
||||||
|
captureSessionId,
|
||||||
|
providerAnnouncement,
|
||||||
|
consentExecution,
|
||||||
|
derivedAt
|
||||||
|
}) {
|
||||||
|
const providerRaw = cloneConsentDiffValue(
|
||||||
|
providerAnnouncement?.rawTcData ?? null
|
||||||
|
);
|
||||||
|
const executionRaw = cloneConsentDiffValue(consentExecution?.rawTcData ?? null);
|
||||||
|
const providerWork = buildConsentDiffWorkCopy(
|
||||||
|
providerAnnouncement,
|
||||||
|
providerRaw
|
||||||
|
);
|
||||||
|
const executionWork = buildConsentDiffWorkCopy(consentExecution, executionRaw);
|
||||||
|
const axes = {};
|
||||||
|
|
||||||
|
CONSENT_DIFF_SCALAR_AXES.forEach((axis) => {
|
||||||
|
axes[axis] = compareConsentDiffScalarAxis(
|
||||||
|
axis,
|
||||||
|
providerWork,
|
||||||
|
executionWork
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
CONSENT_DIFF_OBJECT_AXES.forEach((axis) => {
|
||||||
|
axes[axis] = compareConsentDiffObjectAxis(
|
||||||
|
axis,
|
||||||
|
providerWork,
|
||||||
|
executionWork
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
ruleId: CONSENT_DIFF_RULE_ID,
|
||||||
|
ruleVersion: CONSENT_DIFF_RULE_VERSION,
|
||||||
|
derivedAt,
|
||||||
|
captureSessionId,
|
||||||
|
sources: {
|
||||||
|
providerAnnouncement: buildConsentDiffSourceReference(
|
||||||
|
providerAnnouncement
|
||||||
|
),
|
||||||
|
consentExecution: buildConsentDiffSourceReference(consentExecution)
|
||||||
|
},
|
||||||
|
sourceSelection: {
|
||||||
|
providerAnnouncement:
|
||||||
|
"first_provider_announcement_for_capture_session_available_in_memory",
|
||||||
|
consentExecution: "current_useractioncomplete_execution_event"
|
||||||
|
},
|
||||||
|
comparisonAxes: [
|
||||||
|
...CONSENT_DIFF_SCALAR_AXES,
|
||||||
|
...CONSENT_DIFF_OBJECT_AXES
|
||||||
|
],
|
||||||
|
axes,
|
||||||
|
notes: buildConsentDiffNotes(providerAnnouncement, consentExecution)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildConsentDiffWorkCopy(eventRecord, rawTcData) {
|
||||||
|
return {
|
||||||
|
eventRecord: cloneConsentDiffValue(eventRecord ?? null),
|
||||||
|
rawTcData: rawTcData ?? null
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function compareConsentDiffScalarAxis(axis, providerWork, executionWork) {
|
||||||
|
const providerValue = readConsentDiffAxisValue(providerWork, axis);
|
||||||
|
const executionValue = readConsentDiffAxisValue(executionWork, axis);
|
||||||
|
|
||||||
|
return {
|
||||||
|
kind: "scalar",
|
||||||
|
status: getConsentDiffScalarStatus(providerValue, executionValue),
|
||||||
|
provider: buildConsentDiffAxisSide(providerValue),
|
||||||
|
execution: buildConsentDiffAxisSide(executionValue)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function compareConsentDiffObjectAxis(axis, providerWork, executionWork) {
|
||||||
|
const providerValue = readConsentDiffAxisValue(providerWork, axis);
|
||||||
|
const executionValue = readConsentDiffAxisValue(executionWork, axis);
|
||||||
|
const providerObject = isConsentDiffPlainObject(providerValue.value)
|
||||||
|
? providerValue.value
|
||||||
|
: null;
|
||||||
|
const executionObject = isConsentDiffPlainObject(executionValue.value)
|
||||||
|
? executionValue.value
|
||||||
|
: null;
|
||||||
|
const providerKeys = providerObject ? Object.keys(providerObject).sort() : [];
|
||||||
|
const executionKeys = executionObject ? Object.keys(executionObject).sort() : [];
|
||||||
|
const allKeys = Array.from(new Set([...providerKeys, ...executionKeys])).sort();
|
||||||
|
const added = [];
|
||||||
|
const removed = [];
|
||||||
|
const changed = [];
|
||||||
|
const unchanged = [];
|
||||||
|
|
||||||
|
allKeys.forEach((key) => {
|
||||||
|
const inProvider = providerObject
|
||||||
|
? Object.prototype.hasOwnProperty.call(providerObject, key)
|
||||||
|
: false;
|
||||||
|
const inExecution = executionObject
|
||||||
|
? Object.prototype.hasOwnProperty.call(executionObject, key)
|
||||||
|
: false;
|
||||||
|
|
||||||
|
if (inProvider && !inExecution) {
|
||||||
|
removed.push({
|
||||||
|
key,
|
||||||
|
providerValue: cloneConsentDiffValue(providerObject[key])
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!inProvider && inExecution) {
|
||||||
|
added.push({
|
||||||
|
key,
|
||||||
|
executionValue: cloneConsentDiffValue(executionObject[key])
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
stableConsentDiffValue(providerObject[key]) ===
|
||||||
|
stableConsentDiffValue(executionObject[key])
|
||||||
|
) {
|
||||||
|
unchanged.push(key);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
changed.push({
|
||||||
|
key,
|
||||||
|
providerValue: cloneConsentDiffValue(providerObject[key]),
|
||||||
|
executionValue: cloneConsentDiffValue(executionObject[key])
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
kind: "object",
|
||||||
|
status: getConsentDiffObjectStatus(
|
||||||
|
providerValue,
|
||||||
|
executionValue,
|
||||||
|
added,
|
||||||
|
removed,
|
||||||
|
changed
|
||||||
|
),
|
||||||
|
provider: {
|
||||||
|
present: providerValue.present,
|
||||||
|
sourcePath: providerValue.sourcePath,
|
||||||
|
keys: providerKeys
|
||||||
|
},
|
||||||
|
execution: {
|
||||||
|
present: executionValue.present,
|
||||||
|
sourcePath: executionValue.sourcePath,
|
||||||
|
keys: executionKeys
|
||||||
|
},
|
||||||
|
addedKeys: added,
|
||||||
|
removedKeys: removed,
|
||||||
|
changedKeys: changed,
|
||||||
|
unchangedKeys: unchanged,
|
||||||
|
counts: {
|
||||||
|
providerKeys: providerKeys.length,
|
||||||
|
executionKeys: executionKeys.length,
|
||||||
|
added: added.length,
|
||||||
|
removed: removed.length,
|
||||||
|
changed: changed.length,
|
||||||
|
unchanged: unchanged.length
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function readConsentDiffAxisValue(workCopy, axis) {
|
||||||
|
const rawValue = readConsentDiffPath(workCopy.rawTcData, axis);
|
||||||
|
|
||||||
|
if (rawValue.present) {
|
||||||
|
return {
|
||||||
|
...rawValue,
|
||||||
|
sourcePath: `rawTcData.${axis}`
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const eventValue = readConsentDiffPath(workCopy.eventRecord, axis);
|
||||||
|
|
||||||
|
if (eventValue.present) {
|
||||||
|
return {
|
||||||
|
...eventValue,
|
||||||
|
sourcePath: axis
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
present: false,
|
||||||
|
value: undefined,
|
||||||
|
sourcePath: null
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function readConsentDiffPath(value, path) {
|
||||||
|
if (value === null || value === undefined) {
|
||||||
|
return {
|
||||||
|
present: false,
|
||||||
|
value: undefined
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const parts = path.split(".");
|
||||||
|
let current = value;
|
||||||
|
|
||||||
|
for (const part of parts) {
|
||||||
|
if (
|
||||||
|
current === null ||
|
||||||
|
current === undefined ||
|
||||||
|
!Object.prototype.hasOwnProperty.call(Object(current), part)
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
present: false,
|
||||||
|
value: undefined
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
current = current[part];
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
present: true,
|
||||||
|
value: cloneConsentDiffValue(current)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function getConsentDiffScalarStatus(providerValue, executionValue) {
|
||||||
|
if (!providerValue.present && !executionValue.present) {
|
||||||
|
return "absent";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (providerValue.present && !executionValue.present) {
|
||||||
|
return "removed";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!providerValue.present && executionValue.present) {
|
||||||
|
return "added";
|
||||||
|
}
|
||||||
|
|
||||||
|
return stableConsentDiffValue(providerValue.value) ===
|
||||||
|
stableConsentDiffValue(executionValue.value)
|
||||||
|
? "unchanged"
|
||||||
|
: "changed";
|
||||||
|
}
|
||||||
|
|
||||||
|
function getConsentDiffObjectStatus(
|
||||||
|
providerValue,
|
||||||
|
executionValue,
|
||||||
|
added,
|
||||||
|
removed,
|
||||||
|
changed
|
||||||
|
) {
|
||||||
|
if (!providerValue.present && !executionValue.present) {
|
||||||
|
return "absent";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (providerValue.present && !executionValue.present) {
|
||||||
|
return "removed";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!providerValue.present && executionValue.present) {
|
||||||
|
return "added";
|
||||||
|
}
|
||||||
|
|
||||||
|
return added.length === 0 && removed.length === 0 && changed.length === 0
|
||||||
|
? "unchanged"
|
||||||
|
: "changed";
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildConsentDiffAxisSide(axisValue) {
|
||||||
|
return {
|
||||||
|
present: axisValue.present,
|
||||||
|
sourcePath: axisValue.sourcePath,
|
||||||
|
value: axisValue.present ? cloneConsentDiffValue(axisValue.value) : null
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildConsentDiffSourceReference(eventRecord) {
|
||||||
|
if (!eventRecord) {
|
||||||
|
return {
|
||||||
|
available: false,
|
||||||
|
reason: "missing_source_event"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
available: true,
|
||||||
|
store: "consent_events",
|
||||||
|
id: eventRecord.id ?? null,
|
||||||
|
eventType: eventRecord.eventType ?? null,
|
||||||
|
rawEventName: eventRecord.rawEventName ?? null,
|
||||||
|
capturedAt: eventRecord.capturedAt ?? null,
|
||||||
|
recordedAt: eventRecord.recordedAt ?? null,
|
||||||
|
captureSessionId: eventRecord.captureSessionId ?? null
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildConsentDiffNotes(providerAnnouncement, consentExecution) {
|
||||||
|
const notes = [];
|
||||||
|
|
||||||
|
if (!providerAnnouncement) {
|
||||||
|
notes.push("provider_announcement_source_missing");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!consentExecution) {
|
||||||
|
notes.push("consent_execution_source_missing");
|
||||||
|
}
|
||||||
|
|
||||||
|
notes.push("primary_evidence_not_modified");
|
||||||
|
notes.push("no_legal_assessment");
|
||||||
|
notes.push("no_gvl_semantic_assessment");
|
||||||
|
|
||||||
|
return notes;
|
||||||
|
}
|
||||||
|
|
||||||
|
function stableConsentDiffValue(value) {
|
||||||
|
if (value === undefined) {
|
||||||
|
return "[[undefined]]";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value === null || typeof value !== "object") {
|
||||||
|
return JSON.stringify(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
return `[${value.map((item) => stableConsentDiffValue(item)).join(",")}]`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return `{${Object.keys(value)
|
||||||
|
.sort()
|
||||||
|
.map((key) => `${JSON.stringify(key)}:${stableConsentDiffValue(value[key])}`)
|
||||||
|
.join(",")}}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function cloneConsentDiffValue(value) {
|
||||||
|
if (value === undefined) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value === null || typeof value !== "object") {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return JSON.parse(JSON.stringify(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
function isConsentDiffPlainObject(value) {
|
||||||
|
return (
|
||||||
|
value !== null &&
|
||||||
|
typeof value === "object" &&
|
||||||
|
!Array.isArray(value)
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -20,7 +20,7 @@
|
|||||||
|
|
||||||
function cloneSerializable(value, seen) {
|
function cloneSerializable(value, seen) {
|
||||||
if (value === undefined) {
|
if (value === undefined) {
|
||||||
return null;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
if (value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
||||||
@@ -50,10 +50,6 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Object.getPrototypeOf(value) !== Object.prototype && Object.getPrototypeOf(value) !== null) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
const result = {};
|
const result = {};
|
||||||
|
|
||||||
Object.keys(value).forEach(function (key) {
|
Object.keys(value).forEach(function (key) {
|
||||||
@@ -75,31 +71,8 @@
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
window.__tcfapi("ping", 2, function (pingData, pingSuccess) {
|
function buildTcfEventCapture(tcData) {
|
||||||
|
return {
|
||||||
console.log("VendorGet __tcfapi ping:", {
|
|
||||||
success: pingSuccess,
|
|
||||||
data: pingData
|
|
||||||
});
|
|
||||||
|
|
||||||
emitToContentScript("tcf_ping", {
|
|
||||||
success: pingSuccess,
|
|
||||||
data: pingData
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
window.__tcfapi("addEventListener", 2, function (tcData, success) {
|
|
||||||
|
|
||||||
if (!success || !tcData) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log("VendorGet raw event:", tcData);
|
|
||||||
|
|
||||||
if (tcData.eventStatus === "useractioncomplete") {
|
|
||||||
|
|
||||||
const capture = {
|
|
||||||
url: window.location.href,
|
url: window.location.href,
|
||||||
origin: window.location.origin,
|
origin: window.location.origin,
|
||||||
|
|
||||||
@@ -133,8 +106,64 @@
|
|||||||
|
|
||||||
addtlConsent: tcData.addtlConsent,
|
addtlConsent: tcData.addtlConsent,
|
||||||
|
|
||||||
|
rawTcString: {
|
||||||
|
tcString: tcData.tcString,
|
||||||
|
addtlConsent: tcData.addtlConsent
|
||||||
|
},
|
||||||
|
|
||||||
rawTcData: cloneSerializable(tcData)
|
rawTcData: cloneSerializable(tcData)
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener("VendorGetTcfProbeRequest", function (event) {
|
||||||
|
const requestId = event?.detail?.requestId ?? null;
|
||||||
|
|
||||||
|
window.__tcfapi("getTCData", 2, function (tcData, success) {
|
||||||
|
window.dispatchEvent(new CustomEvent("VendorGetTcfProbeResponse", {
|
||||||
|
detail: {
|
||||||
|
requestId: requestId,
|
||||||
|
success: success,
|
||||||
|
capture: success && tcData ? buildTcfEventCapture(tcData) : null
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
window.__tcfapi("ping", 2, function (pingData, pingSuccess) {
|
||||||
|
|
||||||
|
console.log("VendorGet __tcfapi ping:", {
|
||||||
|
success: pingSuccess,
|
||||||
|
data: pingData
|
||||||
|
});
|
||||||
|
|
||||||
|
emitToContentScript("tcf_ping", {
|
||||||
|
success: pingSuccess,
|
||||||
|
data: pingData
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
window.__tcfapi("addEventListener", 2, function (tcData, success) {
|
||||||
|
|
||||||
|
if (!success || !tcData) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("VendorGet raw event:", tcData);
|
||||||
|
|
||||||
|
if (
|
||||||
|
tcData.eventStatus === "cmpuishown" ||
|
||||||
|
tcData.eventStatus === "tcloaded"
|
||||||
|
) {
|
||||||
|
emitToContentScript(
|
||||||
|
"tcf_pre_consent_event",
|
||||||
|
buildTcfEventCapture(tcData)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tcData.eventStatus === "useractioncomplete") {
|
||||||
|
|
||||||
|
const capture = buildTcfEventCapture(tcData);
|
||||||
|
|
||||||
console.log("VendorGet CONSENT CAPTURE:", capture);
|
console.log("VendorGet CONSENT CAPTURE:", capture);
|
||||||
|
|
||||||
|
|||||||
@@ -123,6 +123,42 @@ h1 {
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.pre-consent-capture {
|
||||||
|
display: grid;
|
||||||
|
gap: 8px;
|
||||||
|
margin-bottom: 14px;
|
||||||
|
padding: 10px;
|
||||||
|
border: 1px solid #6366f1;
|
||||||
|
border-radius: 6px;
|
||||||
|
background: #172033;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pre-consent-capture[hidden] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pre-consent-capture h2 {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pre-consent-capture p {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 1.35;
|
||||||
|
color: #cbd5e1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pre-consent-actions {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pre-consent-actions button[hidden] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
.evidence-counts {
|
.evidence-counts {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: 6px;
|
gap: 6px;
|
||||||
|
|||||||
@@ -29,6 +29,31 @@
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<section
|
||||||
|
id="pre-consent-capture"
|
||||||
|
class="pre-consent-capture"
|
||||||
|
aria-label="Pre-Consent-Erfassung"
|
||||||
|
hidden
|
||||||
|
>
|
||||||
|
<h2>Consent-Vorgang erfassen?</h2>
|
||||||
|
<p id="pre-consent-capture-summary">
|
||||||
|
CMP-Vorgang erkannt.
|
||||||
|
</p>
|
||||||
|
<div class="pre-consent-actions">
|
||||||
|
<button id="pre-consent-capture-decline" type="button">
|
||||||
|
Nein
|
||||||
|
</button>
|
||||||
|
<button id="pre-consent-capture-start" type="button">
|
||||||
|
Ja
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
id="pre-consent-capture-status"
|
||||||
|
class="retention-status"
|
||||||
|
aria-live="polite"
|
||||||
|
></div>
|
||||||
|
</section>
|
||||||
|
|
||||||
<section class="evidence-retention" aria-label="Kurzstatus">
|
<section class="evidence-retention" aria-label="Kurzstatus">
|
||||||
<h2>Workspace-Kurzstatus</h2>
|
<h2>Workspace-Kurzstatus</h2>
|
||||||
<div id="maintenance-warning" class="maintenance-warning" hidden>
|
<div id="maintenance-warning" class="maintenance-warning" hidden>
|
||||||
|
|||||||
@@ -8,6 +8,19 @@ const requestMonitoringStatus = document.getElementById(
|
|||||||
);
|
);
|
||||||
const consentCaptureToggle = document.getElementById("consent-capture-toggle");
|
const consentCaptureToggle = document.getElementById("consent-capture-toggle");
|
||||||
const consentCaptureStatus = document.getElementById("consent-capture-status");
|
const consentCaptureStatus = document.getElementById("consent-capture-status");
|
||||||
|
const preConsentCapture = document.getElementById("pre-consent-capture");
|
||||||
|
const preConsentCaptureSummary = document.getElementById(
|
||||||
|
"pre-consent-capture-summary"
|
||||||
|
);
|
||||||
|
const preConsentCaptureStart = document.getElementById(
|
||||||
|
"pre-consent-capture-start"
|
||||||
|
);
|
||||||
|
const preConsentCaptureDecline = document.getElementById(
|
||||||
|
"pre-consent-capture-decline"
|
||||||
|
);
|
||||||
|
const preConsentCaptureStatus = document.getElementById(
|
||||||
|
"pre-consent-capture-status"
|
||||||
|
);
|
||||||
const maintenanceWarning = document.getElementById("maintenance-warning");
|
const maintenanceWarning = document.getElementById("maintenance-warning");
|
||||||
const evidenceLockedCount = document.getElementById("evidence-locked-count");
|
const evidenceLockedCount = document.getElementById("evidence-locked-count");
|
||||||
const evidenceDashboardButton = document.getElementById(
|
const evidenceDashboardButton = document.getElementById(
|
||||||
@@ -47,6 +60,7 @@ const evidenceStoreCountCells = {
|
|||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", async () => {
|
document.addEventListener("DOMContentLoaded", async () => {
|
||||||
await renderSettings();
|
await renderSettings();
|
||||||
|
await renderPreConsentCaptureStatus();
|
||||||
await renderEvidenceMaintenanceStatus();
|
await renderEvidenceMaintenanceStatus();
|
||||||
await renderEvidenceRetentionStatus();
|
await renderEvidenceRetentionStatus();
|
||||||
|
|
||||||
@@ -57,7 +71,13 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|||||||
"consentCaptureEnabled",
|
"consentCaptureEnabled",
|
||||||
consentCaptureToggle.checked
|
consentCaptureToggle.checked
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (consentCaptureToggle.checked) {
|
||||||
|
await probePreConsentCaptureForActiveTab();
|
||||||
|
}
|
||||||
|
|
||||||
await renderSettings();
|
await renderSettings();
|
||||||
|
await renderPreConsentCaptureStatus();
|
||||||
|
|
||||||
consentCaptureToggle.disabled = false;
|
consentCaptureToggle.disabled = false;
|
||||||
});
|
});
|
||||||
@@ -84,6 +104,8 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|||||||
evidencePurgeUnlockedButton.addEventListener("click", openPurgeConfirmModal);
|
evidencePurgeUnlockedButton.addEventListener("click", openPurgeConfirmModal);
|
||||||
evidencePurgeCancelButton.addEventListener("click", closePurgeConfirmModal);
|
evidencePurgeCancelButton.addEventListener("click", closePurgeConfirmModal);
|
||||||
evidencePurgeConfirmButton.addEventListener("click", purgeUnlockedEvidence);
|
evidencePurgeConfirmButton.addEventListener("click", purgeUnlockedEvidence);
|
||||||
|
preConsentCaptureStart.addEventListener("click", startPreConsentCapture);
|
||||||
|
preConsentCaptureDecline.addEventListener("click", declinePreConsentCapture);
|
||||||
});
|
});
|
||||||
|
|
||||||
async function renderSettings() {
|
async function renderSettings() {
|
||||||
@@ -158,6 +180,137 @@ function renderEvidenceRetentionMessage(message) {
|
|||||||
evidenceRetentionStatus.textContent = message;
|
evidenceRetentionStatus.textContent = message;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function renderPreConsentCaptureStatus() {
|
||||||
|
try {
|
||||||
|
const activeTab = await getActiveTab();
|
||||||
|
|
||||||
|
if (!activeTab?.id) {
|
||||||
|
renderNoPreConsentCapture();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await browser.runtime.sendMessage({
|
||||||
|
type: "get_pre_consent_capture_status",
|
||||||
|
payload: {
|
||||||
|
tabId: activeTab.id
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!result?.success) {
|
||||||
|
throw new Error(result?.error ?? "get_pre_consent_capture_status_failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
renderPreConsentCapture(result.capture);
|
||||||
|
} catch (error) {
|
||||||
|
renderNoPreConsentCapture();
|
||||||
|
console.warn("VendorGet-IV pre-consent status failed", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function probePreConsentCaptureForActiveTab() {
|
||||||
|
try {
|
||||||
|
const activeTab = await getActiveTab();
|
||||||
|
|
||||||
|
if (!activeTab?.id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await browser.runtime.sendMessage({
|
||||||
|
type: "probe_pre_consent_capture_for_tab",
|
||||||
|
payload: {
|
||||||
|
tabId: activeTab.id
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.warn("VendorGet-IV pre-consent probe failed", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderPreConsentCapture(capture) {
|
||||||
|
if (!capture) {
|
||||||
|
renderNoPreConsentCapture();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
preConsentCapture.hidden = false;
|
||||||
|
preConsentCaptureStart.hidden = capture.status !== "attention";
|
||||||
|
preConsentCaptureDecline.hidden = capture.status !== "attention";
|
||||||
|
|
||||||
|
if (capture.status === "recording") {
|
||||||
|
preConsentCaptureSummary.textContent = "Pre-Consent-Erfassung läuft.";
|
||||||
|
preConsentCaptureStatus.textContent =
|
||||||
|
"Provider-Announcement wurde gesichert; warte auf useractioncomplete.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
preConsentCaptureSummary.textContent = buildPreConsentCaptureSummary(capture);
|
||||||
|
preConsentCaptureStatus.textContent = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderNoPreConsentCapture() {
|
||||||
|
preConsentCapture.hidden = true;
|
||||||
|
preConsentCaptureSummary.textContent = "";
|
||||||
|
preConsentCaptureStatus.textContent = "";
|
||||||
|
preConsentCaptureStart.disabled = false;
|
||||||
|
preConsentCaptureDecline.disabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildPreConsentCaptureSummary(capture) {
|
||||||
|
const eventLabel = capture.firstEventStatus ?? "TCF-Event";
|
||||||
|
const eventCount = Number(capture.eventCount ?? 0);
|
||||||
|
|
||||||
|
return `${eventLabel} erkannt, ${eventCount} Pre-Consent-Event(s) gepuffert.`;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function startPreConsentCapture() {
|
||||||
|
await answerPreConsentCapture("start_pre_consent_capture");
|
||||||
|
}
|
||||||
|
|
||||||
|
async function declinePreConsentCapture() {
|
||||||
|
await answerPreConsentCapture("decline_pre_consent_capture");
|
||||||
|
}
|
||||||
|
|
||||||
|
async function answerPreConsentCapture(messageType) {
|
||||||
|
preConsentCaptureStart.disabled = true;
|
||||||
|
preConsentCaptureDecline.disabled = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const activeTab = await getActiveTab();
|
||||||
|
|
||||||
|
if (!activeTab?.id) {
|
||||||
|
throw new Error("active_tab_unavailable");
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await browser.runtime.sendMessage({
|
||||||
|
type: messageType,
|
||||||
|
payload: {
|
||||||
|
tabId: activeTab.id
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!result?.success) {
|
||||||
|
throw new Error(result?.error ?? messageType);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderPreConsentCapture(result.capture);
|
||||||
|
} catch (error) {
|
||||||
|
preConsentCaptureStatus.textContent = "Aktion konnte nicht ausgeführt werden";
|
||||||
|
console.warn("VendorGet-IV pre-consent action failed", error);
|
||||||
|
} finally {
|
||||||
|
preConsentCaptureStart.disabled = false;
|
||||||
|
preConsentCaptureDecline.disabled = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getActiveTab() {
|
||||||
|
const tabs = await browser.tabs.query({
|
||||||
|
active: true,
|
||||||
|
currentWindow: true
|
||||||
|
});
|
||||||
|
|
||||||
|
return tabs[0] ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
function openPurgeConfirmModal() {
|
function openPurgeConfirmModal() {
|
||||||
evidencePurgeConfirmModal.hidden = false;
|
evidencePurgeConfirmModal.hidden = false;
|
||||||
evidencePurgeCancelButton.focus();
|
evidencePurgeCancelButton.focus();
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
{
|
||||||
|
"folders": [
|
||||||
|
{
|
||||||
|
"name": "VG-Consent",
|
||||||
|
"path": ".."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"settings": {
|
||||||
|
"codex.projectRole": "Consent-Workspace",
|
||||||
|
"codex.scope": [
|
||||||
|
"Consent-Capture",
|
||||||
|
"Consent-State-Rekonstruktion",
|
||||||
|
"Consent-Events",
|
||||||
|
"Consent-Explorer",
|
||||||
|
"Evidence-Chain-Resolver",
|
||||||
|
"lesende Request-Korrelation",
|
||||||
|
"GVL nur als Referenz/Vault"
|
||||||
|
],
|
||||||
|
"codex.outOfScope": [
|
||||||
|
"allgemeiner Cleanup",
|
||||||
|
"GVL-Import",
|
||||||
|
"GVL-Export",
|
||||||
|
"GVL-Verify",
|
||||||
|
"Dashboard-Refactoring",
|
||||||
|
"Request-Explorer-Ausbau",
|
||||||
|
"neue Datenquellen",
|
||||||
|
"IndexedDB-Migrationen",
|
||||||
|
"Analyse-Engine"
|
||||||
|
],
|
||||||
|
"codex.primaryQuestion": "Hilft diese Änderung dabei, die technisch beobachtbare Wirkung einer konkreten Consent-Entscheidung nachvollziehbar, reproduzierbar und evidenzorientiert zu rekonstruieren?"
|
||||||
|
}
|
||||||
|
}
|
||||||
In neuem Issue referenzieren
Einen Benutzer sperren