import test from 'node:test'; import assert from 'node:assert/strict'; import https from 'node:https'; import fs from 'node:fs'; import {once} from 'node:events'; import {createRequire} from 'node:module'; const require=createRequire(import.meta.url),{createHttpsForwarder}=require('../apps/network-broker/https-forwarder.cjs'); 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)); async function fixture(t,handler,configuration={}){ const received=[];let connections=0; const server=https.createServer({key,cert},async(req,res)=>{const chunks=[];for await(const chunk of req)chunks.push(chunk);received.push({body:Buffer.concat(chunks).toString(),headers:req.headers,path:req.url});handler?.(req,res)||(!handler&&res.end('public response'));}); server.on('connection',()=>connections++);server.on('tlsClientError',()=>{}); server.listen(0,'127.0.0.1');await once(server,'listening'); t.after(async()=>{server.closeAllConnections();await new Promise(resolve=>server.close(resolve));}); const port=server.address().port; const forward=createHttpsForwarder({authority:'policy-1',current:()=>true,resolveRoute:async destination=>({approved:true,address:'127.0.0.1',port:destination.port}),ca:cert,deadlineMs:1000,...configuration}); const request={method:'POST',url:`https://localhost:${port}/submit`,headers:{'content-type':'text/plain'}}; return {request,forward,received,get connections(){return connections;}}; } test('numeric approved route uses original TLS hostname and strips proxy credentials',async t=>{ let release,entered;const wait=new Promise(resolve=>release=resolve),started=new Promise(resolve=>entered=resolve); const f=await fixture(t,undefined,{resolveRoute:async destination=>{entered();await wait;return {approved:true,address:'127.0.0.1',port:destination.port};}}); const body=Buffer.from('public original'); f.request.headers['proxy-authorization']='Bearer PRIVATE_PROXY_KEY';f.request.headers.connection='x-hop';f.request.headers['x-hop']='PRIVATE_HOP_VALUE'; const pending=f.forward({request:f.request,body});await started;body.fill(120);release(); const result=await pending;assert.equal(result.body.toString(),'public response'); assert.equal(f.received[0].body,'public original');assert.equal(f.received[0].headers['proxy-authorization'],undefined);assert.equal(f.received[0].headers['x-hop'],undefined); assert.equal(f.connections,1); }); test('revocation during route resolution prevents any upstream TCP dial',async t=>{ let valid=true;const f=await fixture(t,undefined,{current:()=>valid,resolveRoute:async destination=>{valid=false;return {approved:true,address:'127.0.0.1',port:destination.port};}}); await assert.rejects(f.forward({request:f.request,body:Buffer.from('public')}),{code:'authority-expired'}); assert.equal(f.connections,0); }); test('unapproved and nonnumeric routes cannot invoke system DNS or dial',async t=>{ const f=await fixture(t,undefined,{resolveRoute:async destination=>({approved:true,address:'localhost',port:destination.port})}); await assert.rejects(f.forward({request:f.request,body:Buffer.alloc(0)}),{code:'route-unapproved'});assert.equal(f.connections,0); }); test('revocation after TLS establishment prevents HTTP headers and payload release',async t=>{ let checks=0;const f=await fixture(t,undefined,{current:()=>++checks<4}); await assert.rejects(f.forward({request:f.request,body:Buffer.from('must never transmit')}),{code:'authority-expired'}); assert.equal(f.connections,1);assert.equal(f.received.length,0); }); test('untrusted server certificate is rejected with fixed error and no retry',async t=>{ const f=await fixture(t,undefined,{ca:undefined}); await assert.rejects(f.forward({request:f.request,body:Buffer.from('public')}),{code:'upstream-failed'}); assert.equal(f.connections,1);assert.equal(f.received.length,0); }); test('approved numeric route does not substitute certificate hostname',async t=>{ const f=await fixture(t); await assert.rejects(f.forward({request:{...f.request,url:f.request.url.replace('localhost','wrong-host.example')},body:Buffer.alloc(0)}),{code:'upstream-failed'}); assert.equal(f.connections,1);assert.equal(f.received.length,0); }); test('oversized streamed response is destroyed at configured limit',async t=>{ const f=await fixture(t,(_req,res)=>{res.write('1234');res.end('56789');},{maxResponseBytes:8}); await assert.rejects(f.forward({request:f.request,body:Buffer.alloc(0)}),{code:'upstream-response-too-large'}); assert.equal(f.connections,1); }); test('redirect response is returned without following location',async t=>{ const f=await fixture(t,(_req,res)=>{res.writeHead(302,{location:'/private'});res.end('redirect');}); const result=await f.forward({request:f.request,body:Buffer.alloc(0)}); assert.equal(result.statusCode,302);assert.equal(result.headers.location,'/private');assert.equal(f.received.length,1);assert.equal(f.received[0].path,'/submit'); }); test('abort during route resolution prevents dial and does not retry',async t=>{ let entered,release;const started=new Promise(resolve=>entered=resolve),wait=new Promise(resolve=>release=resolve); const f=await fixture(t,undefined,{resolveRoute:async destination=>{entered();await wait;return {approved:true,address:'127.0.0.1',port:destination.port};}}); const controller=new AbortController(),pending=f.forward({request:f.request,body:Buffer.alloc(0),signal:controller.signal}); await started;controller.abort();await assert.rejects(pending,{code:'upstream-cancelled'});release();assert.equal(f.connections,0); }); test('wall clock timeout terminates a stalled upstream connection',async t=>{ const f=await fixture(t,()=>{}, {deadlineMs:30}); await assert.rejects(f.forward({request:f.request,body:Buffer.alloc(0)}),{code:'upstream-cancelled'}); assert.ok(f.connections<=1); }); test('timed out unresolved routes retain bounded underlying resolution admission',async t=>{ let calls=0;const pending=[]; const f=await fixture(t,undefined,{deadlineMs:15,maxPendingResolutions:2,resolveRoute:destination=>{ calls++;return new Promise(resolve=>pending.push(()=>resolve({approved:true,address:'127.0.0.1',port:destination.port}))); }}); for(let index=0;index<2;index++)await assert.rejects(f.forward({request:f.request,body:Buffer.alloc(0)}),{code:'upstream-cancelled'}); for(let index=0;index<3;index++)await assert.rejects(f.forward({request:f.request,body:Buffer.alloc(0)}),{code:'route-capacity-exhausted'}); assert.equal(calls,2);assert.equal(f.connections,0); pending[0]();await new Promise(resolve=>setImmediate(resolve)); await assert.rejects(f.forward({request:f.request,body:Buffer.alloc(0)}),{code:'upstream-cancelled'}); assert.equal(calls,3);assert.equal(f.connections,0); for(const release of pending)release(); }); test('invalid destinations and methods reject before resolver invocation',async t=>{ let resolutions=0;const f=await fixture(t,undefined,{resolveRoute:async()=>{resolutions++;}}); for(const change of [{url:'http://localhost/'},{url:'https://user:password@localhost/'},{url:'https://localhost/#fragment'},{url:'https://127.1/'},{method:'CONNECT'}])await assert.rejects(f.forward({request:{...f.request,...change},body:Buffer.alloc(0)}),{code:'invalid-forward-request'}); assert.equal(resolutions,0);assert.equal(f.connections,0); });