98 lines
11 KiB
JavaScript
98 lines
11 KiB
JavaScript
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import {EventEmitter} from 'node:events';
|
|
import {createRequire} from 'node:module';
|
|
import {randomUUID,generateKeyPairSync} from 'node:crypto';
|
|
import {defaultPolicy,signPolicy} from '../packages/core/policy.mjs';
|
|
const require=createRequire(import.meta.url),{runProductionProcess}=require('../apps/network-broker/production-entry.cjs'),{createProductionBroker}=require('../apps/network-broker/production-factory.cjs'),{createBrokerAudit}=require('../apps/network-broker/audit-gate.cjs');
|
|
class Channel extends EventEmitter{connected=true;sent=[];send(value,callback){this.sent.push(value);callback?.();this.emit('sent',value);}disconnect(){this.connected=false;this.emit('disconnect');}}
|
|
const tick=()=>new Promise(resolve=>setImmediate(resolve));
|
|
const start=()=>({type:'start',version:1,generation:randomUUID(),policyVersion:1,config:{brokerPort:43100}});
|
|
|
|
test('close retains late original factory ownership and cleans once before disconnect',async()=>{
|
|
const channel=new Channel();let releaseFactory,releaseCleanup,closeCalls=0,listens=0;
|
|
const created={close:()=>{closeCalls++;return new Promise(resolve=>{releaseCleanup=resolve;});},listen:()=>{listens++;return 43100;}};
|
|
const handle=runProductionProcess(()=>new Promise(resolve=>{releaseFactory=resolve;}),channel);
|
|
channel.emit('message',start());await tick();const closing=handle.close();assert.equal(handle.close(),closing);
|
|
await tick();assert.equal(channel.connected,true);releaseFactory(created);await tick();
|
|
assert.equal(closeCalls,1);assert.equal(listens,0);assert.equal(channel.connected,true);
|
|
releaseCleanup();await closing;assert.equal(channel.connected,false);assert.equal(channel.sent.length,0);
|
|
});
|
|
test('real cleanup failure is sticky and cannot be reported as successful close',async()=>{
|
|
const channel=new Channel();let calls=0;
|
|
const handle=runProductionProcess(async()=>({authMode:'basic',credentials:{username:'safetynet-'+'a'.repeat(32),password:'b'.repeat(43)},listen:async()=>43100,close:async()=>{calls++;throw Error('fixed-cleanup-failure');}}),channel);
|
|
channel.emit('message',start());await tick();const closing=handle.close();await assert.rejects(closing,/fixed-cleanup-failure/);
|
|
assert.equal(handle.close(),closing);await assert.rejects(handle.close(),/fixed-cleanup-failure/);assert.equal(calls,1);assert.equal(channel.connected,false);
|
|
});
|
|
test('close waits original listen settlement and never sends late readiness',async()=>{
|
|
const channel=new Channel();let releaseListen,closed=0,finished=false;
|
|
const handle=runProductionProcess(async()=>({listen:()=>new Promise(resolve=>{releaseListen=resolve;}),close:async()=>closed++}),channel);
|
|
channel.emit('message',start());await tick();const closing=handle.close().then(()=>{finished=true;});await tick();
|
|
assert.equal(closed,1);assert.equal(finished,false);assert.equal(channel.connected,true);
|
|
releaseListen(43100);await closing;assert.equal(channel.connected,false);assert.equal(channel.sent.length,0);
|
|
});
|
|
test('private v1 IPC requires matching durable receipt before factory continues',async()=>{
|
|
const channel=new Channel(),message=start();let callbacks,closed=0;
|
|
runProductionProcess(async(_config,value)=>{callbacks=value;return {authMode:'basic',credentials:{username:'safetynet-'+'a'.repeat(32),password:'b'.repeat(43)},listen:async()=>43100,close:async()=>closed++};},channel);
|
|
channel.emit('message',message);await tick();assert.equal(channel.sent[0].type,'ready');
|
|
const event={eventId:randomUUID(),kind:'upload.authorized',ruleId:'broker-approved',policyVersion:1,request:{bytes:3,detectedType:'text'}};
|
|
let completed=false;const pending=callbacks.persistAudit(event).then(()=>completed=true);await tick();assert.equal(completed,false);const request=channel.sent[1];assert.equal(request.type,'audit');
|
|
channel.emit('message',{type:'audit-receipt',version:1,generation:message.generation,policyVersion:1,requestId:request.requestId,sequence:request.sequence,receipt:{persisted:true,eventId:event.eventId}});await pending;
|
|
channel.emit('message',{type:'stop',version:1,generation:message.generation,policyVersion:1});await tick();assert.equal(closed,1);
|
|
});
|
|
test('wrong generation or uncertain receipt closes child with no automatic retry',async()=>{
|
|
const channel=new Channel(),message=start();let callbacks;
|
|
runProductionProcess(async(_config,value)=>{callbacks=value;return {authMode:'basic',credentials:{username:'safetynet-'+'a'.repeat(32),password:'b'.repeat(43)},listen:async()=>43100,close:async()=>{}};},channel);
|
|
channel.emit('message',message);await tick();const pending=callbacks.acknowledgeCertificate({authority:'example.com:443',cert:'public certificate'});const rejected=assert.rejects(pending,/broker-parent-unavailable/);const request=channel.sent[1];
|
|
channel.emit('message',{type:'certificate-receipt',version:1,generation:randomUUID(),policyVersion:1,requestId:request.requestId,sequence:request.sequence,accepted:true});await rejected;await tick();assert.equal(channel.connected,false);assert.equal(channel.sent.length,2);
|
|
});
|
|
test('real factory rejects untrusted paths and unsupported signed proxy before inspector launch',async()=>{
|
|
const keys=generateKeyPairSync('ed25519'),p=defaultPolicy();p.assignment={deviceId:'device',groups:[]};
|
|
const config={publicKey:keys.publicKey.export({type:'spki',format:'pem'}),deviceId:'device',minimumVersion:1,envelope:signPolicy(p,keys.privateKey.export({type:'pkcs8',format:'pem'})),brokerPort:43100,network:{mode:'direct',dns:'system'}};
|
|
const callbacks={persistAudit:async()=>{},acknowledgeCertificate:async()=>true,expectedPolicyVersion:1};
|
|
await assert.rejects(createProductionBroker({...config,resourceRoot:'C:/arbitrary'},callbacks),/broker-bootstrap-invalid/);
|
|
await assert.rejects(createProductionBroker(config,callbacks),/broker-route-unsupported/);
|
|
await assert.rejects(createProductionBroker({...config,network:{mode:'system',dns:'system'}},callbacks),/broker-bootstrap-invalid/);
|
|
});
|
|
test('audit callback cannot mutate frozen request causation',async()=>{
|
|
const eventId=randomUUID();let event;
|
|
const audit=createBrokerAudit({current:()=>true,policyVersion:1,persist:async value=>{event=value;assert.throws(()=>value.broker.requestId=eventId,TypeError);return {persisted:true,eventId:value.eventId};}});
|
|
const requestId=randomUUID();await audit.record('allow',{bytes:3,detectedType:'text',broker:{requestId,phase:'request.authorized',reason:'broker-approved'}});assert.equal(event.broker.requestId,requestId);
|
|
});
|
|
|
|
test('protected deployment CA bundle rejects noncertificate payload and preserves default roots',()=>{
|
|
const {deploymentRoots}=require('../apps/network-broker/production-factory.cjs'),fs=require('node:fs'),tls=require('node:tls');const cert=fs.readFileSync(new URL('./fixtures/icap/cert.pem',import.meta.url),'utf8');
|
|
assert.equal(deploymentRoots(undefined),undefined);assert.equal(deploymentRoots(cert).length,tls.rootCertificates.length+1);
|
|
for(const value of [cert+'\nPRIVATE PAYLOAD',cert+'\n-----BEGIN PRIVATE KEY-----\nsecret\n-----END PRIVATE KEY-----',Array(33).fill(cert).join('\n'),'-----BEGIN CERTIFICATE-----\nbroken\n-----END CERTIFICATE-----'])assert.throws(()=>deploymentRoots(value),/broker-bootstrap-invalid/);
|
|
});
|
|
|
|
test('startup diagnosis is one bounded fixed stage without original exception detail',async()=>{
|
|
const channel=new Channel();runProductionProcess(async()=>{throw Error('broker-startup-transport');},channel);channel.emit('message',start());await tick();assert.equal(channel.sent.length,1);assert.equal(channel.sent[0].type,'startup-fault');assert.equal(channel.sent[0].code,'broker-startup-transport');assert.equal(channel.connected,false);
|
|
const unsafe=new Channel();runProductionProcess(async()=>{throw Error('private certificate secret');},unsafe);unsafe.emit('message',start());await tick();assert.equal(unsafe.sent[0].code,'broker-startup-failed');assert.equal(JSON.stringify(unsafe.sent).includes('private certificate'),false);
|
|
});
|
|
|
|
test('additive deployment roots satisfy actual strict HTTPS transport construction',()=>{
|
|
const {deploymentRoots}=require('../apps/network-broker/production-factory.cjs'),{createHttpsForwarder}=require('../apps/network-broker/https-forwarder.cjs'),fs=require('node:fs');
|
|
const roots=deploymentRoots(fs.readFileSync(new URL('./fixtures/icap/cert.pem',import.meta.url),'utf8'));
|
|
assert.equal(typeof createHttpsForwarder({authority:'current',current:()=>true,resolveRoute:async()=>({approved:true,address:'127.0.0.1',port:443}),ca:roots.join('\n')}),'function');
|
|
});
|
|
|
|
test('signed proxy and protected network mode cannot silently diverge or bypass',async()=>{
|
|
const keys=generateKeyPairSync('ed25519'),p=defaultPolicy();p.assignment={deviceId:'device',groups:[]};p.proxy={mode:'fixed_servers',rules:'http://proxy.example',bypass:'*'};
|
|
const config={publicKey:keys.publicKey.export({type:'spki',format:'pem'}),deviceId:'device',minimumVersion:1,envelope:signPolicy(p,keys.privateKey.export({type:'pkcs8',format:'pem'})),brokerPort:43100,network:{mode:'fixed_servers',dns:'system',credentials:{username:'synthetic',password:'synthetic-password'}}},callbacks={persistAudit:async()=>{},acknowledgeCertificate:async()=>true,expectedPolicyVersion:1};
|
|
await assert.rejects(createProductionBroker(config,callbacks),/broker-route-unsupported/);await assert.rejects(createProductionBroker({...config,network:{mode:'direct',dns:'system'}},callbacks),/broker-route-unsupported/);
|
|
await assert.rejects(createProductionBroker({...config,network:{...config.network,credentials:{username:'bad:username',password:'synthetic'}}},callbacks),/broker-bootstrap-invalid/);
|
|
});
|
|
|
|
test('protected additive corporate CA bundles satisfy both strict transport constructors',()=>{
|
|
const {deploymentRoots}=require('../apps/network-broker/production-factory.cjs'),{createProxyForwarder}=require('../apps/network-broker/proxy-forwarder.cjs'),fs=require('node:fs');const ca=deploymentRoots(fs.readFileSync(new URL('./fixtures/icap/cert.pem',import.meta.url),'utf8')).join('\n');
|
|
assert.equal(typeof createProxyForwarder({authority:'current',current:()=>true,proxyPolicy:{mode:'fixed_servers',rules:'https://proxy.example:443',bypass:''},resolveProxyRoute:async()=>({approved:true,address:'127.0.0.1',port:443}),ca,proxyCa:ca}),'function');
|
|
});
|
|
test('unsupported secure DNS fails before inspector startup',async()=>{
|
|
const keys=generateKeyPairSync('ed25519');
|
|
for(const proxy of [false]){const p=defaultPolicy();p.assignment={deviceId:'device',groups:[]};p.enterprise.dns={mode:'secure',templates:['https://resolver.example/dns-query']};
|
|
const config={publicKey:keys.publicKey.export({type:'spki',format:'pem'}),deviceId:'device',minimumVersion:1,envelope:signPolicy(p,keys.privateKey.export({type:'pkcs8',format:'pem'})),brokerPort:43100,network:{mode:proxy?'fixed_servers':'direct',dns:'system'}};
|
|
await assert.rejects(createProductionBroker(config,{persistAudit:async()=>{},acknowledgeCertificate:async()=>true,expectedPolicyVersion:1}),/broker-route-unsupported/);
|
|
}
|
|
});
|