'use strict'; const {execFile}=require('node:child_process'); const path=require('node:path'); const net=require('node:net'); // No administrator input is interpolated into PowerShell. Adapter matching happens // after bounded JSON parsing; the deployment cannot turn names into shell code. const postureScript="$ErrorActionPreference='Stop'; $dnsRecords=@(Get-DnsClientServerAddress -ErrorAction Stop); $adapters=@(Get-NetAdapter -IncludeHidden | Where-Object {$_.Status -eq 'Up'} | ForEach-Object { $adapter=$_; $dns=@($dnsRecords | Where-Object {$_.InterfaceIndex -eq $adapter.ifIndex} | ForEach-Object {$_.ServerAddresses}); [pscustomobject]@{name=$adapter.Name; dns=$dns} }); ConvertTo-Json -InputObject $adapters -Depth 4 -Compress"; function resolverOptions(dns={mode:'system',templates:[]}){ if(dns.mode==='system')return {enableBuiltInResolver:false,secureDnsMode:'off',secureDnsServers:[]}; if(dns.mode!=='secure'||!Array.isArray(dns.templates)||!dns.templates.length||dns.templates.length>8)throw Error('Managed DNS configuration is invalid'); for(const template of dns.templates){const u=new URL(template.replace('{?dns}',''));if(u.protocol!=='https:'||u.username||u.password||u.hash)throw Error('Managed DNS requires HTTPS templates');} return {enableBuiltInResolver:true,secureDnsMode:'secure',secureDnsServers:[...dns.templates]}; } function evaluatePosture(vpn,adapters){ if(!vpn?.required)return {allowed:true,reason:'not-required'}; if(!Array.isArray(vpn.interfaceNames)||!vpn.interfaceNames.length||!Array.isArray(vpn.requiredDnsServers)||vpn.requiredDnsServers.some(ip=>!net.isIP(ip)))return {allowed:false,reason:'configuration-invalid'}; if(!Array.isArray(adapters)||adapters.length>256)return {allowed:false,reason:'probe-invalid'}; const matches=adapters.filter(a=>typeof a?.name==='string'&&Array.isArray(a.dns)&&vpn.interfaceNames.some(name=>name.toLowerCase()===a.name.toLowerCase())); return matches.some(a=>vpn.requiredDnsServers.every(ip=>a.dns.some(found=>typeof found==='string'&&found.toLowerCase()===ip.toLowerCase())))?{allowed:true,reason:'approved-interface-present'}:{allowed:false,reason:'required-interface-or-dns-missing'}; } function probeWindows({platform=process.platform,run=execFile}={}){ if(platform!=='win32')return Promise.reject(Error('Network posture is available only on Windows')); const executable=path.join(process.env.SystemRoot||'C:\\Windows','System32','WindowsPowerShell','v1.0','powershell.exe'); return new Promise((resolve,reject)=>run(executable,['-NoLogo','-NoProfile','-NonInteractive','-Command',postureScript],{windowsHide:true,timeout:10000,maxBuffer:65536,encoding:'utf8'},(error,stdout)=>{ if(error)return reject(Error('Network posture probe unavailable')); try{const value=JSON.parse(stdout.replace(/^\uFEFF/,''));if(!Array.isArray(value))throw Error();resolve(value);}catch{reject(Error('Network posture response invalid'));} })); } function createNetworkPosture({probe=probeWindows}={}){ let pending; return {async check(vpn){if(!vpn?.required)return {allowed:true,reason:'not-required'};if(!pending)pending=Promise.resolve().then(probe).finally(()=>{pending=null;});try{return evaluatePosture(vpn,await pending);}catch{return {allowed:false,reason:'probe-unavailable'};}}}; } module.exports={resolverOptions,evaluatePosture,probeWindows,createNetworkPosture};