27 lines
3.1 KiB
JavaScript
27 lines
3.1 KiB
JavaScript
'use strict';
|
|
const {validateDownloadEvidence,validateInteractionEvidence}=require('./evidence-capture.cjs');
|
|
function evidenceType(record){return record?.protectedEvidence?.type??'download';}
|
|
function policyAllows(context,type='download'){return ['download','interaction'].includes(type)&&context?.policy?.evidence?.enabled===true&&context.policy.evidence[type==='download'?'captureDownloads':'captureInteractions']===true&&Date.parse(context.policy.expiresAt)>Date.now();}
|
|
function collectionAllowed(context){return !!context?.ready&&policyAllows(context,context?.type??'download');}
|
|
function validatedEvidence(payload,type='download'){if(!(type==='download'?validateDownloadEvidence(payload):type==='interaction'&&validateInteractionEvidence(payload)))throw Error('Invalid protected evidence');return structuredClone(payload);}
|
|
function stripPendingEvidence(queue,context){return queue.map(record=>{if(!record.protectedEvidence)return record;if(context&&policyAllows(context,evidenceType(record)))return record;const {protectedEvidence,...event}=record;return event;});}
|
|
async function deliverAuditRecord({record,send,getContext}){
|
|
const {protectedEvidence,...event}=record;await send('/api/device/events',event);
|
|
if(!protectedEvidence)return {eventDelivered:true,evidenceDelivered:false};
|
|
const type=evidenceType(record);
|
|
function state(){const context=getContext();if(protectedEvidence.deviceId!==context?.deviceId||context?.policy&&!policyAllows(context,type))return 'discard';if(!context?.ready||!context.policy)return 'pause';return 'allow';}
|
|
const current=state();if(current==='discard')return {eventDelivered:true,evidenceDiscarded:true};if(current==='pause')throw Error('Evidence delivery paused until managed context is ready');
|
|
const payload=validatedEvidence(protectedEvidence.payload,type);
|
|
if(type==='interaction'){
|
|
const expectedKind=payload.captureSource==='guarded-clipboard'?`clipboard.${payload.action}`:payload.captureSource==='human-observer'?'browser.interaction':'agent.action';
|
|
if(event.kind!==expectedKind||payload.captureSource==='guarded-clipboard'&&!['copy','cut','paste'].includes(payload.action)||payload.captureSource==='agent-adapter'&&payload.action!=='click')throw Error('Invalid protected evidence source');
|
|
}
|
|
try{const result=await send('/api/device/evidence',{eventId:event.eventId,type,payload});
|
|
// A successful network response must not resurrect collection after a
|
|
// concurrent policy revocation or a change of enrolled device.
|
|
const after=state();if(after==='discard')return {eventDelivered:true,evidenceDiscarded:true};if(after==='pause')throw Error('Evidence delivery paused until managed context is ready');
|
|
if(!/^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(result?.id||'')||!Number.isSafeInteger(result.sourceEventId)||result.sourceEventId<1)throw Error('Evidence acknowledgement invalid');return {eventDelivered:true,evidenceDelivered:true};}
|
|
catch(error){if(error.status===403)return {eventDelivered:true,evidenceDiscarded:true};throw error;}
|
|
}
|
|
module.exports={validatedEvidence,collectionAllowed,stripPendingEvidence,deliverAuditRecord,evidenceType,policyAllows};
|