Files
safetynet/tests/broker-wfp-plan.test.mjs

59 lines
4.3 KiB
JavaScript

import test from 'node:test';
import assert from 'node:assert/strict';
import {createRequire} from 'node:module';
const require=createRequire(import.meta.url);
const {GUIDS,planBrowserNetworkBoundary}=require('../apps/network-broker/windows-wfp-plan.cjs');
const identity=Buffer.from('\\device\\harddiskvolume3\\program files\\safetynet\\safetynet.exe\0'.replaceAll('\\\\','\\').replace('\\0','\0'),'utf16le');
function plan(port=43871){return planBrowserNetworkBoundary({appId:identity,brokerPort:port});}
// Independent interpreter of a serialized WFP conjunction: no production matcher.
function matchingFilters(p,packet){return p.filters.filter(rule=>rule.layer===(packet.family===4?GUIDS.connectV4:GUIDS.connectV6)&&rule.conditions.every(c=>{
const values={[GUIDS.appId]:packet.appId,[GUIDS.protocol]:packet.protocol,[GUIDS.remoteAddress]:packet.address,[GUIDS.remotePort]:packet.port};
if(c.match==='equal')return values[c.field]===c.value;
if(c.match==='not-equal')return values[c.field]!==c.value;
throw Error('Unsupported match');
}));}
test('only approved IPv4 TCP broker tuple survives exhaustive port/protocol edges',()=>{
const p=plan();
for(const address of [0,0x7f000001,0x7f000002,0x7fffffff,0x0a000001,0xffffffff]){
for(const protocol of [0,1,6,17,58,255]){
for(const port of [0,1,53,80,443,3478,43870,43871,43872,65535]){
const matches=matchingFilters(p,{family:4,address,protocol,port,appId:p.applicationId});
const approved=address===0x7f000001&&protocol===6&&port===43871;
assert.equal(matches.length,approved?0:1,JSON.stringify({address,protocol,port}));
}
}
}
for(let port=0;port<=65535;port++)assert.equal(matchingFilters(p,{family:4,address:0x7f000001,protocol:6,port,appId:p.applicationId}).length,port===43871?0:1);
});
test('all IPv6 including mapped loopback is denied, unrelated applications untouched',()=>{
const p=plan();
for(const address of ['::1','::ffff:127.0.0.1','fe80::1','2001:db8::1'])for(const protocol of [6,17,58]){
assert.equal(matchingFilters(p,{family:6,address,protocol,port:43871,appId:p.applicationId}).length,1);
assert.equal(matchingFilters(p,{family:6,address,protocol,port:43871,appId:'other-app'}).length,0);
}
assert.equal(matchingFilters(p,{family:4,address:0x08080808,protocol:17,port:53,appId:'other-app'}).length,0);
});
test('plan contains only deny rules and never overrides enterprise veto or activates BFE',()=>{
const p=plan();assert.equal(p.activation,'not-installed');assert.ok(p.filters.every(f=>f.action==='FWP_ACTION_BLOCK'&&f.clearActionRight===false));
assert.ok(Object.isFrozen(p)&&Object.isFrozen(p.filters)&&p.filters.every(f=>Object.isFrozen(f.conditions)));
const copy=Buffer.from(identity);const q=planBrowserNetworkBoundary({appId:copy,brokerPort:43871});copy.fill(0);assert.equal(q.applicationId,identity.toString('hex'));
});
test('rejects invalid ports and noncanonical or arbitrary executable identities',()=>{
for(const port of [0,65536,1.5,'443',NaN])assert.throws(()=>plan(port));
for(const path of ['C:\\safetynet.exe\0','\\device\\harddiskvolume3\\..\\a.exe\0','\\device\\harddiskvolume3\\A.exe\0','\\device\\harddiskvolume3\\a.exe','\\device\\harddiskvolume3\\a.exe:stream\0'])assert.throws(()=>planBrowserNetworkBoundary({appId:Buffer.from(path,'utf16le'),brokerPort:123}));
});
test('inbound-first flows are denied for the browser in both families',()=>{
const p=plan();assert.equal(p.version,2);assert.equal(p.filters.length,6);
for(const layer of [GUIDS.recvV4,GUIDS.recvV6])for(const protocol of [1,6,17,58,255])for(const port of [0,53,43871,65535]){
const packet={[GUIDS.appId]:p.applicationId,[GUIDS.protocol]:protocol,[GUIDS.localPort]:port};
const matches=p.filters.filter(rule=>rule.layer===layer&&rule.conditions.every(c=>c.match==='equal'?packet[c.field]===c.value:packet[c.field]!==c.value));
assert.equal(matches.length,1);assert.equal(matches[0].action,'FWP_ACTION_BLOCK');
assert.equal(matches[0].conditions.length,1);
}
// Model the documented ALE stateful direction: an approved outbound CONNECT
// creates its flow; its reply is not a NEW incoming accept authorization.
assert.equal(matchingFilters(p,{family:4,address:0x7f000001,protocol:6,port:43871,appId:p.applicationId}).length,0);
assert.ok(p.prerequisites.includes('physical-inbound-first-tcp-udp-tests'));
assert.ok(p.prerequisites.includes('browser-management-inspector-telemetry-and-agent-ipc-migration'));
});