35 lines
2.9 KiB
JavaScript
35 lines
2.9 KiB
JavaScript
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import {createRequire} from 'node:module';
|
|
import {fork} from 'node:child_process';
|
|
import {once} from 'node:events';
|
|
import https from 'node:https';
|
|
import fs from 'node:fs';
|
|
import path from 'node:path';
|
|
const require=createRequire(import.meta.url);
|
|
const {createSessionCertificates}=require('../apps/network-broker/tls-certificates.cjs');
|
|
const enabled=process.platform==='win32'&&process.env.SAFETYNET_NATIVE_CERTIFICATE_PINS==='1';
|
|
test('actual stock Electron accepts exact session leaf and rejects unregistered TLS leaf',{skip:!enabled,timeout:40000},async()=>{
|
|
const issuer=await createSessionCertificates({current:()=>true,authorized:()=>true});
|
|
const servers=[],root=path.resolve('artifacts/broker-certificate-pins');fs.mkdirSync(root,{recursive:true});const profile=fs.mkdtempSync(path.join(root,'profile-'));let child;
|
|
try{
|
|
async function server(){const initial=issuer.resolve({authority:'localhost:443'});let requests=0;
|
|
const site=https.createServer({key:initial.key,cert:initial.cert},(_request,response)=>{requests++;response.end('synthetic public page');});servers.push(site);site.listen(0,'127.0.0.1');await once(site,'listening');
|
|
const authority='localhost:'+site.address().port,leaf=issuer.resolve({authority});site.setSecureContext({key:leaf.key,cert:leaf.cert});
|
|
return {url:'https://'+authority+'/',leaf:issuer.publicMetadata({authority}),requests:()=>requests};
|
|
}
|
|
const publicSite=await server(),untrustedSite=await server();
|
|
const env=Object.fromEntries(['SystemRoot','WINDIR','TEMP','TMP','USERPROFILE','APPDATA','LOCALAPPDATA','PATH'].filter(key=>process.env[key]).map(key=>[key,process.env[key]]));
|
|
child=fork(path.resolve('tests/fixtures/broker-certificate-pins-electron.cjs'),[],{execPath:require('electron'),execArgv:[],env,windowsHide:true,stdio:['ignore','ignore','ignore','ipc']});
|
|
const result=once(child,'message',{signal:AbortSignal.timeout(20000)});child.send({type:'start',profile,leaf:publicSite.leaf,publicURL:publicSite.url,untrustedURL:untrustedSite.url});
|
|
const [proof]=await result;assert.equal(proof.type,'proof');assert.equal(proof.publicStatus,200);assert.equal(proof.publicBody,'synthetic public page');assert.equal(proof.untrustedRejected,true);assert.equal(publicSite.requests(),1);assert.equal(untrustedSite.requests(),0);
|
|
fs.writeFileSync(path.join(root,'proof.json'),JSON.stringify({...proof,untrustedApplicationRequests:0,productionBrowserWired:false},null,2)+'\n');
|
|
}finally{
|
|
if(child&&child.exitCode===null&&child.signalCode===null){
|
|
const exited=once(child,'exit',{signal:AbortSignal.timeout(6000)}),limit=setTimeout(()=>child.kill(),3000);
|
|
try{if(child.connected)child.send({type:'stop'},error=>{if(error)child.kill();});else child.kill();await exited;}finally{clearTimeout(limit);}
|
|
}
|
|
for(const site of servers){site.closeAllConnections();await new Promise(resolve=>site.close(resolve));}issuer.close();
|
|
}
|
|
});
|