8 lines
2.7 KiB
JavaScript
8 lines
2.7 KiB
JavaScript
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');
|
||
|
|
}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});}
|