import {createServer} from 'node:https'; import {createRequire} from 'node:module'; import {mkdtempSync,mkdirSync,rmSync,readFileSync} from 'node:fs'; import {tmpdir} from 'node:os'; import {join,resolve,sep} 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-session-agent-'));mkdirSync('artifacts',{recursive:true}); if(!resolve(temporary).startsWith(resolve(tmpdir())+sep)||!temporary.split(/[\\/]/).at(-1).startsWith('safetynet-session-agent-'))throw Error('Unsafe temporary fixture path'); let requested=0;const paths={};const sink=createServer({cert:readFileSync('tests/fixtures/icap/cert.pem'),key:readFileSync('tests/fixtures/icap/key.pem')},(req,res)=>{if(req.url==='/count'){res.setHeader('content-type','application/json');res.end(JSON.stringify({requested,paths}));return;}requested++;const pathname=new URL(req.url,'https://fixture').pathname;paths[pathname]=(paths[pathname]||0)+1;res.setHeader('content-type','text/html');const respond=()=>res.end('
Public research
');if(pathname==='/delayed')setTimeout(respond,800);else respond();}).listen(0,'127.0.0.1');await new Promise(r=>sink.once('listening',r)); const origin=`https://127.0.0.1:${sink.address().port}`,store=openStore(join(temporary,'db')),code=randomBytes(32).toString('base64url'),adminToken=randomBytes(32).toString('base64url'); const p=store.get('policy');p.browser.homeUrl=origin+'/research?q=public#section';p.agents={...p.agents,enabled:true,identityMode:'individual',scopes:['sessions','sessions:save','sessions:list','sessions:restore'],allowedHosts:['127.0.0.1'],grants:['session-a','session-b'].map(id=>({id,name:id,enabled:true,expiresAt:new Date(Date.now()+3600000).toISOString(),scopes:['sessions','sessions:save','sessions:list','sessions:restore'],allowedHosts:['127.0.0.1'],generation:1}))}; 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,'session-fixture'); const config={secure:false,publicUrl:'http://127.0.0.1',adminToken,requirePQManagement:true,requireIndependentPolicyApproval: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 reviewer=randomBytes(32).toString('base64url');store.db.prepare('INSERT INTO sessions VALUES(?,?,?)').run(digest(reviewer),Date.now()+600000,JSON.stringify({sub:'independent-session-reviewer',role:'admin'})); let exit=0;for(const phase of ['save','restore']){const child=spawn(createRequire(import.meta.url)('electron'),['scripts/session-agent-controls-smoke.cjs'],{windowsHide:true,stdio:'inherit',env:{...process.env,SAFETYNET_SMOKE_PHASE:phase,SAFETYNET_SMOKE_SERVER:config.publicUrl,SAFETYNET_SMOKE_CODE:code,SAFETYNET_SMOKE_ADMIN:adminToken,SAFETYNET_SMOKE_REVIEWER:reviewer,SAFETYNET_SMOKE_USERDATA:join(temporary,'browser'),SAFETYNET_SMOKE_ARTIFACTS:resolve('artifacts'),SAFETYNET_SMOKE_ORIGIN:origin}});const deadline=setTimeout(()=>child.kill(),180000);exit=await new Promise(r=>{child.once('exit',v=>r(v??1));child.once('error',()=>r(1));});clearTimeout(deadline);if(exit!==0)break;} for(const server of [management,sink])server.closeAllConnections();await Promise.all([new Promise(r=>management.close(r)),new Promise(r=>sink.close(r))]);store.db.close();await telemetry?.shutdown();rmSync(temporary,{recursive:true,force:true});process.exit(exit);