Harden Windows DLP enforcement and Electron runtime packaging

This commit is contained in:
Karl committed 2026-10-13 23:19:22 +02:00
1 parent e7d778aeac
commit 8a253eda89
120 files changed
+3569 -135

No files matched your search

+2 -2
View File
@@ -8,12 +8,12 @@ async function capture(file,section){await evaluate(`document.getElementById('${
app.whenReady().then(async()=>{
window=new BrowserWindow({show:false,width:1500,height:1100,webPreferences:{nodeIntegration:false,contextIsolation:true,sandbox:true,offscreen:true,backgroundThrottling:false}});
const proofTraceIds=[],requestsById=new Map();session.defaultSession.webRequest.onCompleted({urls:[process.env.SAFETYNET_ADMIN_SMOKE_URL+'/api/*']},details=>{const row=requestsById.get(details.id);if(row){row.outcome='completed';row.statusCode=details.statusCode;}});session.defaultSession.webRequest.onErrorOccurred({urls:[process.env.SAFETYNET_ADMIN_SMOKE_URL+'/api/*']},details=>{const row=requestsById.get(details.id);if(row)row.outcome='failed';});session.defaultSession.webRequest.onBeforeSendHeaders({urls:[process.env.SAFETYNET_ADMIN_SMOKE_URL+'/api/*']},(details,callback)=>{const crypto=require('node:crypto'),traceId=crypto.randomBytes(16).toString('hex'),spanId=crypto.randomBytes(8).toString('hex');const path=new URL(details.url).pathname,route=path==='/api/admin/policy'?'/api/admin/policy':path==='/api/admin/policy/restore'?'/api/admin/policy/restore':/^\/api\/admin\/policy-reviews\/[^/]+\/approve$/.test(path)?'/api/admin/policy-reviews/:id/approve':/^\/api\/admin\/policy-history\/[^/]+\/restore$/.test(path)?'/api/admin/policy-history/:id/restore':'other-api';const row={traceId,parentSpanId:spanId,outcome:'pending',method:details.method,route};proofTraceIds.push(row);requestsById.set(details.id,row);callback({requestHeaders:{...details.requestHeaders,traceparent:'00-'+traceId+'-'+spanId+'-01'}});});
await identity('grant-author');assert.equal(await evaluate('typeof require'),'undefined');for(const scope of ['history:list','history:go','window:state','window:control','vault:status','vault:list','vault:fill','sessions:save','sessions:list','sessions:restore','vault:prepare-submit','screenshot'])assert.deepEqual(await evaluate(`(()=>{const field=document.getElementById('agent-scope-${scope}');return {present:!!field,checked:field.checked,disabled:field.disabled};})()`),{present:true,checked:false,disabled:false});assert.equal(await evaluate(`document.getElementById('vault-controlled-submit').checked`),false);await evaluate(`document.getElementById('agent-enabled').checked=true;document.getElementById('vault-controlled-submit').checked=true;document.getElementById('vault-submit-forms').value=JSON.stringify([{id:'company-login',actionUrl:'https://login.company.example/signin',usernameField:'username',passwordField:'password'}]);document.getElementById('agent-scope-sessions').checked=true;document.getElementById('agent-scope-read').checked=true;for(const scope of ['vault:status','vault:list','vault:fill','sessions:save','sessions:list','sessions:restore','vault:prepare-submit','screenshot'])document.getElementById('agent-scope-'+scope).checked=true;document.getElementById('agent-hosts').value='*.company.example';document.getElementById('agent-identity-mode').value='individual';document.getElementById('agent-identity-mode').dispatchEvent(new Event('change'));document.getElementById('agent-grant-add').click();`);
await identity('grant-author');assert.equal(await evaluate('typeof require'),'undefined');for(const scope of ['history:list','history:go','window:state','window:control','vault:status','vault:list','vault:fill','sessions:save','sessions:list','sessions:restore','vault:prepare-submit','screenshot'])assert.deepEqual(await evaluate(`(()=>{const field=document.getElementById('agent-scope-${scope}');return {present:!!field,checked:field.checked,disabled:field.disabled};})()`),{present:true,checked:false,disabled:false});assert.equal(await evaluate(`document.getElementById('vault-controlled-submit').checked`),false);assert.equal(await evaluate(`document.getElementById('agent-background-workers').checked||document.getElementById('agent-scope-network:background').checked`),false);await evaluate(`document.getElementById('agent-enabled').checked=true;document.getElementById('vault-controlled-submit').checked=true;document.getElementById('vault-rule-add').click();for(const [key,value]of Object.entries({id:'company-login',actionUrl:'https://login.company.example/signin',usernameField:'username',passwordField:'password'})){const field=document.querySelector('#vault-rule-editor [data-credential-rule-field='+key+']');field.value=value;field.dispatchEvent(new Event('input'));}document.getElementById('agent-scope-sessions').checked=true;document.getElementById('agent-scope-read').checked=true;for(const scope of ['vault:status','vault:list','vault:fill','sessions:save','sessions:list','sessions:restore','vault:prepare-submit','screenshot'])document.getElementById('agent-scope-'+scope).checked=true;document.getElementById('agent-hosts').value='*.company.example';document.getElementById('agent-identity-mode').value='individual';document.getElementById('agent-identity-mode').dispatchEvent(new Event('change'));document.getElementById('agent-grant-add').click();`);
assert.equal(await evaluate(`Array.from(document.querySelectorAll('#agent-grants-editor .checks input')).every(x=>!x.checked)`),true);assert.equal(await evaluate(`document.getElementById('agent-vault-help').textContent.includes('A person must unlock')&&document.getElementById('agent-vault-help').textContent.includes('cannot unlock it or export passwords')`),true);assert.equal(await evaluate(`document.getElementById('agent-grants-editor').textContent.includes('human unlock')`),true);assert.equal(await evaluate(`document.getElementById('agent-sessions-help').textContent.includes('no passwords, form values or page state')&&document.getElementById('agent-sessions-help').textContent.includes('never restores revoked agent authority')`),true);
await evaluate(`const box=document.querySelector('#agent-grants-editor>fieldset'),fields=box.querySelectorAll('.grid input,.grid textarea');fields[0].value='research-agent';fields[0].dispatchEvent(new Event('input'));fields[1].value='Research Agent';fields[1].dispatchEvent(new Event('input'));fields[2].checked=true;fields[2].dispatchEvent(new Event('change'));fields[3].value='2099-12-31T23:59:59Z';fields[3].dispatchEvent(new Event('input'));fields[4].value='*.company.example';fields[4].dispatchEvent(new Event('input'));for(const label of box.querySelectorAll('.checks label'))if(['sessions','read','vault:status','vault:list','vault:fill','sessions:save','sessions:list','sessions:restore','vault:prepare-submit','screenshot'].includes(label.textContent)){const input=label.querySelector('input');input.checked=true;input.dispatchEvent(new Event('change'));}document.getElementById('publish').click()`);await waitFor(`document.getElementById('status').textContent.includes('prepared for independent approval')`);assert.equal(await evaluate(`document.getElementById('policy-version').textContent`),'v1');await selectReview();assert.equal(await evaluate(`document.getElementById('independent-review-approve').disabled`),true);
assert.equal(await evaluate(`fetch('/api/admin/policy-reviews/'+selectedIndependentReview.id+'/approve',{method:'POST',headers:{'content-type':'application/json'},body:JSON.stringify({payloadHash:selectedIndependentReview.payloadHash,expectedVersion:selectedIndependentReview.expectedVersion,approve:true})}).then(r=>r.status)`),403);
await identity('grant-reviewer');await selectReview();const diff=await evaluate(`document.getElementById('independent-review-diff').textContent`);for(const text of ['/agents/grants','research-agent','2099','company.example','sessions','read','generation','controlledSubmission','company-login','vault:status','vault:list','vault:fill','sessions:save','sessions:list','sessions:restore','vault:prepare-submit','screenshot'])assert.ok(diff.includes(text),text);await capture('artifacts/agent-grants-review-admin.png','independent-review-detail');await approve(2);
await capture('artifacts/credential-submission-admin-controls.png','password-vault');await identity('grant-auditor');for(const id of ['vault-controlled-submit','vault-submit-forms'])assert.equal(await evaluate(`document.getElementById('${id}').disabled`),true);for(const scope of ['vault:status','vault:list','vault:fill','sessions:save','sessions:list','sessions:restore','vault:prepare-submit','screenshot'])assert.equal(await evaluate(`document.getElementById('agent-scope-${scope}').disabled`),true);assert.equal(await evaluate(`document.getElementById('agent-grant-add').disabled&&document.getElementById('agent-identity-mode').disabled`),true);assert.equal(await evaluate(`Array.from(document.querySelectorAll('#agent-grants-editor input, #agent-grants-editor textarea, #agent-grants-editor button')).every(x=>x.disabled)`),true);
await capture('artifacts/credential-submission-admin-controls.png','password-vault');await identity('grant-auditor');assert.equal(await evaluate(`Array.from(document.querySelectorAll('#vault-rule-editor input,#vault-rule-editor button')).every(x=>x.disabled)`),true);for(const id of ['vault-controlled-submit','vault-submit-forms','vault-rule-add','agent-background-workers','agent-scope-network:background'])assert.equal(await evaluate(`document.getElementById('${id}').disabled`),true);for(const scope of ['vault:status','vault:list','vault:fill','sessions:save','sessions:list','sessions:restore','vault:prepare-submit','screenshot'])assert.equal(await evaluate(`document.getElementById('agent-scope-${scope}').disabled`),true);assert.equal(await evaluate(`document.getElementById('agent-grant-add').disabled&&document.getElementById('agent-identity-mode').disabled`),true);assert.equal(await evaluate(`Array.from(document.querySelectorAll('#agent-grants-editor input, #agent-grants-editor textarea, #agent-grants-editor button')).every(x=>x.disabled)`),true);
await identity('grant-author');assert.equal(await evaluate(`document.querySelector('#agent-grants-editor .grid input').readOnly`),true);await evaluate(`Array.from(document.querySelectorAll('#agent-grants-editor button')).find(x=>x.textContent==='Invalidate previous credentials').click();document.getElementById('publish').click()`);await waitFor(`document.getElementById('status').textContent.includes('prepared for independent approval')`);assert.equal(await evaluate(`document.getElementById('policy-version').textContent`),'v2');await identity('grant-reviewer');await selectReview();assert.ok((await evaluate(`document.getElementById('independent-review-diff').textContent`)).includes('"generation":2'));await approve(3);
await identity('grant-author');await evaluate(`document.getElementById('brand-name').value='Unpublished preserved draft';document.getElementById('history-load').click()`);await waitFor(`document.querySelectorAll('#history-rows tr').length===3`);await evaluate(`document.querySelectorAll('#history-rows button')[1].click()`);await waitFor(`!document.getElementById('history-review').hidden`);assert.equal(await evaluate(`document.getElementById('brand-name').value`),'Unpublished preserved draft');assert.equal(await evaluate(`document.getElementById('policy-version').textContent`),'v3');assert.equal(await evaluate(`document.getElementById('history-restore').disabled`),true);const history=await evaluate(`document.getElementById('history-diff-list').textContent`);assert.ok(history.includes('/agents/grants'));assert.ok(history.includes('generation'));assert.ok((await evaluate(`document.getElementById('history-review-summary').textContent`)).includes('does not restore invalidated credentials'));await capture('artifacts/agent-grants-history-admin.png','history-review');await evaluate(`document.getElementById('history-approve').checked=true;document.getElementById('history-approve').dispatchEvent(new Event('change'));document.getElementById('history-restore').click()`);await waitFor(`document.getElementById('status').textContent.includes('prepared for independent approval')`);assert.equal(await evaluate(`document.getElementById('policy-version').textContent`),'v3');await selectReview();assert.equal(await evaluate(`document.getElementById('independent-review-approve').disabled`),true);await identity('grant-reviewer');await selectReview();assert.ok((await evaluate(`document.getElementById('independent-review-diff').textContent`)).includes('research-agent'));await approve(4);
await identity('grant-author');assert.equal(await evaluate(`document.getElementById('agent-scope-screenshot').checked`),true);assert.equal(await evaluate(`document.getElementById('agent-visual-export-help').textContent.includes('does not authorize desktop')`),true);await evaluate(`document.getElementById('agent-scope-screenshot').checked=false;document.getElementById('agent-scope-screenshot').dispatchEvent(new Event('input'));for(const label of document.querySelectorAll('#agent-grants-editor .checks label'))if(label.textContent==='screenshot'){label.querySelector('input').checked=false;label.querySelector('input').dispatchEvent(new Event('change'));}document.getElementById('publish').click()`);await waitFor(`document.getElementById('status').textContent.includes('prepared for independent approval')`);assert.equal(await evaluate(`document.getElementById('policy-version').textContent`),'v4');await identity('grant-reviewer');await selectReview();const revokeDiff=await evaluate(`document.getElementById('independent-review-diff').textContent`);assert.ok(revokeDiff.includes('screenshot'));await approve(5);assert.equal(await evaluate(`document.getElementById('agent-scope-screenshot').checked`),false);assert.equal(await evaluate(`Array.from(document.querySelectorAll('#agent-grants-editor .checks label')).filter(l=>l.textContent==='screenshot').every(l=>!l.querySelector('input').checked)`),true);
+2 -1
View File
@@ -19,5 +19,6 @@ try {
child=spawn(require('electron'),['scripts/agent-grants-history-admin-smoke.cjs'],{env,windowsHide:true,stdio:['ignore','pipe','pipe']});child.stdout.on('data',d=>{if(String(d).includes('GRANT_ADMIN_OK'))console.log('Grant admin actual UI passed');});child.stderr.on('data',d=>{if(String(d).includes('GRANT_ADMIN_FAILED'))console.error(String(d).trim());});const timer=setTimeout(()=>child.kill(),60000),code=await new Promise(r=>{child.once('exit',c=>r(c??1));child.once('error',()=>r(1));});clearTimeout(timer);assert.equal(code,0);
const p=verifyPolicy(store.envelope(),store.publicKey());assert.equal(p.version,5);assert.equal(p.agents.grants[0].generation,1);assert.equal(p.agents.identityMode,'individual');assert.equal(p.agents.scopes.includes('screenshot'),false);assert.equal(p.agents.grants[0].scopes.includes('screenshot'),false);for(const scope of ['vault:status','vault:list','vault:fill','sessions:save','sessions:list','sessions:restore','vault:prepare-submit']){assert.ok(p.agents.scopes.includes(scope));assert.ok(p.agents.grants[0].scopes.includes(scope));}
assert.equal(p.passwordManager.controlledSubmission.enabled,true);assert.equal(p.passwordManager.controlledSubmission.forms[0].actionUrl,'https://login.company.example/signin');
const traces=JSON.parse(readFileSync('artifacts/agent-grants-admin-request-trace-ids.json','utf8'));await sdk.shutdown();await new Promise(r=>setTimeout(r,100));const exported=[],failedAttempts=traces.filter(item=>item.outcome==='failed');assert.ok(traces.some(item=>item.outcome==='completed'),'Completed API requests required');assert.equal(traces.some(item=>item.outcome!=='completed'&&['POST','PUT','PATCH','DELETE'].includes(item.method)),false,'Every policy mutation must complete and export a correlated span');for(const item of traces.filter(item=>item.outcome==='completed')){const r=await fetch(`http://127.0.0.1:4318/api/trace/${item.traceId}`);assert.equal(r.ok,true,'Completed API request trace must be available');if(r.ok){const trace=await r.json();assert.ok(trace.spans.some(s=>s.name==='server.request'&&s.parentSpanId===item.parentSpanId&&s.durationMs>=0));exported.push({traceId:item.traceId,parentSpanId:item.parentSpanId,trace});}}assert.ok(exported.length,'Actual request traces required');mkdirSync('artifacts',{recursive:true});writeFileSync('artifacts/agent-grants-history-admin-traces.json',JSON.stringify(exported,null,2));writeFileSync('artifacts/agent-grants-history-admin-proof.json',JSON.stringify({actualUi:true,vaultScopesDefaultOffAndReviewed:true,sessionScopesDefaultOffAndReviewed:true,controlledCredentialPolicyReviewed:true,prepareSubmitScopeDefaultOffAndReviewed:true,screenshotScopeDefaultOffAndReviewed:true,screenshotBaselineAndGrantRevocationReviewed:true,separateAuthorReviewer:true,selfApprovalDenied:true,auditorReadonly:true,exactGrantDiff:true,rotationReviewed:true,historyDraftPreserved:true,restoreReviewed:true,finalVersion:p.version,completedRequests:traces.filter(item=>item.outcome==='completed').length,failedAttempts:failedAttempts.map(item=>({traceId:item.traceId,outcome:item.outcome,method:item.method,route:item.route})),traceIds:exported.map(x=>x.traceId)},null,2));console.log('Grant publication/history and exported traces verified');
const traces=JSON.parse(readFileSync('artifacts/agent-grants-admin-request-trace-ids.json','utf8'));await sdk.shutdown();await new Promise(r=>setTimeout(r,100));const exported=[],failedAttempts=traces.filter(item=>item.outcome==='failed');assert.ok(traces.some(item=>item.outcome==='completed'),'Completed API requests required');assert.equal(traces.some(item=>item.outcome!=='completed'&&['POST','PUT','PATCH','DELETE'].includes(item.method)),false,'Every policy mutation must complete and export a correlated span');for(const item of traces.filter(item=>item.outcome==='completed')){const r=await fetch(`http://127.0.0.1:4318/api/trace/${item.traceId}`);assert.equal(r.ok,true,'Completed API request trace must be available');if(r.ok){const trace=await r.json();assert.ok(trace.spans.some(s=>s.name==='server.request'&&s.parentSpanId===item.parentSpanId&&s.durationMs>=0));exported.push({traceId:item.traceId,parentSpanId:item.parentSpanId,trace});}}assert.ok(exported.length,'Actual request traces required');mkdirSync('artifacts',{recursive:true});writeFileSync('artifacts/agent-grants-history-admin-traces.json',JSON.stringify(exported,null,2));writeFileSync('artifacts/agent-grants-history-admin-proof.json',JSON.stringify({actualUi:true,vaultScopesDefaultOffAndReviewed:true,sessionScopesDefaultOffAndReviewed:true,controlledCredentialPolicyReviewed:true,typedCredentialRulesReviewed:true,typedCredentialRulesAuditorReadonly:true,backgroundWorkerControlsDefaultOff:true,prepareSubmitScopeDefaultOffAndReviewed:true,screenshotScopeDefaultOffAndReviewed:true,screenshotBaselineAndGrantRevocationReviewed:true,separateAuthorReviewer:true,selfApprovalDenied:true,auditorReadonly:true,exactGrantDiff:true,rotationReviewed:true,historyDraftPreserved:true,restoreReviewed:true,finalVersion:p.version,completedRequests:traces.filter(item=>item.outcome==='completed').length,failedAttempts:failedAttempts.map(item=>({traceId:item.traceId,outcome:item.outcome,method:item.method,route:item.route})),traceIds:exported.map(x=>x.traceId)},null,2));console.log('Grant publication/history and exported traces verified');
// eslint-disable-next-line no-unsafe-finally -- Cleanup failure must fail closed rather than report successful protected cleanup.
} finally {child?.kill();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-agent-grant-admin-'))throw Error('Unsafe cleanup');rmSync(target,{recursive:true,force:true,maxRetries:10,retryDelay:100});}
+1 -1
View File
@@ -14,7 +14,7 @@ process.env.OTEL_EXPORTER_OTLP_ENDPOINT=collector;const telemetry=startTelemetry
const temporary=mkdtempSync(join(tmpdir(),'safetynet-agent-visual-'));mkdirSync('artifacts',{recursive:true});
if(!resolve(temporary).startsWith(resolve(tmpdir())+sep)||!temporary.split(/[\\/]/).at(-1).startsWith('safetynet-agent-visual-'))throw Error('Unsafe temporary fixture path');
let blockPNG=false;const avFacts={scans:0,pngScans:0,pngBlocks:0},avSockets=new Set(),pngSignature=Buffer.from([137,80,78,71,13,10,26,10]);const av=net.createServer(socket=>{avSockets.add(socket);socket.on('close',()=>avSockets.delete(socket));socket.on('error',()=>{});let input=Buffer.alloc(0),done=false;socket.on('data',chunk=>{input=Buffer.concat([input,chunk]);if(input.length>4194304){socket.destroy();return;}if(done||input.indexOf('\r\n\r\n')<0)return;const options=input.subarray(0,7).toString()==='OPTIONS';if(!options&&!input.subarray(-5).equals(Buffer.from('0\r\n\r\n')))return;done=true;if(options){input.fill(0);socket.end('ICAP/1.0 200 OK\r\nMethods: RESPMOD, REQMOD\r\nAllow: 204\r\nISTag: "visual-fixture"\r\nEncapsulated: null-body=0\r\n\r\n');return;}avFacts.scans++;const png=input.indexOf(pngSignature)>=0;if(png)avFacts.pngScans++;const blocked=png&&blockPNG;if(blocked)avFacts.pngBlocks++;input.fill(0);socket.end(blocked?'ICAP/1.0 204 No Content\r\nISTag: "visual-fixture"\r\nX-Infection-Found: Type=0; Threat=synthetic-visual;\r\n\r\n':'ICAP/1.0 204 No Content\r\nISTag: "visual-fixture"\r\n\r\n');});}).listen(0,'127.0.0.1');await new Promise(r=>av.once('listening',r));
let requests=0;const sink=createServer({cert:readFileSync('tests/fixtures/icap/cert.pem'),key:readFileSync('tests/fixtures/icap/key.pem')},(req,res)=>{requests++;if(req.url==='/av-stats'){res.setHeader('content-type','application/json');res.end(JSON.stringify(avFacts));return;}if(req.url==='/av-block')blockPNG=true;else if(['/public','/canvas','/sensitive','/closed-shadow','/open-shadow','/frames'].includes(req.url))blockPNG=false;res.setHeader('content-type','text/html');const base='<!doctype html><meta name="viewport" content="width=device-width"><style>body{margin:0;background:white;color:#123;font:28px Arial}h1{margin:70px}canvas{display:block}</style><title>Public visual fixture</title>';if(req.url==='/canvas')res.end(base+'<canvas width="1200" height="360"></canvas><script>const c=document.querySelector("canvas").getContext("2d");c.fillStyle="white";c.fillRect(0,0,1200,360);c.fillStyle="black";c.font="bold 48px Arial";c.fillText("CLASSIFIED ORCHID DOCUMENT",40,170);</script>');else if(req.url==='/sensitive')res.end(base+'<h1>Public form</h1><input type="password" value="synthetic-hidden-field" style="position:absolute;left:-9999px"><script>document.querySelectorAll=()=>[]</script>');else if(req.url==='/closed-shadow')res.end(base+`<div id=host></div><script>document.getElementById("host").attachShadow({mode:"closed"}).innerHTML='<input type="password" value="synthetic-closed-shadow-password">';document.querySelectorAll=()=>[]</script>`);else if(req.url==='/open-shadow')res.end(base+`<div id=host></div><script>document.getElementById("host").attachShadow({mode:"open"}).innerHTML='<p style="margin:70px;font:28px Arial">Public shadow content</p>'</script>`);else if(req.url==='/frames')res.end(base+'<h1>Public parent</h1><iframe src="/public"></iframe>');else res.end(base+'<h1>Public approved visual page</h1><p style="margin:70px">Research and documentation workspace.</p>');}).listen(0,'127.0.0.1');await new Promise(r=>sink.once('listening',r));
const sink=createServer({cert:readFileSync('tests/fixtures/icap/cert.pem'),key:readFileSync('tests/fixtures/icap/key.pem')},(req,res)=>{if(req.url==='/av-stats'){res.setHeader('content-type','application/json');res.end(JSON.stringify(avFacts));return;}if(req.url==='/av-block')blockPNG=true;else if(['/public','/canvas','/sensitive','/closed-shadow','/open-shadow','/frames'].includes(req.url))blockPNG=false;res.setHeader('content-type','text/html');const base='<!doctype html><meta name="viewport" content="width=device-width"><style>body{margin:0;background:white;color:#123;font:28px Arial}h1{margin:70px}canvas{display:block}</style><title>Public visual fixture</title>';if(req.url==='/canvas')res.end(base+'<canvas width="1200" height="360"></canvas><script>const c=document.querySelector("canvas").getContext("2d");c.fillStyle="white";c.fillRect(0,0,1200,360);c.fillStyle="black";c.font="bold 48px Arial";c.fillText("CLASSIFIED ORCHID DOCUMENT",40,170);</script>');else if(req.url==='/sensitive')res.end(base+'<h1>Public form</h1><input type="password" value="synthetic-hidden-field" style="position:absolute;left:-9999px"><script>document.querySelectorAll=()=>[]</script>');else if(req.url==='/closed-shadow')res.end(base+`<div id=host></div><script>document.getElementById("host").attachShadow({mode:"closed"}).innerHTML='<input type="password" value="synthetic-closed-shadow-password">';document.querySelectorAll=()=>[]</script>`);else if(req.url==='/open-shadow')res.end(base+`<div id=host></div><script>document.getElementById("host").attachShadow({mode:"open"}).innerHTML='<p style="margin:70px;font:28px Arial">Public shadow content</p>'</script>`);else if(req.url==='/frames')res.end(base+'<h1>Public parent</h1><iframe src="/public"></iframe>');else res.end(base+'<h1>Public approved visual page</h1><p style="margin:70px">Research and documentation workspace.</p>');}).listen(0,'127.0.0.1');await new Promise(r=>sink.once('listening',r));
const origin=`https://127.0.0.1:${sink.address().port}`,store=openStore(join(temporary,'db')),code=randomBytes(32).toString('base64url'),adminToken=randomBytes(32).toString('base64url');
const p=store.get('policy');p.browser.homeUrl=origin+'/public';p.inspection.documentAnalysisEnabled=false;p.dlp.rules.push({id:'visual-confidential-fixture',label:'Synthetic visual confidential rule',kind:'literal',value:'CLASSIFIED ORCHID DOCUMENT',action:'block'});
p.enterprise.icap={...p.enterprise.icap,enabled:true,url:`icap://127.0.0.1:${av.address().port}/scan`,scanUploads:true,scanDownloads:false,scanClipboard:false,scanAI:false};
+33
View File
@@ -0,0 +1,33 @@
'use strict';
// All wrappers observe the real native guards; they never replace enforcement.
const {app,BrowserWindow,session}=require('electron'),fs=require('node:fs'),path=require('node:path'),assert=require('node:assert/strict'),https=require('node:https'),{X509Certificate}=require('node:crypto');
app.setPath('userData',process.env.SAFETYNET_SMOKE_USERDATA);app.disableHardwareAcceleration();
const origin=process.env.SAFETYNET_SMOKE_ORIGIN,forbiddenOrigin=process.env.SAFETYNET_SMOKE_FORBIDDEN_ORIGIN,fingerprint=new X509Certificate(fs.readFileSync(path.join(__dirname,'../tests/fixtures/icap/cert.pem'))).fingerprint256;
const nativeSessions=new WeakMap(),records=[],requests=[],fromPartition=session.fromPartition.bind(session);
session.fromPartition=(...args)=>{const s=fromPartition(...args);if(nativeSessions.has(s))return s;const record={index:records.length,partitionOwned:typeof args[0]==='string'&&args[0].startsWith('safetynet-agent-'),hooks:[],firstRequestHooks:null};records.push(record);nativeSessions.set(s,record);s.setCertificateVerifyProc((req,cb)=>{let valid=false;try{valid=['127.0.0.1','127.0.0.2'].includes(req.hostname)&&new X509Certificate(req.certificate.data).fingerprint256===fingerprint;}catch{}cb(valid?0:-2);});
for(const name of ['onBeforeRequest','onBeforeSendHeaders','onBeforeRedirect','onCompleted','onErrorOccurred']){const original=s.webRequest[name].bind(s.webRequest);s.webRequest[name]=(...values)=>{const index=values.length-1,listener=values[index];if(typeof listener==='function'){record.hooks.push(name);values[index]=(...eventArgs)=>{const d=eventArgs[0];if(name==='onBeforeRequest'&&d.url?.startsWith(origin)){record.firstRequestHooks??=[...record.hooks];if(d.url.includes('/worker/'))requests.push({nativeSession:record.index,requestId:d.id,hasWebContents:Number.isInteger(d.webContentsId)&&d.webContentsId>0,hasFrame:!!d.frame,method:d.method,bytes:(d.uploadData||[]).reduce((n,v)=>n+(v.bytes?.length||0),0),sameInitiator:d.initiator===origin});}return listener(...eventArgs);};}return original(...values);};}
for(const name of ['setPermissionRequestHandler','setPermissionCheckHandler','registerPreloadScript'])if(typeof s[name]==='function'){const original=s[name].bind(s);s[name]=(...values)=>{record.hooks.push(name);return original(...values);};}
const proxy=s.setProxy.bind(s);s.setProxy=async(...values)=>{const result=await proxy(...values);record.hooks.push('proxyReady');return result;};return s;};
const localWitnesses=[],inspection=require('../apps/browser/inspection.cjs'),startInspector=inspection.startInspector;inspection.startInspector=(...args)=>{const client=startInspector(...args),inspect=client.inspect.bind(client);client.inspect=async(bytes,policy)=>{const result=await inspect(bytes,policy);localWitnesses.push({decision:result.decision,ruleId:result.ruleId==='worker-yara-fixture'?'worker-yara-fixture':null,complete:result.coverage?.complete===true});return result;};return client;};
require('../apps/browser/main.cjs');
const wait=ms=>new Promise(r=>setTimeout(r,ms)),base=process.env.SAFETYNET_SMOKE_SERVER,headers={authorization:'Bearer '+process.env.SAFETYNET_SMOKE_ADMIN,'content-type':'application/json'};
async function until(fn){for(let i=0;i<250;i++){const value=await fn();if(value)return value;await wait(100);}throw Error('Native worker fixture timeout');}
const getFacts=()=>new Promise((resolve,reject)=>{const request=https.get(origin+'/facts',{ca:fs.readFileSync(path.join(__dirname,'../tests/fixtures/icap/cert.pem')),checkServerIdentity:(_host,cert)=>new X509Certificate(cert.raw).fingerprint256===fingerprint?undefined:Error('Synthetic certificate mismatch')},response=>{let body='';response.on('data',chunk=>body+=chunk);response.on('end',()=>resolve(JSON.parse(body)));});request.on('error',reject);});
app.whenReady().then(async()=>{const pages=[];let stage='startup';try{const win=await until(()=>{const w=BrowserWindow.getAllWindows()[0];return w&&!w.webContents.isLoading()&&w.webContents.getURL().endsWith('index.html')&&w;});const ui=async(method,value)=>{const result=await win.webContents.executeJavaScript(`window.safetynet.${method}(${value===undefined?'':JSON.stringify(value)})`);assert.equal(result.ok,true,method);return result.data;},overview=async()=>(await fetch(base+'/api/admin/overview',{headers})).json();await ui('enroll',{server:base,code:process.env.SAFETYNET_SMOKE_CODE});const human=win.contentView.children.find(v=>v.webContents!==win.webContents).webContents;pages.push(human);await until(()=>!human.isLoading()&&human.getURL()===origin+'/app');
const connections={a:await ui('agentConnection','worker-a'),b:await ui('agentConnection','worker-b')};async function call(actor,action,args={}){const requestFile=path.join(app.getPath('userData'),'worker-request-'+actor+'.json');fs.writeFileSync(requestFile,JSON.stringify({action,args}));const child=require('node:child_process').spawn(process.execPath,['scripts/agent-client.cjs','--connection',connections[actor].credentialPath,'--request',requestFile],{windowsHide:true,stdio:['ignore','pipe','pipe']});let output='';child.stdout.on('data',v=>output+=v);child.stderr.on('data',()=>{});const exit=await new Promise(r=>{child.once('exit',r);child.once('error',()=>r(1));});return {exit,result:exit===0?JSON.parse(output).result:undefined};}
const change=async modify=>{const p=(await overview()).policy;modify(p);p.version++;const proposed=await fetch(base+'/api/admin/policy',{method:'PUT',headers,body:JSON.stringify(p)});assert.equal(proposed.status,202);const review=await proposed.json();const approved=await fetch(base+`/api/admin/policy-reviews/${review.reviewId}/approve`,{method:'POST',headers:{cookie:'sn_session='+process.env.SAFETYNET_SMOKE_REVIEWER,origin:base,'content-type':'application/json'},body:JSON.stringify({payloadHash:review.payloadHash,expectedVersion:review.expectedVersion,approve:true})});assert.equal(approved.status,200);await until(async()=>{const state=await ui('state');return state.ready&&state.policyVersion===p.version;});};
stage='owned native tab creation';const owned={};for(const actor of ['a','b']){const response=await call(actor,'tabs:create',{url:origin+'/seed-'+actor});assert.equal(response.exit,0,'Owned native creation refused');assert.ok(response.result.sessionId);const page=win.contentView.children.find(v=>v.webContents!==win.webContents).webContents;pages.push(page);await until(()=>!page.isLoading()&&page.getURL()===origin+'/seed-'+actor);owned[actor]={id:response.result.sessionId,page,session:page.session,record:nativeSessions.get(page.session)};assert.ok(owned[actor].record?.partitionOwned,'Actor uses immutable owned partition');assert.equal((await call(actor,'navigate',{sessionId:owned[actor].id,url:origin+'/app'})).exit,0);await until(()=>!page.isLoading()&&page.getURL()===origin+'/app');}
assert.notEqual(owned.a.session,owned.b.session);assert.notEqual(owned.a.session,human.session);for(const actor of ['a','b']){const cookies=await owned[actor].session.cookies.get({url:origin,name:'worker-session'});assert.equal(cookies.length,1);assert.equal(cookies[0].value,actor.toUpperCase());const installed=owned[actor].record.firstRequestHooks;for(const hook of ['onBeforeRequest','onBeforeSendHeaders','onBeforeRedirect','onCompleted','onErrorOccurred','setPermissionRequestHandler','setPermissionCheckHandler','registerPreloadScript','proxyReady'])assert.ok(installed.includes(hook),'Missing guard before first native request: '+hook);}
stage='cross actor control denied';assert.equal((await call('b','read',{sessionId:owned.a.id})).exit,1);assert.equal((await call('b','navigate',{sessionId:owned.a.id,url:origin+'/app'})).exit,1);
const register=page=>page.executeJavaScript(`(async()=>{try{await navigator.serviceWorker.register('/sw.js',{scope:'/'});await Promise.race([navigator.serviceWorker.ready,new Promise((_,r)=>setTimeout(()=>r(Error('deadline')),5000))]);if(!navigator.serviceWorker.controller)await new Promise(r=>{const t=setTimeout(r,2000);navigator.serviceWorker.addEventListener('controllerchange',()=>{clearTimeout(t);r();},{once:true});});return !!navigator.serviceWorker.controller;}catch{return false;}})()`);
const send=(page,url,body)=>page.executeJavaScript(`new Promise(resolve=>{const worker=navigator.serviceWorker.controller;if(!worker){resolve({ok:false});return;}const c=new MessageChannel(),t=setTimeout(()=>resolve({ok:false}),5000);c.port1.onmessage=e=>{clearTimeout(t);resolve({ok:e.data.ok===true});};worker.postMessage(${JSON.stringify({url,body})},[c.port2]);})`);
stage='background gate defaults off';if(await register(owned.a.page))assert.equal((await send(owned.a.page,origin+'/worker/a','public-worker-message')).ok,false);assert.equal((await getFacts()).facts.publicA,0);
stage='independent background policy enable';await change(p=>p.browser.allowAgentBackgroundWorkers=true);for(const actor of ['a','b']){await until(()=>!owned[actor].page.isLoading());assert.equal(await register(owned[actor].page),true,'Owned worker registration unavailable');}
stage='both native workers public requests allowed';for(const actor of ['a','b'])assert.equal((await send(owned[actor].page,origin+'/worker/'+actor,'public-worker-message')).ok,true);const workerRequests=requests.filter(r=>!r.hasWebContents&&!r.hasFrame);assert.ok(workerRequests.some(r=>r.nativeSession===owned.a.record.index));assert.ok(workerRequests.some(r=>r.nativeSession===owned.b.record.index));
stage='core exact body blocks';assert.equal((await send(owned.a.page,origin+'/worker/a','CORE_COMPANY_WORKER_LITERAL')).ok,false);stage='real local YARA body blocks';assert.equal((await send(owned.a.page,origin+'/worker/a','YARA_WORKER_ONLY_SECRET')).ok,false);assert.ok(localWitnesses.some(w=>w.decision==='block'&&w.ruleId==='worker-yara-fixture'));stage='real ICAP body blocks';assert.equal((await send(owned.a.page,origin+'/worker/a','ICAP_WORKER_ONLY_SECRET')).ok,false);stage='worker destination outside grant denied';assert.equal((await send(owned.a.page,forbiddenOrigin+'/forbidden','public-worker-message')).ok,false);
stage='human worker remains ownerless denied';const humanRegistered=await register(human);if(humanRegistered)assert.equal((await send(human,forbiddenOrigin+'/human','public-worker-message')).ok,false);
stage='scope revoke denies running owned worker';await change(p=>p.agents.grants.find(g=>g.id==='worker-a').scopes=p.agents.grants.find(g=>g.id==='worker-a').scopes.filter(s=>s!=='network:background'));if(!owned.a.page.isDestroyed())assert.equal((await send(owned.a.page,origin+'/worker/a','public-worker-message')).ok,false);let staleAllowed=false;try{const response=await owned.a.session.fetch(origin+'/worker/a',{method:'POST',body:'public-worker-message'});staleAllowed=response.ok;}catch{}assert.equal(staleAllowed,false);assert.equal((await send(owned.b.page,origin+'/worker/b','public-worker-message')).ok,true);const bCookie=await owned.b.session.cookies.get({url:origin,name:'worker-session'});assert.equal(bCookie.length,1);assert.equal(bCookie[0].value,'B');
stage='independent receiver validates absence';const receiver=await getFacts();assert.equal(receiver.facts.publicA,1);assert.equal(receiver.facts.publicB,2);for(const name of ['core','yara','icap','forbidden'])assert.equal(receiver.facts[name],0);assert.ok(receiver.avFacts.workerScans>=3);assert.equal(receiver.avFacts.blocks,1);
stage='actual worker observability';await wait(1500);const events=[];let after=0;for(let batch=0;batch<20;batch++){const response=await fetch(base+`/api/admin/events/export?after=${after}&limit=1000&format=ecs`,{headers});assert.equal(response.status,200);const rows=(await response.text()).trim().split('\n').filter(Boolean).map(line=>JSON.parse(line));for(const row of rows){assert.ok(!/CORE_COMPANY_WORKER_LITERAL|YARA_WORKER_ONLY_SECRET|ICAP_WORKER_ONLY_SECRET|worker-session=/.test(JSON.stringify(row)));events.push(row);}if(rows.length<1000)break;const next=Number(response.headers.get('x-safetynet-next-cursor'));assert.ok(next>after);after=next;}const workerEvents=events.filter(e=>e.safetynet?.agent?.action==='network:background'&&e.trace?.id);for(const principal of ['worker-a','worker-b'])assert.ok(workerEvents.some(e=>e.safetynet.agent.principal_id===principal&&e.safetynet.agent.credential_generation===1&&e.safetynet.agent.outcome==='authorized'),'Missing durable native worker owner');assert.ok(workerEvents.some(e=>e.safetynet.agent.principal_id==='worker-a'&&e.safetynet.agent.outcome==='denied'),'Revoked worker denial missing owner attribution');const traced=workerEvents.filter(e=>e.safetynet.agent.outcome==='authorized');let trace,selectedTraceId;for(const event of traced.reverse()){const candidate=await(await fetch('http://127.0.0.1:4318/api/trace/'+event.trace.id)).json();if(candidate.spans?.some(s=>s.name==='browser.request')&&candidate.spans.some(s=>s.name==='browser.inspect.local')){trace=candidate;selectedTraceId=event.trace.id;break;}}assert.ok(trace,'Native worker trace absent');assert.ok(trace.spans.some(s=>s.name==='browser.worker'),'Worker ownership stage absent');const spanMap=new Map(trace.spans.map(s=>[s.spanId,s]));const workerSpan=trace.spans.find(s=>s.name==='browser.worker');let ancestor=workerSpan,parented=false;const seen=new Set();while(ancestor&&!seen.has(ancestor.spanId)){if(ancestor.name==='browser.request'){parented=true;break;}seen.add(ancestor.spanId);ancestor=spanMap.get(ancestor.parentSpanId);}assert.ok(parented,'Native worker authorization is not correlated beneath browser.request');assert.ok(!/CORE_COMPANY_WORKER_LITERAL|YARA_WORKER_ONLY_SECRET|ICAP_WORKER_ONLY_SECRET/.test(JSON.stringify(trace)));const health=await(await fetch('http://127.0.0.1:4318/health')).json();assert.equal(health.rejectedWrites,0);const summary=await(await fetch('http://127.0.0.1:4318/api/summary')).json();
fs.writeFileSync(path.resolve('artifacts/agent-worker-ownership-native-proof.json'),JSON.stringify({actualElectron:true,exactPinnedTLS:true,independentlyPublished:true,immutableNativePartitions:true,cookiesIsolated:true,crossActorAccessDenied:true,hooksInstalledBeforeFirstRequest:true,backgroundDefaultDenied:true,workerNativeAttribution:workerRequests,humanRegistered,humanBackgroundDenied:true,coreBodyBlocked:true,actualYARABlocked:true,actualICAPBlocked:true,forbiddenHostDenied:true,existingWorkerScopeRevocationEnforced:true,staleNativeSessionDenied:true,otherActorCookiePreserved:true,receiver,records,localWitnesses,traceId:selectedTraceId,workerAuditFacts:workerEvents.map(e=>({action:e.safetynet.agent.action,outcome:e.safetynet.agent.outcome,principal:e.safetynet.agent.principal_id,generation:e.safetynet.agent.credential_generation,traceId:e.trace.id})),stages:trace.spans.map(({name,operation,spanId,parentSpanId,durationMs})=>({name,operation,spanId,parentSpanId,durationMs})),summary,health:{storedBytes:health.storedBytes,rejectedWrites:health.rejectedWrites}},null,2));console.log('Native agent worker ownership proof passed');for(const page of pages)if(!page.isDestroyed())require('../apps/browser/page-clipboard-guard.cjs').disposePageClipboardGuard(page);app.exit(0);
}catch(error){fs.writeFileSync(path.resolve('artifacts/agent-worker-ownership-native-error.json'),JSON.stringify({stage,name:error.name,message:error.message,records,requests,localWitnesses},null,2));console.error('Native worker proof failed at '+stage+': '+error.name);for(const page of pages)if(!page.isDestroyed())require('../apps/browser/page-clipboard-guard.cjs').disposePageClipboardGuard(page);app.exit(1);}});
+29
View File
@@ -0,0 +1,29 @@
// Native worker proof: real policy publication, DLP, AV and independent receivers.
import {createServer} from 'node:https';
import {createRequire} from 'node:module';
import {mkdtempSync,mkdirSync,rmSync,readFileSync} from 'node:fs';
import {tmpdir} from 'node:os';
import {join,resolve,sep} from 'node:path';
import {spawn} from 'node:child_process';
import {randomBytes} from 'node:crypto';
import net from 'node:net';
import {createApp} from '../apps/server/app.mjs';
import {openStore,digest} from '../apps/server/store.mjs';
import {startTelemetry} from '../apps/server/telemetry.mjs';
const collector='http://127.0.0.1:4318';if(!(await fetch(collector+'/health')).ok)throw Error('Protected collector unavailable');
process.env.OTEL_EXPORTER_OTLP_ENDPOINT=collector;const telemetry=startTelemetry();
const temporary=mkdtempSync(join(tmpdir(),'safetynet-worker-owner-'));mkdirSync('artifacts',{recursive:true});
if(!resolve(temporary).startsWith(resolve(tmpdir())+sep)||!temporary.split(/[\\/]/).at(-1).startsWith('safetynet-worker-owner-'))throw Error('Unsafe temporary fixture path');
const markers={core:'CORE_COMPANY_WORKER_LITERAL',yara:'YARA_WORKER_ONLY_SECRET',icap:'ICAP_WORKER_ONLY_SECRET'},facts={publicA:0,publicB:0,core:0,yara:0,icap:0,forbidden:0},avFacts={scans:0,workerScans:0,blocks:0},avSockets=new Set();
const av=net.createServer(socket=>{avSockets.add(socket);socket.on('close',()=>avSockets.delete(socket));socket.on('error',()=>{});let input=Buffer.alloc(0),done=false;socket.on('data',chunk=>{input=Buffer.concat([input,chunk]);if(input.length>4194304){input.fill(0);socket.destroy();return;}if(done||input.indexOf('\r\n\r\n')<0)return;const options=input.subarray(0,7).toString()==='OPTIONS';if(!options&&!input.subarray(-5).equals(Buffer.from('0\r\n\r\n')))return;done=true;if(options){input.fill(0);socket.end('ICAP/1.0 200 OK\r\nMethods: RESPMOD, REQMOD\r\nAllow: 204\r\nISTag: "worker-fixture"\r\nEncapsulated: null-body=0\r\n\r\n');return;}avFacts.scans++;if(input.includes(Buffer.from('public-worker-message')))avFacts.workerScans++;const blocked=input.includes(Buffer.from(markers.icap));if(blocked)avFacts.blocks++;input.fill(0);socket.end(blocked?'ICAP/1.0 204 No Content\r\nISTag: "worker-fixture"\r\nX-Infection-Found: Type=0; Threat=synthetic-worker;\r\n\r\n':'ICAP/1.0 204 No Content\r\nISTag: "worker-fixture"\r\n\r\n');});}).listen(0,'127.0.0.1');await new Promise(r=>av.once('listening',r));
const tls={cert:readFileSync('tests/fixtures/icap/cert.pem'),key:readFileSync('tests/fixtures/icap/key.pem')};
const forbidden=createServer(tls,(req,res)=>{facts.forbidden++;req.resume();res.end('public');}).listen(0,'127.0.0.2');await new Promise(r=>forbidden.once('listening',r));
const forbiddenOrigin=`https://127.0.0.2:${forbidden.address().port}`;
const worker=`self.addEventListener('install',e=>e.waitUntil(self.skipWaiting()));self.addEventListener('activate',e=>e.waitUntil(self.clients.claim()));self.addEventListener('message',e=>{const p=e.ports[0];e.waitUntil((async()=>{try{const r=await fetch(e.data.url,{method:'POST',headers:{'content-type':'text/plain'},body:e.data.body,credentials:'include'});p.postMessage({ok:r.ok});}catch{p.postMessage({ok:false});}})());});`;
const sink=createServer(tls,(req,res)=>{if(req.url==='/facts'){res.setHeader('content-type','application/json');res.end(JSON.stringify({facts,avFacts}));return;}if(req.url==='/sw.js'){res.setHeader('content-type','application/javascript');res.setHeader('service-worker-allowed','/');res.end(worker);return;}if(req.method==='POST'){let chunks=[],size=0;req.on('data',chunk=>{size+=chunk.length;if(size>65536){req.destroy();return;}chunks.push(chunk);});req.on('end',()=>{const body=Buffer.concat(chunks);for(const [kind,marker]of Object.entries(markers))if(body.includes(Buffer.from(marker)))facts[kind]++;if(body.equals(Buffer.from('public-worker-message'))){if(req.url==='/worker/a')facts.publicA++;else if(req.url==='/worker/b')facts.publicB++;}for(const chunk of chunks)chunk.fill(0);body.fill(0);chunks=[];res.end('accepted');});return;}if(req.url==='/seed-a')res.setHeader('set-cookie','worker-session=A; Secure; HttpOnly; SameSite=Strict; Path=/');if(req.url==='/seed-b')res.setHeader('set-cookie','worker-session=B; Secure; HttpOnly; SameSite=Strict; Path=/');res.setHeader('content-type','text/html');res.end('<!doctype html><title>Public worker fixture</title><h1>Public worker ownership fixture</h1>');}).listen(0,'127.0.0.1');await new Promise(r=>sink.once('listening',r));
const origin=`https://127.0.0.1:${sink.address().port}`,store=openStore(join(temporary,'db')),code=randomBytes(32).toString('base64url'),adminToken=randomBytes(32).toString('base64url');
const p=store.get('policy');p.browser.homeUrl=origin+'/app';p.browser.allowAgentBackgroundWorkers=false;p.dlp.rules.push({id:'worker-core-fixture',label:'Synthetic confidential worker content',kind:'literal',value:markers.core,action:'block'});p.security.yara.enabled=true;p.security.yara.rules=[{id:'worker-yara-fixture',name:'Synthetic worker YARA',source:`rule worker_yara_fixture { strings: $a = "${markers.yara}" condition: $a }`}];p.enterprise.icap={...p.enterprise.icap,enabled:true,url:`icap://127.0.0.1:${av.address().port}/scan`,scanUploads:true,scanDownloads:false,scanClipboard:false,scanAI:false};
const scopes=['sessions','navigate','read','tabs:create','network:background'];p.agents={...p.agents,enabled:true,identityMode:'individual',scopes,allowedHosts:['127.0.0.1'],grants:['worker-a','worker-b'].map(id=>({id,name:id,enabled:true,expiresAt:new Date(Date.now()+3600000).toISOString(),scopes,allowedHosts:['127.0.0.1'],generation:1}))};p.diagnostics={...p.diagnostics,enabled:true,mode:'debug',endpoint:collector,sampleRate:1,debugSampleRate:1,flushIntervalMs:250};store.put('policy',p);store.db.prepare('INSERT INTO enrollments VALUES(?,?,?)').run(digest(code),Date.now()+600000,'worker-fixture');
const config={secure:false,publicUrl:'http://127.0.0.1',adminToken,requirePQManagement:true,requireIndependentPolicyApproval:true};const management=createApp(store,config).listen(0,'127.0.0.1');await new Promise(r=>management.once('listening',r));config.publicUrl=`http://127.0.0.1:${management.address().port}`;const reviewer=randomBytes(32).toString('base64url');store.db.prepare('INSERT INTO sessions VALUES(?,?,?)').run(digest(reviewer),Date.now()+600000,JSON.stringify({sub:'independent-worker-reviewer',role:'admin'}));
const child=spawn(createRequire(import.meta.url)('electron'),['scripts/agent-worker-ownership-smoke.cjs'],{windowsHide:true,stdio:['ignore','pipe','pipe'],env:{...process.env,SAFETYNET_SMOKE_SERVER:config.publicUrl,SAFETYNET_SMOKE_CODE:code,SAFETYNET_SMOKE_ADMIN:adminToken,SAFETYNET_SMOKE_REVIEWER:reviewer,SAFETYNET_SMOKE_USERDATA:join(temporary,'browser'),SAFETYNET_SMOKE_ORIGIN:origin,SAFETYNET_SMOKE_FORBIDDEN_ORIGIN:forbiddenOrigin}});child.stdout.on('data',v=>process.stdout.write(v));child.stderr.on('data',v=>process.stderr.write(v));const deadline=setTimeout(()=>child.kill(),240000);const exit=await new Promise(r=>{child.once('exit',v=>r(v??1));child.once('error',()=>r(1));});clearTimeout(deadline);
for(const socket of avSockets)socket.destroy();await new Promise(r=>av.close(r));for(const server of [management,sink,forbidden])server.closeAllConnections();await Promise.all([management,sink,forbidden].map(server=>new Promise(r=>server.close(r))));store.db.close();await telemetry?.shutdown();rmSync(temporary,{recursive:true,force:true});process.exit(exit);
+1 -1
View File
@@ -1,7 +1,6 @@
// Finite, isolated administrator workflow proof using actual UI and HTTP routes.
import assert from 'node:assert/strict';
import { createRequire } from 'node:module';
import { createServer } from 'node:net';
import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join, resolve, sep, basename } from 'node:path';
@@ -22,5 +21,6 @@ try{
const policy=verifyPolicy(store.envelope(),store.publicKey());assert.equal(policy.version,3);assert.deepEqual(policy.browser.bookmarks.managed.map(({title,url,folder})=>({title,url,folder})),[{title:'Company intranet',url:'https://intranet.synthetic.example/',folder:'Company'}]);assert.equal(policy.browser.bookmarks.adminReadUserBookmarks,false);assert.equal(store.personalBookmarks('synthetic-bookmark-device').revision,1);assert.equal(store.personalBookmarks('synthetic-bookmark-device').items[0].title,'Remote personal reference');
const report={actualAdministratorUi:true,managedTableRoundtrip:true,explicitPublication:true,deviceReadEdit:true,revisionCompared:true,deviceEditGlobalPolicyUnchanged:true,privacyRevoked:true,nodeUnavailable:true,cspPreserved:true};mkdirSync('artifacts',{recursive:true});writeFileSync('artifacts/bookmarks-admin-proof.json',JSON.stringify(report,null,2));console.log(JSON.stringify(report));
}finally{
// eslint-disable-next-line no-unsafe-finally -- Cleanup failure must fail closed rather than report successful protected cleanup.
child?.kill();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-bookmarks-admin-smoke-'))throw Error('Unsafe proof cleanup');rmSync(target,{recursive:true,force:true,maxRetries:10,retryDelay:100});
}
+1 -1
View File
@@ -6,7 +6,7 @@ app.setPath('userData',process.env.SAFETYNET_SMOKE_USERDATA);app.disableHardware
app.whenReady().then(()=>{const requests=session.fromPartition('persist:safetynet-managed').webRequest,original=requests.onBeforeSendHeaders.bind(requests);requests.onBeforeSendHeaders=(filter,listener)=>original(filter,(details,callback)=>{if(details.url.endsWith('/public')){const entries=Object.entries(details.requestHeaders),one=entries.find(([name])=>name.toLowerCase()==='x-credential-one'),two=entries.find(([name])=>name.toLowerCase()==='x-credential-two'),known='fixtureControlledCredentialDelta0123456789';if(one&&two){headerFacts.push({names:entries.map(([name])=>name),fragmentOneIndex:entries.indexOf(one),fragmentTwoIndex:entries.indexOf(two),fullJoinMatch:entries.map(([,value])=>value).join('').includes(known),pairJoinMatch:(one[1]+two[1])===known,reversePairJoinMatch:(two[1]+one[1])===known});fs.writeFileSync(path.resolve('artifacts/controlled-login-header-facts.json'),JSON.stringify(headerFacts,null,2));}}if(fixtureCredentialHeader&&details.resourceType==='mainFrame'&&details.method==='POST')details.requestHeaders['X-Synthetic-Credential']='sk-syntheticControlledLoginCredential0123456789';listener(details,callback);});});require('../apps/browser/main.cjs');
const wait=ms=>new Promise(r=>setTimeout(r,ms)),base=process.env.SAFETYNET_SMOKE_SERVER,origin=process.env.SAFETYNET_SMOKE_ORIGIN,headers={authorization:`Bearer ${process.env.SAFETYNET_SMOKE_ADMIN}`,'content-type':'application/json'};
async function until(fn){for(let i=0;i<200;i++){const r=await fn();if(r)return r;await wait(100);}throw Error('Controlled login fixture timeout');}
app.whenReady().then(async()=>{const pages=[];let stage='startup';try{const win=await until(()=>{const w=BrowserWindow.getAllWindows()[0];return w&&!w.webContents.isLoading()&&w.webContents.getURL().endsWith('index.html')&&w;});const fingerprint=new X509Certificate(fs.readFileSync(path.join(__dirname,'../tests/fixtures/icap/cert.pem'))).fingerprint256;session.fromPartition('persist:safetynet-managed').setCertificateVerifyProc((req,callback)=>callback(req.hostname==='127.0.0.1'&&new X509Certificate(req.certificate.data).fingerprint256===fingerprint?0:-2));const ui=async(method,value)=>{const result=await win.webContents.executeJavaScript(`window.safetynet.${method}(${value===undefined?'':JSON.stringify(value)})`);if(!result.ok)throw Object.assign(Error('Native UI operation failed'),{operation:method,reason:/not managed|not ready|paused/i.test(result.error||'')?'not-ready':/context.*changed/i.test(result.error||'')?'context-changed':/BLOCKED_BY_CLIENT/i.test(result.error||'')?'native-request-blocked':/ERR_ABORTED/i.test(result.error||'')?'native-navigation-aborted':'ui-operation-refused'});return result.data;};const overview=async()=>(await fetch(base+'/api/admin/overview',{headers})).json();stage='enrollment';await ui('enroll',{server:base,code:process.env.SAFETYNET_SMOKE_CODE});const page=win.contentView.children.find(v=>v.webContents!==win.webContents).webContents;pages.push(page);await until(()=>!page.isLoading()&&page.getURL()===origin+'/login');const a=await ui('agentConnection','vault-a'),b=await ui('agentConnection','vault-b'),requestFile=path.join(app.getPath('userData'),'controlled-request.json');async function call(connection,action,args={}){fs.writeFileSync(requestFile,JSON.stringify({action,args}));const child=require('node:child_process').spawn(process.execPath,['scripts/agent-client.cjs','--connection',connection.credentialPath,'--request',requestFile],{windowsHide:true,stdio:['ignore','pipe','pipe']});let output='';child.stdout.on('data',v=>output+=v);child.stderr.on('data',()=>{});const exit=await new Promise(r=>{child.once('exit',r);child.once('error',()=>r(1));});return {exit,result:exit===0?JSON.parse(output).result:undefined};}const sessionId=(await call(a,'sessions')).result[0].id,master='Synthetic master password for controlled native proof',password='sk-syntheticControlledLoginCredential0123456789',username='public-native-user',alternatePassword='fixtureControlledCredentialDelta0123456789';stage='human unlock';await ui('vaultCreate',master);const saved=await ui('vaultSave',{origin,username,password,label:'Approved synthetic login'});const reset=async(mode='200')=>{await ui('navigate',origin+'/login/'+mode);await until(()=>!page.isLoading()&&page.getURL()===origin+'/login/'+mode);await ui('vaultUnlock',master);win.focus();page.focus();await wait(100);};const stats=()=>new Promise((resolve,reject)=>{const req=require('node:https').get(origin+'/stats',{ca:fs.readFileSync(path.join(__dirname,'../tests/fixtures/icap/cert.pem')),checkServerIdentity:(_host,cert)=>new X509Certificate(cert.raw).fingerprint256===fingerprint?undefined:Error('Synthetic certificate mismatch')},res=>{let body='';res.on('data',v=>body+=v);res.on('end',()=>resolve(JSON.parse(body)));});req.on('error',reject);});const inventory=async()=>{const r=await call(a,'vault:list',{sessionId});assert.equal(r.exit,0);return r.result[0].credentialRef;};const submit=async()=>{const terminal=new Promise((resolve,reject)=>{const timer=setTimeout(()=>{page.removeListener('did-stop-loading',done);reject(Error('Native form navigation terminal timeout'));},22000);const done=()=>{clearTimeout(timer);resolve();};page.once('did-stop-loading',done);});await page.executeJavaScript("document.querySelector('form').requestSubmit()");await terminal;await wait(200);};const prepare=async()=>{const ref=await inventory(),r=await call(a,'vault:prepare-submit',{sessionId,credentialRef:ref});assert.equal(r.exit,0,'Agent prepare failed');assert.deepEqual(r.result,{completed:true,prepared:true});return ref;};const count=async()=>{const result=await fetch(base+'/api/admin/overview',{headers});assert.equal(result.status,200);return (await result.json()).events.filter(e=>e.kind==='vault.submission-authorized').length;};
app.whenReady().then(async()=>{const pages=[];let stage='startup';try{const win=await until(()=>{const w=BrowserWindow.getAllWindows()[0];return w&&!w.webContents.isLoading()&&w.webContents.getURL().endsWith('index.html')&&w;});const fingerprint=new X509Certificate(fs.readFileSync(path.join(__dirname,'../tests/fixtures/icap/cert.pem'))).fingerprint256;session.fromPartition('persist:safetynet-managed').setCertificateVerifyProc((req,callback)=>callback(req.hostname==='127.0.0.1'&&new X509Certificate(req.certificate.data).fingerprint256===fingerprint?0:-2));const ui=async(method,value)=>{const result=await win.webContents.executeJavaScript(`window.safetynet.${method}(${value===undefined?'':JSON.stringify(value)})`);if(!result.ok)throw Object.assign(Error('Native UI operation failed'),{operation:method,reason:/not managed|not ready|paused/i.test(result.error||'')?'not-ready':/context.*changed/i.test(result.error||'')?'context-changed':/BLOCKED_BY_CLIENT/i.test(result.error||'')?'native-request-blocked':/ERR_ABORTED/i.test(result.error||'')?'native-navigation-aborted':'ui-operation-refused'});return result.data;};const overview=async()=>(await fetch(base+'/api/admin/overview',{headers})).json();stage='enrollment';await ui('enroll',{server:base,code:process.env.SAFETYNET_SMOKE_CODE});const page=win.contentView.children.find(v=>v.webContents!==win.webContents).webContents;pages.push(page);await until(()=>!page.isLoading()&&page.getURL()===origin+'/login');const a=await ui('agentConnection','vault-a'),b=await ui('agentConnection','vault-b'),requestFile=path.join(app.getPath('userData'),'controlled-request.json');async function call(connection,action,args={}){fs.writeFileSync(requestFile,JSON.stringify({action,args}));const child=require('node:child_process').spawn(process.execPath,['scripts/agent-client.cjs','--connection',connection.credentialPath,'--request',requestFile],{windowsHide:true,stdio:['ignore','pipe','pipe']});let output='';child.stdout.on('data',v=>output+=v);child.stderr.on('data',()=>{});const exit=await new Promise(r=>{child.once('exit',r);child.once('error',()=>r(1));});return {exit,result:exit===0?JSON.parse(output).result:undefined};}const sessionId=(await call(a,'sessions')).result[0].id,master='Synthetic master password for controlled native proof',password='sk-syntheticControlledLoginCredential0123456789',username='public-native-user',alternatePassword='fixtureControlledCredentialDelta0123456789';stage='human unlock';await ui('vaultCreate',master);const saved=await ui('vaultSave',{origin,username,password,label:'Approved synthetic login'});const reset=async(mode='200')=>{await ui('navigate',origin+'/login/'+mode);await until(()=>!page.isLoading()&&page.getURL()===origin+'/login/'+mode);await ui('vaultUnlock',master);win.focus();page.focus();await wait(100);};const stats=()=>new Promise((resolve,reject)=>{const req=require('node:https').get(origin+'/stats',{ca:fs.readFileSync(path.join(__dirname,'../tests/fixtures/icap/cert.pem')),checkServerIdentity:(_host,cert)=>new X509Certificate(cert.raw).fingerprint256===fingerprint?undefined:Error('Synthetic certificate mismatch')},res=>{let body='';res.on('data',v=>body+=v);res.on('end',()=>resolve(JSON.parse(body)));});req.on('error',reject);});const inventory=async()=>{const r=await call(a,'vault:list',{sessionId});assert.equal(r.exit,0);return r.result[0].credentialRef;};const submit=async()=>{const terminal=new Promise((resolve,reject)=>{const timer=setTimeout(()=>{page.removeListener('did-stop-loading',done);reject(Error('Native form navigation terminal timeout'));},22000);const done=()=>{clearTimeout(timer);resolve();};page.once('did-stop-loading',done);});await page.executeJavaScript("document.querySelector('form').requestSubmit()");await terminal;await wait(200);};const prepare=async()=>{const ref=await inventory(),r=await call(a,'vault:prepare-submit',{sessionId,credentialRef:ref});assert.equal(r.exit,0,'Agent prepare failed');assert.deepEqual(r.result,{completed:true,prepared:true});return ref;};
stage='ordinary fill remains DLP blocked';await reset();assert.equal((await call(a,'vault:fill',{sessionId,credentialRef:await inventory()})).exit,0);await submit();assert.equal((await stats()).submitted,0);
stage='agent prepare does not submit';await reset();let ref=await prepare();assert.equal((await stats()).submitted,0);assert.equal((await call(a,'vault:prepare-submit',{sessionId,credentialRef:ref})).exit,1);assert.equal((await call(b,'vault:prepare-submit',{sessionId,credentialRef:await inventory()})).exit,1);await submit();await until(()=>!page.isLoading()&&page.getURL()===origin+'/signin');assert.equal((await stats()).submitted,1);
stage='controlled302 safeGET';await reset('302');await prepare();await submit();await until(()=>!page.isLoading()&&page.getURL()===origin+'/final');assert.equal((await stats()).submitted,2);assert.equal((await stats()).finalGets,1);
+1
View File
@@ -5,4 +5,5 @@ try{
store=openStore(join(directory,'management'));const policy=store.get('policy');policy.browser.homeUrl=sinkUrl;policy.browser.allowedHosts=['127.0.0.1'];store.put('policy',policy);const code=randomBytes(32).toString('base64url'),adminToken=randomBytes(32).toString('base64url');store.db.prepare('INSERT INTO enrollments VALUES(?,?,?)').run(digest(code),Date.now()+600000,'synthetic-developer');const config={adminToken,publicUrl:'http://127.0.0.1',secure:false};server=createApp(store,config).listen(0,'127.0.0.1');await new Promise(r=>server.once('listening',r));config.publicUrl=`http://127.0.0.1:${server.address().port}`;
const env={...process.env,SAFETYNET_DEVTOOLS_SERVER:config.publicUrl,SAFETYNET_DEVTOOLS_CODE:code,SAFETYNET_DEVTOOLS_ADMIN:adminToken,SAFETYNET_DEVTOOLS_SINK:sinkUrl,SAFETYNET_DEVTOOLS_USERDATA:join(directory,'browser'),SAFETYNET_SMOKE_USERDATA:join(directory,'browser')};delete env.ELECTRON_RUN_AS_NODE;
child=spawn(require('electron'),['scripts/devtools-profile-smoke.cjs'],{env,windowsHide:true,stdio:['ignore','pipe','pipe']});child.stdout.on('data',b=>process.stdout.write(b));child.stderr.on('data',b=>process.stderr.write(String(b).replaceAll(code,'[fixture enrollment]').replaceAll(adminToken,'[fixture admin]')));const timer=setTimeout(()=>child.kill(),90000);const exit=await new Promise(r=>{child.once('exit',c=>r(c??1));child.once('error',()=>r(1));});clearTimeout(timer);assert.equal(exit,0,'Native developer-profile proof failed');
// eslint-disable-next-line no-unsafe-finally -- Cleanup failure must fail closed rather than report successful protected cleanup.
}finally{child?.kill();for(const service of [server,sink])if(service)await new Promise(r=>{service.closeAllConnections();service.close(r);});store?.db.close();const target=resolve(directory);if(!target.startsWith(resolve(tmpdir())+sep)||!basename(target).startsWith('safetynet-devtools-proof-'))throw Error('Unsafe fixture cleanup');rmSync(target,{recursive:true,force:true,maxRetries:10,retryDelay:100});}
+1
View File
@@ -24,5 +24,6 @@ try{
assert.equal(policy.diagnostics.enabled,true);assert.equal(policy.diagnostics.mode,'debug');assert.equal(policy.diagnostics.endpoint,'https://collector.synthetic.example');assert.ok(policy.agents.scopes.includes('bookmarks:mutate'));
const report={signedDiagnosticsUi:true,newAgentScopesUi:true,adminUi:true,isolatedControlPlane:true,previewDidNotPublish:true,explicitSignedPublication:true,integrationFormRoundtrip:true,optionsOnlyProbe:true,probeContentSent:false,cspPreserved:true,nodeUnavailable:true,hiddenCpuWindow:true,policyVersion:policy.version,groupedNavigation:true,draftChangeReview:true,reviewDoesNotPublish:true,fleetAndAuditFilters:true,consoleThemePersistence:true,mobileNoHorizontalOverflow:true,readOnlyRoleControls:true,sessionInventoryUi:true};mkdirSync('artifacts',{recursive:true});writeFileSync('artifacts/enterprise-admin-proof.json',JSON.stringify(report,null,2));console.log(JSON.stringify(report));
}finally{
// eslint-disable-next-line no-unsafe-finally -- Cleanup failure must fail closed rather than report successful protected cleanup.
child?.kill();for(const service of [server,icap])if(service)await new Promise(r=>{service.closeAllConnections?.();service.close(r);});store?.db.close();const target=resolve(directory);if(!target.startsWith(resolve(tmpdir())+sep)||!basename(target).startsWith('safetynet-enterprise-admin-smoke-'))throw Error('Unsafe proof cleanup');rmSync(target,{recursive:true,force:true,maxRetries:10,retryDelay:100});
}
+14
View File
@@ -0,0 +1,14 @@
'use strict';
const {app,BrowserWindow,session}=require('electron'),fs=require('node:fs'),path=require('node:path'),assert=require('node:assert/strict'),{X509Certificate}=require('node:crypto');
app.setPath('userData',process.env.SAFETYNET_SMOKE_USERDATA);app.disableHardwareAcceleration();const mode=process.env.SAFETYNET_WT_MODE,target=process.env.SAFETYNET_WT_TARGET,hashBytes=Array.from(Buffer.from(process.env.SAFETYNET_WT_HASH,'hex')),origin=process.env.SAFETYNET_SMOKE_ORIGIN;let actualDiagnostics,nativeWTCallbacks=0;
if(mode==='managed'){const partition=session.fromPartition.bind(session),seenSessions=new WeakSet();session.fromPartition=(...args)=>{const current=partition(...args);if(!seenSessions.has(current)){seenSessions.add(current);const register=current.webRequest.onBeforeRequest.bind(current.webRequest);current.webRequest.onBeforeRequest=(...values)=>{const index=values.length-1,listener=values[index];if(typeof listener==='function')values[index]=(details,callback)=>{if(details.url?.startsWith(target))nativeWTCallbacks++;return listener(details,callback);};return register(...values);};}return current;};const diagnosticModule=require('../apps/browser/diagnostics.cjs'),createDiagnostics=diagnosticModule.createDiagnostics;diagnosticModule.createDiagnostics=(...args)=>{const value=createDiagnostics(...args);actualDiagnostics=value;return value;};require('../apps/browser/main.cjs');}else app.enableSandbox();
// Executed in the real Chromium Window and dedicated Worker, without a shim.
/* global WebTransport */
async function attemptWT(url,hash){const result={api:typeof WebTransport==='function',connected:false,datagramWritten:false,streamWritten:false,errorClass:null};let transport;try{transport=new WebTransport(url,{serverCertificateHashes:[{algorithm:'sha-256',value:new Uint8Array(hash)}]});await Promise.race([transport.ready,new Promise((_,reject)=>setTimeout(()=>reject(Error('timeout')),5000))]);result.connected=true;const bytes=new TextEncoder().encode('SYNTHETIC_WEBTRANSPORT_DLP_MARKER'),datagram=transport.datagrams.writable.getWriter();await datagram.write(bytes);datagram.releaseLock();result.datagramWritten=true;const stream=await transport.createUnidirectionalStream(),writer=stream.getWriter();await writer.write(bytes);await writer.close();result.streamWritten=true;await new Promise(r=>setTimeout(r,350));}catch(error){result.errorClass=['Error','TypeError','WebTransportError','NetworkError'].includes(error.name)?error.name:'OtherError';}finally{transport?.close();}return result;}
const wait=ms=>new Promise(r=>setTimeout(r,ms));async function until(fn){for(let i=0;i<300;i++){const value=await fn();if(value)return value;await wait(100);}throw Error('Full main WebTransport fixture timeout');}
async function attemptRealms(page){const window=await page.executeJavaScript(`(${attemptWT.toString()})(${JSON.stringify(target)},${JSON.stringify(hashBytes)})`);const source=`const attempt=${attemptWT.toString()};onmessage=async e=>{postMessage(await attempt(e.data.url,e.data.hash));close();};`,worker=await page.executeJavaScript(`new Promise((resolve,reject)=>{const url=URL.createObjectURL(new Blob([${JSON.stringify(source)}],{type:'text/javascript'})),worker=new Worker(url),timer=setTimeout(()=>{worker.terminate();URL.revokeObjectURL(url);reject(Error('Worker timeout'));},8000);worker.onmessage=e=>{clearTimeout(timer);worker.terminate();URL.revokeObjectURL(url);resolve(e.data)};worker.onerror=()=>{clearTimeout(timer);worker.terminate();URL.revokeObjectURL(url);reject(Error('Worker fault'));};worker.postMessage({url:${JSON.stringify(target+'/worker')},hash:${JSON.stringify(hashBytes)}});})`);return {window,worker};}
app.whenReady().then(async()=>{let page,win,stage='startup';try{const fingerprint=new X509Certificate(fs.readFileSync(path.join(__dirname,'../tests/fixtures/icap/cert.pem'))).fingerprint256;if(mode==='baseline'){session.defaultSession.setCertificateVerifyProc((req,cb)=>{let valid=false;try{valid=req.hostname==='127.0.0.1'&&new X509Certificate(req.certificate.data).fingerprint256===fingerprint;}catch{}cb(valid?0:-2);});let callbacks=0;session.defaultSession.webRequest.onBeforeRequest({urls:[target+'*']},(_details,callback)=>{callbacks++;callback({cancel:true});});win=new BrowserWindow({show:false,webPreferences:{sandbox:true,nodeIntegration:false,contextIsolation:true}});page=win.webContents;await win.loadURL(origin+'/workspace');const realms=await attemptRealms(page);assert.equal(callbacks,0);for(const realm of [realms.window,realms.worker])assert.ok(realm.connected&&realm.datagramWritten&&realm.streamWritten);fs.writeFileSync(process.env.SAFETYNET_WT_OUTPUT,JSON.stringify({actualElectron:true,mode,realms,webRequestCallbacks:callbacks,electron:process.versions.electron,chromium:process.versions.chrome},null,2));win.destroy();app.exit(0);return;}
win=await until(()=>{const w=BrowserWindow.getAllWindows()[0];return w&&!w.webContents.isLoading()&&w.webContents.getURL().endsWith('index.html')&&w;});for(const partition of ['persist:safetynet-managed','persist:safetynet-management'])session.fromPartition(partition).setCertificateVerifyProc((req,cb)=>{let valid=false;try{valid=req.hostname==='127.0.0.1'&&new X509Certificate(req.certificate.data).fingerprint256===fingerprint;}catch{}cb(valid?0:-2);});const base=process.env.SAFETYNET_SMOKE_SERVER,headers={authorization:'Bearer '+process.env.SAFETYNET_SMOKE_ADMIN,'content-type':'application/json'},ui=async(method,value)=>{const response=await win.webContents.executeJavaScript(`window.safetynet.${method}(${value===undefined?'':JSON.stringify(value)})`);assert.equal(response.ok,true);return response.data;};stage='actual enrollment';await ui('enroll',{server:base,code:process.env.SAFETYNET_SMOKE_CODE});page=win.contentView.children.find(v=>v.webContents!==win.webContents).webContents;await until(()=>!page.isLoading()&&page.getURL()===origin+'/workspace');const phases=[];const test=async name=>{stage=name;const before=await(await fetch(process.env.SAFETYNET_WT_STATS)).json(),resolvedProxy=await page.session.resolveProxy(target);assert.notEqual(resolvedProxy,'DIRECT');const realms=await attemptRealms(page);for(const realm of [realms.window,realms.worker])assert.ok(!realm.connected&&!realm.datagramWritten&&!realm.streamWritten);const after=await(await fetch(process.env.SAFETYNET_WT_STATS)).json();for(const field of ['packets','connections','datagrams','streams','bytes','workerBytes'])assert.equal(after[field],before[field],'WebTransport reached receiver');assert.equal(await page.executeJavaScript("fetch('/receiver',{method:'POST',body:'Public full main ordinary prompt'}).then(r=>r.ok).catch(()=>false)"),true);phases.push({name,policyVersion:(await ui('state')).policyVersion,proxyIsNonDirect:true,realms,receiverDelta:Object.fromEntries(['packets','connections','datagrams','streams','bytes','workerBytes'].map(key=>[key,after[key]-before[key]])),benignHTTPSPostAllowed:true});};await test('enrolled private relay');
const change=async proxy=>{const p=(await(await fetch(base+'/api/admin/overview',{headers})).json()).policy;p.version++;p.proxy=proxy;const proposal=await fetch(base+'/api/admin/policy',{method:'PUT',headers,body:JSON.stringify(p)});assert.equal(proposal.status,202);const review=await proposal.json();const approved=await fetch(base+`/api/admin/policy-reviews/${review.reviewId}/approve`,{method:'POST',headers:{cookie:'sn_session='+process.env.SAFETYNET_SMOKE_REVIEWER,origin:base,'content-type':'application/json'},body:JSON.stringify({payloadHash:review.payloadHash,expectedVersion:review.expectedVersion,approve:true})});assert.equal(approved.status,200);await until(async()=>{const state=await ui('state');return state.ready&&state.policyVersion===p.version;});await until(()=>!page.isLoading()&&page.getURL()===origin+'/workspace');};stage='reviewed direct upstream policy';await change({mode:'direct'});await test('reviewed DIRECT upstream retains relay');stage='reviewed PAC direct upstream policy';await change({mode:'pac_script',pacUrl:origin+'/direct.pac'});await test('reviewed PAC DIRECT upstream retains relay');
stage='actual ordinary request observability';assert.equal(nativeWTCallbacks,0);await actualDiagnostics.flush();await wait(1500);const events=[];let cursor=0;for(let batch=0;batch<20;batch++){const response=await fetch(base+`/api/admin/events/export?after=${cursor}&limit=1000&format=ecs`,{headers});assert.equal(response.status,200);const rows=(await response.text()).trim().split('\n').filter(Boolean).map(JSON.parse);events.push(...rows);if(rows.length<1000)break;cursor=Number(response.headers.get('x-safetynet-next-cursor'));}const event=events.filter(e=>e.event.action==='upload.authorized'&&e.trace?.id).at(-1);assert.ok(event);const trace=await(await fetch('http://127.0.0.1:4318/api/trace/'+event.trace.id)).json();assert.ok(trace.spans.some(s=>s.name==='browser.request'));const diagnostics=actualDiagnostics.status();assert.equal(diagnostics.failedExports,0);assert.equal(diagnostics.lifetimeFailedExports,0);const health=await(await fetch('http://127.0.0.1:4318/health')).json();assert.equal(health.rejectedWrites,0);fs.writeFileSync(process.env.SAFETYNET_WT_OUTPUT,JSON.stringify({actualFullMain:true,mode,phases,nativeWebRequestCallbacks:nativeWTCallbacks,ordinaryTraceId:event.trace.id,ordinaryStages:trace.spans.map(({name,operation,spanId,parentSpanId,durationMs})=>({name,operation,spanId,parentSpanId,durationMs})),unsupportedTransportAuditGap:'Chromium WebTransport bypasses Electron webRequest; receiver absence is native containment evidence, not an attributed per-attempt DLP audit event.',diagnostics,health:{storedBytes:health.storedBytes,rejectedWrites:health.rejectedWrites}},null,2));console.log('Full enrolled main WebTransport protection passed');require('../apps/browser/page-clipboard-guard.cjs').disposePageClipboardGuard(page);app.exit(0);
}catch(error){fs.writeFileSync(path.resolve('artifacts/fullmain-wt-native-error.json'),JSON.stringify({mode,stage,name:error.name},null,2));console.error('Full main WebTransport failed at '+stage+': '+error.name);if(mode==='managed'&&page&&!page.isDestroyed())require('../apps/browser/page-clipboard-guard.cjs').disposePageClipboardGuard(page);app.exit(1);}});
+21
View File
@@ -0,0 +1,21 @@
// Real Window/Worker QUIC control, then enrolled production main across policies.
import assert from 'node:assert/strict';
import {createRequire} from 'node:module';
import {createServer} from 'node:https';
import {spawn} from 'node:child_process';
import {randomBytes,createHash} from 'node:crypto';
import {mkdtempSync,mkdirSync,readFileSync,writeFileSync,rmSync} from 'node:fs';
import {tmpdir} from 'node:os';
import {join,resolve,sep} from 'node:path';
import {createApp} from '../apps/server/app.mjs';
import {openStore,digest} from '../apps/server/store.mjs';
import {startTelemetry} from '../apps/server/telemetry.mjs';
const collector='http://127.0.0.1:4318';assert.equal((await fetch(collector+'/health')).ok,true);process.env.OTEL_EXPORTER_OTLP_ENDPOINT=collector;const telemetry=startTelemetry(),root=mkdtempSync(join(tmpdir(),'safetynet-fullmain-wt-'));mkdirSync('artifacts',{recursive:true});assert.ok(resolve(root).startsWith(resolve(tmpdir())+sep)&&root.split(/[\\/]/).at(-1).startsWith('safetynet-fullmain-wt-'));
const files=['apps/browser/main.cjs','apps/browser/startup-security.cjs','apps/browser/local-dlp-relay.cjs','apps/browser/diagnostics.cjs','packages/core/policy.mjs','package.json'],sourceHashFacts=Object.fromEntries(files.map(file=>[file,createHash('sha256').update(readFileSync(file)).digest('hex')]));let python,page,management,store,child,stage='receiver startup';const ordinary={posts:0,pacRequests:0};
try{python=spawn('python',['scripts/webtransport-receiver.py',root],{env:{...process.env,PYTHONPATH:resolve('artifacts/webtransport-python')},windowsHide:true,stdio:['ignore','pipe','pipe']});python.stderr.on('data',()=>{});const metadata=await new Promise((accept,reject)=>{let text='';const timer=setTimeout(()=>reject(Error('Pinned QUIC receiver timeout')),15000);python.stdout.on('data',chunk=>{text+=chunk;try{const value=JSON.parse(text.split('\n')[0]);clearTimeout(timer);accept(value);}catch{}});python.once('error',()=>reject(Error('Pinned QUIC receiver startup refused')));python.once('exit',()=>{clearTimeout(timer);reject(Error('Pinned QUIC receiver exited'));});});const stats=`http://127.0.0.1:${metadata.statsPort}/facts`,target=`https://127.0.0.1:${metadata.quicPort}/webtransport`;
page=createServer({cert:readFileSync('tests/fixtures/icap/cert.pem'),key:readFileSync('tests/fixtures/icap/key.pem')},(req,res)=>{if(req.url==='/direct.pac'){ordinary.pacRequests++;res.setHeader('content-type','application/x-ns-proxy-autoconfig');res.end('function FindProxyForURL(url,host) { return "DIRECT"; }');return;}if(req.method==='POST'){ordinary.posts++;req.resume();res.end('accepted');return;}res.setHeader('content-type','text/html');res.end('<!doctype html><title>Native transport fixture</title><h1>Public native transport workspace</h1>');}).listen(0,'127.0.0.1');await new Promise(r=>page.once('listening',r));const origin=`https://127.0.0.1:${page.address().port}`,env={...process.env,SAFETYNET_WT_TARGET:target,SAFETYNET_WT_HASH:metadata.hash,SAFETYNET_WT_STATS:stats,SAFETYNET_SMOKE_ORIGIN:origin};delete env.ELECTRON_RUN_AS_NODE;
const run=async(mode,extra={})=>{const output=join(root,mode+'.json');child=spawn(createRequire(import.meta.url)('electron'),['scripts/fullmain-wt-native.cjs'],{windowsHide:true,stdio:['ignore','pipe','pipe'],env:{...env,SAFETYNET_WT_MODE:mode,SAFETYNET_SMOKE_USERDATA:join(root,mode+'-profile'),SAFETYNET_WT_OUTPUT:output,...extra}});child.stdout.on('data',chunk=>process.stdout.write(chunk));child.stderr.on('data',chunk=>process.stderr.write(chunk));const timer=setTimeout(()=>child.kill(),120000),exit=await new Promise(r=>{child.once('exit',v=>r(v??1));child.once('error',()=>r(1));});clearTimeout(timer);assert.equal(exit,0,'Full main native transport phase failed');return JSON.parse(readFileSync(output,'utf8'));};
stage='real baseline delivery';const before=await(await fetch(stats)).json(),baseline=await run('baseline'),after=await(await fetch(stats)).json();assert.ok(after.bytes-before.bytes>=132);assert.ok(after.workerBytes-before.workerBytes>=66);assert.ok(after.datagrams>before.datagrams&&after.streams>before.streams&&after.marker);const baselineReceiverDelta=Object.fromEntries(['packets','connections','datagrams','streams','bytes','workerBytes'].map(key=>[key,after[key]-before[key]]));
store=openStore(join(root,'db'));const code=randomBytes(32).toString('base64url'),adminToken=randomBytes(32).toString('base64url'),reviewer=randomBytes(32).toString('base64url'),policy=store.get('policy');policy.browser.homeUrl=origin+'/workspace';policy.browser.allowedHosts=['127.0.0.1'];policy.agents.enabled=false;policy.diagnostics={...policy.diagnostics,enabled:true,mode:'debug',endpoint:collector,sampleRate:1,debugSampleRate:1,flushIntervalMs:250};store.put('policy',policy);store.db.prepare('INSERT INTO enrollments VALUES(?,?,?)').run(digest(code),Date.now()+600000,'fullmain-wt-fixture');store.db.prepare('INSERT INTO sessions VALUES(?,?,?)').run(digest(reviewer),Date.now()+600000,JSON.stringify({sub:'independent-wt-reviewer',role:'admin'}));const config={secure:false,publicUrl:'http://127.0.0.1',adminToken,requirePQManagement:true,requireIndependentPolicyApproval:true};management=createApp(store,config).listen(0,'127.0.0.1');await new Promise(r=>management.once('listening',r));config.publicUrl=`http://127.0.0.1:${management.address().port}`;
stage='real enrolled main and reviewed policies';const managed=await run('managed',{SAFETYNET_SMOKE_SERVER:config.publicUrl,SAFETYNET_SMOKE_CODE:code,SAFETYNET_SMOKE_ADMIN:adminToken,SAFETYNET_SMOKE_REVIEWER:reviewer});assert.equal(managed.phases.length,3);assert.equal(ordinary.posts,3);assert.ok(ordinary.pacRequests>=1,'PAC was not actually retrieved');for(const file of files)assert.equal(createHash('sha256').update(readFileSync(file)).digest('hex'),sourceHashFacts[file],'Production source changed during native proof');const report={actualFullMain:true,actualPinnedHTTP3Receiver:true,aioquicVersion:metadata.aioquic,sourceHashFacts,baseline,baselineReceiverDelta,managed,ordinary,unsupportedTransportAuditGap:managed.unsupportedTransportAuditGap};writeFileSync('artifacts/fullmain-webtransport-native-proof.json',JSON.stringify(report,null,2));console.log('Full main pinned Window/Worker WebTransport proof passed');
}catch(error){writeFileSync('artifacts/fullmain-wt-harness-error.json',JSON.stringify({stage,name:error.name},null,2));console.error('Full main WT harness failed at '+stage+': '+error.name);process.exitCode=1;}finally{child?.kill();python?.kill();for(const server of [page,management])if(server){server.closeAllConnections();await new Promise(r=>server.close(r));}store?.db.close();await telemetry?.shutdown();rmSync(root,{recursive:true,force:true});}
+1 -1
View File
@@ -1,7 +1,6 @@
// Finite, isolated administrator workflow proof using actual UI and HTTP routes.
import assert from 'node:assert/strict';
import { createRequire } from 'node:module';
import { createServer } from 'node:net';
import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join, resolve, sep, basename } from 'node:path';
@@ -24,6 +23,7 @@ try{
const policy=verifyPolicy(store.envelope(),store.publicKey());assert.equal(policy.version,config.requireIndependentPolicyApproval?2:3);const group=store.getRaw('policyGroups').find(group=>group.id==='engineering');assert.equal(group.rollout.stage,config.requireIndependentPolicyApproval?'pilot':'all');assert.deepEqual([...group.memberDeviceIds].sort(),['member-device','pilot-device']);assert.equal(group.restrictions.disableAI,true);
const report={actualAdministratorUi:true,explicitGroupMembership:true,pilotCohortPublished:true,noNode:true,cspPreserved:true,...(config.requireIndependentPolicyApproval?{operatorProposalLeftPolicyUnchanged:true,exactMembersCohortRestrictionsReviewed:true,distinctAdministratorApproval:true}:{effectivePreview:true,explicitExpansion:true,reportedVersionDisplayed:true})};mkdirSync('artifacts',{recursive:true});writeFileSync(config.requireIndependentPolicyApproval?'artifacts/groups-review-admin-proof.json':'artifacts/groups-admin-proof.json',JSON.stringify(report,null,2));console.log(JSON.stringify(report));
}finally{
// eslint-disable-next-line no-unsafe-finally -- Cleanup failure must fail closed rather than report successful protected cleanup.
child?.kill();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-groups-admin-smoke-'))throw Error('Unsafe proof cleanup');rmSync(target,{recursive:true,force:true,maxRetries:10,retryDelay:100});
}
+1 -2
View File
@@ -1,7 +1,6 @@
// Finite, isolated administrator workflow proof using actual UI and HTTP routes.
import assert from 'node:assert/strict';
import { createRequire } from 'node:module';
import { createServer } from 'node:net';
import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join, resolve, sep, basename } from 'node:path';
@@ -10,7 +9,6 @@ import { randomBytes } from 'node:crypto';
import { digest } from '../apps/server/store.mjs';
import { openStore } from '../apps/server/store.mjs';
import { createApp } from '../apps/server/app.mjs';
import { verifyPolicy } from '../packages/core/policy.mjs';
const require=createRequire(import.meta.url),directory=mkdtempSync(join(tmpdir(),'safetynet-incident-admin-smoke-'));
let store,server,child;
try{
@@ -24,6 +22,7 @@ try{
const row=store.db.prepare('SELECT * FROM incident_cases').get();assert.equal(row.revision,3);assert.equal(row.status,'resolved');assert.equal(row.assignee,'Synthetic SOC');
const report={actualAdministratorUi:true,createFromSecurityEvent:true,statusSeverityAssignment:true,revisionConflictPreservesEdits:true,historyShown:true,adversarialNotesRenderedAsText:true,filters:true,auditorReadOnly:true,rawContentExcluded:true,noNode:true,cspPreserved:true};mkdirSync('artifacts',{recursive:true});writeFileSync('artifacts/incident-admin-proof.json',JSON.stringify(report,null,2));console.log(JSON.stringify(report));
}finally{
// eslint-disable-next-line no-unsafe-finally -- Cleanup failure must fail closed rather than report successful protected cleanup.
child?.kill();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-incident-admin-smoke-'))throw Error('Unsafe proof cleanup');rmSync(target,{recursive:true,force:true,maxRetries:10,retryDelay:100});
}
+1 -1
View File
@@ -1,7 +1,6 @@
// Finite, isolated administrator workflow proof using actual UI and HTTP routes.
import assert from 'node:assert/strict';
import { createRequire } from 'node:module';
import { createServer } from 'node:net';
import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join, resolve, sep, basename } from 'node:path';
@@ -24,5 +23,6 @@ try{
const policy=verifyPolicy(store.envelope(),store.publicKey());assert.equal(policy.version,3);assert.equal(store.db.prepare('SELECT document_analysis_enabled FROM devices WHERE id=?').get('synthetic-bookmark-device').document_analysis_enabled,1);assert.equal(policy.brand.name,'Independently approved');
const report={actualAdministratorUi:true,mandatoryIndependentApproval:true,payloadDiff:true,selfApprovalHttpDenied:true,distinctReviewer:true,operatorPrepareOnly:true,inspectionOperationVisible:true,inspectionUnchangedUntilApproval:true,rejectedAndStaleVisible:true,publishedOnlyAfterApproval:true,nodeUnavailable:true};mkdirSync('artifacts',{recursive:true});writeFileSync('artifacts/independent-review-admin-proof.json',JSON.stringify(report,null,2));console.log(JSON.stringify(report));
}finally{
// eslint-disable-next-line no-unsafe-finally -- Cleanup failure must fail closed rather than report successful protected cleanup.
child?.kill();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-independent-review-admin-smoke-'))throw Error('Unsafe proof cleanup');rmSync(target,{recursive:true,force:true,maxRetries:10,retryDelay:100});
}
+1 -1
View File
@@ -1,4 +1,4 @@
const {app,BrowserWindow,webContents}=require('electron');const assert=require('node:assert/strict'),fs=require('node:fs'),path=require('node:path');
const {app,BrowserWindow,webContents}=require('electron');const assert=require('node:assert/strict'),fs=require('node:fs');
app.setPath('userData',process.env.SAFETYNET_TABS_USERDATA);app.disableHardwareAcceleration();require('../apps/browser/main.cjs');
function finish(code){for(const contents of webContents.getAllWebContents()){if(contents.getURL().endsWith('index.html'))continue;require('../apps/browser/page-clipboard-guard.cjs').disposePageClipboardGuard(contents);}app.exit(code);}
const wait=ms=>new Promise(r=>setTimeout(r,ms));async function until(fn,label){for(let i=0;i<150;i++){if(await fn())return;await wait(100);}throw Error(label+' timed out');}
+1
View File
@@ -29,4 +29,5 @@ try{
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');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));
// eslint-disable-next-line no-unsafe-finally -- Cleanup failure must fail closed rather than report successful protected cleanup.
}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});}
+16
View File
@@ -0,0 +1,16 @@
import assert from 'node:assert/strict';
import {readFile} from 'node:fs/promises';
import {createHash} from 'node:crypto';
import {createRequire} from 'node:module';
const require=createRequire(import.meta.url);
export const hardenedElectronFuses=Object.freeze({runAsNode:false,enableCookieEncryption:true,enableNodeOptionsEnvironmentVariable:false,enableNodeCliInspectArguments:false,enableEmbeddedAsarIntegrityValidation:true,onlyLoadAppFromAsar:true});
export function assertEmbeddedIntegrity(entries,header){
const matching=entries.filter(row=>row.file==='resources\\app.asar');assert.equal(matching.length,1,'Missing or ambiguous embedded ASAR integrity');const entry=matching[0];assert.equal(entry.alg,'SHA256');assert.equal(entry.value,createHash('sha256').update(header).digest('hex'),'Embedded ASAR integrity does not match archive header');
}
export async function verifyPackagedFuses(executable,archive){
const {getCurrentFuseWire,FuseV1Options}=require('@electron/fuses');const wire=await getCurrentFuseWire(executable);
const expected={RunAsNode:false,EnableCookieEncryption:true,EnableNodeOptionsEnvironmentVariable:false,EnableNodeCliInspectArguments:false,EnableEmbeddedAsarIntegrityValidation:true,OnlyLoadAppFromAsar:true};
for(const [name,enabled] of Object.entries(expected))assert.equal(wire[FuseV1Options[name]],enabled?49:48,`Packaged security fuse mismatch: ${name}`);
const {NtExecutable,NtExecutableResource}=require('resedit'),binary=NtExecutable.from(await readFile(executable)),resources=NtExecutableResource.from(binary);const entries=resources.entries.filter(row=>row.type==='INTEGRITY'&&row.id==='ELECTRONASAR');assert.equal(entries.length,1,'Missing embedded Windows ASAR integrity resource');
const integrity=JSON.parse(Buffer.from(entries[0].bin).toString('utf8'));const {getRawHeader}=require('@electron/asar');assertEmbeddedIntegrity(integrity,getRawHeader(archive).headerString);return expected;
}
+4 -1
View File
@@ -1,3 +1,4 @@
import {hardenedElectronFuses,verifyPackagedFuses} from './package-fuses.mjs';
import { spawnSync } from 'node:child_process';
import {readFileSync,readdirSync} from 'node:fs';
import {createHash} from 'node:crypto';
@@ -16,8 +17,10 @@ const electronDirectory=path.dirname(createRequire(import.meta.url)('electron'))
if(readFileSync(path.join(electronDirectory,'version'),'utf8').trim().replace(/^v/,'')!==configuration.devDependencies.electron)throw Error('Installed Electron does not match the build pin');
function sourceSnapshot(){const entries=[];function walk(directory){for(const item of readdirSync(directory,{withFileTypes:true})){const file=path.join(directory,item.name);if(item.isDirectory())walk(file);else if(item.isFile())entries.push([file,createHash('sha256').update(readFileSync(file)).digest('hex')]);}}for(const directory of ['apps/browser','packages/core'])walk(directory);entries.push(['package.json',createHash('sha256').update(readFileSync('package.json')).digest('hex')]);return JSON.stringify(entries.sort((a,b)=>a[0].localeCompare(b[0])));}
const sourcesBefore=sourceSnapshot();
const r=spawnSync(process.execPath,['node_modules/electron-builder/out/cli/cli.js',`--${platform}`,'--dir',`--config.electronDist=${electronDirectory}`,`--config.productName=${name}`,`--config.appId=${id}`],{stdio:'inherit',env:process.env});
const r=spawnSync(process.execPath,['node_modules/electron-builder/out/cli/cli.js',`--${platform}`,'--dir',`--config.electronDist=${electronDirectory}`,`--config.productName=${name}`,`--config.appId=${id}`,...Object.entries(hardenedElectronFuses).map(([key,value])=>`--config.electronFuses.${key}=${value}`)],{stdio:'inherit',env:process.env});
if(r.status!==0)process.exit(r.status??1);
if(sourceSnapshot()!==sourcesBefore)throw Error('Browser sources changed during packaging; discard this artifact and rebuild from stable sources');
const resources=path.join(output,'win-unpacked/resources');
await verifyPackagedFuses(path.join(output,'win-unpacked',`${name}.exe`),path.join(resources,'app.asar'));
console.log('Packaged Electron security fuses and embedded ASAR integrity verified.');
const acl=spawnSync(path.join(resources,'python/python.exe'),[path.join(resources,'inspector/provision_runtime_acl.py'),'--provision'],{stdio:'inherit',windowsHide:true});process.exit(acl.status??1);
+1 -1
View File
@@ -1,7 +1,6 @@
// Finite, isolated administrator workflow proof using actual UI and HTTP routes.
import assert from 'node:assert/strict';
import { createRequire } from 'node:module';
import { createServer } from 'node:net';
import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join, resolve, sep, basename } from 'node:path';
@@ -22,5 +21,6 @@ try{
const policy=verifyPolicy(store.envelope(),store.publicKey());assert.equal(policy.version,3);assert.equal(policy.brand.name,'SafetyNet');assert.equal(policy.agents.enabled,false);assert.deepEqual(policy.security.dns.blockedHosts,[]);assert.deepEqual(policy.security.content.blockedPhrases,[]);
const report={actualAdministratorUi:true,persistedHistory:true,serverDiff:true,explicitRestoreApproval:true,monotonicRestore:true,draftPreservedDuringReview:true,structuredDnsContentAgentControls:true,nodeUnavailable:true,cspPreserved:true};mkdirSync('artifacts',{recursive:true});writeFileSync('artifacts/policy-history-admin-proof.json',JSON.stringify(report,null,2));console.log(JSON.stringify(report));
}finally{
// eslint-disable-next-line no-unsafe-finally -- Cleanup failure must fail closed rather than report successful protected cleanup.
child?.kill();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-policy-history-admin-smoke-'))throw Error('Unsafe proof cleanup');rmSync(target,{recursive:true,force:true,maxRetries:10,retryDelay:100});
}
+10
View File
@@ -0,0 +1,10 @@
// Run with Node --cpu-prof / --heap-prof; profiles belong in ignored artifacts.
import {createRequire} from 'node:module';
import {performance} from 'node:perf_hooks';
const require=createRequire(import.meta.url);
for(const name of ['diagnostics.cjs','inspect-text-representations.cjs']){
const before=process.memoryUsage(),started=performance.now();
require('../apps/browser/'+name);
const after=process.memoryUsage();
console.log(JSON.stringify({module:name,loadMs:Number((performance.now()-started).toFixed(3)),heapDeltaBytes:after.heapUsed-before.heapUsed,rssDeltaBytes:after.rss-before.rss}));
}
+1 -1
View File
@@ -1,7 +1,6 @@
// Finite, isolated administrator workflow proof using actual UI and HTTP routes.
import assert from 'node:assert/strict';
import { createRequire } from 'node:module';
import { createServer } from 'node:net';
import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join, resolve, sep, basename } from 'node:path';
@@ -24,6 +23,7 @@ try{
const policy=verifyPolicy(store.envelope(),store.publicKey());assert.equal(policy.version,2);
const report={actualAdministratorUi:true,explicitDeviceAssignments:true,employeeDeveloperFlags:true,websiteRestrictions:true,noNode:true,cspPreserved:true,...(config.requireIndependentPolicyApproval?{operatorProposalLeftPolicyUnchanged:true,exactProfileReviewed:true,distinctAdministratorApproval:true}:{effectivePreview:true})};mkdirSync('artifacts',{recursive:true});writeFileSync(config.requireIndependentPolicyApproval?'artifacts/profiles-review-admin-proof.json':'artifacts/profiles-admin-proof.json',JSON.stringify(report,null,2));console.log(JSON.stringify(report));
}finally{
// eslint-disable-next-line no-unsafe-finally -- Cleanup failure must fail closed rather than report successful protected cleanup.
child?.kill();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-profiles-admin-smoke-'))throw Error('Unsafe proof cleanup');rmSync(target,{recursive:true,force:true,maxRetries:10,retryDelay:100});
}
+1 -1
View File
@@ -1,6 +1,6 @@
// Finite native fixture: synthetic teaching data, real signed policy and CPU model.
const {app,BrowserWindow,clipboard,ClipboardItem}=require('electron');
const fs=require('node:fs'),path=require('node:path'),assert=require('node:assert/strict');
const assert=require('node:assert/strict');
app.setPath('userData',process.env.SAFETYNET_SEMANTIC_PROFILE);
app.setPath('sessionData',process.env.SAFETYNET_SEMANTIC_PROFILE);
app.disableHardwareAcceleration();
+1 -1
View File
@@ -61,7 +61,7 @@ const inspectorPort=await new Promise((resolve,reject)=>{
const p=store.get('policy');p.browser.homeUrl=`http://127.0.0.1:${sink.address().port}`;p.brand={name:'Acme Secure Browser',color:'#245fbc'};p.dlp.rules=[{id:'secret-roadmap',label:'Roadmap',kind:'literal',value:'Secret Roadmap',action:'block'}];if(process.env.SAFETYNET_TEST_OTLP_ENDPOINT)p.diagnostics={...p.diagnostics,enabled:true,mode:'debug',endpoint:process.env.SAFETYNET_TEST_OTLP_ENDPOINT,sampleRate:1,debugSampleRate:1,flushIntervalMs:250};store.put('policy',p);store.db.prepare('INSERT INTO enrollments VALUES(?,?,?)').run(digest(code),Date.now()+600000,'smoke-user');
p.evidence={enabled:true,captureDownloads:true};p.browser.allowDownloads=true;p.security.yara={enabled:true,rules:[{id:'test-marker',name:'Controlled malicious marker',source:'rule Block_Test { strings: $a = "SAFETYNET_BLOCK_TEST" condition: $a }'}],maxScanBytes:1048576};p.agents={enabled:true,scopes:['sessions','navigate','back','forward','reload','read','inspect','scroll','click','type','select','check','application:state','settings:get','settings:update','bookmarks:list','bookmarks:mutate','tabs:create','tabs:activate','tabs:close','tabs:group'],allowedHosts:['127.0.0.1'],allowSensitiveRead:false};if(process.env.SAFETYNET_TEST_OTLP_ENDPOINT)p.diagnostics={...p.diagnostics,enabled:true,mode:'debug',endpoint:process.env.SAFETYNET_TEST_OTLP_ENDPOINT,sampleRate:1,debugSampleRate:1,flushIntervalMs:250};store.put('policy',p);
const config={publicUrl:'http://127.0.0.1',secure:false,adminToken,evidenceKeyFile,requirePQManagement:true,inspectorUrl:`http://127.0.0.1:${inspectorPort}`,inspectorToken};const management=createApp(store,config).listen(0,'127.0.0.1');await new Promise(r=>management.on('listening',r));config.publicUrl=`http://127.0.0.1:${management.address().port}`;
const evidenceWire=[];management.on('request',(req,res)=>{if(!req.url.startsWith('/api/device/channel/evidence'))return;const chunks=[];req.on('data',chunk=>chunks.push(chunk));req.on('end',()=>{const text=Buffer.concat(chunks).toString('utf8');evidenceWire.push({route:req.url,containsMetadata:['safe.bin','blocked.bin','sourceUrl','sha256'].some(value=>text.includes(value))});});});
const evidenceWire=[];management.on('request',(req,_res)=>{if(!req.url.startsWith('/api/device/channel/evidence'))return;const chunks=[];req.on('data',chunk=>chunks.push(chunk));req.on('end',()=>{const text=Buffer.concat(chunks).toString('utf8');evidenceWire.push({route:req.url,containsMetadata:['safe.bin','blocked.bin','sourceUrl','sha256'].some(value=>text.includes(value))});});});
const child=spawn(electron,['scripts/electron-smoke.cjs'],{stdio:'inherit',windowsHide:true,env:{...process.env,SAFETYNET_SMOKE_SERVER:config.publicUrl,SAFETYNET_SMOKE_CODE:code,SAFETYNET_SMOKE_SINK:p.browser.homeUrl,SAFETYNET_SMOKE_SECONDARY:`http://127.0.0.2:${secondary.address().port}`,SAFETYNET_SMOKE_USERDATA:join(temporary,'browser'),SAFETYNET_SMOKE_ARTIFACTS:resolve('artifacts'),SAFETYNET_SMOKE_ADMIN:adminToken,SAFETYNET_SMOKE_PUBLIC_UPLOAD:publicUploadFile,SAFETYNET_SMOKE_SECRET_UPLOAD:secretUploadFile}});
const timeout=setTimeout(()=>child.kill(),240000);
+27
View File
@@ -0,0 +1,27 @@
import assert from 'node:assert/strict';
import {mkdtemp,readFile,writeFile,copyFile,mkdir,readdir,rm,access} from 'node:fs/promises';
import path from 'node:path';
import {spawn} from 'node:child_process';
import {verifyPackagedFuses} from './package-fuses.mjs';
const root=path.resolve('dist/win-unpacked'),executable=path.join(root,'SafetyNet.exe');
let currentStage='initialization';
function run(stage,file,args,extra={}){currentStage=stage;console.log(`Package fuse proof: ${stage}`);return new Promise((resolve,reject)=>{const env={...process.env,...extra};delete env.NODE_OPTIONS;if(extra.NODE_OPTIONS)env.NODE_OPTIONS=extra.NODE_OPTIONS;delete env.ELECTRON_RUN_AS_NODE;if(extra.ELECTRON_RUN_AS_NODE)env.ELECTRON_RUN_AS_NODE=extra.ELECTRON_RUN_AS_NODE;const child=spawn(file,args,{env,windowsHide:true,stdio:['ignore','pipe','pipe']});let output='',timedOut=false;const timer=setTimeout(()=>{timedOut=true;child.kill();},15000);child.on('error',error=>{clearTimeout(timer);reject(error);});const append=b=>{output=(output+b).slice(-16384);};child.stdout.on('data',append);child.stderr.on('data',append);child.on('close',async code=>{clearTimeout(timer);const diagnostic={stage,code,timedOut,output};await writeFile('artifacts/package-fuses-last-stage.json',JSON.stringify(diagnostic,null,2));if(timedOut)reject(Error(`Finite package fuse proof timed out during ${stage}; bounded diagnostic persisted`));else resolve({code,output});});});}
const fixtureRoot=path.resolve('artifacts/package-fuse-fixtures');await mkdir(fixtureRoot,{recursive:true});const directory=await mkdtemp(path.join(fixtureRoot,'case-'));
try{
currentStage='fuses-and-integrity';const fuses=await verifyPackagedFuses(executable,path.join(root,'resources/app.asar'));
const args=[`--agent-connection=${path.join(directory,'missing-connection.json')}`,'--agent-action={"action":"sessions","args":{}}'];
const baseline=await run('baseline-cli',executable,args);assert.equal(baseline.code,1);assert.match(baseline.output,/Local agent action failed/);
const nodeMode=await run('node-mode',executable,args,{ELECTRON_RUN_AS_NODE:'1'});assert.equal(nodeMode.code,1);assert.match(nodeMode.output,/Local agent action failed/,'RUN_AS_NODE altered normal entrypoint');
const marker=path.join(directory,'node-options-ran'),preload=path.join(directory,'injection.cjs');await writeFile(preload,`require('node:fs').writeFileSync(${JSON.stringify(marker)},'injected')`);
const injected=await run('node-options-injection',executable,args,{NODE_OPTIONS:`--require=${JSON.stringify(preload)}`});assert.equal(injected.code,1);assert.match(injected.output,/Local agent action failed/);await assert.rejects(access(marker),{code:'ENOENT'});
const inspector=await run('cli-inspector',executable,[...args,'--inspect=127.0.0.1:0']);assert.notEqual(inspector.code,0);assert.doesNotMatch(inspector.output,/Debugger listening|DevTools listening/);
for(const flag of ['--ignore-certificate-errors','--ignore-certificate-errors-spki-list=synthetic-fixture','--allow-insecure-localhost','--disable-features=ConnectionAllowlists','-disable-features=ConnectionAllowlists','/disable-features=ConnectionAllowlists','/DISABLE-FEATURES=ConnectionAllowlists',' --disable-features=ConnectionAllowlists ','\u0085/disable-features=ConnectionAllowlists\u0085','--disable-blink-features=WebTransport','--enable-experimental-web-platform-features']){const rejected=await run('unsafe-startup-'+flag.split('=')[0].slice(2),executable,[...args,flag]);assert.equal(rejected.code,1);assert.match(rejected.output,/Managed browser startup denied/);assert.doesNotMatch(rejected.output,/Local agent action failed|Debugger listening|DevTools listening/);}
const copied=path.join(directory,'copied');await mkdir(path.join(copied,'resources'),{recursive:true});for(const entry of await readdir(root,{withFileTypes:true}))if(entry.isFile())await copyFile(path.join(root,entry.name),path.join(copied,entry.name));
const archive=await readFile(path.join(root,'resources/app.asar'));const original=Buffer.from(archive);const index=archive.indexOf(Buffer.from('apps'));assert.ok(index>16&&index<archive.readUInt32LE(12)+16,'No safe header tamper fixture offset');archive[index]^=1;await writeFile(path.join(copied,'resources/app.asar'),archive);
const tampered=await run('tampered-asar',path.join(copied,'SafetyNet.exe'),args);assert.notEqual(tampered.code,0);assert.match(tampered.output,/integrity|hash.*mismatch/i,'Tampered archive did not fail with integrity evidence');assert.doesNotMatch(tampered.output,/Local agent action failed/,'Tampered application reached its entrypoint');
assert.ok(original.equals(await readFile(path.join(root,'resources/app.asar'))),'Real packaged archive changed');
const report={fuses,nodeModeDisabled:true,nodeOptionsInjectionIgnored:true,noInspectorListener:true,unsafeStartupOverridesRejected:true,embeddedAsarHeaderIntegrity:true,tamperedCopiedArchiveRejected:true,originalPackageUnmodified:true};await writeFile('artifacts/package-fuses-proof.json',JSON.stringify(report,null,2));console.log(JSON.stringify(report));
}catch(error){await writeFile('artifacts/package-fuses-failure.json',JSON.stringify({stage:currentStage,message:error.message},null,2));throw error;
// eslint-disable-next-line no-unsafe-finally -- Unsafe cleanup paths must fail closed before deletion.
}finally{const target=path.resolve(directory);if(!target.startsWith(fixtureRoot+path.sep)||path.dirname(target)!==fixtureRoot)throw Error('Unsafe fuse fixture cleanup');await rm(target,{recursive:true,force:true,maxRetries:10,retryDelay:100});}
+1
View File
@@ -83,6 +83,7 @@ try{
console.log('Packaged security sources, Windows upload handle lease, Windows-contained inspector, offline EmbeddingGemma 2 CPU inference, legacy and individual Windows agent helpers and bundled DPAPI runtime passed.');
}finally{
await service?.close();
// eslint-disable-next-line no-unsafe-finally -- Cleanup failure must fail closed rather than report successful protected cleanup.
if(!temporary.startsWith(path.join(os.tmpdir(),'safetynet-package-')))throw Error('Unexpected temporary directory');
await fs.rm(temporary,{recursive:true,force:true});
}
+25
View File
@@ -0,0 +1,25 @@
const {app,BrowserWindow,session}=require('electron');
const {writeFileSync}=require('node:fs');
// This fixed helper is serialized into native Chromium Window and Worker realms.
/* global WebTransport */
app.disableHardwareAcceleration();app.enableSandbox();
const mode=process.env.SAFETYNET_WT_MODE;
if(mode==='disable-quic')app.commandLine.appendSwitch('disable-quic');
if(mode==='disable-blink')app.commandLine.appendSwitch('disable-blink-features','WebTransport');
async function sendWebTransport(target,hash){
const outcome={api:typeof WebTransport==='function',supportsReliableOnly:typeof WebTransport==='function'&&WebTransport.supportsReliableOnly===true,connected:false,sent:false,error:''};let transport;
try{transport=new WebTransport(target,{serverCertificateHashes:[{algorithm:'sha-256',value:new Uint8Array(hash)}]});await Promise.race([transport.ready,new Promise((_,reject)=>setTimeout(()=>reject(Error('fixture-timeout')),6000))]);outcome.connected=true;const bytes=new TextEncoder().encode('SYNTHETIC_WEBTRANSPORT_DLP_MARKER');const datagram=transport.datagrams.writable.getWriter();await datagram.write(bytes);datagram.releaseLock();const stream=await transport.createUnidirectionalStream();const writer=stream.getWriter();await writer.write(bytes);await writer.close();outcome.sent=true;await new Promise(resolve=>setTimeout(resolve,300));}catch(error){outcome.error=error.name+':'+error.message;}finally{transport?.close();}return outcome;
}
app.on('login',(event,_contents,_details,authInfo,callback)=>{if(mode==='private-relay'&&authInfo.isProxy&&authInfo.host==='127.0.0.1'&&Number(authInfo.port)===Number(process.env.SAFETYNET_WT_PROXY.split(':').at(-1))){event.preventDefault();callback(process.env.SAFETYNET_WT_USERNAME,process.env.SAFETYNET_WT_PASSWORD);}});
app.whenReady().then(async()=>{
const target=process.env.SAFETYNET_WT_TARGET,hash=process.env.SAFETYNET_WT_HASH;let events=0;
if(['forced-proxy','private-relay'].includes(mode))await session.defaultSession.setProxy({mode:'fixed_servers',proxyRules:process.env.SAFETYNET_WT_PROXY,proxyBypassRules:'<-loopback>'});
session.defaultSession.webRequest.onBeforeRequest({urls:[target+'*']},(_details,callback)=>{events++;callback({cancel:true});});
const win=new BrowserWindow({show:false,webPreferences:{sandbox:true,nodeIntegration:false,contextIsolation:true}});
await win.loadURL(process.env.SAFETYNET_WT_PAGE);
const hashBytes=Array.from(Buffer.from(hash,'hex')),result=await win.webContents.executeJavaScript(`(${sendWebTransport.toString()})(${JSON.stringify(target)},${JSON.stringify(hashBytes)})`);
const workerSource=`const send=${sendWebTransport.toString()};onmessage=async event=>{postMessage(await send(event.data.target,event.data.hash));close();};`;
const worker=await win.webContents.executeJavaScript(`new Promise((resolve,reject)=>{const url=URL.createObjectURL(new Blob([${JSON.stringify(workerSource)}],{type:'text/javascript'}));const worker=new Worker(url);const timer=setTimeout(()=>{worker.terminate();URL.revokeObjectURL(url);reject(Error('worker-fixture-timeout'));},10000);worker.onmessage=event=>{clearTimeout(timer);worker.terminate();URL.revokeObjectURL(url);resolve(event.data);};worker.onerror=()=>{clearTimeout(timer);worker.terminate();URL.revokeObjectURL(url);reject(Error('worker-fixture-failed'));};worker.postMessage({target:${JSON.stringify(target+'/worker')},hash:${JSON.stringify(hashBytes)}});})`);
const ordinaryPageLoaded=await win.webContents.executeJavaScript("document.querySelector('h1')?.textContent==='Local WebTransport receiver proof'");
writeFileSync(process.env.SAFETYNET_WT_OUTPUT,JSON.stringify({...result,worker,ordinaryPageLoaded,mode,resolvedProxy:await session.defaultSession.resolveProxy(target),webRequestEvents:events,chromium:process.versions.chrome,electron:process.versions.electron}));win.destroy();app.quit();
}).catch(error=>{console.error(error.stack);app.exit(1);});
+24
View File
@@ -0,0 +1,24 @@
import {spawn} from 'node:child_process';
import {createRequire} from 'node:module';
import {createServer,request} from 'node:http';
import {mkdtempSync,readFileSync,writeFileSync} from 'node:fs';
import {resolve,join} from 'node:path';
import assert from 'node:assert/strict';
const require=createRequire(import.meta.url),root=mkdtempSync(resolve('artifacts/webtransport-')),env={...process.env};delete env.ELECTRON_RUN_AS_NODE;
const python=spawn('python',['scripts/webtransport-receiver.py',root],{env:{...env,PYTHONPATH:resolve('artifacts/webtransport-python')},windowsHide:true,stdio:['ignore','pipe','pipe']});let errors='';python.stderr.on('data',b=>errors+=b);
const metadata=await new Promise((resolve,reject)=>{let text='';const timer=setTimeout(()=>reject(Error('WebTransport receiver startup timeout: '+errors)),15000);python.stdout.on('data',b=>{text+=b;const line=text.split('\n')[0];try{const result=JSON.parse(line);clearTimeout(timer);resolve(result);}catch{}});python.once('exit',()=>{clearTimeout(timer);reject(Error('Receiver exited: '+errors));});});
const page=createServer((_req,res)=>{res.setHeader('content-type','text/html');res.end('<!doctype html><h1>Local WebTransport receiver proof</h1>');}).listen(0,'127.0.0.1');await new Promise(r=>page.once('listening',r));
let forwarded=0;const proxy=createServer((req,res)=>{const target=new URL(req.url);if(target.hostname!=='127.0.0.1'||Number(target.port)!==page.address().port){res.writeHead(403);res.end();return;}forwarded++;const outgoing=request(target,{method:req.method,headers:req.headers},incoming=>{res.writeHead(incoming.statusCode,incoming.headers);incoming.pipe(res);});outgoing.on('error',()=>{res.writeHead(502);res.end();});req.pipe(outgoing);}).listen(0,'127.0.0.1');await new Promise(r=>proxy.once('listening',r));
const outcomes=[];
let relayAuthorizations=0;const authority={version:1},relay=await require('../apps/browser/local-dlp-relay.cjs').startLocalDlpRelay({authorize:async()=>{relayAuthorizations++;return authority;},isCurrent:a=>a===authority,resolveUpstream:async()=> 'DIRECT',resolveAddress:async()=> '127.0.0.1'});
try{
for(const mode of ['baseline','disable-quic','disable-blink','forced-proxy','private-relay']){
const before=await (await fetch(`http://127.0.0.1:${metadata.statsPort}/facts`)).json(),output=join(root,mode+'.json');
const forwardedBefore=forwarded;
const credentials=relay.credentials(),relayBefore=relayAuthorizations;
const child=spawn(require('electron'),['scripts/webtransport-native-probe.cjs'],{env:{...env,SAFETYNET_WT_MODE:mode,SAFETYNET_WT_USERNAME:credentials.username,SAFETYNET_WT_PASSWORD:credentials.password,SAFETYNET_WT_PROXY:mode==='private-relay'?relay.proxyRules:`127.0.0.1:${proxy.address().port}`,SAFETYNET_WT_TARGET:`https://127.0.0.1:${metadata.quicPort}/webtransport`,SAFETYNET_WT_PAGE:`http://127.0.0.1:${page.address().port}/`,SAFETYNET_WT_HASH:metadata.hash,SAFETYNET_WT_OUTPUT:output},windowsHide:true,stdio:['ignore','pipe','pipe']});let log='';child.stdout.on('data',b=>log+=b);child.stderr.on('data',b=>log+=b);const timer=setTimeout(()=>child.kill(),20000),code=await new Promise(r=>child.once('exit',r));clearTimeout(timer);if(code!==0)throw Error(`Native ${mode} exited ${code}: ${log}`);
const after=await (await fetch(`http://127.0.0.1:${metadata.statsPort}/facts`)).json(),result=JSON.parse(readFileSync(output,'utf8'));outcomes.push({...result,forwardedHTTP:mode==='private-relay'?relayAuthorizations-relayBefore:forwarded-forwardedBefore,receiver:{connections:after.connections-before.connections,datagrams:after.datagrams-before.datagrams,streams:after.streams-before.streams,bytes:after.bytes-before.bytes,workerBytes:after.workerBytes-before.workerBytes,packets:after.packets-before.packets,markerReceived:after.bytes>before.bytes&&after.marker}});
}
for(const result of outcomes){assert.equal(result.ordinaryPageLoaded,true);if(['baseline','disable-quic','disable-blink'].includes(result.mode)){assert.equal(result.sent,true);assert.equal(result.worker.sent,true);assert.ok(result.receiver.bytes>=132);assert.ok(result.receiver.workerBytes>=66);assert.equal(result.webRequestEvents,0);}else{assert.equal(result.sent,false);assert.equal(result.worker.sent,false);assert.equal(result.receiver.packets,0);assert.equal(result.receiver.bytes,0);assert.ok(result.forwardedHTTP>0);}}
const report={actualElectron:true,localPinnedReceiver:true,serverVersion:metadata.aioquic,outcomes};writeFileSync('artifacts/webtransport-native-proof.json',JSON.stringify(report,null,2));console.log(JSON.stringify(report,null,2));
}finally{page.closeAllConnections();proxy.closeAllConnections();await Promise.all([new Promise(r=>page.close(r)),new Promise(r=>proxy.close(r)),relay.close()]);python.kill();}
+71
View File
@@ -0,0 +1,71 @@
"""Finite loopback-only HTTP/3 WebTransport receiver for native security proof."""
import asyncio, datetime, hashlib, ipaddress, json, os, pathlib, sys
from aioquic.asyncio import QuicConnectionProtocol, serve
from aioquic.h3.connection import H3Connection, H3_ALPN
from aioquic.h3.events import HeadersReceived, DatagramReceived, WebTransportStreamDataReceived
from aioquic.quic.configuration import QuicConfiguration
from aioquic.quic.events import ProtocolNegotiated
from cryptography import x509
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.x509.oid import NameOID
facts = {"packets": 0, "connections": 0, "datagrams": 0, "streams": 0, "bytes": 0, "workerBytes": 0, "marker": False}
marker = b"SYNTHETIC_WEBTRANSPORT_DLP_MARKER"
class Receiver(QuicConnectionProtocol):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.http = None
self.worker_sessions = set()
def datagram_received(self, data, addr):
facts["packets"] += 1
super().datagram_received(data, addr)
def quic_event_received(self, event):
if isinstance(event, ProtocolNegotiated):
self.http = H3Connection(self._quic, enable_webtransport=True)
if not self.http:
return
for item in self.http.handle_event(event):
if isinstance(item, HeadersReceived):
headers = dict(item.headers)
if headers.get(b":method") == b"CONNECT" and headers.get(b":protocol") == b"webtransport":
facts["connections"] += 1
if headers.get(b":path", b"").endswith(b"/worker"):
self.worker_sessions.add(item.stream_id)
self.http.send_headers(item.stream_id, [(b":status", b"200"), (b"sec-webtransport-http3-draft", b"draft02")])
self.transmit()
elif isinstance(item, (DatagramReceived, WebTransportStreamDataReceived)):
facts["datagrams" if isinstance(item, DatagramReceived) else "streams"] += 1
facts["bytes"] += len(item.data)
session_id = item.stream_id if isinstance(item, DatagramReceived) else item.session_id
if session_id in self.worker_sessions:
facts["workerBytes"] += len(item.data)
facts["marker"] |= marker in item.data
async def main():
root = pathlib.Path(sys.argv[1]).resolve()
root.mkdir(parents=True, exist_ok=True)
key = ec.generate_private_key(ec.SECP256R1())
now = datetime.datetime.now(datetime.timezone.utc)
name = x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, "127.0.0.1")])
cert = x509.CertificateBuilder().subject_name(name).issuer_name(name).public_key(key.public_key()).serial_number(x509.random_serial_number()).not_valid_before(now-datetime.timedelta(minutes=1)).not_valid_after(now+datetime.timedelta(days=7)).add_extension(x509.SubjectAlternativeName([x509.IPAddress(ipaddress.ip_address("127.0.0.1"))]), critical=False).sign(key, hashes.SHA256())
cert_file, key_file = root / "certificate.pem", root / "key.pem"
cert_file.write_bytes(cert.public_bytes(serialization.Encoding.PEM))
key_file.write_bytes(key.private_bytes(serialization.Encoding.PEM, serialization.PrivateFormat.PKCS8, serialization.NoEncryption()))
config = QuicConfiguration(is_client=False, alpn_protocols=H3_ALPN, max_datagram_frame_size=65536)
config.load_cert_chain(str(cert_file), str(key_file))
server = await serve("127.0.0.1", 0, configuration=config, create_protocol=Receiver)
async def stats(reader, writer):
await reader.read(8192)
body = json.dumps(facts).encode()
writer.write(b"HTTP/1.1 200 OK\r\nContent-Type: application/json\r\nContent-Length: " + str(len(body)).encode() + b"\r\nConnection: close\r\n\r\n" + body)
await writer.drain()
writer.close()
control = await asyncio.start_server(stats, "127.0.0.1", 0)
print(json.dumps({"quicPort": server._transport.get_extra_info("sockname")[1], "statsPort": control.sockets[0].getsockname()[1], "hash": hashlib.sha256(cert.public_bytes(serialization.Encoding.DER)).hexdigest(), "aioquic": __import__("aioquic").__version__}), flush=True)
await asyncio.sleep(180)
server.close()
control.close()
if __name__ == "__main__":
asyncio.run(main())
+16
View File
@@ -0,0 +1,16 @@
'use strict';
// A second Electron process reads its original DPAPI enrollment, never reenrolls.
const {app,BrowserWindow,session,clipboard,ClipboardItem}=require('electron'),fs=require('node:fs'),path=require('node:path'),assert=require('node:assert/strict'),https=require('node:https'),{X509Certificate}=require('node:crypto');
assert.equal(process.env.SAFETYNET_SMOKE_CODE,undefined,'Restart must not receive an enrollment code');app.setPath('userData',process.env.SAFETYNET_SMOKE_USERDATA);app.disableHardwareAcceleration();
const origin=process.env.SAFETYNET_SMOKE_ORIGIN,base=process.env.SAFETYNET_SMOKE_SERVER,headers={authorization:'Bearer '+process.env.SAFETYNET_SMOKE_ADMIN},secret='MVP Company Forecast 2030',fingerprint=new X509Certificate(fs.readFileSync(path.join(__dirname,'../tests/fixtures/icap/cert.pem'))).fingerprint256,fromPartition=session.fromPartition.bind(session);
session.fromPartition=(...args)=>{const current=fromPartition(...args);if(args[0]==='persist:safetynet-managed')current.setCertificateVerifyProc((req,callback)=>{let valid=false;try{valid=req.hostname==='127.0.0.1'&&new X509Certificate(req.certificate.data).fingerprint256===fingerprint;}catch{}callback(valid?0:-2);});return current;};
let actualDiagnostics;const moduleDiagnostics=require('../apps/browser/diagnostics.cjs'),createDiagnostics=moduleDiagnostics.createDiagnostics;moduleDiagnostics.createDiagnostics=(...args)=>{const value=createDiagnostics(...args);actualDiagnostics=value;return value;};require('../apps/browser/main.cjs');
const wait=ms=>new Promise(r=>setTimeout(r,ms));async function until(fn){for(let i=0;i<300;i++){const result=await fn();if(result)return result;await wait(100);}throw Error('Restarted native MVP timeout');}
const privateFacts=()=>new Promise((resolve,reject)=>{const request=https.get(origin+'/facts',{ca:fs.readFileSync(path.join(__dirname,'../tests/fixtures/icap/cert.pem')),checkServerIdentity:(_host,cert)=>new X509Certificate(cert.raw).fingerprint256===fingerprint?undefined:Error('Synthetic certificate mismatch')},response=>{let body='';response.on('data',chunk=>body+=chunk);response.on('end',()=>resolve(JSON.parse(body)));});request.on('error',reject);});
app.whenReady().then(async()=>{let stage='persisted startup',page,original=[];const checks=[];try{for(const item of await clipboard.read()){const data={};for(const type of item.types)data[type]=await item.getType(type);original.push(new ClipboardItem(data));}const win=await until(()=>{const w=BrowserWindow.getAllWindows()[0];return w&&!w.webContents.isLoading()&&w.webContents.getURL().endsWith('index.html')&&w;});const ui=async()=>{const result=await win.webContents.executeJavaScript('window.safetynet.state()');assert.equal(result.ok,true);return result.data;};const initial=await until(async()=>{const current=await ui();return current.ready&&current.enrolled&&current;});assert.equal(initial.deviceId,process.env.SAFETYNET_SMOKE_EXPECT_DEVICE);assert.equal(initial.policyVersion,Number(process.env.SAFETYNET_SMOKE_EXPECT_POLICY));assert.notEqual(process.pid,Number(process.env.SAFETYNET_SMOKE_FIRST_PID));page=win.contentView.children.find(v=>v.webContents!==win.webContents).webContents;await until(()=>!page.isLoading()&&page.getURL()===origin+'/workspace');checks.push('same-dpapi-device-current-policy-new-process');
const before=await privateFacts(),key=async keyCode=>{win.focus();page.focus();page.sendInputEvent({type:'keyDown',keyCode,modifiers:['control']});page.sendInputEvent({type:'keyUp',keyCode,modifiers:['control']});await wait(400);await until(async()=>!(await ui()).clipboardPending);};const editor=async text=>page.executeJavaScript(`(()=>{const e=document.getElementById('editor');e.innerHTML='<p></p>';e.firstChild.textContent=${JSON.stringify(text)};e.focus();const r=document.createRange();r.selectNodeContents(e.firstChild);r.collapse(false);const s=getSelection();s.removeAllRanges();s.addRange(r);})()`);const value=()=>page.executeJavaScript("document.getElementById('editor').innerText.trimEnd()");
stage='post-restart public paste submit';await editor('');await clipboard.writeText('Public restarted AI prompt');await key('V');assert.equal(await value(),'Public restarted AI prompt');assert.equal(await page.executeJavaScript("window.mvpSubmit('editor')"),true);assert.equal((await privateFacts()).receiver.posts,before.receiver.posts+1);checks.push('restarted-public-paste-submit');stage='post-restart confidential paste held';await editor('Public preserved restart baseline');await clipboard.writeText(secret);await key('V');assert.equal(await value(),'Public preserved restart baseline');assert.equal(await page.executeJavaScript("window.mvpSubmit('editor')"),true);assert.equal((await privateFacts()).receiver.posts,before.receiver.posts+2);assert.equal((await privateFacts()).receiver.secretPosts,0);checks.push('restarted-confidential-paste-denied');stage='post-restart typed confidential body denied';assert.equal(await page.executeJavaScript(`fetch('/receiver/typed',{method:'POST',body:${JSON.stringify(secret)}}).then(r=>r.ok).catch(()=>false)`),false);assert.equal((await privateFacts()).receiver.posts,before.receiver.posts+2);checks.push('restarted-confidential-body-denied');
stage='post-restart public file allowed';const doc=await page.debugger.sendCommand('DOM.getDocument',{}),node=await page.debugger.sendCommand('DOM.querySelector',{nodeId:doc.root.nodeId,selector:'#file'});const upload=async file=>{await page.debugger.sendCommand('DOM.setFileInputFiles',{nodeId:node.nodeId,files:[file]});return page.executeJavaScript("(()=>{const body=new FormData();body.append('file',document.getElementById('file').files[0]);return fetch('/receiver/file',{method:'POST',body}).then(r=>r.ok).catch(()=>false)})()");};assert.equal(await upload(process.env.SAFETYNET_SMOKE_PUBLIC_FILE),true);let after=await privateFacts();assert.equal(after.receiver.posts,before.receiver.posts+3);assert.equal(after.receiver.filePosts,before.receiver.filePosts+1);assert.equal(after.receiver.exactPublicFile,before.receiver.exactPublicFile+1);stage='post-restart confidential file denied';assert.equal(await upload(process.env.SAFETYNET_SMOKE_SECRET_FILE),false);after=await privateFacts();assert.equal(after.receiver.posts,before.receiver.posts+3);assert.equal(after.receiver.secretPosts,0);checks.push('restarted-public-secret-ntfs-upload');
stage='post-restart audit and traces';await actualDiagnostics.flush();await wait(1500);const events=[];let cursor=Number(process.env.SAFETYNET_SMOKE_AFTER_EVENT);for(let batch=0;batch<20;batch++){const response=await fetch(base+`/api/admin/events/export?after=${cursor}&limit=1000&format=ecs`,{headers});assert.equal(response.status,200);const rows=(await response.text()).trim().split('\n').filter(Boolean).map(JSON.parse);for(const row of rows){assert.equal(row.safetynet.policy_version,initial.policyVersion,'Restart audit policy binding changed');assert.equal(row.host.id,initial.deviceId);assert.ok(!JSON.stringify(row).includes(secret));events.push(row);}if(rows.length<1000)break;const next=Number(response.headers.get('x-safetynet-next-cursor'));assert.ok(next>cursor);cursor=next;}assert.ok(events.some(e=>e.event.action==='browser.started'),'New browser process start was not audited');const traces=[];for(const action of ['clipboard.paste','clipboard.block','upload.authorized','upload.block']){const event=events.filter(e=>e.event.action===action&&e.trace?.id).at(-1);assert.ok(event,action+' restart event absent');const trace=await(await fetch('http://127.0.0.1:4318/api/trace/'+event.trace.id)).json();assert.ok(trace.spans.length);assert.ok(!JSON.stringify(trace).includes(secret));const expected=action.startsWith('clipboard')?'browser.clipboard':'browser.request';assert.ok(trace.spans.some(s=>s.name===expected));if(action.startsWith('upload.'))assert.ok(trace.spans.some(s=>s.name==='browser.upload.lease'),'Restart native file lease trace missing');const byId=new Map(trace.spans.map(s=>[s.spanId,s]));if(action.startsWith('upload.')){for(const operation of ['text.local','text.enterprise']){const normalized=trace.spans.find(s=>s.operation===operation);assert.ok(normalized,'Normalized URL stage absent');let current=normalized,linked=false;const seen=new Set();while(current&&!seen.has(current.spanId)){if(current.name==='browser.request'){linked=true;break;}seen.add(current.spanId);current=byId.get(current.parentSpanId);}assert.ok(linked,'Normalized URL parent correlation absent');}}for(const stage of trace.spans.filter(s=>s.name==='browser.upload.lease'||s.name==='browser.upload.part'||s.name==='browser.inspect.local')){let current=stage,linked=false;const seen=new Set();while(current&&!seen.has(current.spanId)){if(current.name===expected){linked=true;break;}seen.add(current.spanId);current=byId.get(current.parentSpanId);}assert.ok(linked,'Restart span parent binding absent');}traces.push({action,traceId:event.trace.id,stages:trace.spans.map(({name,operation,spanId,parentSpanId,durationMs})=>({name,operation,spanId,parentSpanId,durationMs}))});}const status=actualDiagnostics.status();assert.equal(status.enabled,true);assert.equal(status.failedExports,0);assert.equal(status.lifetimeFailedExports,0);const health=await(await fetch('http://127.0.0.1:4318/health')).json();assert.equal(health.rejectedWrites,0);
fs.writeFileSync(path.resolve('artifacts/windows-dlp-mvp-restart-native-proof.json'),JSON.stringify({actualNewNativeProcess:true,firstProcessId:Number(process.env.SAFETYNET_SMOKE_FIRST_PID),restartedProcessId:process.pid,enrollmentCodeAbsent:true,persistedDeviceId:initial.deviceId,policyVersion:initial.policyVersion,checks,receiverBefore:before.receiver,receiverAfter:after.receiver,scannerBefore:before.scanner,scannerAfter:after.scanner,traces,diagnostics:status,health:{storedBytes:health.storedBytes,rejectedWrites:health.rejectedWrites}},null,2));console.log('Windows DLP MVP actual process restart proof passed');if(original.length)await clipboard.write(original);else await clipboard.clear();require('../apps/browser/page-clipboard-guard.cjs').disposePageClipboardGuard(page);app.exit(0);
}catch(error){let facts;try{facts=await privateFacts();await actualDiagnostics?.flush();}catch{}fs.writeFileSync(path.resolve('artifacts/windows-dlp-mvp-restart-native-error.json'),JSON.stringify({stage,name:error.name,message:error.message,checks,facts,diagnostics:actualDiagnostics?.status()},null,2));console.error('Restarted Windows MVP failed at '+stage+': '+error.name);try{if(original.length)await clipboard.write(original);else await clipboard.clear();}catch{}if(page&&!page.isDestroyed())require('../apps/browser/page-clipboard-guard.cjs').disposePageClipboardGuard(page);app.exit(1);}});
+27
View File
@@ -0,0 +1,27 @@
'use strict';
const {app,BrowserWindow,session,clipboard,ClipboardItem}=require('electron'),fs=require('node:fs'),path=require('node:path'),assert=require('node:assert/strict'),https=require('node:https'),{X509Certificate}=require('node:crypto');
app.setPath('userData',process.env.SAFETYNET_SMOKE_USERDATA);app.disableHardwareAcceleration();let actualDiagnostics;const diagnosticsModule=require('../apps/browser/diagnostics.cjs'),createDiagnostics=diagnosticsModule.createDiagnostics;diagnosticsModule.createDiagnostics=(...args)=>{const instance=createDiagnostics(...args);actualDiagnostics=instance;return instance;};require('../apps/browser/main.cjs');
const wait=ms=>new Promise(r=>setTimeout(r,ms)),base=process.env.SAFETYNET_SMOKE_SERVER,origin=process.env.SAFETYNET_SMOKE_ORIGIN,headers={authorization:'Bearer '+process.env.SAFETYNET_SMOKE_ADMIN,'content-type':'application/json'},secret='MVP Company Forecast 2030',checks=[],editorCaretFacts=[];
async function until(fn){for(let i=0;i<250;i++){const value=await fn();if(value)return value;await wait(100);}throw Error('Native Windows DLP fixture timeout');}
app.whenReady().then(async()=>{let page,readFailureFacts,originalClipboard=[],stage='startup';try{for(const item of await clipboard.read()){const data={};for(const type of item.types)data[type]=await item.getType(type);originalClipboard.push(new ClipboardItem(data));}const win=await until(()=>{const w=BrowserWindow.getAllWindows()[0];return w&&!w.webContents.isLoading()&&w.webContents.getURL().endsWith('index.html')&&w;});const fingerprint=new X509Certificate(fs.readFileSync(path.join(__dirname,'../tests/fixtures/icap/cert.pem'))).fingerprint256;session.fromPartition('persist:safetynet-managed').setCertificateVerifyProc((req,callback)=>{let valid=false;try{valid=req.hostname==='127.0.0.1'&&new X509Certificate(req.certificate.data).fingerprint256===fingerprint;}catch{}callback(valid?0:-2);});const ui=async(method,value)=>{const result=await win.webContents.executeJavaScript(`window.safetynet.${method}(${value===undefined?'':JSON.stringify(value)})`);assert.equal(result.ok,true,method);return result.data;};
const privateGET=route=>new Promise((resolve,reject)=>{const request=https.get(origin+route,{ca:fs.readFileSync(path.join(__dirname,'../tests/fixtures/icap/cert.pem')),checkServerIdentity:(_host,cert)=>new X509Certificate(cert.raw).fingerprint256===fingerprint?undefined:Error('Synthetic certificate mismatch')},response=>{let body='';response.on('data',chunk=>body+=chunk);response.on('end',()=>resolve(route==='/facts'?JSON.parse(body):true));});request.on('error',reject);});
readFailureFacts=()=>privateGET('/facts');stage='enrollment';await ui('enroll',{server:base,code:process.env.SAFETYNET_SMOKE_CODE});page=win.contentView.children.find(v=>v.webContents!==win.webContents).webContents;await until(()=>!page.isLoading()&&page.getURL()===origin+'/workspace');let loadGeneration=0;page.on('did-finish-load',()=>loadGeneration++);const navigate=async()=>{const before=loadGeneration;await ui('navigate',origin+'/workspace');await until(()=>loadGeneration>before&&!page.isLoading()&&page.getURL()===origin+'/workspace');win.focus();page.focus();await wait(200);};
const change=async modify=>{const p=(await(await fetch(base+'/api/admin/overview',{headers})).json()).policy;modify(p);p.version++;const generation=loadGeneration,proposed=await fetch(base+'/api/admin/policy',{method:'PUT',headers,body:JSON.stringify(p)});assert.equal(proposed.status,202);const review=await proposed.json();const approval=await fetch(base+`/api/admin/policy-reviews/${review.reviewId}/approve`,{method:'POST',headers:{cookie:'sn_session='+process.env.SAFETYNET_SMOKE_REVIEWER,origin:base,'content-type':'application/json'},body:JSON.stringify({payloadHash:review.payloadHash,expectedVersion:review.expectedVersion,approve:true})});assert.equal(approval.status,200);await until(async()=>{const s=await ui('state');return s.ready&&s.policyVersion===p.version;});await until(()=>loadGeneration>generation&&!page.isLoading());win.focus();page.focus();await wait(200);};
const key=async(code,shift=false)=>{win.focus();page.focus();page.sendInputEvent({type:'keyDown',keyCode:code,modifiers:shift?['shift']:['control']});page.sendInputEvent({type:'keyUp',keyCode:code,modifiers:shift?['shift']:['control']});await wait(400);await until(async()=>!(await ui('state')).clipboardPending);};
const setField=(id,text='')=>page.executeJavaScript(`(()=>{const e=document.getElementById(${JSON.stringify(id)});if(e.isContentEditable){e.innerHTML='<p><br></p>';if(${JSON.stringify(text)})e.firstChild.textContent=${JSON.stringify(text)};e.focus();const range=document.createRange();range.selectNodeContents(e.firstChild);range.collapse(!e.firstChild.textContent);const s=getSelection();s.removeAllRanges();s.addRange(range);}else{e.value=${JSON.stringify(text)};e.focus();e.setSelectionRange(e.value.length,e.value.length);}})()`);
const clickEditor=async()=>{const point=await page.executeJavaScript("(()=>{const r=document.getElementById('editor').getBoundingClientRect();return {x:Math.round(r.left+8),y:Math.round(r.top+10)}})()");page.sendInputEvent({type:'mouseDown',button:'left',clickCount:1,...point});page.sendInputEvent({type:'mouseUp',button:'left',clickCount:1,...point});await wait(80);const facts=await page.executeJavaScript("(()=>{const e=document.getElementById('editor'),s=getSelection(),r=s.rangeCount===1?s.getRangeAt(0):null,p=e.firstChild;return {focused:document.activeElement===e,collapsed:!!r?.collapsed,startOwned:!!r&&e.contains(r.startContainer),endOwned:!!r&&e.contains(r.endContainer),startInParagraph:!!r&&p.contains(r.startContainer),endInParagraph:!!r&&p.contains(r.endContainer)}})()");assert.ok(facts.focused&&facts.collapsed&&facts.startOwned&&facts.endOwned);editorCaretFacts.push({nativeClick:true,...facts});};
const value=id=>page.executeJavaScript(`(()=>{const e=document.getElementById(${JSON.stringify(id)});return e.isContentEditable?e.innerText.trimEnd():e.value;})()`);
const post=(kind,text)=>page.executeJavaScript(`fetch(${JSON.stringify('/receiver/'+kind)},{method:'POST',headers:{'content-type':'text/plain'},body:${JSON.stringify(text)}}).then(r=>r.ok).catch(()=>false)`);
stage='typed public body allowed';let before=(await privateGET('/facts')).receiver.posts;assert.equal(await post('typed','Public typed prompt'),true);assert.equal((await privateGET('/facts')).receiver.posts,before+1);checks.push('public-body');stage='typed confidential body blocked';before=(await privateGET('/facts')).receiver.posts;assert.equal(await post('typed',secret),false);assert.equal((await privateGET('/facts')).receiver.posts,before);checks.push('secret-body');
for(const id of ['input','textarea','editor']){stage=id+' public paste to actual receiver';await setField(id);if(id==='editor')await clickEditor();const text='Public '+id+' prompt for AI';await clipboard.writeText(text);await key('V');stage=id+' public paste commit';assert.equal(await value(id),text,'Public AI editor paste did not commit');before=(await privateGET('/facts')).receiver.posts;if(id==='input'){stage='input native form dispatch';const loading=new Promise(resolve=>page.once('did-stop-loading',resolve));assert.equal(await page.executeJavaScript("(()=>{setTimeout(()=>document.getElementById('prompt-form').requestSubmit(),0);return true})()"),true);await Promise.race([loading,new Promise((_,reject)=>setTimeout(()=>reject(Error('Native form did not terminate')),15000))]);assert.equal((await privateGET('/facts')).receiver.posts,before+1);await navigate();}else{assert.equal(await page.executeJavaScript(`window.mvpSubmit(${JSON.stringify(id)})`),true);assert.equal((await privateGET('/facts')).receiver.posts,before+1);}checks.push(id+'-public-paste-submit');
stage=id+' confidential paste cannot reach receiver';const baseline='Public preserved '+id;await setField(id,baseline);await clipboard.writeText(secret);await key('V');assert.equal(await value(id),baseline,'Confidential paste changed the AI editor');before=(await privateGET('/facts')).receiver.posts;assert.equal(await page.executeJavaScript(`window.mvpSubmit(${JSON.stringify(id)})`),true);const facts=await privateGET('/facts');assert.equal(facts.receiver.posts,before+1);assert.equal(facts.receiver.secretPosts,0);checks.push(id+'-secret-paste-denied');}
stage='plain part of HTML clipboard stays inert';await setField('editor');await clickEditor();await clipboard.write([new ClipboardItem({'text/plain':new Blob(['Public HTML-source prompt'],{type:'text/plain'}),'text/html':new Blob(['<p>Public HTML-source prompt</p><img id="mvp-html-must-not-render" src="/html-clipboard-network">'],{type:'text/html'})})]);await key('V');assert.equal(await value('editor'),'Public HTML-source prompt');assert.equal(await page.executeJavaScript("!!document.querySelector('#editor img,#mvp-html-must-not-render')"),false);before=(await privateGET('/facts')).receiver.posts;assert.equal(await page.executeJavaScript("window.mvpSubmit('editor')"),true);assert.equal((await privateGET('/facts')).receiver.posts,before+1);stage='confidential plain part of HTML clipboard blocked';await setField('editor','Public HTML baseline');await clipboard.write([new ClipboardItem({'text/plain':new Blob([secret],{type:'text/plain'}),'text/html':new Blob(['<p>Public decoy</p>'],{type:'text/html'})})]);await key('V');assert.equal(await value('editor'),'Public HTML baseline');checks.push('html-clipboard-inert-plain-part-dlp');
stage='native form typed public allowed';await setField('input','Public typed standard form');before=(await privateGET('/facts')).receiver.posts;let stopped=new Promise(resolve=>page.once('did-stop-loading',resolve));assert.equal(await page.executeJavaScript("(()=>{setTimeout(()=>document.getElementById('prompt-form').requestSubmit(),0);return true})()"),true);await Promise.race([stopped,new Promise((_,reject)=>setTimeout(()=>reject(Error('Native form did not terminate')),15000))]);assert.equal((await privateGET('/facts')).receiver.posts,before+1);await navigate();checks.push('public-standard-form');stage='native form typed confidential denied';await setField('input',secret);before=(await privateGET('/facts')).receiver.posts;stopped=new Promise(resolve=>page.once('did-stop-loading',resolve));assert.equal(await page.executeJavaScript("(()=>{setTimeout(()=>document.getElementById('prompt-form').requestSubmit(),0);return true})()"),true);await Promise.race([stopped,new Promise((_,reject)=>setTimeout(()=>reject(Error('Blocked form did not terminate')),15000))]);assert.equal((await privateGET('/facts')).receiver.posts,before);await navigate();checks.push('secret-standard-form');
const selectCopy=text=>page.executeJavaScript(`(()=>{const e=document.getElementById('copy-source');e.textContent=${JSON.stringify(text)};const range=document.createRange();range.selectNodeContents(e);const s=getSelection();s.removeAllRanges();s.addRange(range);})()`);stage='public native copy';await selectCopy('Public copy sample');await clipboard.writeText('Public clipboard sentinel');await key('C');assert.equal(await clipboard.readText(),'Public copy sample');stage='confidential copy holds OS clipboard';await clipboard.writeText('Public clipboard sentinel');await selectCopy(secret);await key('C');assert.equal(await clipboard.readText(),'Public clipboard sentinel');stage='confidential cut preserves source';await setField('input',secret);await page.executeJavaScript("document.getElementById('input').select()");await key('X');assert.equal(await clipboard.readText(),'Public clipboard sentinel');assert.equal(await value('input'),secret);checks.push('copy-cut-secret-denied');stage='public native cut';await setField('textarea','Public cut sample');await page.executeJavaScript("document.getElementById('textarea').select()");await key('X');assert.equal(await clipboard.readText(),'Public cut sample');assert.equal(await value('textarea'),'');checks.push('public-copy-cut');
stage='native NTFS public file';const doc=await page.debugger.sendCommand('DOM.getDocument',{}),node=await page.debugger.sendCommand('DOM.querySelector',{nodeId:doc.root.nodeId,selector:'#file'});const upload=async file=>{await page.debugger.sendCommand('DOM.setFileInputFiles',{nodeId:node.nodeId,files:[file]});return page.executeJavaScript("(()=>{const body=new FormData();body.append('file',document.getElementById('file').files[0]);return fetch('/receiver/file',{method:'POST',body}).then(r=>r.ok).catch(()=>false)})()");};assert.equal(await upload(process.env.SAFETYNET_SMOKE_PUBLIC_FILE),true);let facts=await privateGET('/facts');assert.equal(facts.receiver.filePosts,1);assert.equal(facts.receiver.exactPublicFile,1);stage='native NTFS confidential file denied';assert.equal(await upload(process.env.SAFETYNET_SMOKE_SECRET_FILE),false);facts=await privateGET('/facts');assert.equal(facts.receiver.filePosts,1);assert.equal(facts.receiver.secretPosts,0);checks.push('native-file-public-secret');
stage='actual local YARA blocks';before=(await privateGET('/facts')).receiver.posts;assert.equal(await post('yara','MVP_YARA_ONLY_BLOCK'),false);assert.equal((await privateGET('/facts')).receiver.posts,before);stage='actual ICAP blocks';assert.equal(await post('icap','MVP_ICAP_ONLY_BLOCK'),false);assert.equal((await privateGET('/facts')).receiver.posts,before);stage='actual configured scanner failure fails closed';await privateGET('/scanner/fail');assert.equal(await post('scanner','Public scanner outage prompt'),false);assert.equal((await privateGET('/facts')).receiver.posts,before);await privateGET('/scanner/normal');checks.push('real-yara-icap-scanner-failure');
stage='policy changes while real scan pending deny release';await privateGET('/scanner/gate');before=(await privateGET('/facts')).receiver.posts;const pending=post('race','Public pending scan').catch(()=>false);await until(async()=>(await privateGET('/facts')).scanner.pending);await change(p=>p.dlp.rules.push({id:'mvp-late-classification',label:'Synthetic policy race',kind:'literal',value:'Public pending scan',action:'block'}));await privateGET('/scanner/release');assert.equal(await pending,false);assert.equal((await privateGET('/facts')).receiver.posts,before);await privateGET('/scanner/normal');checks.push('policy-race-denied');
stage='central paste disable then re-enable';await change(p=>p.security.clipboard.allowPaste=false);await setField('editor');await clipboard.writeText('Public centrally disabled paste');await key('V');assert.equal(await value('editor'),'');await change(p=>p.security.clipboard.allowPaste=true);await setField('editor');await clipboard.writeText('Public centrally restored paste');await key('V');assert.equal(await value('editor'),'Public centrally restored paste');checks.push('independent-central-paste-control');
stage='real correlated diagnostics and SIEM';await wait(1800);const events=[];let after=0;for(let batch=0;batch<20;batch++){const response=await fetch(base+`/api/admin/events/export?after=${after}&limit=1000&format=ecs`,{headers});assert.equal(response.status,200);const rows=(await response.text()).trim().split('\n').filter(Boolean).map(line=>JSON.parse(line));for(const row of rows){assert.ok(!/MVP Company Forecast 2030|MVP_YARA_ONLY_BLOCK|MVP_ICAP_ONLY_BLOCK/.test(JSON.stringify(row)));events.push(row);}if(rows.length<1000)break;const next=Number(response.headers.get('x-safetynet-next-cursor'));assert.ok(next>after);after=next;}const traceEvidence=[];for(const action of ['clipboard.paste','clipboard.block','upload.authorized','upload.block']){const candidates=events.filter(e=>e.event.action===action&&e.trace?.id);assert.ok(candidates.length,action+' durable audit missing');let found;for(const event of candidates.reverse()){const trace=await(await fetch('http://127.0.0.1:4318/api/trace/'+event.trace.id)).json();const expected=action.startsWith('clipboard')?'browser.clipboard':'browser.request';if(trace.spans?.some(s=>s.name===expected)){assert.ok(!/MVP Company Forecast 2030|MVP_YARA_ONLY_BLOCK|MVP_ICAP_ONLY_BLOCK/.test(JSON.stringify(trace)));const byId=new Map(trace.spans.map(s=>[s.spanId,s]));if(action.startsWith('upload.')){for(const operation of ['text.local','text.enterprise']){const normalized=trace.spans.find(s=>s.operation===operation);assert.ok(normalized,'Normalized URL stage absent');let current=normalized,linked=false;const seen=new Set();while(current&&!seen.has(current.spanId)){if(current.name==='browser.request'){linked=true;break;}seen.add(current.spanId);current=byId.get(current.parentSpanId);}assert.ok(linked,'Normalized URL parent correlation absent');}}if(action==='clipboard.paste'){for(const name of ['browser.inspect.local','browser.inspect.enterprise']){const stage=trace.spans.find(s=>s.name===name);assert.ok(stage,'Clipboard inspection stage missing');let node=stage,linked=false;const seen=new Set();while(node&&!seen.has(node.spanId)){if(node.name==='browser.clipboard'){linked=true;break;}seen.add(node.spanId);node=byId.get(node.parentSpanId);}assert.ok(linked,'Clipboard inspection parent correlation missing');}}if(action==='upload.authorized'){assert.ok(trace.spans.some(s=>s.name==='browser.upload.lease'));for(const s of trace.spans.filter(s=>s.name==='browser.upload.lease'||s.name==='browser.upload.part')){let node=s,linked=false;const seen=new Set();while(node&&!seen.has(node.spanId)){if(node.name==='browser.request'){linked=true;break;}seen.add(node.spanId);node=byId.get(node.parentSpanId);}assert.ok(linked,'File stage parent correlation missing');}}found={action,traceId:event.trace.id,stages:trace.spans.map(({name,operation,spanId,parentSpanId,durationMs})=>({name,operation,spanId,parentSpanId,durationMs}))};break;}}assert.ok(found,action+' exported trace missing');traceEvidence.push(found);}facts=await privateGET('/facts');assert.equal(facts.receiver.secretPosts,0);assert.equal(facts.receiver.filePosts,1);assert.equal(facts.receiver.exactPublicFile,1);assert.equal(facts.scanner.blocks,1);assert.ok(facts.scanner.failures>=1);const health=await(await fetch('http://127.0.0.1:4318/health')).json();assert.equal(health.rejectedWrites,0);const summary=await(await fetch('http://127.0.0.1:4318/api/summary')).json();await actualDiagnostics.flush();const diagnostics=actualDiagnostics.status();assert.equal(diagnostics.enabled,true);assert.equal(diagnostics.failedExports,0);assert.equal(diagnostics.lifetimeFailedExports,0);
fs.writeFileSync(path.resolve('artifacts/windows-dlp-mvp-native-proof.json'),JSON.stringify({actualWindows:process.platform==='win32',actualElectron:true,processId:process.pid,deviceId:(await ui('state')).deviceId,policyVersion:(await ui('state')).policyVersion,exactPinnedHTTPS:true,independentReviewedPublication:true,checks,editorCaretFacts,receiver:facts.receiver,scanner:facts.scanner,ordinaryAuditConfidentialFree:true,traceEvidence,summary,health:{storedBytes:health.storedBytes,rejectedWrites:health.rejectedWrites},diagnostics},null,2));console.log('Windows DLP MVP native proof passed');if(originalClipboard.length)await clipboard.write(originalClipboard);else await clipboard.clear();require('../apps/browser/page-clipboard-guard.cjs').disposePageClipboardGuard(page);app.exit(0);
}catch(error){let failureFacts;try{failureFacts=await readFailureFacts?.();await actualDiagnostics?.flush();}catch{}fs.writeFileSync(path.resolve('artifacts/windows-dlp-mvp-native-error.json'),JSON.stringify({stage,name:error.name,message:error.message,checks,editorCaretFacts,failureFacts,diagnostics:actualDiagnostics?.status()},null,2));console.error('Windows DLP MVP failed at '+stage+': '+error.name);try{if(originalClipboard.length)await clipboard.write(originalClipboard);else await clipboard.clear();}catch{}if(page&&!page.isDestroyed())require('../apps/browser/page-clipboard-guard.cjs').disposePageClipboardGuard(page);app.exit(1);}});
+27
View File
@@ -0,0 +1,27 @@
// A bounded, real Windows browser DLP acceptance receiver; never a production bypass.
import {createServer} from 'node:https';
import {createRequire} from 'node:module';
import {mkdtempSync,mkdirSync,rmSync,readFileSync,writeFileSync} from 'node:fs';
import {tmpdir} from 'node:os';
import {join,resolve,sep} from 'node:path';
import {spawn} from 'node:child_process';
import {randomBytes} from 'node:crypto';
import net from 'node:net';
import {createApp} from '../apps/server/app.mjs';
import {openStore,digest} from '../apps/server/store.mjs';
import {startTelemetry} from '../apps/server/telemetry.mjs';
const collector='http://127.0.0.1:4318';if(!(await fetch(collector+'/health')).ok)throw Error('Protected collector unavailable');process.env.OTEL_EXPORTER_OTLP_ENDPOINT=collector;const telemetry=startTelemetry();
const temporary=mkdtempSync(join(tmpdir(),'safetynet-windows-dlp-'));mkdirSync('artifacts',{recursive:true});if(!resolve(temporary).startsWith(resolve(tmpdir())+sep)||!temporary.split(/[\\/]/).at(-1).startsWith('safetynet-windows-dlp-'))throw Error('Unsafe temporary fixture path');
const secret='MVP Company Forecast 2030',yaraMarker='MVP_YARA_ONLY_BLOCK',icapMarker='MVP_ICAP_ONLY_BLOCK',publicFile=Buffer.from('Public Windows DLP file content\n'),files={public:join(temporary,'public.txt'),secret:join(temporary,'secret.txt')};writeFileSync(files.public,publicFile);writeFileSync(files.secret,secret);
const receiver={posts:0,secretPosts:0,filePosts:0,exactPublicFile:0,kinds:{}},scanner={scans:0,blocks:0,failures:0,pending:false};let avMode='normal',pendingAV=null;const sockets=new Set();
const av=net.createServer(socket=>{sockets.add(socket);socket.on('close',()=>sockets.delete(socket));socket.on('error',()=>{});let input=Buffer.alloc(0),done=false;socket.on('data',chunk=>{input=Buffer.concat([input,chunk]);if(input.length>4194304){input.fill(0);socket.destroy();return;}if(done||input.indexOf('\r\n\r\n')<0)return;const options=input.subarray(0,7).toString()==='OPTIONS';if(!options&&!input.subarray(-5).equals(Buffer.from('0\r\n\r\n')))return;done=true;if(options){input.fill(0);socket.end('ICAP/1.0 200 OK\r\nMethods: RESPMOD, REQMOD\r\nAllow: 204\r\nISTag: "mvp-fixture"\r\nEncapsulated: null-body=0\r\n\r\n');return;}scanner.scans++;const block=input.includes(Buffer.from(icapMarker)),gate=avMode==='gate'&&input.includes(Buffer.from('Public pending scan'));input.fill(0);if(avMode==='fail'){scanner.failures++;socket.end('ICAP/1.0 500 Server Error\r\n\r\n');return;}const reply=()=>socket.end(block?'ICAP/1.0 204 No Content\r\nISTag: "mvp-fixture"\r\nX-Infection-Found: Type=0; Threat=synthetic-mvp;\r\n\r\n':'ICAP/1.0 204 No Content\r\nISTag: "mvp-fixture"\r\n\r\n');if(block)scanner.blocks++;if(gate){scanner.pending=true;pendingAV=()=>{scanner.pending=false;pendingAV=null;reply();};}else reply();});}).listen(0,'127.0.0.1');await new Promise(r=>av.once('listening',r));
const page=`<!doctype html><meta charset="utf-8"><title>Public AI workspace</title><style>body{font:18px Arial;padding:24px}input,textarea,[contenteditable]{display:block;width:80%;min-height:44px;border:1px solid #aaa;margin:15px}button{padding:12px}</style><h1>Public AI prompt workspace</h1><form id="prompt-form" method="post" action="/receiver/form"><input id="input" name="prompt" type="text"><button id="form-submit">Send prompt</button></form><textarea id="textarea"></textarea><div id="editor" role="textbox" contenteditable="true"><p><br></p></div><input id="file" type="file"><pre id="copy-source"></pre><script>window.mvpSubmit=(id)=>{const e=document.getElementById(id),text=e.isContentEditable?e.innerText:e.value;return fetch('/receiver/'+id,{method:'POST',headers:{'content-type':'text/plain'},body:text}).then(r=>r.ok).catch(()=>false)};</script>`;
// eslint-disable-next-line no-useless-assignment -- Explicitly clear sensitive transient text after use.
const sink=createServer({cert:readFileSync('tests/fixtures/icap/cert.pem'),key:readFileSync('tests/fixtures/icap/key.pem')},(req,res)=>{if(req.url==='/facts'){res.setHeader('content-type','application/json');res.end(JSON.stringify({receiver,scanner}));return;}if(req.url.startsWith('/scanner/')){const mode=req.url.split('/').at(-1);if(mode==='release')pendingAV?.();else avMode=mode;res.end('ok');return;}if(req.method==='POST'){let chunks=[],size=0;req.on('data',chunk=>{size+=chunk.length;if(size>1048576){req.destroy();return;}chunks.push(chunk);});req.on('end',()=>{receiver.posts++;const body=Buffer.concat(chunks);let decoded=body.toString('utf8');try{decoded=decodeURIComponent(decoded.replace(/\+/g,' '));}catch{}if(decoded.includes(secret)||body.includes(Buffer.from(secret))||decoded.includes(yaraMarker)||decoded.includes(icapMarker))receiver.secretPosts++;const kind=/^\/receiver\/(input|textarea|editor|form|typed|file|race|scanner|yara|icap)$/.exec(req.url)?.[1]||'other';receiver.kinds[kind]=(receiver.kinds[kind]||0)+1;if(kind==='file'){receiver.filePosts++;const boundary=/boundary=(?:"([^"]+)"|([^;]+))/.exec(req.headers['content-type']||'');if(boundary){const start=body.indexOf(Buffer.from('\r\n\r\n')),end=body.indexOf(Buffer.from('\r\n--'+(boundary[1]||boundary[2])),start+4);if(start>=0&&end>=0&&body.subarray(start+4,end).equals(publicFile))receiver.exactPublicFile++;}}decoded='';body.fill(0);for(const chunk of chunks)chunk.fill(0);chunks=[];res.end('accepted');});return;}res.setHeader('content-type','text/html');res.end(page);}).listen(0,'127.0.0.1');await new Promise(r=>sink.once('listening',r));
const origin=`https://127.0.0.1:${sink.address().port}`,store=openStore(join(temporary,'db')),code=randomBytes(32).toString('base64url'),adminToken=randomBytes(32).toString('base64url');const p=store.get('policy');p.browser.homeUrl=origin+'/workspace';p.browser.allowAgentBackgroundWorkers=false;p.dlp.rules.push({id:'mvp-company-secret',label:'Synthetic company confidential forecast',kind:'literal',value:secret,action:'block'});p.security.yara.enabled=true;p.security.yara.rules=[{id:'mvp-yara-only',name:'Synthetic MVP YARA',source:`rule mvp_yara_only { strings: $a = "${yaraMarker}" condition: $a }`}];p.enterprise.icap={...p.enterprise.icap,enabled:true,url:`icap://127.0.0.1:${av.address().port}/scan`,scanUploads:true,scanDownloads:false,scanClipboard:false,scanAI:false};p.agents.enabled=false;p.diagnostics={...p.diagnostics,enabled:true,mode:'debug',endpoint:collector,sampleRate:1,debugSampleRate:1,flushIntervalMs:250};store.put('policy',p);store.db.prepare('INSERT INTO enrollments VALUES(?,?,?)').run(digest(code),Date.now()+600000,'mvp-fixture');
const config={secure:false,publicUrl:'http://127.0.0.1',adminToken,requirePQManagement:true,requireIndependentPolicyApproval:true};const management=createApp(store,config).listen(0,'127.0.0.1');await new Promise(r=>management.once('listening',r));config.publicUrl=`http://127.0.0.1:${management.address().port}`;const reviewer=randomBytes(32).toString('base64url');store.db.prepare('INSERT INTO sessions VALUES(?,?,?)').run(digest(reviewer),Date.now()+600000,JSON.stringify({sub:'independent-mvp-reviewer',role:'admin'}));
const executable=createRequire(import.meta.url)('electron'),environment={...process.env,SAFETYNET_SMOKE_SERVER:config.publicUrl,SAFETYNET_SMOKE_ADMIN:adminToken,SAFETYNET_SMOKE_REVIEWER:reviewer,SAFETYNET_SMOKE_USERDATA:join(temporary,'browser'),SAFETYNET_SMOKE_ORIGIN:origin,SAFETYNET_SMOKE_PUBLIC_FILE:files.public,SAFETYNET_SMOKE_SECRET_FILE:files.secret};delete environment.SAFETYNET_SMOKE_CODE;
async function runNative(script,extra={}){const child=spawn(executable,[script],{windowsHide:true,stdio:['ignore','pipe','pipe'],env:{...environment,...extra}});child.stdout.on('data',v=>process.stdout.write(v));child.stderr.on('data',v=>process.stderr.write(v));const deadline=setTimeout(()=>child.kill(),240000),exit=await new Promise(r=>{child.once('exit',v=>r(v??1));child.once('error',()=>r(1));});clearTimeout(deadline);return {exit,pid:child.pid};}
let exit;try{const first=await runNative('scripts/windows-dlp-mvp-smoke.cjs',{SAFETYNET_SMOKE_CODE:code});exit=first.exit;
if(exit===0){const proof=JSON.parse(readFileSync('artifacts/windows-dlp-mvp-native-proof.json','utf8'));writeFileSync('artifacts/windows-dlp-mvp-first-process-native-proof.json',JSON.stringify(proof,null,2));const cursor=store.db.prepare('SELECT COALESCE(MAX(id),0) AS id FROM events').get().id,devicesBefore=store.db.prepare('SELECT COUNT(*) AS count FROM devices').get().count;const restarted=await runNative('scripts/windows-dlp-mvp-restart-smoke.cjs',{SAFETYNET_SMOKE_FIRST_PID:String(proof.processId),SAFETYNET_SMOKE_EXPECT_DEVICE:proof.deviceId,SAFETYNET_SMOKE_EXPECT_POLICY:String(store.get('policy').version),SAFETYNET_SMOKE_AFTER_EVENT:String(cursor)});exit=restarted.exit;if(exit===0){const second=JSON.parse(readFileSync('artifacts/windows-dlp-mvp-restart-native-proof.json','utf8'));if(first.pid===restarted.pid||proof.processId===second.restartedProcessId||devicesBefore!==1||store.db.prepare('SELECT COUNT(*) AS count FROM devices').get().count!==devicesBefore)throw Error('Native restart enrollment isolation failed');writeFileSync('artifacts/windows-dlp-mvp-process-restart-summary.json',JSON.stringify({actualTwoProcessRuns:true,sameProtectedUserData:true,sameManagementBackend:true,deviceCount:devicesBefore,enrollmentNotRepeated:true,firstProcessId:proof.processId,restartedProcessId:second.restartedProcessId,policyVersion:second.policyVersion,firstProof:'windows-dlp-mvp-first-process-native-proof.json',restartProof:'windows-dlp-mvp-restart-native-proof.json'},null,2));}}
}catch(error){console.error('Native restart harness failed: '+error.name);exit=1;}finally{pendingAV?.();for(const socket of sockets)socket.destroy();await new Promise(r=>av.close(r));for(const server of [management,sink])server.closeAllConnections();await Promise.all([management,sink].map(server=>new Promise(r=>server.close(r))));store.db.close();await telemetry?.shutdown();rmSync(temporary,{recursive:true,force:true});}process.exit(exit);
+13
View File
@@ -0,0 +1,13 @@
'use strict';
const {app,BrowserWindow,session,clipboard,ClipboardItem}=require('electron'),fs=require('node:fs'),path=require('node:path'),https=require('node:https'),assert=require('node:assert/strict'),{X509Certificate}=require('node:crypto');
app.setPath('userData',process.env.SAFETYNET_SMOKE_USERDATA);app.disableHardwareAcceleration();const fixtures=JSON.parse(process.env.SAFETYNET_SEMANTIC_FIXTURES),origin=process.env.SAFETYNET_SMOKE_ORIGIN,base=process.env.SAFETYNET_SMOKE_SERVER,headers={authorization:'Bearer '+process.env.SAFETYNET_SMOKE_ADMIN},localWitnesses=[];
let actualDiagnostics;const diagnosticsModule=require('../apps/browser/diagnostics.cjs'),createDiagnostics=diagnosticsModule.createDiagnostics;diagnosticsModule.createDiagnostics=(...args)=>{const value=createDiagnostics(...args);actualDiagnostics=value;return value;};const inspection=require('../apps/browser/inspection.cjs'),startInspector=inspection.startInspector;inspection.startInspector=(...args)=>{const value=startInspector(...args),inspect=value.inspect.bind(value);value.inspect=async(bytes,policy)=>{const result=await inspect(bytes,policy);localWitnesses.push({model:result.model,decision:result.decision,semanticRule:result.ruleId==='semantic-workforce-fixture',coverageComplete:result.coverage?.complete===true,score:result.similarity?.find(s=>s.categoryId==='semantic-workforce-fixture')?.score});return result;};return value;};require('../apps/browser/main.cjs');
const wait=ms=>new Promise(r=>setTimeout(r,ms));async function until(fn){for(let i=0;i<400;i++){const value=await fn();if(value)return value;await wait(100);}throw Error('Native semantic fixture timeout');}
app.whenReady().then(async()=>{let page,original=[],stage='startup',getFacts;const checks=[];try{for(const item of await clipboard.read()){const data={};for(const type of item.types)data[type]=await item.getType(type);original.push(new ClipboardItem(data));}const win=await until(()=>{const w=BrowserWindow.getAllWindows()[0];return w&&!w.webContents.isLoading()&&w.webContents.getURL().endsWith('index.html')&&w;});const fingerprint=new X509Certificate(fs.readFileSync(path.join(__dirname,'../tests/fixtures/icap/cert.pem'))).fingerprint256;session.fromPartition('persist:safetynet-managed').setCertificateVerifyProc((req,callback)=>{let valid=false;try{valid=req.hostname==='127.0.0.1'&&new X509Certificate(req.certificate.data).fingerprint256===fingerprint;}catch{}callback(valid?0:-2);});getFacts=()=>new Promise((resolve,reject)=>{const request=https.get(origin+'/facts',{ca:fs.readFileSync(path.join(__dirname,'../tests/fixtures/icap/cert.pem')),checkServerIdentity:(_host,cert)=>new X509Certificate(cert.raw).fingerprint256===fingerprint?undefined:Error('Synthetic certificate mismatch')},response=>{let body='';response.on('data',chunk=>body+=chunk);response.on('end',()=>resolve(JSON.parse(body)));});request.on('error',reject);});const ui=async(method,value)=>{const result=await win.webContents.executeJavaScript(`window.safetynet.${method}(${value===undefined?'':JSON.stringify(value)})`);assert.equal(result.ok,true);return result.data;};stage='enrollment under reviewed semantic policy';const enrolled=await ui('enroll',{server:base,code:process.env.SAFETYNET_SMOKE_CODE});assert.equal(enrolled.policyVersion,Number(process.env.SAFETYNET_SEMANTIC_POLICY));page=win.contentView.children.find(v=>v.webContents!==win.webContents).webContents;await until(()=>!page.isLoading()&&page.getURL()===origin+'/workspace');
const key=async keyCode=>{win.focus();page.focus();page.sendInputEvent({type:'keyDown',keyCode,modifiers:['control']});page.sendInputEvent({type:'keyUp',keyCode,modifiers:['control']});await wait(500);await until(async()=>!(await ui('state')).clipboardPending);};const editor=async text=>page.executeJavaScript(`(()=>{const e=document.getElementById('editor');e.innerHTML='<p></p>';e.firstChild.textContent=${JSON.stringify(text)};e.focus();const r=document.createRange();r.selectNodeContents(e.firstChild);r.collapse(false);const s=getSelection();s.removeAllRanges();s.addRange(r);})()`),value=()=>page.executeJavaScript("document.getElementById('editor').innerText.trimEnd()");
stage='native public AI editor paste';await editor('');const point=await page.executeJavaScript("(()=>{const r=document.getElementById('editor').getBoundingClientRect();return {x:Math.round(r.left+8),y:Math.round(r.top+10)}})()");page.sendInputEvent({type:'mouseDown',button:'left',clickCount:1,...point});page.sendInputEvent({type:'mouseUp',button:'left',clickCount:1,...point});await clipboard.writeText(fixtures.publicText);await key('V');assert.equal(await value(),fixtures.publicText);assert.equal(await page.executeJavaScript('window.submitPrompt()'),true);assert.equal((await getFacts()).publicPosts,1);checks.push('real-gemma-public-paste-submit');
stage='native nonliteral paraphrase paste denied';await editor(fixtures.publicText);await clipboard.writeText(fixtures.paraphrase);await key('V');assert.equal(await value(),fixtures.publicText);assert.equal(await page.executeJavaScript('window.submitPrompt()'),true);assert.equal((await getFacts()).paraphrasePosts,0);assert.ok(localWitnesses.some(w=>w.model==='google/embeddinggemma-2'&&w.semanticRule&&w.decision==='block'&&w.score>=0.65));checks.push('real-gemma-paraphrase-paste-denied');
stage='native confidential typed paraphrase denied';const before=(await getFacts()).posts;assert.equal(await page.executeJavaScript(`fetch('/receiver/prompt',{method:'POST',headers:{'content-type':'text/plain'},body:${JSON.stringify(fixtures.paraphrase)}}).then(r=>r.ok).catch(()=>false)`),false);assert.equal((await getFacts()).posts,before);checks.push('real-gemma-paraphrase-body-denied');
stage='native public NTFS document allowed';const doc=await page.debugger.sendCommand('DOM.getDocument',{}),node=await page.debugger.sendCommand('DOM.querySelector',{nodeId:doc.root.nodeId,selector:'#file'});const upload=async file=>{await page.debugger.sendCommand('DOM.setFileInputFiles',{nodeId:node.nodeId,files:[file]});return page.executeJavaScript("(()=>{const body=new FormData();body.append('file',document.getElementById('file').files[0]);return fetch('/receiver/file',{method:'POST',body}).then(r=>r.ok).catch(()=>false)})()");};assert.equal(await upload(process.env.SAFETYNET_SMOKE_PUBLIC_FILE),true);assert.equal((await getFacts()).exactPublicFiles,1);stage='native paraphrase NTFS document denied';assert.equal(await upload(process.env.SAFETYNET_SMOKE_SECRET_FILE),false);let facts=await getFacts();assert.equal(facts.filePosts,1);assert.equal(facts.paraphrasePosts,0);assert.equal(facts.posts,3);checks.push('real-gemma-public-paraphrase-ntfs-document');
stage='actual semantic OTEL and durable audit';await actualDiagnostics.flush();await wait(2000);const events=[];let cursor=0;for(let batch=0;batch<20;batch++){const response=await fetch(base+`/api/admin/events/export?after=${cursor}&limit=1000&format=ecs`,{headers});assert.equal(response.status,200);const rows=(await response.text()).trim().split('\n').filter(Boolean).map(JSON.parse);for(const row of rows){assert.ok(!JSON.stringify(row).includes(fixtures.reference));assert.ok(!JSON.stringify(row).includes(fixtures.paraphrase));events.push(row);}if(rows.length<1000)break;const next=Number(response.headers.get('x-safetynet-next-cursor'));assert.ok(next>cursor);cursor=next;}assert.ok(events.some(e=>e.event.action==='teaching.published'));assert.ok(events.some(e=>e.event.action==='policy.published'));const traces=[];for(const action of ['clipboard.paste','clipboard.block','upload.authorized','upload.block']){const candidates=events.filter(e=>e.event.action===action&&e.trace?.id&&(action.endsWith('block')?e.rule?.id==='semantic-workforce-fixture':true));assert.ok(candidates.length,action+' semantic audit missing');let found;for(const event of candidates.reverse()){const trace=await(await fetch('http://127.0.0.1:4318/api/trace/'+event.trace.id)).json();if(action.startsWith('upload.')&&!trace.spans.some(s=>s.name==='browser.upload.lease'))continue;assert.equal(event.safetynet.policy_version,enrolled.policyVersion);assert.equal(event.host.id,enrolled.deviceId);assert.ok(!JSON.stringify(trace).includes(fixtures.reference));assert.ok(!JSON.stringify(trace).includes(fixtures.paraphrase));const semantic=trace.spans.find(s=>s.name==='inspector.semantic');assert.ok(semantic,'Real imported Gemma semantic stage absent');assert.ok(trace.spans.some(s=>s.name==='inspector.semantic.load'));const byId=new Map(trace.spans.map(s=>[s.spanId,s])),expected=action.startsWith('clipboard.')?'browser.clipboard':'browser.request';let current=semantic,linked=false;const seen=new Set();while(current&&!seen.has(current.spanId)){if(current.name===expected){linked=true;break;}seen.add(current.spanId);current=byId.get(current.parentSpanId);}assert.ok(linked,'Semantic inference parent correlation missing');found={action,traceId:event.trace.id,stages:trace.spans.map(({name,operation,spanId,parentSpanId,durationMs})=>({name,operation,spanId,parentSpanId,durationMs}))};break;}assert.ok(found);traces.push(found);}const diagnostics=actualDiagnostics.status();assert.equal(diagnostics.enabled,true);assert.equal(diagnostics.failedExports,0);assert.equal(diagnostics.lifetimeFailedExports,0);const health=await(await fetch('http://127.0.0.1:4318/health')).json();assert.equal(health.rejectedWrites,0);facts=await getFacts();fs.writeFileSync(path.resolve('artifacts/windows-semantic-dlp-native-proof.json'),JSON.stringify({actualWindows:process.platform==='win32',actualElectron:true,localModelAutomaticBundledPaths:true,exactPinnedHTTPS:true,nonliteralParaphrase:true,policyVersion:enrolled.policyVersion,deviceId:enrolled.deviceId,checks,receiver:facts,localWitnesses,traces,diagnostics,health:{storedBytes:health.storedBytes,rejectedWrites:health.rejectedWrites}},null,2));console.log('Native local Gemma semantic paste/upload proof passed');if(original.length)await clipboard.write(original);else await clipboard.clear();require('../apps/browser/page-clipboard-guard.cjs').disposePageClipboardGuard(page);app.exit(0);
}catch(error){let facts;try{facts=await getFacts?.();await actualDiagnostics?.flush();}catch{}fs.writeFileSync(path.resolve('artifacts/windows-semantic-dlp-native-error.json'),JSON.stringify({stage,name:error.name,checks,facts,localWitnesses,diagnostics:actualDiagnostics?.status()},null,2));console.error('Native semantic MVP failed at '+stage+': '+error.name);try{if(original.length)await clipboard.write(original);else await clipboard.clear();}catch{}if(page&&!page.isDestroyed())require('../apps/browser/page-clipboard-guard.cjs').disposePageClipboardGuard(page);app.exit(1);}});
+27
View File
@@ -0,0 +1,27 @@
// Real pinned local Gemma inference, reviewed teaching and a private HTTPS receiver.
import assert from 'node:assert/strict';
import {createServer} from 'node:https';
import {createRequire} from 'node:module';
import {mkdtempSync,mkdirSync,readFileSync,writeFileSync,rmSync,existsSync} from 'node:fs';
import {tmpdir} from 'node:os';
import {join,resolve,sep} from 'node:path';
import {spawn} from 'node:child_process';
import {randomBytes} from 'node:crypto';
import {createApp} from '../apps/server/app.mjs';
import {openStore,digest} from '../apps/server/store.mjs';
import {inspect,verifyPolicy} from '../packages/core/policy.mjs';
import {startTelemetry} from '../apps/server/telemetry.mjs';
const collector='http://127.0.0.1:4318';assert.equal((await fetch(collector+'/health')).ok,true);process.env.OTEL_EXPORTER_OTLP_ENDPOINT=collector;const telemetry=startTelemetry(),temporary=mkdtempSync(join(tmpdir(),'safetynet-semantic-mvp-'));mkdirSync('artifacts',{recursive:true});assert.ok(resolve(temporary).startsWith(resolve(tmpdir())+sep)&&temporary.split(/[\\/]/).at(-1).startsWith('safetynet-semantic-mvp-'));
const fixtures={reference:'Confidential corporate restructuring will close the Lisbon office next autumn and eliminate two hundred positions before the public announcement. Leadership is negotiating a six-million-euro acquisition of a rival. These undisclosed staffing reductions and acquisition plans are restricted internal company information.',paraphrase:'Private workforce planning calls for shutting the Portugal branch after summer and cutting roughly two hundred jobs. Executives are discussing a six-million-euro purchase of a competitor; the timetable and personnel reductions remain undisclosed.',publicText:'A simple tomato pasta recipe combines ripe tomatoes, olive oil, garlic and fresh basil. Boil the pasta in salted water and serve it warm.'};
const runtime=resolve('.runtime/windows/semantic'),python=resolve('.runtime/windows/python/python.exe');assert.ok(existsSync(join(runtime,'models/model-manifest.json')));const publicFile=join(temporary,'public.txt'),secretFile=join(temporary,'paraphrase.txt');writeFileSync(publicFile,fixtures.publicText);writeFileSync(secretFile,fixtures.paraphrase);
const facts={posts:0,publicPosts:0,paraphrasePosts:0,filePosts:0,exactPublicFiles:0};let inspector,management,sink,store,child,stage='startup';
try{const inspectorToken=randomBytes(32).toString('base64url'),adminToken=randomBytes(32).toString('base64url'),code=randomBytes(32).toString('base64url');inspector=spawn(python,['apps/inspector/server.py'],{windowsHide:true,stdio:['ignore','pipe','pipe'],env:{...process.env,EMBEDDING_MODEL_DIR:join(runtime,'models'),EMBEDDING_WORKER_PATH:join(runtime,'searchi-gemma-worker.exe'),INSPECTOR_TOKEN:inspectorToken,INSPECTOR_PORT:'0',INSPECTOR_PARENT_PID:String(process.pid)}});inspector.stderr.on('data',()=>{});const inspectorPort=await new Promise((accept,reject)=>{let output='';const timer=setTimeout(()=>{inspector.kill();reject(Error('Private semantic inspector startup timed out'));},40000);inspector.once('error',()=>reject(Error('Private semantic inspector startup failed')));inspector.once('exit',()=>{clearTimeout(timer);reject(Error('Private semantic inspector exited'));});inspector.stdout.on('data',chunk=>{output+=chunk;if(output.includes('\n')){clearTimeout(timer);try{const startup=JSON.parse(output.split('\n')[0]);assert.equal(startup.containment.parserIsolation.active,true);assert.equal(startup.containment.parserIsolation.networkCapabilities,0);accept(startup.port);}catch{reject(Error('Private semantic inspector attestation invalid'));}}});});
sink=createServer({cert:readFileSync('tests/fixtures/icap/cert.pem'),key:readFileSync('tests/fixtures/icap/key.pem')},(req,res)=>{if(req.url==='/facts'){res.setHeader('content-type','application/json');res.end(JSON.stringify(facts));return;}if(req.method==='POST'){let chunks=[],size=0;req.on('data',chunk=>{size+=chunk.length;if(size>1048576){req.destroy();return;}chunks.push(chunk);});req.on('end',()=>{facts.posts++;const bytes=Buffer.concat(chunks);if(bytes.includes(Buffer.from(fixtures.paraphrase)))facts.paraphrasePosts++;if(bytes.includes(Buffer.from(fixtures.publicText)))facts.publicPosts++;if(req.url==='/receiver/file'){facts.filePosts++;const boundary=/boundary=(?:"([^"]+)"|([^;]+))/.exec(req.headers['content-type']||'');if(boundary){const start=bytes.indexOf(Buffer.from('\r\n\r\n')),end=bytes.indexOf(Buffer.from('\r\n--'+(boundary[1]||boundary[2])),start+4);if(start>=0&&end>=0&&bytes.subarray(start+4,end).equals(Buffer.from(fixtures.publicText)))facts.exactPublicFiles++;}}bytes.fill(0);for(const chunk of chunks)chunk.fill(0);chunks=[];res.end('accepted');});return;}res.setHeader('content-type','text/html');res.end('<!doctype html><title>Public local AI workspace</title><style>body{font:18px Arial;padding:25px}[contenteditable]{border:1px solid #999;width:85%;min-height:60px;margin:25px 0}</style><h1>Public AI workspace</h1><div id="editor" role="textbox" contenteditable="true"><p><br></p></div><input id="file" type="file"><script>window.submitPrompt=()=>fetch("/receiver/prompt",{method:"POST",headers:{"content-type":"text/plain"},body:document.getElementById("editor").innerText.trimEnd()}).then(r=>r.ok).catch(()=>false)</script>');}).listen(0,'127.0.0.1');await new Promise(r=>sink.once('listening',r));const origin=`https://127.0.0.1:${sink.address().port}`;
store=openStore(join(temporary,'db'));const p=store.get('policy');p.browser.homeUrl=origin+'/workspace';p.browser.allowedHosts=['127.0.0.1'];p.agents.enabled=false;p.dlp.rules=[];p.security.yara.enabled=false;p.diagnostics={...p.diagnostics,enabled:true,mode:'debug',endpoint:collector,sampleRate:1,debugSampleRate:1,flushIntervalMs:250};store.put('policy',p);store.db.prepare('INSERT INTO enrollments VALUES(?,?,?)').run(digest(code),Date.now()+1800000,'semantic-mvp-user');const config={secure:false,publicUrl:'http://127.0.0.1',adminToken,requirePQManagement:true,requireIndependentPolicyApproval:true,inspectorUrl:`http://127.0.0.1:${inspectorPort}`,inspectorToken};management=createApp(store,config).listen(0,'127.0.0.1');await new Promise(r=>management.once('listening',r));config.publicUrl=`http://127.0.0.1:${management.address().port}`;const reviewer=randomBytes(32).toString('base64url');store.db.prepare('INSERT INTO sessions VALUES(?,?,?)').run(digest(reviewer),Date.now()+1800000,JSON.stringify({sub:'independent-semantic-reviewer',role:'admin'}));const headers={authorization:'Bearer '+adminToken,'content-type':'application/json'};
async function admin(route,body,method='POST',status=200){const result=await fetch(config.publicUrl+route,{method,headers,body:JSON.stringify(body),signal:AbortSignal.timeout(120000)});assert.equal(result.status,status,'Semantic admin operation refused');return result.json();}
async function approve(review){const result=await fetch(config.publicUrl+`/api/admin/policy-reviews/${review.reviewId}/approve`,{method:'POST',headers:{cookie:'sn_session='+reviewer,origin:config.publicUrl,'content-type':'application/json'},body:JSON.stringify({payloadHash:review.payloadHash,expectedVersion:review.expectedVersion,approve:true})});assert.equal(result.status,200);}
stage='reviewed document teaching';const teaching=await admin('/api/admin/teaching',{label:'Synthetic restricted workforce reference',filename:'synthetic-reference.txt',data:Buffer.from(fixtures.reference).toString('base64')});assert.equal(teaching.reference.registrationReady,true);assert.ok(teaching.reference.fragmentHashes.length>0);const publication=await admin(`/api/admin/teaching/${teaching.id}/publish`,{expectedVersion:p.version,approve:true},'POST',202);await approve(publication);const retained=JSON.parse(store.db.prepare('SELECT reference FROM teaching WHERE id=?').get(teaching.id).reference);assert.deepEqual(Object.keys(retained).sort(),['bytes','contentType','coverage','fragmentHashes','registrationReady','sha256']);assert.ok(!JSON.stringify(retained).includes(fixtures.reference));assert.ok(!JSON.stringify(retained).includes('Lisbon office'));assert.ok(retained.fragmentHashes.every(hash=>/^[a-f0-9]{64}$/.test(hash)));
stage='real pinned local document embedding';const reference=await admin('/api/admin/semantic/document-reference',{filename:'synthetic-reference.txt',data:Buffer.from(fixtures.reference).toString('base64')});const model={model:'google/embeddinggemma-2',revision:'bfcd298762cc34d0357ece5ebdd31791a3a374d8',dimensions:768,profile:'document-v1',artifactFormat:'gguf',quantization:'Q8_0',runtimeRevision:'b11461',local:true};for(const [key,value]of Object.entries(model))assert.equal(reference[key],value,'Pinned local model contract changed');assert.equal(reference.coverage.complete,true);const semanticPolicy=store.get('policy');semanticPolicy.version++;semanticPolicy.dlp.semantic={enabled:true,model:reference.model,revision:reference.revision,dimensions:reference.dimensions,profile:reference.profile,artifactFormat:reference.artifactFormat,quantization:reference.quantization,runtimeRevision:reference.runtimeRevision,categories:[{id:'semantic-workforce-fixture',label:'Synthetic restricted workforce plan',threshold:0.65,prototype:reference.vector,prototypes:reference.vectors}]};assert.equal(inspect(Buffer.from(fixtures.paraphrase),semanticPolicy).decision,'allow','Paraphrase matches deterministic teaching; semantic proof invalid');const secretResult=await admin('/api/admin/policy/test',{text:fixtures.paraphrase,policy:semanticPolicy}),publicResult=await admin('/api/admin/policy/test',{text:fixtures.publicText,policy:semanticPolicy});assert.equal(secretResult.ruleId,'semantic-workforce-fixture');assert.equal(publicResult.decision,'allow');await approve(await admin('/api/admin/policy',semanticPolicy,'PUT',202));const signed=verifyPolicy(store.envelope(),store.publicKey());assert.equal(signed.dlp.semantic.revision,model.revision);assert.ok(!JSON.stringify(signed).includes(fixtures.reference));
const teachingProof={independentlyReviewedTeaching:true,independentlyReviewedSemanticPolicy:true,fullDocumentNotRetained:true,retainedKeys:Object.keys(retained).sort(),retainedFragmentCount:retained.fragmentHashes.length,referenceCoverageComplete:true,deterministicParaphraseAllowed:true,semanticParaphraseBlocked:true,publicSemanticAllowed:true,policyVersion:signed.version,model,paraphraseSimilarity:secretResult.similarity?.[0]?.score,publicSimilarity:publicResult.similarity?.[0]?.score};writeFileSync('artifacts/windows-semantic-teaching-proof.json',JSON.stringify(teachingProof,null,2));
stage='actual native semantic paste and upload';const env={...process.env,SAFETYNET_SMOKE_SERVER:config.publicUrl,SAFETYNET_SMOKE_CODE:code,SAFETYNET_SMOKE_ADMIN:adminToken,SAFETYNET_SMOKE_USERDATA:join(temporary,'browser'),SAFETYNET_SMOKE_ORIGIN:origin,SAFETYNET_SMOKE_PUBLIC_FILE:publicFile,SAFETYNET_SMOKE_SECRET_FILE:secretFile,SAFETYNET_SEMANTIC_FIXTURES:JSON.stringify(fixtures),SAFETYNET_SEMANTIC_POLICY:String(signed.version)};delete env.EMBEDDING_MODEL_DIR;delete env.EMBEDDING_WORKER_PATH;child=spawn(createRequire(import.meta.url)('electron'),['scripts/windows-semantic-dlp-smoke.cjs'],{windowsHide:true,stdio:['ignore','pipe','pipe'],env});child.stdout.on('data',chunk=>process.stdout.write(chunk));child.stderr.on('data',chunk=>process.stderr.write(chunk));const timer=setTimeout(()=>child.kill(),240000),exit=await new Promise(r=>{child.once('exit',v=>r(v??1));child.once('error',()=>r(1));});clearTimeout(timer);assert.equal(exit,0,'Native semantic MVP failed');assert.equal(facts.paraphrasePosts,0);assert.equal(facts.filePosts,1);assert.equal(facts.exactPublicFiles,1);console.log('Reviewed local Gemma semantic MVP proof passed');
}catch(error){writeFileSync('artifacts/windows-semantic-harness-error.json',JSON.stringify({stage,name:error.name},null,2));console.error('Semantic MVP harness failed at '+stage+': '+error.name);process.exitCode=1;}finally{child?.kill();inspector?.kill();for(const server of [management,sink])if(server){server.closeAllConnections();await new Promise(r=>server.close(r));}store?.db.close();await telemetry?.shutdown();rmSync(temporary,{recursive:true,force:true});}