Files
safetynet/apps/network-broker/windows-wfp-plan.cjs
T

55 lines
4.8 KiB
JavaScript

'use strict';
// Planner only: it never opens BFE, installs filters, or changes Windows policy.
// GUIDs checked against Windows SDK 10.0.26100.0 fwpmu.h.
// https://learn.microsoft.com/en-us/windows/win32/fwp/filtering-condition-identifiers-
// https://learn.microsoft.com/en-us/windows/win32/fwp/filtering-conditions-available-at-each-filtering-layer
// https://learn.microsoft.com/en-us/windows/win32/fwp/filter-arbitration
const GUIDS=Object.freeze({
connectV4:'c38d57d1-05a7-4c33-904f-7fbceee60e82',connectV6:'4a72393b-319f-44bc-84c3-ba54dcb3b6b4',
appId:'d78e1e87-8644-4ea5-9437-d809ecefc971',protocol:'3971ef2b-623e-4f9a-8cb1-6e79b806b9a7',
remoteAddress:'b235ae9a-1d64-49b8-a44c-5ff3d9095045',remotePort:'c35a604d-d22b-4e1a-91b4-68f674ee674b',
recvV4:'e1cd9fe7-f4b5-4273-96c0-592e487b8650',recvV6:'a3b42c97-9f04-4672-b87e-cee9c483257f',
localAddress:'d9ee00de-c1ef-4617-bfe3-ffd8f5a08957',localPort:'0c1ba1af-5765-453f-af22-a8f791ac775b'
});
function condition(field,match,type,value){return Object.freeze({field:GUIDS[field],match,type,value});}
function planBrowserNetworkBoundary({appId,brokerPort}={}){
// App ID must come from FwpmGetAppIdFromFileName0 for the verified installed EXE.
// Immutable hex prevents callers mutating a Buffer after planning.
if(!Buffer.isBuffer(appId)||appId.length<8||appId.length>65536||appId.length%2||!Number.isInteger(brokerPort)||brokerPort<1||brokerPort>65535)throw new TypeError('Invalid browser network boundary');
const devicePath=appId.toString('utf16le');
if(!/^\\device\\harddiskvolume[0-9]+\\[^\0]+\.exe\0$/.test(devicePath)||devicePath!==devicePath.toLowerCase()||devicePath.slice(0,-1).split('\\').some(part=>part==='.'||part==='..')||devicePath.includes(':'))throw new TypeError('Native canonical application ID required');
const applicationId=appId.toString('hex');
const identity=condition('appId','equal','FWP_BYTE_BLOB_TYPE',applicationId);
const tcp=condition('protocol','equal','FWP_UINT8',6);
const localhost=condition('remoteAddress','equal','FWP_UINT32',0x7f000001);
function rule(id,layer,conditions){return Object.freeze({id,layer,action:'FWP_ACTION_BLOCK',clearActionRight:false,conditions:Object.freeze([identity,...conditions])});}
// Disjoint deny predicates cover the complement of the one approved tuple.
// No PERMIT filter: existing corporate firewall blocks retain their veto.
const filters=Object.freeze([
rule('v4-non-tcp',GUIDS.connectV4,[condition('protocol','not-equal','FWP_UINT8',6)]),
rule('v4-tcp-non-broker-address',GUIDS.connectV4,[tcp,condition('remoteAddress','not-equal','FWP_UINT32',0x7f000001)]),
rule('v4-tcp-other-local-port',GUIDS.connectV4,[tcp,localhost,condition('remotePort','not-equal','FWP_UINT16',brokerPort)]),
rule('v6-all',GUIDS.connectV6,[]),
// ALE authorizes both directions of an inbound-first flow. Denying only
// CONNECT would allow replies to an accepted hostile UDP/TCP peer.
// Approved outbound broker responses already belong to their CONNECT flow.
rule('browser-v4-incoming',GUIDS.recvV4,[]),
rule('browser-v6-incoming',GUIDS.recvV6,[])
]);
return Object.freeze({version:2,activation:'not-installed',applicationId,devicePath:devicePath.slice(0,-1),broker:Object.freeze({family:4,address:'127.0.0.1',protocol:6,port:brokerPort}),filters,
prerequisites:Object.freeze(['verified-native-app-id','signed-installer-transaction','persistent-owned-provider-and-sublayer','native-filter-enumeration','physical-ipv4-ipv6-and-loopback-tests','physical-inbound-first-tcp-udp-tests','browser-management-inspector-telemetry-and-agent-ipc-migration','browser-local-dns-denial','authenticated-http-only-broker'])});
}
// Separate supplemental plan; the existing six-filter native installer does
// NOT install this plan. It prevents a different executable accepting the
// approved TCP tuple, but does not distinguish instances of reusable node.exe.
// A service-token or continuously protected listener remains mandatory.
function planBrokerListenerProtection({appId,brokerPort}={}){
const validated=planBrowserNetworkBoundary({appId,brokerPort});
const filters=Object.freeze([Object.freeze({id:'broker-v4-other-executable',layer:GUIDS.recvV4,action:'FWP_ACTION_BLOCK',clearActionRight:false,conditions:Object.freeze([
condition('protocol','equal','FWP_UINT8',6),condition('localAddress','equal','FWP_UINT32',0x7f000001),condition('localPort','equal','FWP_UINT16',brokerPort),condition('appId','not-equal','FWP_BYTE_BLOB_TYPE',validated.applicationId)
])})]);
return Object.freeze({version:1,activation:'not-installed',completeBoundary:false,applicationId:validated.applicationId,filters,
prerequisites:Object.freeze(['native-recv-accept-filter-verification','same-executable-instance-protection','physical-port-takeover-tests','corporate-firewall-veto-preserved'])});
}
module.exports={GUIDS,planBrowserNetworkBoundary,planBrokerListenerProtection};