22 lines
4.1 KiB
JavaScript
22 lines
4.1 KiB
JavaScript
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('<!doctype html><title>Public research fixture</title><input name="private" value="PRIVATE_FORM_SENTINEL"><input type="password" value="PRIVATE_PASSWORD_SENTINEL"><p>Public research</p>');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);
|