2026-10-13 16:58:15 +02:00
import { createServer } from 'node:http' ;
import { createRequire } from 'node:module' ;
import { mkdtempSync , mkdirSync , rmSync } from 'node:fs' ;
import { tmpdir } from 'node:os' ;
import { join , resolve } from 'node:path' ;
import { spawn } from 'node:child_process' ;
import { randomBytes } from 'node:crypto' ;
import { createApp } from '../apps/server/app.mjs' ;
import { openStore , digest } from '../apps/server/store.mjs' ;
import { startTelemetry } from '../apps/server/telemetry.mjs' ;
const collector = 'http://127.0.0.1:4318' ;
if ( ! ( await fetch ( collector + '/health' )). ok ) throw Error ( 'Protected collector unavailable' );
process . env . OTEL_EXPORTER_OTLP_ENDPOINT = collector ; const telemetry = startTelemetry ();
const temporary = mkdtempSync ( join ( tmpdir (), 'safetynet-individual-' )); mkdirSync ( 'artifacts' ,{ recursive : true });
if ( ! resolve ( temporary ). startsWith ( resolve ( tmpdir ()) + requireSeparator ()) ||! temporary . split ( /[\\/]/ ). at ( - 1 ). startsWith ( 'safetynet-individual-' )) throw Error ( 'Unsafe temporary fixture path' );
function requireSeparator (){ return process . platform === 'win32' ? '\\' : '/' ;}
const store = openStore ( join ( temporary , 'db' )), code = randomBytes ( 32 ). toString ( 'base64url' ), adminToken = randomBytes ( 32 ). toString ( 'base64url' );
let forbiddenRequests = 0 , ownerlessRequests = 0 ; const forbidden = createServer (( _req , res )=>{ forbiddenRequests ++ ; res . end ( 'Forbidden receiver' );}). listen ( 0 , '127.0.0.2' ); await new Promise ( r => forbidden . once ( 'listening' , r ));
const forbiddenUrl = `http://127.0.0.2: ${ forbidden . address (). port } /forbidden` ;
2026-10-13 18:04:02 +02:00
const sink = createServer (( req , res )=>{ if ( req . url === '/count' ){ res . setHeader ( 'content-type' , 'application/json' ); res . end ( JSON . stringify ({ forbiddenRequests , ownerlessRequests })); return ;} if ( req . url === '/ownerless' ) ownerlessRequests ++ ; if ( req . url === '/redirect' ){ res . writeHead ( 302 ,{ location : forbiddenUrl }); res . end (); return ;} res . setHeader ( 'content-type' , 'text/html' ); res . end ( req . url === '/protected-title' ? '<!doctype html><title>Synthetic confidential inventory title</title><h1>Public actor fixture</h1>' : '<!doctype html><title>Individual actor fixture</title><h1>Public actor fixture</h1>' );}). listen ( 0 , '127.0.0.1' ); await new Promise ( r => sink . once ( 'listening' , r ));
2026-10-13 16:58:15 +02:00
const p = store . get ( 'policy' ); p . browser . homeUrl = `http://127.0.0.1: ${ sink . address (). port } ` ;
p . agents = {... p . agents , enabled : true , identityMode : 'individual' , scopes : [ 'sessions' , 'navigate' , 'read' , 'application:state' , 'settings:get' ], allowedHosts : [ '127.0.0.1' , '127.0.0.2' ], grants : [{ id : 'reader-a' , name : 'Reader A' , enabled : true , expiresAt : new Date ( Date . now () + 3600000 ). toISOString (), scopes : [ 'sessions' , 'navigate' , 'read' ], allowedHosts : [ '127.0.0.1' ], generation : 1 },{ id : 'state-b' , name : 'State B' , enabled : true , expiresAt : new Date ( Date . now () + 3600000 ). toISOString (), scopes : [ 'application:state' ], allowedHosts : [ '127.0.0.1' ], generation : 1 }]};
2026-10-13 18:04:02 +02:00
p . dlp . rules . push ({ id : 'inventory-confidential-fixture' , label : 'Synthetic inventory fixture' , action : 'block' , kind : 'literal' , value : 'Synthetic confidential inventory title' });
2026-10-13 16:58:15 +02:00
p . diagnostics = {... p . diagnostics , enabled : true , mode : 'debug' , endpoint : collector , sampleRate : 1 , debugSampleRate : 1 , flushIntervalMs : 250 }; store . put ( 'policy' , p ); store . db . prepare ( 'INSERT INTO enrollments VALUES(?,?,?)' ). run ( digest ( code ), Date . now () + 600000 , 'individual-fixture' );
const config = { secure : false , publicUrl : 'http://127.0.0.1' , adminToken , requirePQManagement : true };
const management = createApp ( store , config ). listen ( 0 , '127.0.0.1' ); await new Promise ( r => management . once ( 'listening' , r )); config . publicUrl = `http://127.0.0.1: ${ management . address (). port } ` ;
const electron = createRequire ( import . meta . url )( 'electron' );
async function run ( phase ){ const child = spawn ( electron ,[ 'scripts/individual-agent-electron.cjs' ],{ windowsHide : true , stdio : 'inherit' , env : {... process . env , SAFETYNET_SMOKE_SERVER : config . publicUrl , SAFETYNET_SMOKE_CODE : code , SAFETYNET_SMOKE_ADMIN : adminToken , SAFETYNET_SMOKE_USERDATA : join ( temporary , 'browser' ), SAFETYNET_SMOKE_ARTIFACTS : resolve ( 'artifacts' ), SAFETYNET_SMOKE_PHASE : phase , SAFETYNET_SMOKE_SINK : p . browser . homeUrl , SAFETYNET_SMOKE_FORBIDDEN : forbiddenUrl }}); const deadline = setTimeout (()=> child . kill (), 180000 ); const exit = await new Promise ( r =>{ child . once ( 'exit' , v => r ( v ?? 1 )); child . once ( 'error' ,()=> r ( 1 ));}); clearTimeout ( deadline ); return exit ;}
let exit = await run ( 'initial' ); if ( exit === 0 ) exit = await run ( 'restart' );
for ( const server of [ management , sink , forbidden ]) server . closeAllConnections (); await Promise . all ([ new Promise ( r => management . close ( r )), new Promise ( r => sink . close ( r )), new Promise ( r => forbidden . close ( r ))]); store . db . close (); await telemetry ? . shutdown (); rmSync ( temporary ,{ recursive : true , force : true }); process . exit ( exit );