Commits vergleichen
7 Commits
61a20c424c
...
master
| Autor | SHA1 | Datum | |
|---|---|---|---|
| 904787c115 | |||
| 1f10389016 | |||
| 3a174128e0 | |||
| e19eef67b2 | |||
| d68735ffc4 | |||
| 0defb37e63 | |||
| 8d923ba962 |
@@ -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
|
||||
|
||||
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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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-Diff unterdrückt, da er zu groß ist
Diff laden
@@ -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
|
||||
];
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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)
|
||||
);
|
||||
}
|
||||
@@ -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(),
|
||||
|
||||
@@ -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%;
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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.`;
|
||||
}
|
||||
|
||||
function buildGvlReferenceDataPurgeSuccessMessage(result) {
|
||||
const clearedCount = Object.values(result.clearedStores ?? {}).reduce(
|
||||
(total, count) => total + Number(count ?? 0),
|
||||
0
|
||||
);
|
||||
|
||||
return `GVL-Referenzdaten bereinigt: ${clearedCount} Records entfernt.`;
|
||||
return "Bereinigung abgeschlossen.";
|
||||
}
|
||||
|
||||
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(", ")}`
|
||||
: "";
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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