Files
safetynet/tests/enterprise-gateway.test.mjs
T

28 lines
4.4 KiB
JavaScript
Raw Normal View History

2026-10-13 10:33:31 +02:00
import test from 'node:test';
import assert from 'node:assert/strict';
import net from 'node:net';
import {createServer} from 'node:http';
import {mkdtempSync,rmSync} from 'node:fs';
import {tmpdir} from 'node:os';
import {join} from 'node:path';
import {createApp} from '../apps/server/app.mjs';
import {openStore,digest} from '../apps/server/store.mjs';
test('central ICAP capability probe sends no content; gateway cannot bypass external scanning',async t=>{
const directory=mkdtempSync(join(tmpdir(),'safetynet-enterprise-gateway-')),store=openStore(directory),adminToken='public-fixture-administrator-0123456789';
const marker='PUBLIC_AV_TEST_MARKER';let scans=0,upstreamRequests=0,infectAnswer=false;const sockets=new Set();
const icap=net.createServer(socket=>{sockets.add(socket);socket.once('close',()=>sockets.delete(socket));socket.on('error',()=>{});let input=Buffer.alloc(0),done=false;socket.on('data',chunk=>{input=Buffer.concat([input,chunk]);if(done||input.indexOf('\r\n\r\n')<0)return;const options=input.subarray(0,7).toString()==='OPTIONS';if(!options&&!input.subarray(-5).equals(Buffer.from('0\r\n\r\n')))return;done=true;if(options)return socket.end('ICAP/1.0 200 OK\r\nMethods: RESPMOD\r\nAllow: 204\r\nISTag: "fixture"\r\nEncapsulated: null-body=0\r\n\r\n');scans++;socket.end(input.includes(Buffer.from(marker))?'ICAP/1.0 200 OK\r\nEncapsulated: res-hdr=0, res-body=99\r\n\r\nHTTP/1.1 403 Forbidden':'ICAP/1.0 204 No Content\r\nISTag: "fixture"\r\n\r\n');});}).listen(0,'127.0.0.1');await new Promise(r=>icap.once('listening',r));
const upstream=createServer((req,res)=>{upstreamRequests++;req.resume();res.setHeader('content-type','application/json');res.end(JSON.stringify({choices:[{message:{content:infectAnswer?marker:'Public safe answer'}}]}));}).listen(0,'127.0.0.1');await new Promise(r=>upstream.once('listening',r));
const policy=store.get('policy');policy.ai={...policy.ai,enabled:true,trustMode:'local',model:'public-fixture-model'};policy.enterprise.icap={...policy.enterprise.icap,enabled:true,url:`icap://127.0.0.1:${icap.address().port}/av`,timeoutMs:1000};store.put('policy',policy);
const config={publicUrl:'http://127.0.0.1',secure:false,adminToken,requirePQManagement:true,aiBaseUrl:`http://127.0.0.1:${upstream.address().port}`,aiModel:policy.ai.model,aiEndpointId:'default'};
const server=createApp(store,config).listen(0,'127.0.0.1');await new Promise(r=>server.once('listening',r));const base=`http://127.0.0.1:${server.address().port}`;
t.after(async()=>{for(const socket of sockets)socket.destroy();for(const service of [server,upstream])service.closeAllConnections();await Promise.all([server,upstream,icap].map(service=>new Promise(r=>service.close(r))));store.db.close();assert.ok(directory.startsWith(join(tmpdir(),'safetynet-enterprise-gateway-')));rmSync(directory,{recursive:true,force:true});});
async function api(route,body,token=adminToken){const response=await fetch(base+route,{method:'POST',headers:{authorization:`Bearer ${token}`,'content-type':'application/json'},body:JSON.stringify(body)});return {status:response.status,body:await response.json()};}
const code='public-fixture-one-use-enrollment-code';store.db.prepare('INSERT INTO enrollments VALUES(?,?,?)').run(digest(code),Date.now()+60000,'public-user');const device=(await api('/api/device/enroll',{name:'public-device',platform:'win32',code},'')).body;
assert.equal((await api('/api/admin/integrations/icap/probe',policy.enterprise.icap,device.token)).status,401);
const probe=await api('/api/admin/integrations/icap/probe',policy.enterprise.icap);assert.equal(probe.status,200);assert.equal(probe.body.ready,true);assert.equal(probe.body.contentSent,false);assert.equal(scans,0);
const blocked=await api('/api/device/ai',{prompt:marker},device.token);assert.equal(blocked.status,403);assert.equal(upstreamRequests,0);
const allowed=await api('/api/device/ai',{prompt:'Public benign prompt'},device.token);assert.equal(allowed.status,200);assert.equal(allowed.body.answer,'Public safe answer');assert.equal(upstreamRequests,1);
infectAnswer=true;const response=await api('/api/device/ai',{prompt:'Public second prompt'},device.token);assert.equal(response.status,403);assert.equal(Object.hasOwn(response.body,'answer'),false);
const events=store.db.prepare('SELECT metadata FROM events').all();assert.equal(JSON.stringify(events).includes(marker),false);assert.equal(JSON.stringify(events).includes('Public benign prompt'),false);assert.ok(scans>=5);
});