Add protected interaction evidence and measured debugging phases
This commit is contained in:
1 parent
ea448de2d4
commit
244f01630b
42 files changed
+640
-87
No files matched your search
@@ -3,10 +3,13 @@ const {app,BrowserWindow}=require('electron');
|
||||
const fs=require('node:fs'),path=require('node:path'),assert=require('node:assert/strict');
|
||||
app.setPath('userData',process.env.SAFETYNET_SMOKE_USERDATA);app.disableHardwareAcceleration();require('../apps/browser/main.cjs');
|
||||
const wait=ms=>new Promise(r=>setTimeout(r,ms)),headers={authorization:`Bearer ${process.env.SAFETYNET_SMOKE_ADMIN}`,'content-type':'application/json'};
|
||||
async function until(fn){for(let n=0;n<200;n++){const result=await fn();if(result)return result;await wait(100);}throw Error('Individual native fixture timed out');}
|
||||
const startupAt=Date.now();
|
||||
function safeSnapshot(stage){return {stage,elapsedMs:Date.now()-startupAt,phase:process.env.SAFETYNET_SMOKE_PHASE,windows:BrowserWindow.getAllWindows().map(w=>({destroyed:w.isDestroyed(),loading:w.webContents.isLoading(),urlKind:w.webContents.getURL().endsWith('index.html')?'shell':w.webContents.getURL()===''?'empty':'other',children:w.contentView.children.length})),metrics:app.getAppMetrics().map(m=>({type:m.type,cpu:m.cpu.percentCPUUsage,idleWakeupsPerSecond:m.cpu.idleWakeupsPerSecond,memoryWorkingSetSize:m.memory.workingSetSize}))};}
|
||||
async function until(fn,stage='native-wait'){const started=Date.now();for(let n=0;n<200;n++){const result=await fn();if(result)return result;await wait(100);}fs.writeFileSync(path.join(process.env.SAFETYNET_SMOKE_ARTIFACTS,'individual-agent-timeout-'+process.env.SAFETYNET_SMOKE_PHASE+'.json'),JSON.stringify({...safeSnapshot(stage),waitElapsedMs:Date.now()-started},null,2));throw Error('Individual native fixture timed out: '+stage);}
|
||||
const overview=async()=> (await fetch(process.env.SAFETYNET_SMOKE_SERVER+'/api/admin/overview',{headers})).json();
|
||||
app.whenReady().then(async()=>{let page;try{
|
||||
const win=await until(()=>{const w=BrowserWindow.getAllWindows()[0];return w&&!w.webContents.isLoading()&&w.webContents.getURL().endsWith('index.html')&&w;});
|
||||
const win=await until(()=>{const w=BrowserWindow.getAllWindows()[0];return w&&!w.webContents.isLoading()&&w.webContents.getURL().endsWith('index.html')&&w;},'initial-shell-load');
|
||||
fs.writeFileSync(path.join(process.env.SAFETYNET_SMOKE_ARTIFACTS,'individual-agent-startup-'+process.env.SAFETYNET_SMOKE_PHASE+'.json'),JSON.stringify(safeSnapshot('shell-ready'),null,2));
|
||||
if(process.env.SAFETYNET_SMOKE_PHASE==='restart'){
|
||||
await until(async()=> (await win.webContents.executeJavaScript('window.safetynet.state()')).data.ready);
|
||||
page=win.contentView.children.find(v=>v.webContents!==win.webContents).webContents;
|
||||
@@ -17,6 +20,7 @@ app.whenReady().then(async()=>{let page;try{
|
||||
const proofFile=path.join(process.env.SAFETYNET_SMOKE_ARTIFACTS,'individual-agent-native-proof.json'),proof=JSON.parse(fs.readFileSync(proofFile,'utf8'));proof.durableGenerationFloorAfterRestart=true;proof.oldDescriptorRejectedAfterRestart=true;fs.writeFileSync(proofFile,JSON.stringify(proof,null,2));
|
||||
require('../apps/browser/page-clipboard-guard.cjs').disposePageClipboardGuard(page);console.log('Individual generation floor restart proof passed');app.exit(0);return;
|
||||
}
|
||||
page=win.contentView.children.find(v=>v.webContents!==win.webContents)?.webContents;
|
||||
const enroll=await win.webContents.executeJavaScript(`window.safetynet.enroll(${JSON.stringify({server:process.env.SAFETYNET_SMOKE_SERVER,code:process.env.SAFETYNET_SMOKE_CODE})})`);assert.equal(enroll.ok,true);
|
||||
page=win.contentView.children.find(v=>v.webContents!==win.webContents).webContents;await until(()=>!page.isLoading());
|
||||
assert.equal((await win.webContents.executeJavaScript('window.safetynet.agentConnection()')).ok,false);
|
||||
@@ -32,6 +36,10 @@ app.whenReady().then(async()=>{let page;try{
|
||||
const requestFile=path.join(app.getPath('userData'),'individual-fixture-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 sessions=await call(a,'sessions');assert.equal(sessions.exit,0);const sessionId=sessions.result[0].id;
|
||||
assert.equal((await call(a,'navigate',{sessionId,url:process.env.SAFETYNET_SMOKE_SINK+'/protected-title'})).exit,0);
|
||||
await until(()=>page.getTitle()==='Synthetic confidential inventory title','protected-title-load');
|
||||
const protectedInventory=await call(a,'sessions');assert.equal(protectedInventory.exit,0);assert.equal(protectedInventory.result.find(row=>row.id===sessionId)?.title,'Protected page','Page-controlled confidential title escaped inventory classification');
|
||||
assert.equal((await call(a,'navigate',{sessionId,url:process.env.SAFETYNET_SMOKE_SINK+'/allowed'})).exit,0);
|
||||
assert.equal((await call(a,'read',{sessionId})).exit,0);assert.equal((await call(a,'application:state')).exit,1);assert.equal((await call(b,'application:state')).exit,0);assert.equal((await call(b,'read',{sessionId})).exit,1);
|
||||
assert.equal((await call(a,'navigate',{sessionId,url:process.env.SAFETYNET_SMOKE_SINK+'/allowed'})).exit,0,'Approved host navigation failed');
|
||||
assert.equal((await call(a,'navigate',{sessionId,url:process.env.SAFETYNET_SMOKE_FORBIDDEN})).exit,1,'Actor reached forbidden host');
|
||||
@@ -51,6 +59,6 @@ app.whenReady().then(async()=>{let page;try{
|
||||
assert.ok(traceProof,'Actual agent request trace missing');
|
||||
const root=traceProof.stages.find(s=>s.operation==='agent.request');assert.equal(root.parentSpanId,null);
|
||||
for(const operation of ['agent.decrypt','agent.authorize','agent.dispatch','agent.project','agent.audit','agent.encrypt'])assert.ok(traceProof.stages.some(s=>s.operation===operation&&s.parentSpanId===root.spanId&&Number.isFinite(s.durationMs)&&s.durationMs>=0),'Missing correlated stage '+operation);
|
||||
fs.writeFileSync(path.join(process.env.SAFETYNET_SMOKE_ARTIFACTS,'individual-agent-native-proof.json'),JSON.stringify({actualElectron:true,explicitGrantSelection:true,renderedGrantChoices:true,uiPairingBothActors:true,separateDPAPIDescriptors:true,noLegacyDescriptor:true,twoActors:true,scopeIntersection:true,approvedHostNavigation:true,forbiddenHostDeniedBeforeTransmission:true,forbiddenRedirectDeniedBeforeTransmission:true,forbiddenReceiverRequests:0,humanNavigationPreserved:true,ownedTabFetchAfterHumanNavigation:true,ownerlessSessionFetchBlockedBeforeTransmission:true,ownerlessReceiverRequests:0,targetedRevocation:true,unaffectedPeer:true,liveGenerationRollbackDenied:true,attributedAudit:true,traceProof},null,2));console.log('Individual agent native proof passed');
|
||||
fs.writeFileSync(path.join(process.env.SAFETYNET_SMOKE_ARTIFACTS,'individual-agent-native-proof.json'),JSON.stringify({actualElectron:true,classifiedInventoryRedactsTitlePreservesId:true,explicitGrantSelection:true,renderedGrantChoices:true,uiPairingBothActors:true,separateDPAPIDescriptors:true,noLegacyDescriptor:true,twoActors:true,scopeIntersection:true,approvedHostNavigation:true,forbiddenHostDeniedBeforeTransmission:true,forbiddenRedirectDeniedBeforeTransmission:true,forbiddenReceiverRequests:0,humanNavigationPreserved:true,ownedTabFetchAfterHumanNavigation:true,ownerlessSessionFetchBlockedBeforeTransmission:true,ownerlessReceiverRequests:0,targetedRevocation:true,unaffectedPeer:true,liveGenerationRollbackDenied:true,attributedAudit:true,traceProof},null,2));console.log('Individual agent native proof passed');
|
||||
require('../apps/browser/page-clipboard-guard.cjs').disposePageClipboardGuard(page);app.exit(0);
|
||||
}catch(error){console.error(error.stack);if(page)require('../apps/browser/page-clipboard-guard.cjs').disposePageClipboardGuard(page);app.exit(1);}});
|
||||
Reference in new issue
Block a user