2026-10-13 18:04:02 +02:00
import test from 'node:test' ;
import assert from 'node:assert/strict' ;
import vm from 'node:vm' ;
import { readFileSync } from 'node:fs' ;
import { createRequire } from 'node:module' ;
import { createServer } from 'node:http' ;
import { generateKeyPairSync } from 'node:crypto' ;
import { defaultPolicy , signPolicy , verifyPolicy } from '../packages/core/policy.mjs' ;
import { signBookmarkSnapshot , verifyBookmarkSnapshot } from '../packages/core/bookmarks.mjs' ;
import { DiagnosticsSchema } from '../packages/core/diagnostics.mjs' ;
const require = createRequire ( import . meta . url ),{ createDiagnostics } = require ( '../apps/browser/diagnostics.cjs' );
const source = readFileSync ( new URL ( '../apps/browser/main.cjs' , import . meta . url ), 'utf8' );
// Substitute only the dynamic module-loader boundary; execute the actual shipped
// application function and the real OTLP diagnostics helper.
const extracted = source . slice ( source . indexOf ( 'function applyPolicy(envelope)' ), source . indexOf ( 'async function sync()' )). replace ( "import('../../packages/core/bookmarks.mjs')" , "Promise.resolve(bookmarkModule)" );
async function fixture ( t ){
const payloads = [], server = createServer (( req , res )=>{ const chunks = []; req . on ( 'data' , v => chunks . push ( v )); req . on ( 'end' ,()=>{ payloads . push ( JSON . parse ( Buffer . concat ( chunks ). toString ())); res . setHeader ( 'content-type' , 'application/json' ); res . end ( '{}' );});}). listen ( 0 , '127.0.0.1' ); await new Promise ( resolve => server . once ( 'listening' , resolve ));
const diagnostics = createDiagnostics (), config = DiagnosticsSchema . parse ({ enabled : true , mode : 'debug' , endpoint : `http://127.0.0.1: ${ server . address (). port } ` , sampleRate : 1 , debugSampleRate : 1 , flushIntervalMs : 10000 });
t . after ( async ()=>{ await diagnostics . shutdown (); server . closeAllConnections (); await new Promise ( resolve => server . close ( resolve ));}); diagnostics . configure ( config );
const calls = [], next = defaultPolicy (); next . diagnostics = config ; const keys = generateKeyPairSync ( 'ed25519' ), privateKey = keys . privateKey . export ({ type : 'pkcs8' , format : 'pem' }), publicKey = keys . publicKey . export ({ type : 'spki' , format : 'pem' }), deviceId = '00000000-0000-4000-8000-000000000001' ;
const envelope = ()=>({... signPolicy ( next , privateKey ), bookmarks : signBookmarkSnapshot ({ deviceId , policyVersion : next . version , revision : 0 , expiresAt : next . expiresAt , items : []}, privateKey )});
const tracked = name => async ()=>{ calls . push ({ name , enabled : diagnostics . status (). enabled });};
2026-10-13 21:00:41 +02:00
const c = vm . createContext ({ require , process : { env : {}}, core : { verifyPolicy }, assertPolicyAssignment : require ( '../apps/browser/policy-assignment.cjs' ). assertPolicyAssignment , credentialBroker : { revoke : ()=> calls . push ({ name : 'credential-revoke' , enabled : diagnostics . status (). enabled })}, device : { id : deviceId , publicKey , minimumVersion : 0 }, policy : next , personalBookmarks : { revision : 0 , items : []}, bookmarkSnapshotJSON : '' , bookmarkModule : { verifyBookmarkSnapshot }, saveDevice : ()=> calls . push ({ name : 'save' , enabled : diagnostics . status (). enabled }), diagnosticSpan : ( name , attributes , fn )=> diagnostics . span ( name , attributes , fn ), configureDiagnostics : v =>{ calls . push ({ name : 'configure' , enabled : v . enabled }); diagnostics . configure ( v );}, reconcileAgentPolicy : tracked ( 'agent' ), enforceEvidencePolicy : ()=>{}, emit : ()=>{}, quarantine : ()=>{}, browserSession : { closeAllConnections : tracked ( 'browser-close' ), setProxy : tracked ( 'browser-proxy' ), clearHostResolverCache : tracked ( 'browser-dns' )}, managementSession : { closeAllConnections : tracked ( 'management-close' ), setProxy : tracked ( 'management-proxy' ), clearHostResolverCache : tracked ( 'management-dns' )}, app : { configureHostResolver : ()=> calls . push ({ name : 'resolver' , enabled : diagnostics . status (). enabled })}, resolverOptions : v => v , recentRequests : new Map (), tabs : { all : ()=>[{ security : { developerToolsGuard : { close : ()=>{}}}, view : { webContents : { closeDevTools : ()=>{}}}}]}, configureVisualPage : tracked ( 'page' ), audit : tracked ( 'audit' )});
2026-10-13 23:19:22 +02:00
c . localRelay = { proxyRules : '127.0.0.1:45678' , proxyBypassRules : '<-loopback>' , invalidate : tracked ( 'relay-invalidate' )};
const originalSetProxy = c . browserSession . setProxy ; c . browserSession . setProxy = async settings =>{ assert . equal ( settings . mode , 'fixed_servers' ); assert . equal ( settings . proxyRules , c . localRelay . proxyRules ); assert . equal ( settings . proxyBypassRules , '<-loopback>' ); await originalSetProxy ( settings );};
2026-10-13 18:04:02 +02:00
vm . runInContext ( extracted , c ); return { c , next , envelope , calls , diagnostics , payloads , spans : ()=> payloads . flatMap ( v => v . resourceSpans || []). flatMap ( v => v . scopeSpans || []). flatMap ( v => v . spans || [])};
}
test ( 'actual unchanged policy exports correlated verification, bookmark and agent child stages' , async t =>{
const f = await fixture ( t ); await f . c . applyPolicy ( f . envelope ()); await f . diagnostics . flush (); const spans = f . spans (), parent = spans . find ( s => ! s . parentSpanId ); assert . equal ( parent . name , 'browser.policy' );
for ( const operation of [ 'policy.verify' , 'policy.bookmarks' , 'policy.agent-reconcile' ]){ const child = spans . find ( s => s . attributes . some ( a => a . key === 'safetynet.operation' && a . value . stringValue === operation )); assert . ok ( child , operation ); assert . equal ( child . traceId , parent . traceId ); assert . equal ( child . parentSpanId , parent . spanId ); assert . ok ( BigInt ( child . endTimeUnixNano ) >= BigInt ( child . startTimeUnixNano ));}
assert . equal ( JSON . stringify ( f . payloads ). includes ( 'fixture' ), false );
});
test ( 'new signed policy disables diagnostics throughout network transition and honors final disable' , async t =>{
const f = await fixture ( t ); f . c . policy = {... f . next , version : 0 }; f . next . diagnostics = { enabled : false }; await f . c . applyPolicy ( f . envelope ()); await f . diagnostics . flush ();
2026-10-13 21:00:41 +02:00
for ( const name of [ 'browser-close' , 'browser-proxy' , 'management-close' , 'management-proxy' , 'browser-dns' , 'management-dns' , 'credential-revoke' , 'resolver' , 'save' , 'page' , 'agent' , 'audit' ]) assert . equal ( f . calls . find ( v => v . name === name ). enabled , false , name );
2026-10-13 18:04:02 +02:00
assert . deepEqual ( f . calls . filter ( v => v . name === 'configure' ). map ( v => v . enabled ),[ false , false ]); assert . equal ( f . diagnostics . status (). enabled , false ); assert . equal ( f . spans (). length , 0 , 'Retired old generation must not export' );
});
test ( 'enabled new policy exports only post-configuration stages after protected network transition' , async t =>{
const f = await fixture ( t ); f . c . policy = {... f . next , version : 0 }; await f . c . applyPolicy ( f . envelope ()); await f . diagnostics . flush ();
assert . equal ( f . calls . find ( v => v . name === 'browser-proxy' ). enabled , false ); assert . equal ( f . calls . find ( v => v . name === 'page' ). enabled , true );
const operations = f . spans (). map ( s => s . attributes . find ( a => a . key === 'safetynet.operation' ) ? . value . stringValue ); assert . deepEqual ( operations . sort (),[ 'policy.page-configure' , 'policy.agent-reconcile' , 'policy.audit' ]. sort ());
});
test ( 'signed verification failure prevents bookmark, proxy and policy mutations' , async t =>{
const f = await fixture ( t ); const tampered = f . envelope (); tampered . payload += 'A' ; await assert . rejects ( f . c . applyPolicy ( tampered ), /signature rejected/ ); await f . diagnostics . flush (); assert . equal ( f . calls . length , 0 ); assert . equal ( f . c . policy . version , 1 ); assert . ok ( f . spans (). some ( s => s . status . code === 2 ));
});