Add packaged DLP broker and protected Windows service foundation
This commit is contained in:
1 parent
95d23f4e0b
commit
7b5057901a
140 files changed
+11215
-60
No files matched your search
@@ -0,0 +1,55 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import {fork} from 'node:child_process';
|
||||
import {once} from 'node:events';
|
||||
import fs from 'node:fs';
|
||||
import https from 'node:https';
|
||||
import http from 'node:http';
|
||||
import tls from 'node:tls';
|
||||
import net from 'node:net';
|
||||
import {fileURLToPath} from 'node:url';
|
||||
const cert=fs.readFileSync(new URL('./fixtures/icap/cert.pem',import.meta.url)),key=fs.readFileSync(new URL('./fixtures/icap/key.pem',import.meta.url));
|
||||
const brokerNode=fileURLToPath(new URL('../.runtime/windows/broker/node.exe',import.meta.url));
|
||||
function child(t) {
|
||||
assert.equal(fs.existsSync(brokerNode),true,'Verified bundled broker Node prerequisite');
|
||||
const process=fork(fileURLToPath(new URL('./fixtures/broker-process-child.cjs',import.meta.url)),[],{execPath:brokerNode,execArgv:[],stdio:['ignore','ignore','ignore','ipc'],env:{SystemRoot:globalThis.process.env.SystemRoot}});
|
||||
t.after(()=>{if(process.exitCode===null)process.kill();});return process;
|
||||
}
|
||||
function bounded(event,ms=3000){let timer;return Promise.race([event,new Promise((_,reject)=>timer=setTimeout(()=>reject(Error('test-deadline')),ms))]).finally(()=>clearTimeout(timer));}
|
||||
async function request(ready,destination,body) {
|
||||
return await new Promise((resolve,reject)=>{
|
||||
const req=http.request({host:'127.0.0.1',port:ready.port,method:'CONNECT',path:destination,headers:{host:destination,'proxy-authorization':`Bearer ${ready.capability}`}});
|
||||
req.setTimeout(2000,()=>req.destroy(Error('test-deadline')));req.on('error',reject);
|
||||
req.on('connect',(res,socket)=>{
|
||||
if(res.statusCode!==200){socket.destroy();reject(Error('connect-rejected'));return;}
|
||||
const secure=tls.connect({socket,servername:'localhost',ca:cert,rejectUnauthorized:true});let text='';
|
||||
secure.setTimeout(2000,()=>secure.destroy(Error('test-deadline')));secure.on('error',reject);secure.on('secureConnect',()=>secure.write(`POST /submit HTTP/1.1\r\nHost: ${destination}\r\nContent-Length: ${Buffer.byteLength(body)}\r\nConnection: close\r\n\r\n${body}`));secure.on('data',chunk=>text+=chunk);secure.on('end',()=>{secure.destroy();resolve(text);});
|
||||
});req.end();
|
||||
});
|
||||
}
|
||||
test('separate Node child handles inspected CONNECT and closes on private IPC disconnect',async t=>{
|
||||
const received=[];let connections=0;
|
||||
const target=https.createServer({key,cert},async(req,res)=>{const chunks=[];for await(const chunk of req)chunks.push(chunk);received.push(Buffer.concat(chunks).toString());res.writeHead(201);res.end('accepted');});
|
||||
target.on('connection',()=>connections++);target.listen(0,'127.0.0.1');await once(target,'listening');
|
||||
t.after(async()=>{target.closeAllConnections();await new Promise(r=>target.close(r));});
|
||||
const destination=`localhost:${target.address().port}`,worker=child(t);
|
||||
const readyEvent=bounded(once(worker,'message'));worker.send({type:'start',config:{destination}});
|
||||
const [ready]=await readyEvent;assert.equal(ready.type,'ready');assert.notEqual(worker.pid,process.pid);assert.match(ready.capability,/^[a-zA-Z0-9_-]{43}$/);
|
||||
assert.match(await request(ready,destination,'SECRET'),/^HTTP\/1.1 403/);assert.equal(connections,0);
|
||||
assert.match(await request(ready,destination,'public'),/^HTTP\/1.1 201/);assert.deepEqual(received,['public']);
|
||||
const exit=bounded(once(worker,'exit'));worker.disconnect();await exit;
|
||||
const failure=await new Promise(resolve=>{const socket=net.connect(ready.port,'127.0.0.1');socket.setTimeout(1000,()=>{socket.destroy();resolve('timeout');});socket.on('connect',()=>{socket.destroy();resolve('connected');});socket.on('error',error=>resolve(error.code));});
|
||||
assert.equal(failure,'ECONNREFUSED');
|
||||
});
|
||||
test('invalid and oversized private bootstrap reject without spawning another process',async t=>{
|
||||
for(const message of [{type:'command',command:'arbitrary'},{type:'start',config:{destination:'x'.repeat(70000)}}]) {
|
||||
const worker=child(t),reply=bounded(once(worker,'message')),exit=bounded(once(worker,'exit'));
|
||||
worker.send(message);assert.deepEqual((await reply)[0],{type:'error',code:'invalid-message'});await exit;
|
||||
}
|
||||
});
|
||||
test('explicit private stop closes the sole child without restart',async t=>{
|
||||
const worker=child(t),ready=bounded(once(worker,'message'));
|
||||
worker.send({type:'start',config:{destination:'localhost:443'}});assert.equal((await ready)[0].type,'ready');
|
||||
const exit=bounded(once(worker,'exit'));worker.send({type:'stop'});await exit;
|
||||
assert.equal(worker.exitCode,0);
|
||||
});
|
||||
Reference in new issue
Block a user