Commits vergleichen

...

4 Commits

8 geänderte Dateien mit 377 neuen und 36 gelöschten Zeilen
Binäre Datei nicht angezeigt.

Nachher

Breite:  |  Höhe:  |  Größe: 213 KiB

+23 -3
Datei anzeigen
@@ -619,7 +619,8 @@ function isGvlImportCandidate(value) {
async function handleFetchOfficialGvlMessage() {
try {
const { rawJson, responseStatus } = await fetchOfficialGvlJson();
const { rawJson, rawGvlSha256, responseStatus } =
await fetchOfficialGvlJson();
if (!isGvlImportCandidate(rawJson)) {
return {
@@ -633,6 +634,7 @@ async function handleFetchOfficialGvlMessage() {
const result = await VendorGetGvlService.ingestGvlSnapshot(db, rawJson, {
sourceUrl: OFFICIAL_IAB_GVL_URL,
fetchedAt: new Date().toISOString(),
rawGvlSha256: rawGvlSha256,
diagnostics: {
ingestionSource: "official_iab_fetch",
responseStatus: responseStatus
@@ -672,8 +674,24 @@ 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),
rawGvlSha256: rawGvlSha256,
responseStatus: response.status
};
}
@@ -758,7 +776,8 @@ async function runStartupGvlAutoUpdateCheck() {
result: "started"
});
const { rawJson, responseStatus } = await fetchOfficialGvlJson();
const { rawJson, rawGvlSha256, responseStatus } =
await fetchOfficialGvlJson();
if (!isGvlImportCandidate(rawJson)) {
throw new Error("invalid_gvl_json");
@@ -778,6 +797,7 @@ async function runStartupGvlAutoUpdateCheck() {
{
sourceUrl: OFFICIAL_IAB_GVL_URL,
fetchedAt: new Date().toISOString(),
rawGvlSha256: rawGvlSha256,
diagnostics: {
ingestionSource: "official_iab_auto_update",
responseStatus: responseStatus,
+2 -1
Datei anzeigen
@@ -1,12 +1,13 @@
"use strict";
const VENDORGET_DB_NAME = "vendorget_iv";
const VENDORGET_DB_VERSION = 5;
const VENDORGET_DB_VERSION = 6;
const VENDORGET_STORE_NAMES = {
consentStates: "consent_states",
consentEvents: "consent_events",
observedRequests: "observed_requests",
gvlRawEvidence: "gvl_raw_evidence",
gvlSnapshots: "gvl_snapshots",
gvlSnapshotEvents: "gvl_snapshot_events",
gvlVendors: "gvl_vendors",
+173 -30
Datei anzeigen
@@ -72,7 +72,7 @@ function openVendorGetDb() {
);
}
ensureGvlStores(db);
ensureGvlStores(db, event.target.transaction);
};
request.onsuccess = () => resolve(request.result);
@@ -110,18 +110,73 @@ async function deleteVendorGetDatabase() {
});
}
function ensureGvlStores(db) {
function ensureGvlStores(db, upgradeTransaction) {
if (!db.objectStoreNames.contains("gvl_raw_evidence")) {
const gvlRawEvidence = db.createObjectStore("gvl_raw_evidence", {
keyPath: "rawGvlSha256"
});
createIndexesIfMissing(gvlRawEvidence, [
"canonicalGvlSha256",
"vendorListVersion",
"fetchedAt",
"sourceUrl",
"ingestionSource",
"contentType",
"parseStatus"
]);
}
const gvlRawEvidence = getUpgradeObjectStore(
db,
upgradeTransaction,
"gvl_raw_evidence"
);
if (gvlRawEvidence) {
createIndexesIfMissing(gvlRawEvidence, [
"canonicalGvlSha256",
"vendorListVersion",
"fetchedAt",
"sourceUrl",
"ingestionSource",
"contentType",
"parseStatus"
]);
}
if (!db.objectStoreNames.contains("gvl_snapshots")) {
const gvlSnapshots = db.createObjectStore("gvl_snapshots", {
keyPath: "sha256"
});
gvlSnapshots.createIndex("gvlRevision", "gvlRevision", { unique: false });
gvlSnapshots.createIndex("vendorListVersion", "vendorListVersion", {
unique: false
});
gvlSnapshots.createIndex("fetchedAt", "fetchedAt", { unique: false });
gvlSnapshots.createIndex("sourceUrl", "sourceUrl", { unique: false });
createIndexesIfMissing(gvlSnapshots, [
"gvlRevision",
"vendorListVersion",
"fetchedAt",
"sourceUrl",
"rawGvlSha256",
"canonicalGvlSha256",
"snapshotSha256"
]);
}
const gvlSnapshots = getUpgradeObjectStore(
db,
upgradeTransaction,
"gvl_snapshots"
);
if (gvlSnapshots) {
createIndexesIfMissing(gvlSnapshots, [
"gvlRevision",
"vendorListVersion",
"fetchedAt",
"sourceUrl",
"rawGvlSha256",
"canonicalGvlSha256",
"snapshotSha256"
]);
}
if (!db.objectStoreNames.contains("gvl_snapshot_events")) {
@@ -130,22 +185,43 @@ function ensureGvlStores(db) {
autoIncrement: true
});
gvlSnapshotEvents.createIndex("eventType", "eventType", { unique: false });
gvlSnapshotEvents.createIndex("capturedAt", "capturedAt", { unique: false });
gvlSnapshotEvents.createIndex("gvlRevision", "gvlRevision", {
unique: false
});
gvlSnapshotEvents.createIndex("vendorListVersion", "vendorListVersion", {
unique: false
});
gvlSnapshotEvents.createIndex("sha256", "sha256", { unique: false });
gvlSnapshotEvents.createIndex("sourceUrl", "sourceUrl", { unique: false });
createIndexesIfMissing(gvlSnapshotEvents, [
"eventType",
"capturedAt",
"gvlRevision",
"vendorListVersion",
"sha256",
"sourceUrl",
"rawGvlSha256",
"canonicalGvlSha256",
"snapshotSha256"
]);
}
ensureGvlRelationshipStores(db);
const gvlSnapshotEvents = getUpgradeObjectStore(
db,
upgradeTransaction,
"gvl_snapshot_events"
);
if (gvlSnapshotEvents) {
createIndexesIfMissing(gvlSnapshotEvents, [
"eventType",
"capturedAt",
"gvlRevision",
"vendorListVersion",
"sha256",
"sourceUrl",
"rawGvlSha256",
"canonicalGvlSha256",
"snapshotSha256"
]);
}
function ensureGvlRelationshipStores(db) {
ensureGvlRelationshipStores(db, upgradeTransaction);
}
function ensureGvlRelationshipStores(db, upgradeTransaction) {
const gvlRelationshipStoreDefinitions = [
{
name: "gvl_vendors",
@@ -154,28 +230,66 @@ function ensureGvlRelationshipStores(db) {
"vendorId",
"name",
"policyUrl",
"deletedDate"
"deletedDate",
"rawGvlSha256",
"canonicalGvlSha256",
"snapshotSha256"
]
},
{
name: "gvl_purposes",
indexes: ["vendorListVersion", "purposeId", "name"]
indexes: [
"vendorListVersion",
"purposeId",
"name",
"rawGvlSha256",
"canonicalGvlSha256",
"snapshotSha256"
]
},
{
name: "gvl_special_purposes",
indexes: ["vendorListVersion", "specialPurposeId", "name"]
indexes: [
"vendorListVersion",
"specialPurposeId",
"name",
"rawGvlSha256",
"canonicalGvlSha256",
"snapshotSha256"
]
},
{
name: "gvl_features",
indexes: ["vendorListVersion", "featureId", "name"]
indexes: [
"vendorListVersion",
"featureId",
"name",
"rawGvlSha256",
"canonicalGvlSha256",
"snapshotSha256"
]
},
{
name: "gvl_special_features",
indexes: ["vendorListVersion", "specialFeatureId", "name"]
indexes: [
"vendorListVersion",
"specialFeatureId",
"name",
"rawGvlSha256",
"canonicalGvlSha256",
"snapshotSha256"
]
},
{
name: "gvl_data_categories",
indexes: ["vendorListVersion", "dataCategoryId", "name"]
indexes: [
"vendorListVersion",
"dataCategoryId",
"name",
"rawGvlSha256",
"canonicalGvlSha256",
"snapshotSha256"
]
},
{
name: "gvl_vendor_relationships",
@@ -183,13 +297,26 @@ function ensureGvlRelationshipStores(db) {
"vendorListVersion",
"vendorId",
"relationshipType",
"relatedId"
"relatedId",
"rawGvlSha256",
"canonicalGvlSha256",
"snapshotSha256"
]
}
];
gvlRelationshipStoreDefinitions.forEach((storeDefinition) => {
if (db.objectStoreNames.contains(storeDefinition.name)) {
const objectStore = getUpgradeObjectStore(
db,
upgradeTransaction,
storeDefinition.name
);
if (objectStore) {
createIndexesIfMissing(objectStore, storeDefinition.indexes);
}
return;
}
@@ -197,8 +324,24 @@ function ensureGvlRelationshipStores(db) {
keyPath: "id"
});
storeDefinition.indexes.forEach((indexName) => {
objectStore.createIndex(indexName, indexName, { unique: false });
});
createIndexesIfMissing(objectStore, storeDefinition.indexes);
});
}
function getUpgradeObjectStore(db, upgradeTransaction, storeName) {
if (!upgradeTransaction || !db.objectStoreNames.contains(storeName)) {
return null;
}
return upgradeTransaction.objectStore(storeName);
}
function createIndexesIfMissing(objectStore, indexNames) {
indexNames.forEach((indexName) => {
if (objectStore.indexNames.contains(indexName)) {
return;
}
objectStore.createIndex(indexName, indexName, { unique: false });
});
}
+52 -2
Datei anzeigen
@@ -9,11 +9,58 @@ async function calculateGvlSnapshotSha256(rawJson) {
.join("");
}
async function buildGvlSnapshotRecord(rawJson, sourceUrl, fetchedAt) {
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,
rawGvlSha256
) {
const gvlJson = normalizeGvlSnapshotValueForMetadata(rawJson);
return {
sha256: await calculateGvlSnapshotSha256(rawJson),
rawGvlSha256: rawGvlSha256 ?? null,
vendorListVersion: gvlJson?.vendorListVersion ?? null,
gvlSpecificationVersion: gvlJson?.gvlSpecificationVersion ?? null,
tcfPolicyVersion: gvlJson?.tcfPolicyVersion ?? null,
@@ -88,7 +135,8 @@ async function ingestGvlSnapshot(db, rawJson, options = {}) {
const snapshot = await buildGvlSnapshotRecord(
rawJson,
options.sourceUrl ?? null,
options.fetchedAt ?? null
options.fetchedAt ?? null,
options.rawGvlSha256 ?? null
);
const storeResult = await storeGvlSnapshotIfNew(db, snapshot);
const alreadyKnown = !storeResult.stored;
@@ -149,6 +197,8 @@ function countObjectEntries(value) {
globalThis.VendorGetGvlService = {
calculateGvlSnapshotSha256,
calculateRawGvlSha256,
storeGvlRawEvidenceIfNew,
buildGvlSnapshotRecord,
storeGvlSnapshotIfNew,
recordGvlSnapshotEvent,
+42
Datei anzeigen
@@ -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;
}
+28
Datei anzeigen
@@ -66,11 +66,39 @@
<button id="evidence-export-json-button" type="button">
Export Evidence JSON
</button>
<button id="evidence-purge-unlocked-button" type="button">
Ungesperrte Evidence-Daten löschen
</button>
<div id="evidence-export-json-status" class="retention-status" aria-live="polite"></div>
<div id="evidence-retention-status" class="retention-status" aria-live="polite">
Status wird geladen
</div>
</section>
<div
id="evidence-purge-confirm-modal"
class="confirm-modal"
role="dialog"
aria-modal="true"
aria-labelledby="evidence-purge-confirm-title"
hidden
>
<div class="confirm-modal-panel">
<h2 id="evidence-purge-confirm-title">Evidence-Daten löschen</h2>
<p>
Ungesperrte Evidence-Daten wirklich löschen? Gesperrte
DSGVO-Datensätze bleiben erhalten.
</p>
<div class="confirm-modal-actions">
<button id="evidence-purge-cancel-button" type="button">
Abbrechen
</button>
<button id="evidence-purge-confirm-button" type="button">
Ungesperrte Daten löschen
</button>
</div>
</div>
</div>
</main>
<script src="../core/settings-storage.js"></script>
+57
Datei anzeigen
@@ -16,6 +16,18 @@ const evidenceDashboardButton = document.getElementById(
const evidenceExportJsonButton = document.getElementById(
"evidence-export-json-button"
);
const evidencePurgeUnlockedButton = document.getElementById(
"evidence-purge-unlocked-button"
);
const evidencePurgeConfirmModal = document.getElementById(
"evidence-purge-confirm-modal"
);
const evidencePurgeCancelButton = document.getElementById(
"evidence-purge-cancel-button"
);
const evidencePurgeConfirmButton = document.getElementById(
"evidence-purge-confirm-button"
);
const evidenceExportJsonStatus = document.getElementById(
"evidence-export-json-status"
);
@@ -69,6 +81,9 @@ document.addEventListener("DOMContentLoaded", async () => {
});
evidenceExportJsonButton.addEventListener("click", exportEvidenceJsonFile);
evidencePurgeUnlockedButton.addEventListener("click", openPurgeConfirmModal);
evidencePurgeCancelButton.addEventListener("click", closePurgeConfirmModal);
evidencePurgeConfirmButton.addEventListener("click", purgeUnlockedEvidence);
});
async function renderSettings() {
@@ -143,6 +158,48 @@ function renderEvidenceRetentionMessage(message) {
evidenceRetentionStatus.textContent = message;
}
function openPurgeConfirmModal() {
evidencePurgeConfirmModal.hidden = false;
evidencePurgeCancelButton.focus();
}
function closePurgeConfirmModal() {
evidencePurgeConfirmModal.hidden = true;
evidencePurgeUnlockedButton.focus();
}
async function purgeUnlockedEvidence() {
closePurgeConfirmModal();
evidencePurgeUnlockedButton.disabled = true;
renderEvidenceRetentionMessage("Ungesperrte Evidence-Daten werden gelöscht...");
try {
const result = await browser.runtime.sendMessage({
type: "purge_unlocked_evidence_records"
});
if (!result?.success) {
throw new Error(result?.error ?? "purge_unlocked_evidence_records_failed");
}
await renderEvidenceRetentionStatus();
renderEvidenceRetentionMessage(buildPurgeUnlockedSuccessMessage(result));
} catch (error) {
renderEvidenceRetentionMessage("Löschen fehlgeschlagen");
console.warn("VendorGet-IV unlocked evidence purge failed", error);
} finally {
evidencePurgeUnlockedButton.disabled = false;
}
}
function buildPurgeUnlockedSuccessMessage(result) {
if (Number.isFinite(result.deletedCount)) {
return `Ungesperrte Evidence-Daten gelöscht: ${result.deletedCount} Records`;
}
return "Ungesperrte Evidence-Daten gelöscht";
}
async function exportEvidenceJsonFile() {
evidenceExportJsonButton.disabled = true;
renderEvidenceExportJsonMessage("Export läuft…");