// Official standalone Node executable gives the broker a distinct Windows // application identity; Electron utilityProcess shares the browser executable. // This provisioning helper is not yet wired into production packaging. import {createHash} from 'node:crypto'; import {createReadStream,createWriteStream} from 'node:fs'; import {lstat,mkdir,mkdtemp,readFile,rename,writeFile,readdir} from 'node:fs/promises'; import {pipeline} from 'node:stream/promises'; import {Readable,Transform} from 'node:stream'; import {spawn} from 'node:child_process'; import path from 'node:path'; import {fileURLToPath} from 'node:url'; export const BROKER_RUNTIME=Object.freeze({version:'24.21.0',platform:'win32',arch:'x64', archive:'node-v24.21.0-win-x64.zip', archiveSha256:'158f7685b44de51f6c0df1d153526cbcd3e1bc739a8dfc607721cef75de9e541', executableSha256:'ba4e6d110e8c1592a1ecd390f6b05f3da124b13871a5be62b341a07a853c6c32', licenseSha256:'ed34dd8e3f0a78dbaf00d0444ce8e285b015b765379c2e17880455f70370f8e9', baseUrl:'https://nodejs.org/dist/v24.21.0/'}); function fail(code){throw Object.assign(new Error(code),{code});} async function sha256(file){const hash=createHash('sha256');for await(const chunk of createReadStream(file))hash.update(chunk);return hash.digest('hex');} export function verifyOfficialChecksums(text){ if(typeof text!=='string'||text.length>65536)fail('broker_runtime_checksums_invalid'); for(const [name,expected] of [[BROKER_RUNTIME.archive,BROKER_RUNTIME.archiveSha256],['win-x64/node.exe',BROKER_RUNTIME.executableSha256]]){ const rows=text.split(/\r?\n/).filter(row=>row.endsWith(' '+name)); if(rows.length!==1||rows[0]!==expected+' '+name)fail('broker_runtime_checksums_mismatch'); } } export async function downloadOfficialArchive(destination){ const checksumResponse=await fetch(BROKER_RUNTIME.baseUrl+'SHASUMS256.txt',{redirect:'error',signal:AbortSignal.timeout(30000)}); if(!checksumResponse.ok)fail('broker_runtime_download_failed'); const chunks=[];let checksumBytes=0; for await(const chunk of checksumResponse.body){checksumBytes+=chunk.length;if(checksumBytes>65536)fail('broker_runtime_checksums_invalid');chunks.push(Buffer.from(chunk));} const checksumText=Buffer.concat(chunks).toString();verifyOfficialChecksums(checksumText); const response=await fetch(BROKER_RUNTIME.baseUrl+BROKER_RUNTIME.archive,{redirect:'error',signal:AbortSignal.timeout(120000)}); if(!response.ok)fail('broker_runtime_download_failed'); let bytes=0;const hash=createHash('sha256'); const bounded=new Transform({transform(chunk,_encoding,callback){bytes+=chunk.length;if(bytes>67108864){callback(new Error('broker_runtime_archive_too_large'));return;}hash.update(chunk);callback(null,chunk);}}); await pipeline(Readable.fromWeb(response.body),bounded,createWriteStream(destination,{flags:'wx',mode:0o600})); if(hash.digest('hex')!==BROKER_RUNTIME.archiveSha256)fail('broker_runtime_archive_mismatch'); return {bytes,archiveSha256:BROKER_RUNTIME.archiveSha256,checksumText}; } function psQuote(value){return "'"+value.replaceAll("'","''")+"'";} async function extractSelected(archive,destination){ // Extract only exact reviewed members from the already hash-verified ZIP. const script=`$ErrorActionPreference='Stop'; Add-Type -AssemblyName System.IO.Compression.FileSystem; $zip=[IO.Compression.ZipFile]::OpenRead(${psQuote(archive)}); try { foreach($name in @('node.exe','LICENSE')) { $member='node-v${BROKER_RUNTIME.version}-win-x64/'+$name; $entries=@($zip.Entries | Where-Object { $_.FullName -ceq $member }); if($entries.Count -ne 1 -or $entries[0].Length -gt 100000000) { throw 'broker_runtime_archive_members_invalid' }; $inputStream=$entries[0].Open(); $outputStream=[IO.File]::Open([IO.Path]::Combine(${psQuote(destination)},$name),[IO.FileMode]::CreateNew); try { $inputStream.CopyTo($outputStream) } finally { $outputStream.Dispose(); $inputStream.Dispose() } } } finally { $zip.Dispose() }`; const child=spawn('powershell.exe',['-NoProfile','-NonInteractive','-EncodedCommand',Buffer.from(script,'utf16le').toString('base64')],{windowsHide:true,stdio:'ignore'}); const code=await new Promise((resolve,reject)=>{child.once('error',reject);child.once('exit',resolve);}); if(code!==0)fail('broker_runtime_extraction_failed'); } async function verifyDirectoryAncestors(directory){ for(let current=path.resolve(directory);;current=path.dirname(current)){ const stat=await lstat(current);if(stat.isSymbolicLink()||!stat.isDirectory())fail('broker_runtime_path_unsafe'); if(path.dirname(current)===current)break; } } export async function verifyBrokerRuntime(directory){ await verifyDirectoryAncestors(directory); const manifestPath=path.join(directory,'runtime-manifest.json'),manifestStat=await lstat(manifestPath); if(manifestStat.isSymbolicLink()||!manifestStat.isFile()||manifestStat.size>16384)fail('broker_runtime_manifest_invalid'); let manifest;try{manifest=JSON.parse(await readFile(manifestPath,'utf8'));}catch{fail('broker_runtime_manifest_invalid');} if(!manifest||typeof manifest!=='object')fail('broker_runtime_manifest_invalid'); if(manifest.format!=='safetynet-node-broker-runtime-v1'||manifest.version!==BROKER_RUNTIME.version||manifest.arch!=='x64'||manifest.platform!=='win32'||manifest.archiveSha256!==BROKER_RUNTIME.archiveSha256)fail('broker_runtime_manifest_invalid'); if(!Array.isArray(manifest.files)||manifest.files.length!==2||manifest.files.some(file=>!file||typeof file.path!=='string')||new Set(manifest.files.map(file=>file.path)).size!==2)fail('broker_runtime_manifest_invalid'); const files=await readdir(directory);if(JSON.stringify(files.sort())!==JSON.stringify(['LICENSE','node.exe','runtime-manifest.json'].sort()))fail('broker_runtime_inventory_mismatch'); for(const name of ['node.exe','LICENSE']){ const stat=await lstat(path.join(directory,name));if(stat.isSymbolicLink()||!stat.isFile())fail('broker_runtime_path_unsafe'); const record=manifest.files?.find(item=>item.path===name); if(!record||record.bytes!==stat.size||!/^[a-f0-9]{64}$/.test(record.sha256)||await sha256(path.join(directory,name))!==record.sha256)fail('broker_runtime_integrity_mismatch'); if(record.sha256!==(name==='node.exe'?BROKER_RUNTIME.executableSha256:BROKER_RUNTIME.licenseSha256))fail('broker_runtime_integrity_mismatch'); } return manifest; } export async function provisionBrokerRuntime(workspace){ if(process.platform!=='win32'||process.arch!=='x64')fail('broker_runtime_platform_unsupported'); const parent=path.join(path.resolve(workspace),'.runtime/windows');await mkdir(parent,{recursive:true}); await verifyDirectoryAncestors(parent); const destination=path.join(parent,'broker'); try{await lstat(destination);return await verifyBrokerRuntime(destination);}catch(error){if(error.code!=='ENOENT')throw error;} const staging=await mkdtemp(path.join(parent,'broker-provision-')); const archive=path.join(staging,BROKER_RUNTIME.archive),payload=path.join(staging,'payload');await mkdir(payload); // Failed and verified archives/proof remain ignored for diagnosis; no // unverified executable is started and no global Node installation changes. const download=await downloadOfficialArchive(archive); await writeFile(path.join(staging,'SHASUMS256.txt'),download.checksumText); await extractSelected(archive,payload); const manifest={format:'safetynet-node-broker-runtime-v1',version:BROKER_RUNTIME.version,platform:'win32',arch:'x64',archiveSha256:BROKER_RUNTIME.archiveSha256,files:[]}; for(const name of ['node.exe','LICENSE'])manifest.files.push({path:name,bytes:(await lstat(path.join(payload,name))).size,sha256:await sha256(path.join(payload,name))}); await writeFile(path.join(payload,'runtime-manifest.json'),JSON.stringify(manifest,null,2)+'\n'); await verifyBrokerRuntime(payload);await rename(payload,destination); return await verifyBrokerRuntime(destination); } if(process.argv[1]&&path.resolve(process.argv[1])===fileURLToPath(import.meta.url)){ const workspace=path.resolve(fileURLToPath(new URL('..',import.meta.url))); const result=process.argv.includes('--verify')?await verifyBrokerRuntime(path.join(workspace,'.runtime/windows/broker')):await provisionBrokerRuntime(workspace); console.log(JSON.stringify({version:result.version,files:result.files.length,verified:true})); }