2026-10-13 10:33:31 +02:00
import assert from 'node:assert/strict' ; import { createServer } from 'node:http' ; import { createRequire } from 'node:module' ; import { mkdtempSync , rmSync } from 'node:fs' ; import { tmpdir } from 'node:os' ; import { join , resolve , sep , basename } 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' ;
const require = createRequire ( import . meta . url ), directory = mkdtempSync ( join ( tmpdir (), 'safetynet-devtools-proof-' )); let sink , server , store , child ;
try {
sink = createServer (( _req , res )=>{ res . setHeader ( 'content-type' , 'text/html' ); res . end ( '<!doctype html><title>Developer fixture</title><h1>Synthetic developer page</h1>' );}). listen ( 0 , '127.0.0.1' ); await new Promise ( r => sink . once ( 'listening' , r )); const sinkUrl = `http://127.0.0.1: ${ sink . address (). port } ` ;
store = openStore ( join ( directory , 'management' )); const policy = store . get ( 'policy' ); policy . browser . homeUrl = sinkUrl ; policy . browser . allowedHosts = [ '127.0.0.1' ]; store . put ( 'policy' , policy ); const code = randomBytes ( 32 ). toString ( 'base64url' ), adminToken = randomBytes ( 32 ). toString ( 'base64url' ); store . db . prepare ( 'INSERT INTO enrollments VALUES(?,?,?)' ). run ( digest ( code ), Date . now () + 600000 , 'synthetic-developer' ); const config = { adminToken , publicUrl : 'http://127.0.0.1' , secure : false }; server = createApp ( store , config ). listen ( 0 , '127.0.0.1' ); await new Promise ( r => server . once ( 'listening' , r )); config . publicUrl = `http://127.0.0.1: ${ server . address (). port } ` ;
const env = {... process . env , SAFETYNET_DEVTOOLS_SERVER : config . publicUrl , SAFETYNET_DEVTOOLS_CODE : code , SAFETYNET_DEVTOOLS_ADMIN : adminToken , SAFETYNET_DEVTOOLS_SINK : sinkUrl , SAFETYNET_DEVTOOLS_USERDATA : join ( directory , 'browser' ), SAFETYNET_SMOKE_USERDATA : join ( directory , 'browser' )}; delete env . ELECTRON_RUN_AS_NODE ;
child = spawn ( require ( 'electron' ),[ 'scripts/devtools-profile-smoke.cjs' ],{ env , windowsHide : true , stdio : [ 'ignore' , 'pipe' , 'pipe' ]}); child . stdout . on ( 'data' , b => process . stdout . write ( b )); child . stderr . on ( 'data' , b => process . stderr . write ( String ( b ). replaceAll ( code , '[fixture enrollment]' ). replaceAll ( adminToken , '[fixture admin]' ))); const timer = setTimeout (()=> child . kill (), 90000 ); const exit = await new Promise ( r =>{ child . once ( 'exit' , c => r ( c ?? 1 )); child . once ( 'error' ,()=> r ( 1 ));}); clearTimeout ( timer ); assert . equal ( exit , 0 , 'Native developer-profile proof failed' );
2026-10-13 23:19:22 +02:00
// eslint-disable-next-line no-unsafe-finally -- Cleanup failure must fail closed rather than report successful protected cleanup.
2026-10-13 10:33:31 +02:00
} finally { child ? . kill (); for ( const service of [ server , sink ]) if ( service ) await new Promise ( r =>{ service . closeAllConnections (); service . close ( r );}); store ? . db . close (); const target = resolve ( directory ); if ( ! target . startsWith ( resolve ( tmpdir ()) + sep ) ||! basename ( target ). startsWith ( 'safetynet-devtools-proof-' )) throw Error ( 'Unsafe fixture cleanup' ); rmSync ( target ,{ recursive : true , force : true , maxRetries : 10 , retryDelay : 100 });}