Files
safetynet/scripts/enterprise-browser-smoke.mjs
T
2026-10-13 10:34:48 +02:00

25 lines
4.8 KiB
JavaScript

import assert from 'node:assert/strict';
import {createRequire} from 'node:module';
import {createServer} from 'node:http';
import net from 'node:net';
import {mkdtempSync,mkdirSync,writeFileSync} from 'node:fs';
import {tmpdir} from 'node:os';
import {join} from 'node:path';
import {spawn} from 'node:child_process';
import {randomBytes} from 'node:crypto';
import {createApp} from '../apps/server/app.mjs';
import {openStore,digest} from '../apps/server/store.mjs';
const electron=createRequire(import.meta.url)('electron'),temporary=mkdtempSync(join(tmpdir(),'safetynet-enterprise-proof-'));
const marker='PUBLIC_MALWARE_FIXTURE',clean=Buffer.from([0,1,2,3,65,66,67,255]);
const report={local:true,icapScans:0,icapBlocks:0,posts:0,websiteRequests:0};
const sockets=new Set();let icap,sink,management,store,child;
try{
icap=net.createServer(socket=>{sockets.add(socket);socket.on('error',()=>{});socket.on('close',()=>sockets.delete(socket));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, REQMOD\r\nISTag: "local-fixture"\r\nAllow: 204\r\nEncapsulated: null-body=0\r\n\r\n');report.icapScans++;if(input.includes(Buffer.from(marker))){report.icapBlocks++;socket.end('ICAP/1.0 200 OK\r\nISTag: "local-fixture"\r\nEncapsulated: res-hdr=0, res-body=99\r\n\r\nHTTP/1.1 403 Forbidden');}else socket.end('ICAP/1.0 204 No Content\r\nISTag: "local-fixture"\r\n\r\n');});}).listen(0,'127.0.0.1');await new Promise(r=>icap.once('listening',r));
sink=createServer((req,res)=>{if(req.url==='/proof-stats'){res.setHeader('content-type','application/json');return res.end(JSON.stringify(report));}report.websiteRequests++;if(req.method==='POST'){const chunks=[];req.on('data',c=>chunks.push(c));req.on('end',()=>{report.posts++;assert.equal(Buffer.concat(chunks).includes(Buffer.from(marker)),false,'Blocked fixture reached receiver');res.end('accepted');});return;}if(req.url.startsWith('/download/')){const bytes=req.url.endsWith('/blocked')?Buffer.from(marker):clean;res.setHeader('content-disposition','attachment; filename="fixture.bin"');res.setHeader('content-type','application/octet-stream');res.setHeader('content-length',bytes.length);return res.end(bytes);}res.setHeader('content-type','text/html');res.end('<!doctype html><title>Enterprise native proof</title><h1>Public local fixture</h1>');}).listen(0,'127.0.0.1');await new Promise(r=>sink.once('listening',r));
const code=randomBytes(32).toString('base64url'),adminToken=randomBytes(32).toString('base64url');store=openStore(join(temporary,'store'));const p=store.get('policy');p.browser.homeUrl=`http://127.0.0.1:${sink.address().port}`;p.browser.allowedHosts=['127.0.0.1'];p.browser.allowDownloads=true;p.dlp.rules=[];p.security.yara.enabled=false;p.security.yara.rules=[];p.proxy.mode='system';p.enterprise.icap={...p.enterprise.icap,enabled:true,url:`icap://127.0.0.1:${icap.address().port}/av`};store.put('policy',p);store.db.prepare('INSERT INTO enrollments VALUES(?,?,?)').run(digest(code),Date.now()+600000,'synthetic-enterprise-user');
const config={publicUrl:'http://127.0.0.1',secure:false,adminToken,requirePQManagement:true};management=createApp(store,config).listen(0,'127.0.0.1');await new Promise(r=>management.once('listening',r));config.publicUrl=`http://127.0.0.1:${management.address().port}`;
const env={...process.env,SAFETYNET_ENTERPRISE_PROFILE:join(temporary,'browser'),SAFETYNET_ENTERPRISE_SERVER:config.publicUrl,SAFETYNET_ENTERPRISE_CODE:code,SAFETYNET_ENTERPRISE_ADMIN:adminToken,SAFETYNET_ENTERPRISE_SINK:p.browser.homeUrl,SAFETYNET_ENTERPRISE_FILE:join(temporary,'released.bin'),SAFETYNET_SMOKE_USERDATA:join(temporary,'browser')};delete env.ELECTRON_RUN_AS_NODE;
child=spawn(electron,['scripts/enterprise-browser-smoke.cjs'],{windowsHide:true,stdio:'inherit',env});const timer=setTimeout(()=>child.kill(),120000);const status=await new Promise(r=>{child.once('exit',c=>r(c??1));child.once('error',()=>r(1));});clearTimeout(timer);assert.equal(status,0,'Native enterprise fixture failed');assert.equal(report.posts,1);assert.ok(report.icapBlocks>=2);Object.assign(report,{nativeBrowserPassed:true,icapOnlyUpload:true,icapBeforeDownloadDialog:true,requiredVpnPaused:true,remotePolicyRecovery:true,managementProtectedChannel:true,managementProxyParity:true});mkdirSync('artifacts',{recursive:true});writeFileSync('artifacts/enterprise-browser-proof.json',JSON.stringify(report,null,2));console.log(JSON.stringify(report));
}finally{child?.kill();for(const socket of sockets)socket.destroy();for(const server of [management,sink,icap])if(server){server.closeAllConnections?.();await new Promise(r=>server.close(r));}store?.db.close();}