Files
safetynet/apps/browser/broker-service-supervisor.cjs
T

35 lines
2.8 KiB
JavaScript

'use strict';
const path=require('node:path');
const {spawn}=require('node:child_process');
const {EventEmitter}=require('node:events');
const {createBrokerSupervisor}=require('./broker-supervisor.cjs');
const {createFramedChannel}=require('../network-broker/framed-channel.cjs');
// Privileged browser construction only. Installation verification covers the
// fixed native client as well as the signed broker payload. No renderer-supplied
// command, path, pipe name, environment or identity enters this transport.
function createServiceBrokerSupervisor({installationRoot,verifyInstallation,verifyPackage,persistAudit,acceptCertificate,onQuarantine,launch=spawn,deadlineMs,shutdownMs,traceOperation}={}){
if(!path.isAbsolute(installationRoot||'')||typeof verifyInstallation!=='function'||typeof launch!=='function'||typeof verifyPackage!=='function')throw Error('broker-supervisor-invalid');
const root=path.resolve(installationRoot),resourceRoot=path.join(root,'resources','broker');
function connect(){
const env={};for(const name of ['SystemRoot','WINDIR','TEMP','TMP','USERPROFILE','LOCALAPPDATA','APPDATA'])if(process.env[name])env[name]=process.env[name];
// --client-stdio authenticates the retained actual browser parent and the
// connected service pipe handle in native code, before forwarding frames.
const child=launch(path.join(root,'safetynet-service-host.exe'),['--client-stdio'],{cwd:root,env,windowsHide:true,shell:false,stdio:['pipe','pipe','ignore']});
if(!child||!child.stdin||!child.stdout||typeof child.kill!=='function')throw Error('broker-supervisor-unavailable');
const transport=new EventEmitter();
const channel=createFramedChannel({readable:child.stdout,writable:child.stdin,inboundFirstLimit:65536,outboundFirstLimit:2097152});
Object.defineProperties(transport,{exitCode:{get:()=>child.exitCode},signalCode:{get:()=>child.signalCode}});
channel.on('message',message=>transport.emit('message',message));
channel.once('disconnect',()=>transport.emit('disconnect'));
child.once('error',()=>{channel.disconnect();transport.emit('error',Error('broker-supervisor-unavailable'));});
child.once('exit',(code,signal)=>{channel.disconnect();transport.emit('exit',code,signal);});
transport.send=(message,callback)=>channel.send(message,callback);
// Only the retained child handle is terminated. The service owns the broker
// Job; losing this connection causes its generation to close independently.
transport.kill=signal=>{channel.disconnect();return child.kill(signal);};
return transport;
}
return createBrokerSupervisor({resourceRoot,verifyPackage:async location=>await verifyInstallation(root)===true&&await verifyPackage(location)===true,persistAudit,acceptCertificate,onQuarantine,spawn:connect,deadlineMs,shutdownMs,traceOperation});
}
module.exports={createServiceBrokerSupervisor};