Commits vergleichen

..

1 Commits

6 geänderte Dateien mit 1626 neuen und 8 gelöschten Zeilen
+1
Datei anzeigen
@@ -40,6 +40,7 @@
"src/background/gvl-service.js", "src/background/gvl-service.js",
"src/core/binary-utils.js", "src/core/binary-utils.js",
"src/core/tcf-core-metadata-decoder.js", "src/core/tcf-core-metadata-decoder.js",
"src/core/consent-diff.js",
"src/background.js" "src/background.js"
] ]
}, },
+71 -6
Datei anzeigen
@@ -224,6 +224,7 @@ async function handleVendorGetMessage(message, sender) {
} }
const latestPingData = tabId !== null ? getLatestTcfPing(tabId) : null; const latestPingData = tabId !== null ? getLatestTcfPing(tabId) : null;
const activeConsentCaptureSession = getActiveConsentCaptureSession(sender);
const consentState = buildConsentStateV1( const consentState = buildConsentStateV1(
message.payload.payload, message.payload.payload,
@@ -235,7 +236,7 @@ async function handleVendorGetMessage(message, sender) {
stableStringify(consentState.fingerprintSource) stableStringify(consentState.fingerprintSource)
); );
const captureSessionId = getActiveConsentCaptureSessionId(sender); const captureSessionId = activeConsentCaptureSession?.captureSessionId ?? null;
rememberLatestConsentState(consentState); rememberLatestConsentState(consentState);
@@ -245,12 +246,19 @@ async function handleVendorGetMessage(message, sender) {
); );
if (captureSessionId) { if (captureSessionId) {
await persistConsentExecutionEvent({ const consentExecutionEvent = await persistConsentExecutionEvent({
captureSessionId, captureSessionId,
consentState, consentState,
rawCapture: message.payload.payload, rawCapture: message.payload.payload,
sender sender
}); });
await persistConsentDiffEvent({
captureSessionId,
providerAnnouncementEvent:
activeConsentCaptureSession?.providerAnnouncementEvents?.[0] ?? null,
consentExecutionEvent
});
} }
completeConsentCaptureSession(sender); completeConsentCaptureSession(sender);
@@ -1044,17 +1052,20 @@ async function handleStartPreConsentCaptureMessage(message) {
session.status = "recording"; session.status = "recording";
session.captureSessionId = createCaptureSessionId(); session.captureSessionId = createCaptureSessionId();
session.providerAnnouncementEvents = [];
updateConsentCaptureBadge(session); updateConsentCaptureBadge(session);
const bufferedEvents = session.bufferedPreConsentEvents; const bufferedEvents = session.bufferedPreConsentEvents;
session.bufferedPreConsentEvents = []; session.bufferedPreConsentEvents = [];
for (const bufferedEvent of bufferedEvents) { for (const bufferedEvent of bufferedEvents) {
await persistProviderAnnouncementEvent({ const providerAnnouncementEvent = await persistProviderAnnouncementEvent({
captureSessionId: session.captureSessionId, captureSessionId: session.captureSessionId,
rawCapture: bufferedEvent.rawCapture, rawCapture: bufferedEvent.rawCapture,
sender: bufferedEvent.sender sender: bufferedEvent.sender
}); });
session.providerAnnouncementEvents.push(providerAnnouncementEvent);
} }
return { return {
@@ -2393,6 +2404,7 @@ function startAttentionConsentCaptureSession(rawCapture, sender) {
status: "attention", status: "attention",
captureSessionId: null, captureSessionId: null,
bufferedPreConsentEvents: [], bufferedPreConsentEvents: [],
providerAnnouncementEvents: [],
timeoutId: null, timeoutId: null,
page: buildConsentEventPageContext(rawCapture, sender), page: buildConsentEventPageContext(rawCapture, sender),
detectedAt: new Date().toISOString(), detectedAt: new Date().toISOString(),
@@ -2440,11 +2452,14 @@ async function handlePreConsentTcfEvent(rawCapture, sender) {
} }
if (session?.status === "recording") { if (session?.status === "recording") {
await persistProviderAnnouncementEvent({ const providerAnnouncementEvent = await persistProviderAnnouncementEvent({
captureSessionId: session.captureSessionId, captureSessionId: session.captureSessionId,
rawCapture, rawCapture,
sender sender
}); });
session.providerAnnouncementEvents =
session.providerAnnouncementEvents ?? [];
session.providerAnnouncementEvents.push(providerAnnouncementEvent);
return; return;
} }
@@ -2452,6 +2467,12 @@ async function handlePreConsentTcfEvent(rawCapture, sender) {
} }
function getActiveConsentCaptureSessionId(sender) { function getActiveConsentCaptureSessionId(sender) {
const session = getActiveConsentCaptureSession(sender);
return session?.captureSessionId ?? null;
}
function getActiveConsentCaptureSession(sender) {
const sessionKey = getConsentCaptureSessionKey(sender); const sessionKey = getConsentCaptureSessionKey(sender);
const session = sessionKey ? consentCaptureSessions.get(sessionKey) : null; const session = sessionKey ? consentCaptureSessions.get(sessionKey) : null;
@@ -2459,7 +2480,7 @@ function getActiveConsentCaptureSessionId(sender) {
return null; return null;
} }
return session.captureSessionId ?? null; return session;
} }
function hasCompletedConsentCaptureSessionForTab(tabId) { function hasCompletedConsentCaptureSessionForTab(tabId) {
@@ -2492,6 +2513,7 @@ function completeConsentCaptureSession(sender) {
status: "completed", status: "completed",
captureSessionId: null, captureSessionId: null,
bufferedPreConsentEvents: [], bufferedPreConsentEvents: [],
providerAnnouncementEvents: [],
timeoutId: null timeoutId: null
}; };
@@ -3094,16 +3116,59 @@ async function persistConsentExecutionEvent({
}); });
} }
async function persistConsentDiffEvent({
captureSessionId,
providerAnnouncementEvent,
consentExecutionEvent
}) {
const db = await openVendorGetDb();
const now = new Date().toISOString();
const consentDiff = buildConsentDiff({
captureSessionId,
providerAnnouncement: providerAnnouncementEvent,
consentExecution: consentExecutionEvent,
derivedAt: now
});
return addConsentEventRecord(db, {
eventType: "consent_diff",
capturedAt: now,
recordedAt: now,
recordingSource: EVIDENCE_RECORDING_SOURCE,
captureSessionId,
stateFingerprint: consentExecutionEvent?.stateFingerprint ?? null,
page: consentExecutionEvent?.page ?? null,
rawEventName: "consent_diff",
ruleId: CONSENT_DIFF_RULE_ID,
ruleVersion: CONSENT_DIFF_RULE_VERSION,
consentDiff,
diagnostics: {
consentEvidenceRole: "consentDiff",
ruleId: CONSENT_DIFF_RULE_ID,
ruleVersion: CONSENT_DIFF_RULE_VERSION,
sourceProviderAnnouncementId: providerAnnouncementEvent?.id ?? null,
sourceConsentExecutionId: consentExecutionEvent?.id ?? null
}
});
}
function addConsentEventRecord(db, record) { function addConsentEventRecord(db, record) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const tx = db.transaction(["consent_events"], "readwrite"); const tx = db.transaction(["consent_events"], "readwrite");
const eventsStore = tx.objectStore("consent_events"); const eventsStore = tx.objectStore("consent_events");
const request = eventsStore.add(record); const request = eventsStore.add(record);
let storedRecord = record;
request.onerror = () => reject(request.error); request.onerror = () => reject(request.error);
request.onsuccess = () => {
storedRecord = {
...record,
id: request.result
};
};
tx.onerror = () => reject(tx.error); tx.onerror = () => reject(tx.error);
tx.onabort = () => reject(tx.error); tx.onabort = () => reject(tx.error);
tx.oncomplete = () => resolve(record); tx.oncomplete = () => resolve(storedRecord);
}); });
} }
+173
Datei anzeigen
@@ -27,6 +27,7 @@ body {
h1, h1,
h2, h2,
h3, h3,
h4,
p { p {
margin: 0; margin: 0;
} }
@@ -49,6 +50,11 @@ h3 {
color: #cbd5e1; color: #cbd5e1;
} }
h4 {
font-size: 12px;
color: #cbd5e1;
}
p { p {
max-width: 760px; max-width: 760px;
font-size: 13px; font-size: 13px;
@@ -139,6 +145,165 @@ th {
margin-top: 18px; 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 th,
.inspector-table td { .inspector-table td {
vertical-align: top; vertical-align: top;
@@ -227,4 +392,12 @@ th {
.inspector-table .inspector-explanation { .inspector-table .inspector-explanation {
width: auto; 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"> <section class="consent-detail" aria-labelledby="consent-detail-title">
<h2 id="consent-detail-title">Ausgewählter Consent-Zustand</h2> <h2 id="consent-detail-title">Ausgewählter Consent-Zustand</h2>
<div id="consent-detail-observation"></div> <div id="consent-detail-observation"></div>
<div id="consent-detail-diff"></div>
<div id="consent-detail-basics"></div> <div id="consent-detail-basics"></div>
<div id="consent-detail-summary"></div> <div id="consent-detail-summary"></div>
<div id="consent-detail-publisher"></div> <div id="consent-detail-publisher"></div>
Datei-Diff unterdrückt, da er zu groß ist Diff laden
+374
Datei anzeigen
@@ -0,0 +1,374 @@
"use strict";
var CONSENT_DIFF_RULE_ID = "consent_diff";
var CONSENT_DIFF_RULE_VERSION = 1;
const CONSENT_DIFF_SCALAR_AXES = [
"tcString",
"addtlConsent",
"vendorListVersion",
"cmpId",
"cmpVersion",
"tcfPolicyVersion",
"gdprApplies",
"cmpStatus"
];
const CONSENT_DIFF_OBJECT_AXES = [
"purpose.consents",
"vendor.consents",
"specialFeatureOptins"
];
function buildConsentDiff({
captureSessionId,
providerAnnouncement,
consentExecution,
derivedAt
}) {
const providerRaw = cloneConsentDiffValue(
providerAnnouncement?.rawTcData ?? null
);
const executionRaw = cloneConsentDiffValue(consentExecution?.rawTcData ?? null);
const providerWork = buildConsentDiffWorkCopy(
providerAnnouncement,
providerRaw
);
const executionWork = buildConsentDiffWorkCopy(consentExecution, executionRaw);
const axes = {};
CONSENT_DIFF_SCALAR_AXES.forEach((axis) => {
axes[axis] = compareConsentDiffScalarAxis(
axis,
providerWork,
executionWork
);
});
CONSENT_DIFF_OBJECT_AXES.forEach((axis) => {
axes[axis] = compareConsentDiffObjectAxis(
axis,
providerWork,
executionWork
);
});
return {
ruleId: CONSENT_DIFF_RULE_ID,
ruleVersion: CONSENT_DIFF_RULE_VERSION,
derivedAt,
captureSessionId,
sources: {
providerAnnouncement: buildConsentDiffSourceReference(
providerAnnouncement
),
consentExecution: buildConsentDiffSourceReference(consentExecution)
},
sourceSelection: {
providerAnnouncement:
"first_provider_announcement_for_capture_session_available_in_memory",
consentExecution: "current_useractioncomplete_execution_event"
},
comparisonAxes: [
...CONSENT_DIFF_SCALAR_AXES,
...CONSENT_DIFF_OBJECT_AXES
],
axes,
notes: buildConsentDiffNotes(providerAnnouncement, consentExecution)
};
}
function buildConsentDiffWorkCopy(eventRecord, rawTcData) {
return {
eventRecord: cloneConsentDiffValue(eventRecord ?? null),
rawTcData: rawTcData ?? null
};
}
function compareConsentDiffScalarAxis(axis, providerWork, executionWork) {
const providerValue = readConsentDiffAxisValue(providerWork, axis);
const executionValue = readConsentDiffAxisValue(executionWork, axis);
return {
kind: "scalar",
status: getConsentDiffScalarStatus(providerValue, executionValue),
provider: buildConsentDiffAxisSide(providerValue),
execution: buildConsentDiffAxisSide(executionValue)
};
}
function compareConsentDiffObjectAxis(axis, providerWork, executionWork) {
const providerValue = readConsentDiffAxisValue(providerWork, axis);
const executionValue = readConsentDiffAxisValue(executionWork, axis);
const providerObject = isConsentDiffPlainObject(providerValue.value)
? providerValue.value
: null;
const executionObject = isConsentDiffPlainObject(executionValue.value)
? executionValue.value
: null;
const providerKeys = providerObject ? Object.keys(providerObject).sort() : [];
const executionKeys = executionObject ? Object.keys(executionObject).sort() : [];
const allKeys = Array.from(new Set([...providerKeys, ...executionKeys])).sort();
const added = [];
const removed = [];
const changed = [];
const unchanged = [];
allKeys.forEach((key) => {
const inProvider = providerObject
? Object.prototype.hasOwnProperty.call(providerObject, key)
: false;
const inExecution = executionObject
? Object.prototype.hasOwnProperty.call(executionObject, key)
: false;
if (inProvider && !inExecution) {
removed.push({
key,
providerValue: cloneConsentDiffValue(providerObject[key])
});
return;
}
if (!inProvider && inExecution) {
added.push({
key,
executionValue: cloneConsentDiffValue(executionObject[key])
});
return;
}
if (
stableConsentDiffValue(providerObject[key]) ===
stableConsentDiffValue(executionObject[key])
) {
unchanged.push(key);
return;
}
changed.push({
key,
providerValue: cloneConsentDiffValue(providerObject[key]),
executionValue: cloneConsentDiffValue(executionObject[key])
});
});
return {
kind: "object",
status: getConsentDiffObjectStatus(
providerValue,
executionValue,
added,
removed,
changed
),
provider: {
present: providerValue.present,
sourcePath: providerValue.sourcePath,
keys: providerKeys
},
execution: {
present: executionValue.present,
sourcePath: executionValue.sourcePath,
keys: executionKeys
},
addedKeys: added,
removedKeys: removed,
changedKeys: changed,
unchangedKeys: unchanged,
counts: {
providerKeys: providerKeys.length,
executionKeys: executionKeys.length,
added: added.length,
removed: removed.length,
changed: changed.length,
unchanged: unchanged.length
}
};
}
function readConsentDiffAxisValue(workCopy, axis) {
const rawValue = readConsentDiffPath(workCopy.rawTcData, axis);
if (rawValue.present) {
return {
...rawValue,
sourcePath: `rawTcData.${axis}`
};
}
const eventValue = readConsentDiffPath(workCopy.eventRecord, axis);
if (eventValue.present) {
return {
...eventValue,
sourcePath: axis
};
}
return {
present: false,
value: undefined,
sourcePath: null
};
}
function readConsentDiffPath(value, path) {
if (value === null || value === undefined) {
return {
present: false,
value: undefined
};
}
const parts = path.split(".");
let current = value;
for (const part of parts) {
if (
current === null ||
current === undefined ||
!Object.prototype.hasOwnProperty.call(Object(current), part)
) {
return {
present: false,
value: undefined
};
}
current = current[part];
}
return {
present: true,
value: cloneConsentDiffValue(current)
};
}
function getConsentDiffScalarStatus(providerValue, executionValue) {
if (!providerValue.present && !executionValue.present) {
return "absent";
}
if (providerValue.present && !executionValue.present) {
return "removed";
}
if (!providerValue.present && executionValue.present) {
return "added";
}
return stableConsentDiffValue(providerValue.value) ===
stableConsentDiffValue(executionValue.value)
? "unchanged"
: "changed";
}
function getConsentDiffObjectStatus(
providerValue,
executionValue,
added,
removed,
changed
) {
if (!providerValue.present && !executionValue.present) {
return "absent";
}
if (providerValue.present && !executionValue.present) {
return "removed";
}
if (!providerValue.present && executionValue.present) {
return "added";
}
return added.length === 0 && removed.length === 0 && changed.length === 0
? "unchanged"
: "changed";
}
function buildConsentDiffAxisSide(axisValue) {
return {
present: axisValue.present,
sourcePath: axisValue.sourcePath,
value: axisValue.present ? cloneConsentDiffValue(axisValue.value) : null
};
}
function buildConsentDiffSourceReference(eventRecord) {
if (!eventRecord) {
return {
available: false,
reason: "missing_source_event"
};
}
return {
available: true,
store: "consent_events",
id: eventRecord.id ?? null,
eventType: eventRecord.eventType ?? null,
rawEventName: eventRecord.rawEventName ?? null,
capturedAt: eventRecord.capturedAt ?? null,
recordedAt: eventRecord.recordedAt ?? null,
captureSessionId: eventRecord.captureSessionId ?? null
};
}
function buildConsentDiffNotes(providerAnnouncement, consentExecution) {
const notes = [];
if (!providerAnnouncement) {
notes.push("provider_announcement_source_missing");
}
if (!consentExecution) {
notes.push("consent_execution_source_missing");
}
notes.push("primary_evidence_not_modified");
notes.push("no_legal_assessment");
notes.push("no_gvl_semantic_assessment");
return notes;
}
function stableConsentDiffValue(value) {
if (value === undefined) {
return "[[undefined]]";
}
if (value === null || typeof value !== "object") {
return JSON.stringify(value);
}
if (Array.isArray(value)) {
return `[${value.map((item) => stableConsentDiffValue(item)).join(",")}]`;
}
return `{${Object.keys(value)
.sort()
.map((key) => `${JSON.stringify(key)}:${stableConsentDiffValue(value[key])}`)
.join(",")}}`;
}
function cloneConsentDiffValue(value) {
if (value === undefined) {
return undefined;
}
if (value === null || typeof value !== "object") {
return value;
}
return JSON.parse(JSON.stringify(value));
}
function isConsentDiffPlainObject(value) {
return (
value !== null &&
typeof value === "object" &&
!Array.isArray(value)
);
}