Implement managed browser security, individual agents and observability

This commit is contained in:
Karl committed 2026-10-13 16:58:15 +02:00
1 parent ff482ebe23
commit ea448de2d4
172 files changed
+5404 -347

No files matched your search

+6 -5
View File
@@ -7,12 +7,13 @@ import {join,resolve,sep,basename} from 'node:path';
import {spawn} from 'node:child_process';
import {randomBytes} from 'node:crypto';
import {createApp} from '../apps/server/app.mjs';
import {startTelemetry} from '../apps/server/telemetry.mjs';
import {openStore,digest} from '../apps/server/store.mjs';
const require=createRequire(import.meta.url),directory=mkdtempSync(join(tmpdir(),'safetynet-native-tabs-'));
let sink,management,collector,store,child;const requests={},posts=[],diagnosticRequests=[];let privateSentinels=[];
const diagnosticStatus=()=>{const spans=diagnosticRequests.filter(r=>r.path.endsWith('/traces')).flatMap(r=>r.payload.resourceSpans||[]).flatMap(r=>r.scopeSpans||[]).flatMap(r=>r.spans||[]);const logs=diagnosticRequests.filter(r=>r.path.endsWith('/logs')).flatMap(r=>r.payload.resourceLogs||[]).flatMap(r=>r.scopeLogs||[]).flatMap(r=>r.logRecords||[]);return {exports:diagnosticRequests.length,names:[...new Set(spans.map(span=>span.name))],spans:spans.length,logs:logs.length,timings:spans.every(span=>BigInt(span.endTimeUnixNano)>=BigInt(span.startTimeUnixNano)&&span.attributes.some(a=>a.key==='safetynet.duration.ms')),parentChild:spans.some(child=>child.parentSpanId&&spans.some(parent=>parent.spanId===child.parentSpanId&&parent.traceId===child.traceId)),correlatedLogs:logs.some(log=>log.traceId&&spans.some(span=>span.traceId===log.traceId&&span.spanId===log.spanId)),sensitivePresent:privateSentinels.some(secret=>JSON.stringify(diagnosticRequests).includes(secret))};};
let sink,management,collector,store,child,sdk;const previousEndpoint=process.env.OTEL_EXPORTER_OTLP_ENDPOINT;const requests={},posts=[],diagnosticRequests=[];let privateSentinels=[];
const diagnosticStatus=()=>{const spans=diagnosticRequests.filter(r=>r.path.endsWith('/traces')).flatMap(r=>r.payload.resourceSpans||[]).flatMap(r=>r.scopeSpans||[]).flatMap(r=>r.spans||[]);const logs=diagnosticRequests.filter(r=>r.path.endsWith('/logs')).flatMap(r=>r.payload.resourceLogs||[]).flatMap(r=>r.scopeLogs||[]).flatMap(r=>r.logRecords||[]);return {exports:diagnosticRequests.length,names:[...new Set(spans.map(span=>span.name))],spans:spans.length,logs:logs.length,timings:spans.every(span=>BigInt(span.endTimeUnixNano)>=BigInt(span.startTimeUnixNano)&&span.attributes.some(a=>a.key==='safetynet.duration.ms')),parentChild:spans.some(child=>child.parentSpanId&&spans.some(parent=>parent.spanId===child.parentSpanId&&parent.traceId===child.traceId)),crossProcessTrace:spans.some(child=>child.name==='server.request'&&spans.some(parent=>parent.name==='browser.management'&&parent.spanId===child.parentSpanId&&parent.traceId===child.traceId)),crossProcessTraceIds:[...new Set(spans.filter(child=>child.name==='server.request'&&spans.some(parent=>parent.name==='browser.management'&&parent.spanId===child.parentSpanId&&parent.traceId===child.traceId)).map(span=>span.traceId))],correlatedLogs:logs.some(log=>log.traceId&&spans.some(span=>span.traceId===log.traceId&&span.spanId===log.spanId)),sensitivePresent:privateSentinels.some(secret=>JSON.stringify(diagnosticRequests).includes(secret))};};
try{
collector=createServer((req,res)=>{if(req.method==='GET'&&req.url==='/status'){res.setHeader('content-type','application/json');res.end(JSON.stringify(diagnosticStatus()));return;}const chunks=[];req.on('data',chunk=>chunks.push(chunk));req.on('end',()=>{diagnosticRequests.push({path:req.url,payload:JSON.parse(Buffer.concat(chunks).toString())});res.setHeader('content-type','application/json');res.end('{}');});}).listen(0,'127.0.0.1');await new Promise(r=>collector.once('listening',r));const collectorUrl=`http://127.0.0.1:${collector.address().port}`;
collector=createServer((req,res)=>{if(req.method==='GET'&&req.url==='/status'){res.setHeader('content-type','application/json');res.end(JSON.stringify(diagnosticStatus()));return;}const chunks=[];req.on('data',chunk=>chunks.push(chunk));req.on('end',()=>{diagnosticRequests.push({path:req.url,payload:JSON.parse(Buffer.concat(chunks).toString())});res.setHeader('content-type','application/json');res.end('{}');});}).listen(0,'127.0.0.1');await new Promise(r=>collector.once('listening',r));const collectorUrl=`http://127.0.0.1:${collector.address().port}`;process.env.OTEL_EXPORTER_OTLP_ENDPOINT=collectorUrl;sdk=startTelemetry();
sink=createServer((req,res)=>{const path=new URL(req.url,'http://localhost').pathname;requests[path]=(requests[path]||0)+1;
if(path==='/counts'){res.setHeader('content-type','application/json');res.end(JSON.stringify({requests,posts}));return;}
if(req.method==='POST'){posts.push(path);req.resume();res.end('received');return;}
@@ -24,5 +25,5 @@ try{
const env={...process.env,SAFETYNET_TABS_COLLECTOR:collectorUrl,SAFETYNET_TABS_SERVER:config.publicUrl,SAFETYNET_TABS_CODE:code,SAFETYNET_TABS_ADMIN:adminToken,SAFETYNET_TABS_SINK:url,SAFETYNET_TABS_USERDATA:join(directory,'browser'),SAFETYNET_SMOKE_USERDATA:join(directory,'browser')};delete env.ELECTRON_RUN_AS_NODE;
child=spawn(require('electron'),['scripts/native-tabs-smoke.cjs'],{env,windowsHide:true,stdio:['ignore','pipe','pipe']});child.stdout.on('data',b=>process.stdout.write(b));child.stderr.on('data',b=>{const message=String(b).replaceAll(code,'[fixture enrollment]').replaceAll(adminToken,'[fixture admin]');process.stderr.write(message);});
const timer=setTimeout(()=>child.kill(),120000);const codeExit=await new Promise(r=>{child.once('exit',c=>r(c??1));child.once('error',()=>r(1));});clearTimeout(timer);assert.equal(codeExit,0,'Actual native tabs proof failed');
assert.equal(posts.length,0,'Blocked fixture data reached receiver');const diagnostics=diagnosticStatus();assert.ok(diagnostics.spans>0&&diagnostics.logs>0&&diagnostics.timings&&diagnostics.parentChild&&diagnostics.correlatedLogs);assert.equal(diagnostics.sensitivePresent,false);for(const name of ['browser.ipc','browser.navigation','browser.dns','browser.request'])assert.ok(diagnostics.names.includes(name),`Missing native timing ${name}`);writeFileSync('artifacts/native-browser-diagnostics-proof.json',JSON.stringify(diagnostics,null,2));mkdirSync('artifacts',{recursive:true});writeFileSync('artifacts/native-tabs-receiver-proof.json',JSON.stringify({noBlockedPosts:true,requests,posts},null,2));
}finally{child?.kill();for(const server of [management,sink,collector])if(server)await new Promise(r=>{server.closeAllConnections?.();server.close(r);});store?.db.close();const target=resolve(directory);if(!target.startsWith(resolve(tmpdir())+sep)||!basename(target).startsWith('safetynet-native-tabs-'))throw Error('Unsafe fixture cleanup');rmSync(target,{recursive:true,force:true,maxRetries:10,retryDelay:100});}
assert.equal(posts.length,0,'Blocked fixture data reached receiver');await sdk.shutdown();sdk=null;const diagnostics=diagnosticStatus();assert.ok(diagnostics.spans>0&&diagnostics.logs>0&&diagnostics.timings&&diagnostics.parentChild&&diagnostics.correlatedLogs);assert.equal(diagnostics.sensitivePresent,false);assert.equal(diagnostics.crossProcessTrace,true,'Browser management span must parent actual backend HTTP request');for(const name of ['browser.ipc','browser.navigation','browser.dns','browser.request'])assert.ok(diagnostics.names.includes(name),`Missing native timing ${name}`);writeFileSync('artifacts/native-browser-diagnostics-proof.json',JSON.stringify(diagnostics,null,2));mkdirSync('artifacts',{recursive:true});writeFileSync('artifacts/native-tabs-receiver-proof.json',JSON.stringify({noBlockedPosts:true,requests,posts},null,2));
}finally{child?.kill();await sdk?.shutdown();if(previousEndpoint===undefined)delete process.env.OTEL_EXPORTER_OTLP_ENDPOINT;else process.env.OTEL_EXPORTER_OTLP_ENDPOINT=previousEndpoint;for(const server of [management,sink,collector])if(server)await new Promise(r=>{server.closeAllConnections?.();server.close(r);});store?.db.close();const target=resolve(directory);if(!target.startsWith(resolve(tmpdir())+sep)||!basename(target).startsWith('safetynet-native-tabs-'))throw Error('Unsafe fixture cleanup');rmSync(target,{recursive:true,force:true,maxRetries:10,retryDelay:100});}