Files
safetynet/apps/browser/enterprise-inspection.cjs
T

47 lines
4.0 KiB
JavaScript
Raw Normal View History

2026-10-13 10:33:31 +02:00
'use strict';
const fs=require('node:fs');
const {createHash}=require('node:crypto');
const {detectType,inspectFileTransfer}=require('../../packages/core/file-transfer.cjs');
const CHANNELS={upload:'scanUploads',download:'scanDownloads',clipboard:'scanClipboard',ai:'scanAI'};
function icapRequired(policy,channel){if(!Object.hasOwn(CHANNELS,channel))throw Error('Unsupported enterprise inspection channel');const settings=policy.enterprise?.icap;return !!settings?.enabled&&settings[CHANNELS[channel]]!==false;}
function deploymentTLS(){
const options={};for(const [key,name] of [['ca','SAFETYNET_ICAP_CA_FILE'],['cert','SAFETYNET_ICAP_CERT_FILE'],['key','SAFETYNET_ICAP_KEY_FILE']]){if(process.env[name]){const stat=fs.statSync(process.env[name]);if(!stat.isFile()||stat.size>131072)throw Error('Enterprise scanner certificate configuration unavailable');options[key]=fs.readFileSync(process.env[name]);}}
if(!!options.cert!==!!options.key)throw Error('Enterprise scanner client identity incomplete');return options;
}
async function scanEnterprise(bytes,policy,channel,scanner){
const digest=createHash('sha256').update(bytes).digest('hex');
const base={engine:'icap',sha256:digest,bytes:bytes.length,coverage:{complete:false},decision:'block',ruleId:'icap-unavailable'};
if(!icapRequired(policy,channel))return {...base,engine:'not-required',decision:'allow',ruleId:null,coverage:{complete:true}};
const snapshot=Buffer.from(bytes);
try{
const result=await (scanner||require('./icap-client.cjs').scanIcap)(snapshot,policy.enterprise.icap,deploymentTLS());
if(result.decision==='allow'&&result.coverage?.complete===true&&result.sha256===digest&&result.bytes===snapshot.length&&createHash('sha256').update(snapshot).digest('hex')===digest)return {...base,...result};
return {...base,ruleId:typeof result.ruleId==='string'&&/^icap-[a-z0-9-]{1,80}$/.test(result.ruleId)?result.ruleId:'icap-inspection-incomplete'};
}catch{return base;}
finally{snapshot.fill(0);}
}
async function scanDownloadEnterprise(bytes,policy,localInspector,scanner){
const digest=createHash('sha256').update(bytes).digest('hex');
const blocked=ruleId=>({decision:'block',ruleId,engine:'enterprise',sha256:digest,bytes:bytes.length,coverage:{complete:false}});
const fileVerdict=inspectFileTransfer(bytes,policy,'download');if(fileVerdict.decision!=='allow')return blocked(fileVerdict.ruleId);
const noAv=!policy.security?.yara.enabled&&!icapRequired(policy,'download');
if(noAv&&!policy.fileTransfers?.download?.allowWithoutMalwareScanner)return blocked('malware-scanner-required');
// Antivirus clean does not classify company secrets. Supported documents always
// require the contained DLP parser; explicit no-antivirus mode still requires DLP.
if(noAv||(policy.fileTransfers?.download?.requireDocumentDlp!==false&&(policy.dlp?.semantic?.enabled||['pdf','zip','ole','png','jpeg','gif','webp','text'].includes(detectType(bytes))))){
2026-10-13 10:33:31 +02:00
if(!localInspector?.inspect)return blocked('download-dlp-unavailable');
let dlp;try{dlp=await localInspector.inspect(bytes,policy);}catch{return blocked('download-dlp-unavailable');}
if(dlp.decision!=='allow')return blocked(typeof dlp.ruleId==='string'?dlp.ruleId:'download-dlp-blocked');
if(dlp.coverage?.complete!==true)return blocked('download-dlp-incomplete');
}
if(noAv)return {decision:'allow',ruleId:null,engine:'explicit-no-antivirus',sha256:digest,bytes:bytes.length,coverage:{complete:true}};
if(policy.security?.yara.enabled){
if(!localInspector)return blocked('yara-unavailable');
let local;try{local=await localInspector.scanDownload(bytes,policy);}catch{return blocked('yara-unavailable');}
if(local.decision!=='allow')return local;
if(local.coverage?.complete!==true||local.sha256!==digest||local.bytes!==bytes.length)return blocked('yara-inspection-incomplete');
if(!icapRequired(policy,'download'))return local;
}
return scanEnterprise(bytes,policy,'download',scanner);
}
module.exports={icapRequired,scanEnterprise,scanDownloadEnterprise,deploymentTLS};