asyncfunctionfixture(t){constdir=mkdtempSync(join(tmpdir(),'safetynet-incidents-')),store=openStore(dir),origin='http://127.0.0.1:4310',server=createApp(store,{publicUrl:origin}).listen(0,'127.0.0.1');awaitnewPromise(r=>server.once('listening',r));t.after(async()=>{awaitnewPromise(r=>server.close(r));store.db.close();rmSync(dir,{recursive:true,force:true});});return{store,api:async(path,method='GET',body,role='operator')=>{consttoken=`synthetic-incident-${role}-credential-1234567890`;store.db.prepare('INSERT OR REPLACE INTO sessions VALUES(?,?,?)').run(digest(token),Date.now()+60000,JSON.stringify({sub:role,role}));constr=awaitfetch(`http://127.0.0.1:${server.address().port}/api/admin/incidents${path}`,{method,headers:{cookie:`sn_session=${token}`,origin,'content-type':'application/json'},body:body?JSON.stringify(body):undefined});return{status:r.status,body:awaitr.json()};}};}
test('incident creates once from immutable event and detail strips payloads',asynct=>{const{store,api}=awaitfixture(t);store.event('device','upload.blocked',{ruleId:'confidential',host:'example.test',prompt:'secret synthetic prompt',document:'secret synthetic document'});consta=awaitapi('','POST',{eventId:1,severity:'high'});assert.equal(a.status,201);assert.equal(a.body.revision,1);assert.equal(a.body.event.metadata.prompt,undefined);assert.equal(a.body.event.metadata.document,undefined);assert.equal((awaitapi('','POST',{eventId:1})).status,409);assert.equal((awaitapi('','POST',{eventId:999})).status,404);constd=awaitapi('/'+a.body.id);assert.equal(d.body.history.length,1);assert.equal(store.db.prepare('SELECT kind FROM events WHERE id=1').get().kind,'upload.blocked');assert.equal((awaitapi('','POST',{eventId:2})).status,400);});
test('triage is revision bound and comments remain outside ordinary audit/SIEM',asynct=>{const{store,api}=awaitfixture(t);store.event(null,'clipboard.blocked',{});consta=awaitapi('','POST',{eventId:1});constid=a.body.id,comment='Contacted owner; synthetic investigation note.';constb=awaitapi('/'+id,'PATCH',{expectedRevision:1,status:'investigating',assignee:'analyst-sub',comment});assert.equal(b.status,200);assert.equal(b.body.revision,2);assert.equal((awaitapi('/'+id,'PATCH',{expectedRevision:1,status:'resolved'})).status,409);assert.equal((awaitapi('/'+id,'PATCH',{expectedRevision:2})).status,400);assert.equal((awaitapi('/'+id,'PATCH',{expectedRevision:2,comment:'x'.repeat(1001)})).status,400);assert.equal((awaitapi('/'+id,'PATCH',{expectedRevision:2,comment:'\u0000bad'})).status,400);assert.equal((awaitapi('/'+id,'PATCH',{expectedRevision:2,document:'payload'})).status,400);constd=awaitapi('/'+id);assert.equal(d.body.history[1].comment,comment);constaudit=store.db.prepare("SELECT metadata FROM events WHERE kind='incident.updated'").get().metadata;assert.equal(audit.includes(comment),false);assert.equal(JSON.parse(audit).commentLength,comment.length);assert.equal(JSON.parse(audit).commentSha256,undefined);assert.equal((awaitapi('/'+id,'PATCH',{expectedRevision:2,assignee:'bad\nsubject'})).status,400);assert.equal((awaitapi('/'+id,'PATCH',{expectedRevision:2,status:'resolved',assignee:null})).status,200);assert.equal((awaitapi('/'+id,'PATCH',{expectedRevision:3,status:'open'})).status,200);assert.equal(store.db.prepare('SELECT COUNT(*) n FROM incident_changes').get().n,4);});
test('auditor cannot triage and unrecognized routes never gain permissions',asynct=>{const{store,api}=awaitfixture(t);store.event(null,'dns.blocked',{});consta=awaitapi('','POST',{eventId:1});assert.equal((awaitapi('','GET',null,'auditor')).status,200);assert.equal((awaitapi('/'+a.body.id,'GET',null,'auditor')).status,200);assert.equal((awaitapi('','POST',{eventId:1},'auditor')).status,403);assert.equal((awaitapi('/'+a.body.id,'PATCH',{expectedRevision:1,status:'resolved'},'auditor')).status,403);assert.equal((awaitapi('/'+a.body.id,'DELETE')).status,403);assert.equal((awaitapi('/not-a-uuid')).status,400);});
test('filters and pagination are bounded and deterministic',asynct=>{const{store,api}=awaitfixture(t);for(leti=0;i<3;i++){store.event(null,'upload.blocked',{});constsource=store.db.prepare("SELECT id FROM events WHERE kind='upload.blocked' ORDER BY id DESC LIMIT 1").get();awaitapi('','POST',{eventId:source.id,severity:i===0?'low':'high'});}constfirst=awaitapi('?limit=1&severity=high');assert.equal(first.body.incidents.length,1);assert.ok(first.body.nextBefore);constnext=awaitapi('?limit=1&severity=high&before='+first.body.nextBefore);assert.equal(next.body.incidents.length,1);assert.notEqual(first.body.incidents[0].id,next.body.incidents[0].id);assert.equal(next.body.nextBefore,null);assert.equal((awaitapi('?status=resolved')).body.incidents.length,0);assert.equal((awaitapi('?limit=101')).status,400);assert.equal((awaitapi('?status=bogus')).status,400);});
test('audit failure rolls back case creation and triage history atomically',asynct=>{const{store,api}=awaitfixture(t);store.event(null,'upload.blocked',{});constemit=store.event;store.event=()=>{throwError('synthetic audit unavailable');};assert.equal((awaitapi('','POST',{eventId:1})).status,500);assert.equal(store.db.prepare('SELECT COUNT(*) n FROM incident_cases').get().n,0);assert.equal(store.db.prepare('SELECT COUNT(*) n FROM incident_changes').get().n,0);store.event=emit;constcreated=awaitapi('','POST',{eventId:1});store.event=()=>{throwError('synthetic audit unavailable');};assert.equal((awaitapi('/'+created.body.id,'PATCH',{expectedRevision:1,status:'resolved'})).status,500);assert.equal(store.db.prepare('SELECT revision,status FROM incident_cases').get().revision,1);assert.equal(store.db.prepare('SELECT COUNT(*) n FROM incident_changes').get().n,1);store.event=emit;});
test('real resolved incident retains source and immutable history through event retention',asynct=>{const{store,api}=awaitfixture(t);store.event(null,'upload.blocked',{ruleId:'confidential'});constsource=store.db.prepare('SELECT id FROM events ORDER BY id DESC LIMIT 1').get().id;constcreated=awaitapi('','POST',{eventId:source});constresolved=awaitapi('/'+created.body.id,'PATCH',{expectedRevision:1,status:'resolved',comment:'Synthetic resolution note'});assert.equal(resolved.status,200);const{pruneAuditEvents}=awaitimport('../apps/server/audit-retention.mjs');constresult=pruneAuditEvents(store,{now:Date.now()+60*86400000,days:30,exportedThrough:100,dryRun:false});assert.ok(result.eligible>0);constdetail=awaitapi('/'+created.body.id);assert.equal(detail.status,200);assert.equal(detail.body.status,'resolved');assert.equal(detail.body.event.id,source);assert.equal(detail.body.history.length,2);assert.equal(detail.body.history[1].comment,'Synthetic resolution note');assert.equal(store.db.prepare('SELECT kind FROM events WHERE id=?').get(source).kind,'upload.blocked');});