151 Zeilen
4.2 KiB
JavaScript
151 Zeilen
4.2 KiB
JavaScript
const REQUEST_DEDUPE_WINDOW_MS = 60 * 1000;
|
|
const REQUEST_CORRELATION_WINDOW_MS = 5 * 60 * 1000;
|
|
const OBSERVED_REQUEST_RECORDING_SOURCE = "vendorget_background_mirror";
|
|
const observedRequestFingerprints = new Map();
|
|
|
|
async function handleObservedRequest(details) {
|
|
if (!(await isRequestMonitoringEnabled())) {
|
|
return;
|
|
}
|
|
|
|
if (!hasConsentQueryParam(details.url)) {
|
|
return;
|
|
}
|
|
|
|
if (isEvidenceWriteSuspended()) {
|
|
console.info("VendorGet-IV evidence write skipped: maintenance mode");
|
|
return;
|
|
}
|
|
|
|
// The browser request is the primary observation; VG-IV records a mirror copy
|
|
// with its own recordedAt timestamp for provenance.
|
|
const requestFingerprint = await buildObservedRequestFingerprint(details);
|
|
const requestFingerprintSource = buildObservedRequestFingerprintSource(details);
|
|
const observedAt = new Date(details.timeStamp).toISOString();
|
|
const recordedAt = new Date().toISOString();
|
|
const correlation = buildObservedRequestCorrelation(
|
|
details.tabId,
|
|
details.frameId,
|
|
observedAt
|
|
);
|
|
const now = Date.now();
|
|
const existingFingerprint = observedRequestFingerprints.get(requestFingerprint);
|
|
const observedRequest = {
|
|
schemaVersion: 1,
|
|
requestFingerprint: requestFingerprint,
|
|
requestFingerprintSource: requestFingerprintSource,
|
|
recordedAt: recordedAt,
|
|
recordingSource: OBSERVED_REQUEST_RECORDING_SOURCE,
|
|
firstSeenAt: observedAt,
|
|
lastSeenAt: observedAt,
|
|
seenCount: 1,
|
|
request: {
|
|
url: details.url,
|
|
origin: requestFingerprintSource.origin,
|
|
pathname: requestFingerprintSource.pathname,
|
|
method: details.method,
|
|
type: details.type,
|
|
thirdParty: details.thirdParty
|
|
},
|
|
consentParams: {
|
|
gdpr: requestFingerprintSource.consentParams.gdpr,
|
|
gdpr_consent: requestFingerprintSource.consentParams.gdpr_consent,
|
|
addtlConsent: requestFingerprintSource.consentParams.addtlConsent
|
|
},
|
|
context: {
|
|
tabId: details.tabId,
|
|
frameId: details.frameId
|
|
},
|
|
correlation: correlation
|
|
};
|
|
|
|
if (
|
|
existingFingerprint &&
|
|
now - existingFingerprint.lastSeenAt < REQUEST_DEDUPE_WINDOW_MS
|
|
) {
|
|
existingFingerprint.lastSeenAt = now;
|
|
existingFingerprint.seenCount += 1;
|
|
await persistObservedRequest(observedRequest);
|
|
return;
|
|
}
|
|
|
|
const seenCount = existingFingerprint
|
|
? existingFingerprint.seenCount + 1
|
|
: 1;
|
|
|
|
observedRequestFingerprints.set(requestFingerprint, {
|
|
lastSeenAt: now,
|
|
seenCount: seenCount
|
|
});
|
|
|
|
await persistObservedRequest(observedRequest);
|
|
|
|
const normalizedObject = {
|
|
observedAt: observedAt,
|
|
recordedAt: recordedAt,
|
|
requestId: details.requestId,
|
|
tabId: details.tabId,
|
|
frameId: details.frameId,
|
|
url: details.url,
|
|
method: details.method,
|
|
type: details.type,
|
|
thirdParty: details.thirdParty,
|
|
requestFingerprint: requestFingerprint,
|
|
seenCount: seenCount,
|
|
requestFingerprintSource: requestFingerprintSource
|
|
};
|
|
|
|
console.log("VendorGet-IV observed request", normalizedObject);
|
|
}
|
|
|
|
function hasConsentQueryParam(url) {
|
|
try {
|
|
const params = new URL(url).searchParams;
|
|
|
|
return (
|
|
params.has("gdpr") ||
|
|
params.has("gdpr_consent") ||
|
|
params.has("addtlConsent")
|
|
);
|
|
} catch (error) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
function buildObservedRequestCorrelation(tabId, frameId, observedAt) {
|
|
const latestConsentState = getLatestConsentStateForRequest(tabId, frameId);
|
|
|
|
if (!latestConsentState) {
|
|
return buildEmptyObservedRequestCorrelation();
|
|
}
|
|
|
|
const requestTime = Date.parse(observedAt);
|
|
const consentTime = Date.parse(latestConsentState.capturedAt);
|
|
|
|
if (!Number.isFinite(requestTime) || !Number.isFinite(consentTime)) {
|
|
return buildEmptyObservedRequestCorrelation();
|
|
}
|
|
|
|
const deltaMs = requestTime - consentTime;
|
|
|
|
if (deltaMs < 0 || deltaMs > REQUEST_CORRELATION_WINDOW_MS) {
|
|
return buildEmptyObservedRequestCorrelation();
|
|
}
|
|
|
|
return {
|
|
stateFingerprint: latestConsentState.stateFingerprint,
|
|
deltaMs: deltaMs,
|
|
method: "latest_in_memory_same_tab_frame",
|
|
windowMs: REQUEST_CORRELATION_WINDOW_MS
|
|
};
|
|
}
|
|
|
|
function buildEmptyObservedRequestCorrelation() {
|
|
return {
|
|
stateFingerprint: null,
|
|
deltaMs: null,
|
|
method: null,
|
|
windowMs: REQUEST_CORRELATION_WINDOW_MS
|
|
};
|
|
}
|