Commits vergleichen

..

7 Commits

25 geänderte Dateien mit 3503 neuen und 154 gelöschten Zeilen
@@ -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.
+2
Datei anzeigen
@@ -1,5 +1,7 @@
# VG-Environment Codex Workflow
Vor Beginn fachlicher Arbeiten ist `docs/architecture/project-philosophy.md` als maßgebliche Beschreibung des Projektkerns zu berücksichtigen.
## Grundprinzip
VG-Environment wird schrittweise modularisiert.
+28
Datei anzeigen
@@ -0,0 +1,28 @@
### GVL-Purge-Summary und Snapshot-Events
Bei der Untersuchung der GVL-Bereinigung wurde festgestellt, dass die Purge-Summary je nach Herkunft einer GVL-Revision unterschiedliche Gesamtzahlen ausweisen kann.
Beobachtung:
* Vault-GVL importiert → Purge-Summary: `13593 Records`
* Web-GVL geladen, passende Vault-GVL verifiziert → Purge-Summary: `13594 Records`
Die Differenz von genau einem Record stellt keinen Zählfehler dar.
Ursache:
Der Web-Ingest einer offiziellen GVL erzeugt zusätzlich einen Eintrag im Store `gvl_snapshot_events`. Dabei handelt es sich um ein Ereignis des lokalen Beobachtungsverlaufs, beispielsweise `gvl_snapshot_ingested`.
Der Import einer GVL-Revision aus einem Vault-Paket rekonstruiert dagegen die Revision selbst, importiert jedoch keine Snapshot-Event-Historie und erzeugt auch kein entsprechendes Ereignis.
Die globale Purge-Summary zählt `gvl_snapshot_events` mit. Daher entsteht im Web-Pfad gegenüber dem Vault-Pfad ein zusätzlicher gelöschter Record.
Fachliche Einordnung:
Die unterschiedliche Gesamtzahl ist erwartetes Verhalten und Ausdruck unterschiedlicher Provenienz:
* Web-GVL: dokumentierter lokaler Ingest-Vorgang.
* Vault-GVL: rekonstruiertes Evidenzobjekt ohne lokale Beobachtungshistorie.
Die Purge-Summary ist daher nicht nur ein Maß für die Größe einer Revision, sondern kann auch Unterschiede im Entstehungskontext der lokalen Evidence widerspiegeln.
+103
Datei anzeigen
@@ -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
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
VG-Environment is a local evidence and runtime reconstruction environment for consent-related browser activity.
+1
Datei anzeigen
@@ -40,6 +40,7 @@
"src/background/gvl-service.js",
"src/core/binary-utils.js",
"src/core/tcf-core-metadata-decoder.js",
"src/core/consent-diff.js",
"src/background.js"
]
},
+1070 -11
Datei anzeigen
Datei-Diff unterdrückt, da er zu groß ist Diff laden
-13
Datei anzeigen
@@ -33,16 +33,3 @@ const VENDORGET_EVIDENCE_STORE_NAMES = [
VENDORGET_STORE_NAMES.gvlDataCategories,
VENDORGET_STORE_NAMES.gvlVendorRelationships
];
const VENDORGET_GVL_REFERENCE_STORE_NAMES = [
VENDORGET_STORE_NAMES.gvlRawEvidence,
VENDORGET_STORE_NAMES.gvlSnapshots,
VENDORGET_STORE_NAMES.gvlSnapshotEvents,
VENDORGET_STORE_NAMES.gvlVendors,
VENDORGET_STORE_NAMES.gvlPurposes,
VENDORGET_STORE_NAMES.gvlSpecialPurposes,
VENDORGET_STORE_NAMES.gvlFeatures,
VENDORGET_STORE_NAMES.gvlSpecialFeatures,
VENDORGET_STORE_NAMES.gvlDataCategories,
VENDORGET_STORE_NAMES.gvlVendorRelationships
];
+5 -25
Datei anzeigen
@@ -89,31 +89,11 @@ async function purgeUnlockedEvidenceRecords(db) {
});
}
function purgeGvlReferenceData(db) {
return new Promise((resolve, reject) => {
const clearedStores = {};
const tx = db.transaction(VENDORGET_GVL_REFERENCE_STORE_NAMES, "readwrite");
tx.onerror = () => reject(tx.error);
tx.onabort = () => reject(tx.error);
tx.oncomplete = () => {
resolve({
success: true,
clearedStores
});
};
for (const storeName of VENDORGET_GVL_REFERENCE_STORE_NAMES) {
const objectStore = tx.objectStore(storeName);
const countRequest = objectStore.count();
countRequest.onsuccess = () => {
clearedStores[storeName] = countRequest.result;
objectStore.clear();
};
}
});
}
// TODO: GVL-Datenpflege darf nicht storeweise per clear() erfolgen.
// Loeschbar ist nur eine GVL-Revision, wenn ihre zugehoerigen Raw-, Snapshot-,
// Event- und normalisierten Records identifiziert sind, ihr Schutzstatus
// vollstaendig bewertet wurde und eine vorhandene Vault-/Workspace-Schutzlogik
// die Loeschung erlaubt.
function buildGvlWorkspaceProtectionIndex(db) {
return new Promise((resolve, reject) => {
+173
Datei anzeigen
@@ -27,6 +27,7 @@ body {
h1,
h2,
h3,
h4,
p {
margin: 0;
}
@@ -49,6 +50,11 @@ h3 {
color: #cbd5e1;
}
h4 {
font-size: 12px;
color: #cbd5e1;
}
p {
max-width: 760px;
font-size: 13px;
@@ -139,6 +145,165 @@ th {
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 td {
vertical-align: top;
@@ -227,4 +392,12 @@ th {
.inspector-table .inspector-explanation {
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">
<h2 id="consent-detail-title">Ausgewählter Consent-Zustand</h2>
<div id="consent-detail-observation"></div>
<div id="consent-detail-diff"></div>
<div id="consent-detail-basics"></div>
<div id="consent-detail-summary"></div>
<div id="consent-detail-publisher"></div>
Datei-Diff unterdrückt, da er zu groß ist Diff laden
+78
Datei anzeigen
@@ -1,9 +1,87 @@
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) => {
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({
type: "vendorget_capture",
payload: event.detail
+374
Datei anzeigen
@@ -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)
);
}
+67 -7
Datei anzeigen
@@ -287,14 +287,21 @@ async function importVendorGetGvlRevisionEvidenceJson(exportContainer) {
};
}
async function markVendorGetGvlRevisionEvidenceVaultCopy(snapshotSha256) {
async function markVendorGetGvlRevisionEvidenceVaultCopy(
snapshotSha256,
verification = null
) {
if (!snapshotSha256) {
throw new Error("missing_snapshot_sha256");
}
const db = await openVendorGetDb();
return markGvlRevisionEvidenceVaultCopyAvailable(db, snapshotSha256);
return markGvlRevisionEvidenceVaultCopyAvailable(
db,
snapshotSha256,
verification
);
}
function getGvlEvidenceRecordByKey(db, storeName, key) {
@@ -632,9 +639,16 @@ function formatGvlEvidenceProvenance(values) {
return "web";
}
function markGvlRevisionEvidenceVaultCopyAvailable(db, snapshotSha256) {
return updateGvlRevisionEvidenceRecords(db, snapshotSha256, (record) =>
markGvlEvidenceRecordVaultCopyAvailable(record)
function markGvlRevisionEvidenceVaultCopyAvailable(
db,
snapshotSha256,
verification = null
) {
return updateGvlRevisionEvidenceRecords(
db,
snapshotSha256,
(record) => markGvlEvidenceRecordVaultCopyAvailable(record),
verification
);
}
@@ -652,7 +666,12 @@ function markGvlRevisionEvidenceProvenance(db, snapshotSha256, provenance) {
);
}
function updateGvlRevisionEvidenceRecords(db, snapshotSha256, updateRecord) {
function updateGvlRevisionEvidenceRecords(
db,
snapshotSha256,
updateRecord,
verification = null
) {
return new Promise((resolve, reject) => {
const tx = db.transaction(
[VENDORGET_STORE_NAMES.gvlSnapshots, VENDORGET_STORE_NAMES.gvlRawEvidence],
@@ -665,7 +684,8 @@ function updateGvlRevisionEvidenceRecords(db, snapshotSha256, updateRecord) {
snapshotMarked: false,
rawEvidenceMarked: false,
snapshotSha256,
rawGvlSha256: null
rawGvlSha256: null,
skippedReason: null
};
snapshotRequest.onerror = () => reject(snapshotRequest.error);
@@ -673,6 +693,12 @@ function updateGvlRevisionEvidenceRecords(db, snapshotSha256, updateRecord) {
const snapshot = snapshotRequest.result ?? null;
if (!snapshot) {
result.skippedReason = "gvl_snapshot_not_found";
return;
}
if (!doesGvlRevisionEvidenceMatchVerification(snapshot, verification)) {
result.skippedReason = "gvl_revision_evidence_verification_mismatch";
return;
}
@@ -708,6 +734,40 @@ function updateGvlRevisionEvidenceRecords(db, snapshotSha256, updateRecord) {
});
}
function doesGvlRevisionEvidenceMatchVerification(snapshot, verification) {
if (!verification) {
return true;
}
if (verification.valid !== true) {
return false;
}
if (
verification.snapshotSha256 &&
snapshot.sha256 !== verification.snapshotSha256
) {
return false;
}
if (
verification.vendorListVersion !== null &&
verification.vendorListVersion !== undefined &&
snapshot.vendorListVersion !== verification.vendorListVersion
) {
return false;
}
if (
verification.rawGvlSha256 &&
snapshot.rawGvlSha256 !== verification.rawGvlSha256
) {
return false;
}
return true;
}
function formatGvlEvidenceUtcCompact(date) {
return [
date.getUTCFullYear(),
+10 -9
Datei anzeigen
@@ -81,15 +81,6 @@
<strong>Analyse-Vorbereitung</strong>
<span>Datenbestände und vorbereitete Prüffelder, keine Engine.</span>
</a>
<a class="workspace-link workspace-placeholder" href="../data-maintenance/data-maintenance.html">
<strong>Datenpflege</strong>
<span>
Gezielte Verwaltung lokaler Datenbestände. Löschen,
Wiederherstellen und Exportieren erfolgen künftig segmentbezogen.
Vorgesehene Segmente sind GVL-Referenzdaten der Browser-DB,
Consent-Daten, Analyse-Daten und weitere künftige Datenbereiche.
</span>
</a>
</div>
</section>
@@ -127,6 +118,16 @@
</dl>
</section>
<section class="panel" aria-labelledby="data-maintenance-title">
<h2 id="data-maintenance-title">Datenpflege</h2>
<div class="workspace-actions">
<a class="workspace-link workspace-placeholder" href="../data-maintenance/data-maintenance.html">
<strong>Datenpflege</strong>
<span>Verwaltung lokaler Datenbestände.</span>
</a>
</div>
</section>
</main>
<script src="dashboard.js"></script>
@@ -113,6 +113,10 @@ p {
background: #172033;
}
.protected-revisions {
white-space: pre-line;
}
button {
width: fit-content;
max-width: 100%;
+11 -2
Datei anzeigen
@@ -31,7 +31,7 @@
Consent-Daten, Request-Beobachtungen und Analyse-Daten bleiben
unberührt.
</p>
<button id="purge-gvl-reference-data-button" type="button">
<button id="purge-gvl-reference-data-button" type="button" disabled>
GVL-Referenzdaten bereinigen
</button>
</div>
@@ -40,7 +40,16 @@
class="segment-status"
aria-live="polite"
>
Bereit.
</p>
<p
id="gvl-reference-protected-revisions"
class="protected-revisions"
aria-live="polite"
hidden
></p>
<p id="gvl-reference-maintenance-message">
Keine GVL-Revisionen.
</p>
</article>
+91 -41
Datei anzeigen
@@ -1,69 +1,119 @@
"use strict";
const purgeGvlReferenceDataButton = document.getElementById(
"purge-gvl-reference-data-button"
);
const gvlReferenceMaintenanceStatus = document.getElementById(
"gvl-reference-maintenance-status"
);
const gvlReferenceProtectedRevisions = document.getElementById(
"gvl-reference-protected-revisions"
);
const gvlReferenceMaintenanceMessage = document.getElementById(
"gvl-reference-maintenance-message"
);
const purgeGvlReferenceDataButton = document.getElementById(
"purge-gvl-reference-data-button"
);
document.addEventListener("DOMContentLoaded", () => {
purgeGvlReferenceDataButton?.addEventListener("click", async () => {
await purgeGvlReferenceData();
document.addEventListener("DOMContentLoaded", async () => {
purgeGvlReferenceDataButton.addEventListener("click", async () => {
await purgeUnlockedEvidenceRecords();
});
await renderGvlReferenceMaintenanceStatus();
});
async function purgeGvlReferenceData() {
if (!confirm(buildGvlReferenceDataPurgeConfirmationText())) {
renderGvlReferenceMaintenanceStatus("Abgebrochen.");
async function renderGvlReferenceMaintenanceStatus() {
try {
const result = await browser.runtime.sendMessage({
type: "list_gvl_snapshots"
});
if (!result?.success) {
throw new Error(result?.error ?? "list_gvl_snapshots_failed");
}
renderGvlReferenceSnapshotStatus(result.gvlSnapshots ?? []);
} catch (error) {
renderGvlReferenceStatus("–", true, "Status nicht verfügbar.");
renderProtectedRevisions([]);
console.warn("VG-Observe GVL maintenance status failed", error);
}
}
function renderGvlReferenceSnapshotStatus(snapshots) {
if (!snapshots.length) {
renderGvlReferenceStatus("–", true, "Keine GVL-Revisionen.");
renderProtectedRevisions([]);
return;
}
const protectedRevisions = snapshots
.filter((snapshot) => snapshot.workspaceDeleteProtected === true)
.map((snapshot) => snapshot.vendorListVersion)
.filter((vendorListVersion) => vendorListVersion !== null)
.filter((vendorListVersion) => vendorListVersion !== undefined);
const allRevisionsDeleteAllowed = snapshots.every((snapshot) => {
return snapshot.workspaceDeleteAllowed === true;
});
if (allRevisionsDeleteAllowed) {
renderGvlReferenceStatus("🔓", false, "GVL-Revisionen löschbar.");
} else if (protectedRevisions.length) {
renderGvlReferenceStatus("🔒", true, "GVL-Revisionen geschützt.");
} else {
renderGvlReferenceStatus("–", true, "Status nicht verfügbar.");
}
renderProtectedRevisions(protectedRevisions);
}
function renderGvlReferenceStatus(statusSymbol, buttonDisabled, message) {
gvlReferenceMaintenanceStatus.textContent = statusSymbol;
purgeGvlReferenceDataButton.disabled = buttonDisabled;
gvlReferenceMaintenanceMessage.textContent = message;
}
async function purgeUnlockedEvidenceRecords() {
if (
!confirm(
"Ungesperrte Evidence-Daten mit bestehender Schutzlogik bereinigen?"
)
) {
return;
}
purgeGvlReferenceDataButton.disabled = true;
renderGvlReferenceMaintenanceStatus("GVL-Referenzdaten werden bereinigt...");
gvlReferenceMaintenanceMessage.textContent = "Bereinigung läuft...";
try {
const result = await browser.runtime.sendMessage({
type: "purge_gvl_reference_data"
type: "purge_unlocked_evidence_records"
});
if (!result?.success) {
throw new Error(result?.error ?? "purge_gvl_reference_data_failed");
throw new Error(result?.error ?? "purge_unlocked_evidence_records_failed");
}
renderGvlReferenceMaintenanceStatus(
buildGvlReferenceDataPurgeSuccessMessage(result)
);
await renderGvlReferenceMaintenanceStatus();
gvlReferenceMaintenanceMessage.textContent = buildPurgeSuccessMessage(result);
} catch (error) {
renderGvlReferenceMaintenanceStatus(
"GVL-Referenzdaten konnten nicht bereinigt werden."
);
console.warn("VG-Observe GVL reference data purge failed", error);
} finally {
purgeGvlReferenceDataButton.disabled = false;
await renderGvlReferenceMaintenanceStatus();
gvlReferenceMaintenanceMessage.textContent = "Bereinigung fehlgeschlagen.";
console.warn("VG-Observe protected purge failed", error);
}
}
function buildGvlReferenceDataPurgeConfirmationText() {
return [
"GVL-Referenzdaten bereinigen?",
"",
"Betroffen: GVL-Referenzdaten der Browser-DB.",
"Nicht betroffen: Consent-Daten, Request-Beobachtungen, Analyse-Daten.",
"",
"Diese Aktion entfernt lokale GVL-Referenzdaten aus der Browser-Datenbank."
].join("\n");
function buildPurgeSuccessMessage(result) {
if (Number.isFinite(result.deletedCount)) {
return `Bereinigung abgeschlossen: ${result.deletedCount} Records.`;
}
return "Bereinigung abgeschlossen.";
}
function buildGvlReferenceDataPurgeSuccessMessage(result) {
const clearedCount = Object.values(result.clearedStores ?? {}).reduce(
(total, count) => total + Number(count ?? 0),
0
);
return `GVL-Referenzdaten bereinigt: ${clearedCount} Records entfernt.`;
}
function renderGvlReferenceMaintenanceStatus(message) {
gvlReferenceMaintenanceStatus.textContent = message;
function renderProtectedRevisions(vendorListVersions) {
gvlReferenceProtectedRevisions.hidden = vendorListVersions.length === 0;
gvlReferenceProtectedRevisions.textContent = vendorListVersions.length
? `Geschützt: ${vendorListVersions
.map((vendorListVersion) => String(vendorListVersion))
.join(", ")}`
: "";
}
+12 -3
Datei anzeigen
@@ -215,7 +215,7 @@ async function exportSelectedGvlRevisionEvidenceJsonFile() {
}
downloadGvlRevisionEvidenceJsonExport(result.export);
await markGvlRevisionEvidenceVaultCopy(result.export);
await markGvlRevisionEvidenceVaultCopy(result.export, verification);
await renderGvlSnapshots();
renderGvlEvidenceTransportStatus(
[
@@ -271,7 +271,10 @@ function downloadGvlRevisionEvidenceJsonExport(exportContainer) {
setTimeout(() => URL.revokeObjectURL(url), 0);
}
async function markGvlRevisionEvidenceVaultCopy(exportContainer) {
async function markGvlRevisionEvidenceVaultCopy(
exportContainer,
verification = null
) {
const snapshotSha256 = exportContainer?.metadata?.snapshotSha256 ?? null;
if (!snapshotSha256) {
@@ -281,7 +284,8 @@ async function markGvlRevisionEvidenceVaultCopy(exportContainer) {
const result = await browser.runtime.sendMessage({
type: "mark_gvl_revision_evidence_vault_copy",
payload: {
snapshotSha256
snapshotSha256,
verification
}
});
@@ -338,6 +342,11 @@ async function verifyGvlRevisionEvidenceJsonFile() {
exportContainer
);
if (verification.valid) {
await markGvlRevisionEvidenceVaultCopy(exportContainer, verification);
await renderGvlSnapshots();
}
renderGvlEvidenceTransportStatus(
buildGvlRevisionEvidenceVerificationMessage(verification),
verification.valid ? "success" : "error"
+59 -30
Datei anzeigen
@@ -20,7 +20,7 @@
function cloneSerializable(value, seen) {
if (value === undefined) {
return null;
return undefined;
}
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 = {};
Object.keys(value).forEach(function (key) {
@@ -75,31 +71,8 @@
return result;
}
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 === "useractioncomplete") {
const capture = {
function buildTcfEventCapture(tcData) {
return {
url: window.location.href,
origin: window.location.origin,
@@ -133,8 +106,64 @@
addtlConsent: tcData.addtlConsent,
rawTcString: {
tcString: tcData.tcString,
addtlConsent: tcData.addtlConsent
},
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);
+36
Datei anzeigen
@@ -123,6 +123,42 @@ h1 {
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 {
display: grid;
gap: 6px;
+25
Datei anzeigen
@@ -29,6 +29,31 @@
</div>
</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">
<h2>Workspace-Kurzstatus</h2>
<div id="maintenance-warning" class="maintenance-warning" hidden>
+153
Datei anzeigen
@@ -8,6 +8,19 @@ const requestMonitoringStatus = document.getElementById(
);
const consentCaptureToggle = document.getElementById("consent-capture-toggle");
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 evidenceLockedCount = document.getElementById("evidence-locked-count");
const evidenceDashboardButton = document.getElementById(
@@ -47,6 +60,7 @@ const evidenceStoreCountCells = {
document.addEventListener("DOMContentLoaded", async () => {
await renderSettings();
await renderPreConsentCaptureStatus();
await renderEvidenceMaintenanceStatus();
await renderEvidenceRetentionStatus();
@@ -57,7 +71,13 @@ document.addEventListener("DOMContentLoaded", async () => {
"consentCaptureEnabled",
consentCaptureToggle.checked
);
if (consentCaptureToggle.checked) {
await probePreConsentCaptureForActiveTab();
}
await renderSettings();
await renderPreConsentCaptureStatus();
consentCaptureToggle.disabled = false;
});
@@ -84,6 +104,8 @@ document.addEventListener("DOMContentLoaded", async () => {
evidencePurgeUnlockedButton.addEventListener("click", openPurgeConfirmModal);
evidencePurgeCancelButton.addEventListener("click", closePurgeConfirmModal);
evidencePurgeConfirmButton.addEventListener("click", purgeUnlockedEvidence);
preConsentCaptureStart.addEventListener("click", startPreConsentCapture);
preConsentCaptureDecline.addEventListener("click", declinePreConsentCapture);
});
async function renderSettings() {
@@ -158,6 +180,137 @@ function renderEvidenceRetentionMessage(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() {
evidencePurgeConfirmModal.hidden = false;
evidencePurgeCancelButton.focus();
+32
Datei anzeigen
@@ -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?"
}
}