41 lines
4.7 KiB
JavaScript
41 lines
4.7 KiB
JavaScript
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { mkdtempSync,readFileSync,rmSync } from 'node:fs';
|
|
import { tmpdir } from 'node:os';
|
|
import { join } from 'node:path';
|
|
import { openStore } from '../apps/server/store.mjs';
|
|
import { startSIEM } from '../apps/server/siem.mjs';
|
|
import { toECS,toCEF } from '../packages/core/siem.mjs';
|
|
test('CEF escapes headers and extension delimiters; ECS excludes raw content',()=>{
|
|
const e={id:1,time:new Date().toISOString(),device_id:'device',kind:'dlp.block',metadata:{ruleId:'a=b\nc',policyVersion:2,prompt:'must never be exported'}};
|
|
const cef=toCEF(e,'install');assert.ok(cef.includes('cs1=a\\=b\\nc'));assert.ok(cef.startsWith('CEF:0|'));assert.equal(cef.includes('must never'),false);
|
|
const ecs=toECS(e,'install');assert.equal(ecs.event.id,'install:1');assert.equal(JSON.stringify(ecs).includes('must never'),false);assert.deepEqual(ecs.event.type,['denied']);
|
|
});
|
|
test('durable file exporter resumes after restart and advances only written events',async t=>{
|
|
const directory=mkdtempSync(join(tmpdir(),'safetynet-siem-')),store=openStore(directory);
|
|
let exporter;t.after(()=>{exporter?.stop();store.db.close();rmSync(directory,{recursive:true,force:true});});
|
|
store.event('device','dlp.block',{ruleId:'secret',policyVersion:1});exporter=startSIEM(store,directory,{});await exporter.flush();
|
|
assert.equal(exporter.status().cursor,1);exporter.stop();
|
|
store.event(null,'policy.published',{actor:'admin',version:2});exporter=startSIEM(store,directory,{});await exporter.flush();assert.equal(exporter.status().cursor,2);
|
|
const rows=readFileSync(join(directory,'security.ndjson'),'utf8').trim().split('\n').map(v=>JSON.parse(v));assert.equal(rows.length,2);assert.notEqual(rows[0].event.id,rows[1].event.id);
|
|
});
|
|
test('remote SIEM export refuses plaintext endpoints',t=>{
|
|
const directory=mkdtempSync(join(tmpdir(),'safetynet-siem-')),store=openStore(directory);
|
|
t.after(()=>{store.db.close();rmSync(directory,{recursive:true,force:true});});assert.throws(()=>startSIEM(store,directory,{SIEM_HTTP_URL:'http://example.com'}),/HTTPS/);
|
|
});
|
|
|
|
test('ECS and CEF correlate validated trace identities without exporting arbitrary trace text',()=>{
|
|
const e={id:1,time:new Date().toISOString(),kind:'clipboard.copy',metadata:{traceId:'1234567890abcdef1234567890abcdef',spanId:'1234567890abcdef'}};
|
|
assert.equal(toECS(e).trace.id,e.metadata.traceId);assert.match(toCEF(e),/cs4Label=TraceId/);
|
|
e.metadata.traceId='token=synthetic-secret';e.metadata.spanId='invalid';assert.equal(toECS(e).trace,undefined);assert.equal(toCEF(e).includes('synthetic-secret'),false);
|
|
});
|
|
test('denied agent actions and correlated Sigma alerts export durably without raw source content',async t=>{
|
|
const directory=mkdtempSync(join(tmpdir(),'safetynet-sigma-')),store=openStore(directory);let exporter;t.after(()=>{exporter?.stop();store.db.close();rmSync(directory,{recursive:true,force:true});});
|
|
const p=store.get('policy');p.security.sigma.rules=[{id:'agent-denial',title:'Agent denial',level:'high',detection:{selection:{kind:'agent.denied'},condition:'selection'}}];store.put('policy',p);
|
|
store.db.exec('BEGIN IMMEDIATE');store.event('device','agent.denied',{ruleId:'agent:read:blocked',policyVersion:1,prompt:'excluded raw content'});store.db.exec('COMMIT');
|
|
const stored=store.db.prepare('SELECT * FROM events ORDER BY id').all().map(e=>({...e,metadata:JSON.parse(e.metadata)}));assert.equal(stored.length,2);assert.equal(stored[1].kind,'sigma.alert');assert.deepEqual(stored[1].metadata,{ruleId:'agent-denial',level:'high',sourceEventId:stored[0].id});assert.equal(store.db.prepare('SELECT count(*) AS n FROM alerts').get().n,1);
|
|
assert.deepEqual(toECS(stored[0],'install').event.type,['denied']);const alert=toECS(stored[1],'install');assert.equal(alert.event.kind,'alert');assert.deepEqual(alert.event.category,['intrusion_detection']);assert.deepEqual(alert.event.type,['info']);assert.equal(alert.event.severity,7);assert.equal(alert.safetynet.source_event_id,`install:${stored[0].id}`);assert.match(toCEF(stored[1],'install'),/cs2Label=SourceEventId cs2=install:1/);
|
|
exporter=startSIEM(store,directory,{});await exporter.flush();const rows=readFileSync(join(directory,'security.ndjson'),'utf8').trim().split('\n').map(JSON.parse);assert.equal(rows.length,2);assert.equal(rows[1].event.action,'sigma.alert');assert.equal(rows[1].safetynet.source_event_id,rows[0].event.id);assert.equal(JSON.stringify(rows).includes('excluded raw content'),false);
|
|
store.db.exec('BEGIN IMMEDIATE');store.event('device','agent.denied',{ruleId:'agent:read:blocked'});store.db.exec('ROLLBACK');assert.equal(store.db.prepare('SELECT count(*) AS n FROM events').get().n,2);assert.equal(store.db.prepare('SELECT count(*) AS n FROM alerts').get().n,1);
|
|
});
|