2026-10-13 16:58:15 +02:00
import test from 'node:test' ;
import assert from 'node:assert/strict' ;
import { mkdtempSync , rmSync } from 'node:fs' ;
import { tmpdir } from 'node:os' ;
import { join } from 'node:path' ;
import { randomUUID } from 'node:crypto' ;
import { openStore , digest } from '../apps/server/store.mjs' ;
import { createApp } from '../apps/server/app.mjs' ;
import { toECS , toCEF } from '../packages/core/siem.mjs' ;
test ( 'central ingestion strips legacy upload body hashes and retains safe request facts' , async t =>{
const dir = mkdtempSync ( join ( tmpdir (), 'safetynet-request-audit-' )), store = openStore ( dir ), credential = 'synthetic-device-request-audit-token' ;
store . db . prepare ( 'INSERT INTO devices(id,name,token_hash) VALUES(?,?,?)' ). run ( 'device' , 'Synthetic' , digest ( credential ));
const server = createApp ( store ,{ publicUrl : 'http://127.0.0.1' , adminToken : 'synthetic-administrator-token-123456789' , secure : false }). listen ( 0 , '127.0.0.1' ); await new Promise ( r => server . once ( 'listening' , r ));
t . after ( async ()=>{ server . closeAllConnections (); await new Promise ( r => server . close ( r )); store . db . close (); assert . ok ( dir . startsWith ( join ( tmpdir (), 'safetynet-request-audit-' ))); rmSync ( dir ,{ recursive : true , force : true });});
const send = body => fetch ( `http://127.0.0.1: ${ server . address (). port } /api/device/events` ,{ method : 'POST' , headers : { authorization : `Bearer ${ credential } ` , 'content-type' : 'application/json' }, body : JSON . stringify ({ eventId : randomUUID (), policyVersion : 1 ,... body })});
const hash = digest ( 'synthetic-low-entropy-password' ); let response = await send ({ kind : 'upload.authorized' , file : { bytes : 32 , detectedType : 'text' , sha256 : hash }}); assert . equal ( response . status , 200 );
let row = store . db . prepare ( 'SELECT * FROM events' ). get (), metadata = JSON . parse ( row . metadata ); assert . equal ( metadata . file , undefined ); assert . deepEqual ( metadata . request ,{ bytes : 32 , detectedType : 'text' }); assert . equal ( row . metadata . includes ( hash ), false );
response = await send ({ kind : 'upload.block' , request : { bytes : 50 , detectedType : 'unknown' }}); assert . equal ( response . status , 200 );
response = await send ({ kind : 'clipboard.copy' , request : { bytes : 50 , detectedType : 'text' }}); assert . equal ( response . status , 400 );
response = await send ({ kind : 'upload.authorized' , request : { bytes : 50 , detectedType : 'text' , sha256 : hash }}); assert . equal ( response . status , 400 );
response = await send ({ kind : 'download.scan' , file : { bytes : 32 , detectedType : 'text' , sha256 : hash }}); assert . equal ( response . status , 200 ); row = store . db . prepare ( "SELECT * FROM events WHERE kind='download.scan'" ). get (); assert . equal ( JSON . parse ( row . metadata ). file . sha256 , hash ); assert . equal ( store . auditIntegrity . verify (). valid , true );
});
test ( 'SIEM suppresses historical upload hashes while preserving transfer amounts and download hashes' ,()=>{
const hash = digest ( 'synthetic-password' ), legacy = { id : 1 , time : new Date (). toISOString (), kind : 'upload.authorized' , metadata : { file : { bytes : 18 , detectedType : 'text' , sha256 : hash }}};
assert . equal ( toECS ( legacy ). file , undefined ); assert . deepEqual ( toECS ( legacy ). safetynet . request ,{ bytes : 18 , detected_type : 'text' }); assert . equal ( toCEF ( legacy ). includes ( hash ), false );
const current = {... legacy , metadata : { request : { bytes : 18 , detectedType : 'text' }}}; assert . deepEqual ( toECS ( current ). safetynet . request ,{ bytes : 18 , detected_type : 'text' }); assert . match ( toCEF ( current ), /cn3=18/ ); assert . equal ( JSON . stringify ( toECS ( current )). includes ( hash ), false );
legacy . kind = 'download.scan' ; assert . equal ( toECS ( legacy ). file . hash . sha256 , hash );
});
2026-10-14 11:16:09 +02:00
test ( 'central API accepts typed broker causation and rejects invalid lifecycle semantics' , async t =>{
const dir = mkdtempSync ( join ( tmpdir (), 'safetynet-request-audit-' )), store = openStore ( dir ), credential = 'synthetic-device-broker-token' ;
store . db . prepare ( 'INSERT INTO devices(id,name,token_hash) VALUES(?,?,?)' ). run ( 'broker-device' , 'Synthetic' , digest ( credential ));
const server = createApp ( store ,{ publicUrl : 'http://127.0.0.1' , adminToken : 'synthetic-administrator-token-123456789' , secure : false }). listen ( 0 , '127.0.0.1' ); await new Promise ( r => server . once ( 'listening' , r ));
t . after ( async ()=>{ server . closeAllConnections (); await new Promise ( r => server . close ( r )); store . db . close (); rmSync ( dir ,{ recursive : true , force : true });});
const send = body => fetch ( `http://127.0.0.1: ${ server . address (). port } /api/device/events` ,{ method : 'POST' , headers : { authorization : `Bearer ${ credential } ` , 'content-type' : 'application/json' }, body : JSON . stringify ( body )});
const requestId = randomUUID (), authorizedEventId = randomUUID ();
const allow = { eventId : authorizedEventId , policyVersion : 1 , kind : 'upload.authorized' , ruleId : 'broker-approved' , request : { bytes : 7 , detectedType : 'text' }, broker : { requestId , phase : 'request.authorized' , reason : 'broker-approved' }};
assert . equal (( await send ( allow )). status , 200 );
const fault = {... allow , eventId : randomUUID (), kind : 'upload.block' , ruleId : 'broker-upstream-failed' , broker : { requestId , phase : 'forward.fault' , reason : 'broker-upstream-failed' , authorizedEventId }};
assert . equal (( await send ( fault )). status , 200 );
for ( const broker of [{... fault . broker , authorizedEventId : undefined },{... fault . broker , authorizedEventId : fault . eventId },{... allow . broker , phase : 'connect.block' },{... allow . broker , reason : 'arbitrary secret details' },{... allow . broker , body : 'private' }]) assert . equal (( await send ({... fault , eventId : broker . authorizedEventId === fault . eventId ? fault . eventId : randomUUID (), broker })). status , 400 );
const rows = store . db . prepare ( 'SELECT metadata FROM events ORDER BY id' ). all (); assert . equal ( rows . length , 2 ); assert . deepEqual ( JSON . parse ( rows [ 1 ]. metadata ). broker , fault . broker ); assert . equal ( store . auditIntegrity . verify (). valid , true );
});