Add verified GVL evidence import provenance and protection

Dieser Commit ist enthalten in:
2026-06-10 19:17:31 +02:00
Ursprung eb70f0ce81
Commit a3ca8019a1
8 geänderte Dateien mit 891 neuen und 34 gelöschten Zeilen
+154 -22
Datei anzeigen
@@ -41,6 +41,14 @@ async function handleVendorGetMessage(message, sender) {
return handleVerifyGvlRevisionEvidenceJsonMessage(message);
}
if (message.type === "import_gvl_revision_evidence_json") {
return handleImportGvlRevisionEvidenceJsonMessage(message);
}
if (message.type === "mark_gvl_revision_evidence_vault_copy") {
return handleMarkGvlRevisionEvidenceVaultCopyMessage(message);
}
if (message.type === "import_gvl_evidence_json") {
return handleImportGvlEvidenceJsonMessage(message);
}
@@ -265,6 +273,42 @@ async function handleImportGvlEvidenceJsonMessage(message) {
}
}
async function handleImportGvlRevisionEvidenceJsonMessage(message) {
try {
const importResult = await importVendorGetGvlRevisionEvidenceJson(
message?.payload?.export ?? null
);
return {
success: importResult.imported,
import: importResult,
verification: importResult.verification,
error: importResult.imported ? null : "invalid_gvl_revision_evidence"
};
} catch (error) {
return {
success: false,
error: error?.message ?? String(error)
};
}
}
async function handleMarkGvlRevisionEvidenceVaultCopyMessage(message) {
try {
return {
success: true,
mark: await markVendorGetGvlRevisionEvidenceVaultCopy(
message?.payload?.snapshotSha256 ?? null
)
};
} catch (error) {
return {
success: false,
error: error?.message ?? String(error)
};
}
}
async function handleGetLatestGvlUpdateStatusMessage() {
const db = await openVendorGetDb();
const latestSnapshot = await getLatestGvlSnapshotByVendorListVersion(db);
@@ -311,12 +355,17 @@ async function handleListGvlSnapshotsMessage() {
const snapshotsWithEvents = await Promise.all(
snapshots.map(async (snapshot) => {
const event = await getAnyGvlSnapshotEventBySha256(db, snapshot.sha256);
const provenanceState = getGvlEvidenceProvenanceState(snapshot);
return {
vendorListVersion: snapshot.vendorListVersion ?? null,
sha256: snapshot.sha256 ?? null,
fetchedAt: snapshot.fetchedAt ?? null,
sourceUrl: snapshot.sourceUrl ?? null,
provenance: provenanceState.provenance,
vaultCopyAvailable: provenanceState.vaultCopyAvailable,
workspaceDeleteAllowed: provenanceState.workspaceDeleteAllowed,
workspaceDeleteProtected: provenanceState.workspaceDeleteProtected,
eventType: event?.eventType ?? null,
eventCapturedAt: event?.capturedAt ?? null
};
@@ -350,6 +399,7 @@ async function handleGetGvlSnapshotSummaryMessage(message) {
db,
vendorListVersion
);
const provenanceState = getGvlEvidenceProvenanceState(snapshot);
return {
success: true,
@@ -360,6 +410,10 @@ async function handleGetGvlSnapshotSummaryMessage(message) {
sourceUrl: snapshot.sourceUrl ?? null,
eventType: event?.eventType ?? null,
eventCapturedAt: event?.capturedAt ?? null,
provenance: provenanceState.provenance,
vaultCopyAvailable: provenanceState.vaultCopyAvailable,
workspaceDeleteAllowed: provenanceState.workspaceDeleteAllowed,
workspaceDeleteProtected: provenanceState.workspaceDeleteProtected,
vendorCount: snapshot.vendorCount ?? counts.vendorCount,
snapshotVendorCount: snapshot.vendorCount ?? null,
normalizedVendorCount: counts.vendorCount,
@@ -1215,7 +1269,14 @@ function isGvlImportCandidate(value) {
async function handleFetchOfficialGvlMessage() {
try {
const { rawJson, rawGvlSha256, responseStatus } =
const {
rawBody,
rawJson,
rawGvlSha256,
fetchedAt,
contentType,
responseStatus
} =
await fetchOfficialGvlJson();
if (!isGvlImportCandidate(rawJson)) {
@@ -1232,16 +1293,50 @@ async function handleFetchOfficialGvlMessage() {
db,
currentVendorListVersion
);
const currentSnapshotSha256 =
await VendorGetGvlService.calculateGvlSnapshotSha256(rawJson);
let webProvenanceMark = null;
if (existingSnapshot?.sha256) {
if (
existingSnapshot.sha256 !== currentSnapshotSha256 ||
existingSnapshot.rawGvlSha256 !== rawGvlSha256
) {
return {
success: false,
error: "gvl_revision_evidence_conflict",
vendorListVersion: currentVendorListVersion,
existingSnapshotSha256: existingSnapshot.sha256 ?? null,
fetchedSnapshotSha256: currentSnapshotSha256,
existingRawGvlSha256: existingSnapshot.rawGvlSha256 ?? null,
fetchedRawGvlSha256: rawGvlSha256
};
}
await VendorGetGvlService.storeGvlRawEvidenceIfNew(db, {
rawGvlSha256,
sourceUrl: OFFICIAL_IAB_GVL_URL,
fetchedAt,
httpStatus: responseStatus,
contentType,
rawBody
});
webProvenanceMark = await markGvlRevisionEvidenceWebSource(
db,
existingSnapshot.sha256
);
}
const ingestResult = existingSnapshot
? null
: await VendorGetGvlService.ingestGvlSnapshot(db, rawJson, {
sourceUrl: OFFICIAL_IAB_GVL_URL,
fetchedAt: new Date().toISOString(),
rawGvlSha256: rawGvlSha256,
diagnostics: {
ingestionSource: "official_iab_fetch",
responseStatus: responseStatus
}
: await ingestOfficialGvlSnapshotFromFetchedEvidence(db, {
rawBody,
rawJson,
rawGvlSha256,
fetchedAt,
contentType,
responseStatus,
ingestionSource: "official_iab_fetch"
});
const snapshot = existingSnapshot ?? ingestResult.snapshot;
const completeness = await getGvlSnapshotNormalizedCompleteness(
@@ -1274,6 +1369,7 @@ async function handleFetchOfficialGvlMessage() {
syncStatus,
vendorListVersion: snapshot.vendorListVersion,
sha256: snapshot.sha256,
webProvenanceMark,
normalizationSummary,
counts
};
@@ -1326,22 +1422,49 @@ async function fetchOfficialGvlJson() {
const fetchedAt = new Date().toISOString();
const contentType = response.headers.get("Content-Type");
const rawGvlSha256 = await VendorGetGvlService.calculateRawGvlSha256(rawBody);
const db = await openVendorGetDb();
return {
rawBody,
rawJson: JSON.parse(rawBody),
rawGvlSha256: rawGvlSha256,
fetchedAt,
contentType,
responseStatus: response.status
};
}
async function ingestOfficialGvlSnapshotFromFetchedEvidence(
db,
{
rawBody,
rawJson,
rawGvlSha256,
fetchedAt,
contentType,
responseStatus,
ingestionSource,
diagnostics
}
) {
await VendorGetGvlService.storeGvlRawEvidenceIfNew(db, {
rawGvlSha256,
sourceUrl: OFFICIAL_IAB_GVL_URL,
fetchedAt,
httpStatus: response.status,
httpStatus: responseStatus,
contentType,
rawBody
});
return {
rawJson: JSON.parse(rawBody),
return VendorGetGvlService.ingestGvlSnapshot(db, rawJson, {
sourceUrl: OFFICIAL_IAB_GVL_URL,
fetchedAt,
rawGvlSha256: rawGvlSha256,
responseStatus: response.status
};
diagnostics: {
ingestionSource,
responseStatus: responseStatus,
...(diagnostics ?? {})
}
});
}
async function runStartupGvlAutoUpdateCheck() {
@@ -1424,7 +1547,14 @@ async function runStartupGvlAutoUpdateCheck() {
result: "started"
});
const { rawJson, rawGvlSha256, responseStatus } =
const {
rawBody,
rawJson,
rawGvlSha256,
fetchedAt,
contentType,
responseStatus
} =
await fetchOfficialGvlJson();
if (!isGvlImportCandidate(rawJson)) {
@@ -1439,15 +1569,17 @@ async function runStartupGvlAutoUpdateCheck() {
previousVendorListVersion
);
const ingestResult = await VendorGetGvlService.ingestGvlSnapshot(
const ingestResult = await ingestOfficialGvlSnapshotFromFetchedEvidence(
db,
rawJson,
{
sourceUrl: OFFICIAL_IAB_GVL_URL,
fetchedAt: new Date().toISOString(),
rawGvlSha256: rawGvlSha256,
rawBody,
rawJson,
rawGvlSha256,
fetchedAt,
contentType,
responseStatus,
ingestionSource: "official_iab_auto_update",
diagnostics: {
ingestionSource: "official_iab_auto_update",
responseStatus: responseStatus,
updateCheckSource: GVL_AUTO_UPDATE_SOURCE,
checkedAt: lastAutoGvlCheckStartedAt,