2026-10-13 18:04:02 +02:00
|
|
|
import interactionAudit from './interaction-facts.cjs';
|
2026-10-13 16:58:15 +02:00
|
|
|
import agentAudit from './agent-audit.cjs';
|
2026-10-13 10:33:31 +02:00
|
|
|
import transferFacts from './transfer-facts.cjs';
|
2026-10-13 16:58:15 +02:00
|
|
|
import requestTransferFacts from './request-transfer-facts.cjs';
|
|
|
|
|
const requestFacts=(metadata,kind)=>{if(!kind?.startsWith('upload.'))return null;const old=metadata.file;const parsed=requestTransferFacts.RequestFactsSchema.safeParse(metadata.request||(old?{bytes:old.bytes,detectedType:old.detectedType}:undefined));return parsed.success?parsed.data:null;};
|
2026-10-13 10:33:31 +02:00
|
|
|
const facts=metadata=>{const parsed=transferFacts.FileFactsSchema.safeParse(metadata.file);return parsed.success?parsed.data:null;};
|
|
|
|
|
export function toECS(event,installationId='safetynet') {
|
2026-10-13 21:00:41 +02:00
|
|
|
const m=event.metadata,interaction=interactionAudit.interactionFactsForEvent(event.kind,event.metadata.interaction),agent=agentAudit.agentFactsForEvent(event.kind,event.metadata.agent),block=event.kind.includes('block')||event.kind.endsWith('.denied')||event.kind==='vault.submission-denied'||m.decision==='block',failure=event.kind.endsWith('.failure')||agent?.outcome==='failed',alert=event.kind==='sigma.alert';
|
2026-10-13 10:33:31 +02:00
|
|
|
const config=event.kind.startsWith('policy.')||event.kind.startsWith('enrollment.')||event.kind==='device.revoked';
|
|
|
|
|
const auth=event.kind.startsWith('sso.'),clipboard=event.kind.startsWith('clipboard.');
|
|
|
|
|
return {
|
|
|
|
|
'@timestamp':event.time,ecs:{version:'8.17.0'},
|
|
|
|
|
event:{id:`${installationId}:${event.id}`,kind:alert?'alert':'event',module:'safetynet',dataset:'safetynet.security',
|
|
|
|
|
action:event.kind,category:[alert?'intrusion_detection':auth?'authentication':config?'configuration':clipboard?'process':'web'],
|
2026-10-13 18:24:44 +02:00
|
|
|
type:[alert||interaction?.outcome==='observed'?'info':failure?'error':auth?'start':config?'change':block?'denied':clipboard?'access':'allowed'],outcome:interaction?.outcome==='observed'?'unknown':failure?'failure':'success',...(alert?{severity:severity(m.level)}:{})},
|
2026-10-13 10:33:31 +02:00
|
|
|
observer:{vendor:'SafetyNet',product:'Managed Enterprise Browser',type:'dlp',version:'0.1.0'},
|
|
|
|
|
...(event.device_id?{host:{id:event.device_id}}:{}),
|
|
|
|
|
...(m.actor?{user:{id:m.actor}}:{}),...(m.host?{destination:{domain:m.host}}:{}),
|
|
|
|
|
...(m.ruleId?{rule:{id:m.ruleId}}:{}),
|
2026-10-13 16:58:15 +02:00
|
|
|
...(event.kind.startsWith('download.')&&facts(m)?{file:{size:facts(m).bytes,type:facts(m).detectedType,hash:{sha256:facts(m).sha256}}}:{}),
|
2026-10-13 10:33:31 +02:00
|
|
|
...(/^[a-f0-9]{32}$/.test(m.traceId||'')?{trace:{id:m.traceId}}:{}),
|
|
|
|
|
...(/^[a-f0-9]{16}$/.test(m.spanId||'')?{span:{id:m.spanId}}:{}),
|
2026-10-13 18:04:02 +02:00
|
|
|
safetynet:{...(interaction?{interaction:{action:interaction.action,outcome:interaction.outcome,session_id:interaction.sessionId}}:{}),...(agent?{agent:{action:agent.action,scope:agent.scope,outcome:agent.outcome,...(agent.sessionId?{session_id:agent.sessionId}:{}),...(agent.principalId?{principal_id:agent.principalId,credential_generation:agent.credentialGeneration}:{})}}:{}),...(requestFacts(m,event.kind)?{request:{bytes:requestFacts(m,event.kind).bytes,detected_type:requestFacts(m,event.kind).detectedType}}:{}),...(event.audit?{audit:event.audit}:{}),policy_version:m.policyVersion??m.version??null,decision:m.decision??(block?'block':null),
|
2026-10-13 10:33:31 +02:00
|
|
|
content_type:m.contentType??null,model:m.model??null,coverage:m.coverage??null,classification:m.classification==='local-only'?'local-only':null,
|
|
|
|
|
...(alert?{source_event_id:`${installationId}:${m.sourceEventId}`,severity:m.level}:{})}
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
const header=v=>String(v).replace(/\\/g,'\\\\').replace(/\|/g,'\\|').replace(/[\r\n]/g,' ');
|
|
|
|
|
const ext=v=>String(v).replace(/\\/g,'\\\\').replace(/=/g,'\\=').replace(/\r/g,'\\r').replace(/\n/g,'\\n');
|
|
|
|
|
const severity=level=>({low:3,medium:5,high:7,critical:10}[level]??3);
|
|
|
|
|
export function toCEF(event,installationId='safetynet') {
|
2026-10-13 21:00:41 +02:00
|
|
|
const m=event.metadata,interaction=interactionAudit.interactionFactsForEvent(event.kind,event.metadata.interaction),agent=agentAudit.agentFactsForEvent(event.kind,event.metadata.agent),blocked=event.kind.includes('block')||event.kind.endsWith('.denied')||event.kind==='vault.submission-denied'||m.decision==='block',alert=event.kind==='sigma.alert';
|
2026-10-13 16:58:15 +02:00
|
|
|
// suid identifies the verified agent capability principal, not an employee account.
|
|
|
|
|
// suser, when present, remains the independently supplied human actor.
|
2026-10-13 18:04:02 +02:00
|
|
|
const fields={...(interaction?{cs2Label:'InteractionAction',cs2:interaction.action,cs3Label:'InteractionOutcome',cs3:interaction.outcome,requestContext:interaction.sessionId}:{}),...(agent?{cs2Label:'AgentAction',cs2:agent.action,cs3Label:'AgentScope',cs3:agent.scope,cs6Label:'AgentOutcome',cs6:agent.outcome,...(agent.sessionId?{requestContext:agent.sessionId}:{}),...(agent.principalId?{suid:agent.principalId,flexNumber1Label:'AgentCredentialGeneration',flexNumber1:agent.credentialGeneration}:{})}:{}),...(requestFacts(m,event.kind)?{cn3Label:'RequestBytes',cn3:requestFacts(m,event.kind).bytes,cs6Label:'DetectedRequestType',cs6:requestFacts(m,event.kind).detectedType}:{}),...(event.audit?{flexString1Label:'AuditRecordHash',flexString1:event.audit.hash,flexString2Label:'AuditSequence',flexString2:event.audit.sequence}:{}),externalId:`${installationId}:${event.id}`,rt:Date.parse(event.time),act:event.kind,
|
2026-10-13 10:33:31 +02:00
|
|
|
...(event.device_id?{deviceExternalId:event.device_id}:{}),...(m.actor?{suser:m.actor}:{}),
|
|
|
|
|
...(m.ruleId?{cs1Label:'RuleId',cs1:m.ruleId}:{}),...(m.host?{dhost:m.host}:{}),
|
|
|
|
|
...(m.policyVersion||m.version?{cn1Label:'PolicyVersion',cn1:m.policyVersion??m.version}:{}),
|
2026-10-13 16:58:15 +02:00
|
|
|
...(event.kind.startsWith('download.')&&facts(m)?{cn2Label:'FileBytes',cn2:facts(m).bytes,cs6Label:'DetectedFileType',cs6:facts(m).detectedType,fileHash:facts(m).sha256}:{}),
|
2026-10-13 10:33:31 +02:00
|
|
|
...(/^[a-f0-9]{32}$/.test(m.traceId||'')?{cs4Label:'TraceId',cs4:m.traceId}:{}),
|
|
|
|
|
...(/^[a-f0-9]{16}$/.test(m.spanId||'')?{cs5Label:'SpanId',cs5:m.spanId}:{}),
|
|
|
|
|
...(alert?{cs2Label:'SourceEventId',cs2:`${installationId}:${m.sourceEventId}`,cs3Label:'AlertLevel',cs3:m.level}:{})};
|
|
|
|
|
return `CEF:0|SafetyNet|Enterprise Browser|0.1.0|${header(event.kind)}|${header(event.kind)}|${alert?severity(m.level):blocked?7:3}|`+Object.entries(fields).map(([k,v])=>`${k}=${ext(v)}`).join(' ');
|
|
|
|
|
}
|