diff --git a/src/background.js b/src/background.js index 39108da..1b90a19 100644 --- a/src/background.js +++ b/src/background.js @@ -672,8 +672,23 @@ async function fetchOfficialGvlJson() { throw error; } + const rawBody = await response.text(); + const fetchedAt = new Date().toISOString(); + const contentType = response.headers.get("Content-Type"); + const rawGvlSha256 = await VendorGetGvlService.calculateRawGvlSha256(rawBody); + const db = await openVendorGetDb(); + + await VendorGetGvlService.storeGvlRawEvidenceIfNew(db, { + rawGvlSha256, + sourceUrl: OFFICIAL_IAB_GVL_URL, + fetchedAt, + httpStatus: response.status, + contentType, + rawBody + }); + return { - rawJson: await response.json(), + rawJson: JSON.parse(rawBody), responseStatus: response.status }; } diff --git a/src/background/gvl-service.js b/src/background/gvl-service.js index f54eca5..916626c 100644 --- a/src/background/gvl-service.js +++ b/src/background/gvl-service.js @@ -9,6 +9,47 @@ async function calculateGvlSnapshotSha256(rawJson) { .join(""); } +async function calculateRawGvlSha256(rawBody) { + const data = new TextEncoder().encode(rawBody); + const hashBuffer = await crypto.subtle.digest("SHA-256", data); + const hashArray = Array.from(new Uint8Array(hashBuffer)); + + return hashArray + .map((byte) => byte.toString(16).padStart(2, "0")) + .join(""); +} + +function storeGvlRawEvidenceIfNew(db, rawEvidence) { + return new Promise((resolve, reject) => { + const tx = db.transaction(["gvl_raw_evidence"], "readwrite"); + const rawEvidenceStore = tx.objectStore("gvl_raw_evidence"); + const getRequest = rawEvidenceStore.get(rawEvidence.rawGvlSha256); + let result = null; + + getRequest.onerror = () => reject(getRequest.error); + + getRequest.onsuccess = () => { + if (getRequest.result) { + result = { + stored: false, + rawGvlSha256: rawEvidence.rawGvlSha256 + }; + return; + } + + rawEvidenceStore.add(rawEvidence); + + result = { + stored: true, + rawGvlSha256: rawEvidence.rawGvlSha256 + }; + }; + + tx.onerror = () => reject(tx.error); + tx.oncomplete = () => resolve(result); + }); +} + async function buildGvlSnapshotRecord(rawJson, sourceUrl, fetchedAt) { const gvlJson = normalizeGvlSnapshotValueForMetadata(rawJson); @@ -149,6 +190,8 @@ function countObjectEntries(value) { globalThis.VendorGetGvlService = { calculateGvlSnapshotSha256, + calculateRawGvlSha256, + storeGvlRawEvidenceIfNew, buildGvlSnapshotRecord, storeGvlSnapshotIfNew, recordGvlSnapshotEvent, diff --git a/src/popup/popup.css b/src/popup/popup.css index a9cd5d7..bc9afb1 100644 --- a/src/popup/popup.css +++ b/src/popup/popup.css @@ -175,3 +175,45 @@ button:disabled { cursor: default; opacity: 0.65; } + +.confirm-modal { + position: fixed; + inset: 0; + display: grid; + place-items: center; + padding: 14px; + background: rgba(15, 23, 42, 0.72); +} + +.confirm-modal[hidden] { + display: none; +} + +.confirm-modal-panel { + display: grid; + gap: 10px; + width: min(100%, 320px); + padding: 12px; + border: 1px solid #475569; + border-radius: 6px; + color: #e5edf5; + background: #111827; + box-shadow: 0 8px 24px rgba(0, 0, 0, 0.35); +} + +.confirm-modal-panel h2 { + margin: 0; + font-size: 14px; +} + +.confirm-modal-panel p { + margin: 0; + font-size: 12px; + line-height: 1.4; + color: #cbd5e1; +} + +.confirm-modal-actions { + display: grid; + gap: 8px; +} diff --git a/src/popup/popup.html b/src/popup/popup.html index f4aa9d5..477e5bf 100644 --- a/src/popup/popup.html +++ b/src/popup/popup.html @@ -66,11 +66,39 @@ +
+ Ungesperrte Evidence-Daten wirklich löschen? Gesperrte + DSGVO-Datensätze bleiben erhalten. +
+