Capture guarded human gestures and verify agent administration
This commit is contained in:
1 parent
244f01630b
commit
adf0940e5f
32 files changed
+523
-52
No files matched your search
@@ -23,12 +23,18 @@ app.whenReady().then(async()=>{try{
|
||||
const sessionId=(await state()).activeTabId,requestFile=path.join(process.env.SAFETYNET_SMOKE_USERDATA,'interaction-request.json');
|
||||
fs.writeFileSync(requestFile,JSON.stringify({action:'click',args:{sessionId,selector:'#public-button'}}));
|
||||
const child=require('node:child_process').spawn(process.execPath,['scripts/agent-client.cjs','--connection',connection.data.credentialPath,'--request',requestFile],{windowsHide:true,stdio:['ignore','pipe','pipe']});let output='';child.stdout.on('data',c=>output+=c);child.stderr.on('data',()=>{});const exit=await new Promise(r=>{child.once('exit',r);child.once('error',()=>r(1));});assert.equal(exit,0);assert.equal(JSON.parse(output).result.completed,true);assert.equal(await page.executeJavaScript("document.getElementById('public-button').dataset.clicked"),'1');await until(async()=>(await records()).length===4);
|
||||
stage='human observer installation';await until(()=>page.executeJavaScriptInIsolatedWorld(998,[{code:"!!globalThis.__safetynetHumanInteraction"}],false));await page.executeJavaScript("window.__fixtureTrustedClick=false;window.addEventListener('click',e=>{window.__fixtureTrustedClick=e.isTrusted},true)");
|
||||
stage='synthetic human exclusion';await page.executeJavaScript("document.getElementById('public-button').click();document.getElementById('public-form').dispatchEvent(new Event('submit',{bubbles:true,cancelable:true}));document.getElementById('public-form').requestSubmit()");await wait(700);assert.equal((await records()).length,4);
|
||||
async function physicalClick(id){const point=await page.executeJavaScript(`(()=>{const r=document.getElementById(${JSON.stringify(id)}).getBoundingClientRect();return {x:Math.round(r.left+r.width/2),y:Math.round(r.top+r.height/2)}})()`);page.focus();page.sendInputEvent({type:'mouseDown',button:'left',clickCount:1,...point});page.sendInputEvent({type:'mouseUp',button:'left',clickCount:1,...point});await wait(900);}
|
||||
stage='physical click';await physicalClick('public-button');assert.equal(await page.executeJavaScript('window.__fixtureTrustedClick'),true);assert.equal(await page.executeJavaScript("document.getElementById('public-button').dataset.clicked"),'3');await until(async()=>(await records()).length===5);
|
||||
stage='physical submit';await physicalClick('submit-button');assert.equal(await page.executeJavaScript("document.getElementById('public-form').dataset.submitted"),'3');await until(async()=>(await records()).length===7);
|
||||
stage='private form exclusion';await physicalClick('private-submit');await wait(500);assert.equal((await records()).length,7);
|
||||
const recorded=await records(),payloads=[];
|
||||
stage='analyst reveal';for(const e of recorded){const response=await fetch(base+`/api/admin/evidence/${e.metadata.evidenceId}?reason=investigation`,{headers});assert.equal(response.status,200);const item=await response.json();payloads.push(item.payload);}
|
||||
assert.deepEqual(payloads.map(p=>p.action).sort(),['click','copy','cut','paste']);assert.ok(payloads.every(p=>p.sessionId===sessionId&&p.outcome==='completed'&&p.sensitiveField===false));assert.ok(payloads.some(p=>p.action==='click'&&p.captureSource==='agent-adapter'&&p.target?.tag==='BUTTON'));
|
||||
assert.deepEqual(payloads.map(p=>p.action).sort(),['click','click','click','copy','cut','paste','submit']);assert.ok(payloads.every(p=>p.sessionId===sessionId&&p.sensitiveField===false));assert.equal(payloads.filter(p=>p.captureSource==='human-observer'&&p.outcome==='observed').length,3);assert.ok(payloads.every(p=>p.pageUrl===new URL(p.pageUrl).origin+'/'));assert.ok(payloads.some(p=>p.action==='click'&&p.captureSource==='agent-adapter'&&p.target?.tag==='BUTTON'));
|
||||
const serialized=JSON.stringify(payloads),eventsBefore=(await overview()).events;
|
||||
for(const marker of ['Synthetic public selection','Synthetic pasted text','Synthetic control label','Synthetic password attempt','never-export-this','#public-button','#private-input']){assert.equal(serialized.includes(marker),false);assert.equal(JSON.stringify(eventsBefore).includes(marker),false);}
|
||||
assert.ok(eventsBefore.filter(e=>e.kind==='evidence.accessed').length>=4);
|
||||
for(const marker of ['Synthetic public selection','Synthetic pasted text','Synthetic control label','Synthetic password attempt','never-export-this','Synthetic private form field','#public-button','#private-input']){assert.equal(serialized.includes(marker),false);assert.equal(JSON.stringify(eventsBefore).includes(marker),false);}
|
||||
assert.equal(eventsBefore.filter(e=>e.kind==='evidence.accessed').length,7);
|
||||
stage='admin policy screenshot';const adminWindow=new BrowserWindow({width:1440,height:1000,show:false,webPreferences:{nodeIntegration:false,contextIsolation:true,sandbox:true}});
|
||||
await adminWindow.webContents.session.cookies.set({url:base,name:'sn_session',value:process.env.SAFETYNET_SMOKE_REVIEWER,httpOnly:true,sameSite:'strict'});await adminWindow.loadURL(base+'/');
|
||||
await until(()=>adminWindow.webContents.executeJavaScript("(()=>{const e=document.getElementById('evidence-policy-interactions');return !!e&&e.checked&&!e.disabled})()"));
|
||||
@@ -37,9 +43,9 @@ app.whenReady().then(async()=>{try{
|
||||
stage='independent policy review';const policy=(await overview()).policy;policy.version++;policy.evidence.captureInteractions=false;
|
||||
const proposed=await fetch(base+'/api/admin/policy',{method:'PUT',headers,body:JSON.stringify(policy)});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()=>(await state()).policyVersion===policy.version&&(await state()).ready);
|
||||
stage='disabled interaction';await page.executeJavaScript("document.getElementById('public-input').focus()");await clipboard.writeText('Synthetic post-disable text');await key('V');assert.equal(await page.executeJavaScript("document.getElementById('public-input').value"),'Synthetic post-disable text');await wait(600);assert.equal((await records()).length,4);
|
||||
stage='disabled interaction';await page.executeJavaScript("document.getElementById('public-input').focus()");await clipboard.writeText('Synthetic post-disable text');await key('V');assert.equal(await page.executeJavaScript("document.getElementById('public-input').value"),'Synthetic post-disable text');await wait(600);await physicalClick('public-button');assert.equal((await records()).length,7);
|
||||
stage='download independently enabled';dialog.showSaveDialog=async()=>({canceled:false,filePath:path.join(process.env.SAFETYNET_SMOKE_USERDATA,'safe.bin')});await page.executeJavaScript("document.getElementById('safe-download').click()");await until(async()=>{const s=await state();return s.ready&&s.message==='Download scanned and saved.';});assert.deepEqual(fs.readFileSync(path.join(process.env.SAFETYNET_SMOKE_USERDATA,'safe.bin')),Buffer.from([0,1,2,3,255]));await until(async()=>((await overview()).events||[]).some(e=>e.kind==='evidence.recorded'&&e.metadata.evidenceType==='download'));
|
||||
const after=(await overview()).events,sources=recorded.map(e=>after.find(source=>source.id===e.metadata.sourceEventId));assert.ok(sources.every(Boolean));assert.ok(sources.every(e=>e.metadata.traceId));
|
||||
fs.writeFileSync(path.resolve('artifacts/interaction-evidence-native-proof.json'),JSON.stringify({actualElectron:true,nativeCopyPasteCut:true,agentClickExactlyOnce:true,passwordControlExcluded:true,noRawContentLabelsSelectors:true,completedActionsBoundToSession:true,analystAccessAudited:true,independentSignedDisable:true,actualAdminControlsScreenshot:true,disabledInteractionLeavesDownloadCapture:true,eventIds:sources.map(e=>e.id),traceIds:sources.map(e=>e.metadata.traceId)},null,2));
|
||||
const after=(await overview()).events,sources=recorded.map(e=>after.find(source=>source.id===e.metadata.sourceEventId));assert.ok(sources.every(Boolean));assert.ok(sources.every(e=>e.metadata.traceId));sources.sort((a,b)=>Number(b.kind==='agent.action')-Number(a.kind==='agent.action'));assert.equal(sources.filter(e=>e.kind==='browser.interaction').length,3);
|
||||
fs.writeFileSync(path.resolve('artifacts/interaction-evidence-native-proof.json'),JSON.stringify({actualElectron:true,physicalTrustedClickAndSubmit:true,syntheticHumanEventsExcluded:true,passwordFormsExcluded:true,humanObservationNotTransmission:true,nativeCopyPasteCut:true,agentClickExactlyOnce:true,passwordControlExcluded:true,noRawContentLabelsSelectors:true,completedActionsBoundToSession:true,analystAccessAudited:true,independentSignedDisable:true,actualAdminControlsScreenshot:true,disabledInteractionLeavesDownloadCapture:true,eventIds:sources.map(e=>e.id),traceIds:sources.map(e=>e.metadata.traceId),humanTraceIds:sources.filter(e=>e.kind==='browser.interaction').map(e=>e.metadata.traceId),humanEventIds:sources.filter(e=>e.kind==='browser.interaction').map(e=>e.id)},null,2));
|
||||
console.log('Native protected interaction evidence proof passed.');await clipboard.writeText(originalClipboard);require('../apps/browser/page-clipboard-guard.cjs').disposePageClipboardGuard(page);app.exit(0);
|
||||
}catch(error){if(originalClipboard!==undefined)await clipboard.writeText(originalClipboard);if(page)require('../apps/browser/page-clipboard-guard.cjs').disposePageClipboardGuard(page);fs.writeFileSync(path.resolve('artifacts/interaction-evidence-native-error.json'),JSON.stringify({stage,name:error.name,message:error.message},null,2));console.error(`Interaction evidence fixture failed at ${stage}: ${error.name}`);app.exit(1);}});
|
||||
}catch(error){if(originalClipboard!==undefined)await clipboard.writeText(originalClipboard);if(page)require('../apps/browser/page-clipboard-guard.cjs').disposePageClipboardGuard(page);let checkpoint={};try{if(page&&!page.isDestroyed())checkpoint.observer=await page.executeJavaScriptInIsolatedWorld(998,[{code:"(()=>{const s=globalThis.__safetynetHumanInteraction;return {installed:!!s,pending:s?.queue?.length||0,dropped:s?.dropped||0}})()"}],false);const queuePath=path.join(process.env.SAFETYNET_SMOKE_USERDATA,'audit-queue.bin');if(fs.existsSync(queuePath)){const queue=JSON.parse(require('electron').safeStorage.decryptString(fs.readFileSync(queuePath)));checkpoint.pendingHumanSources=queue.filter(e=>e.kind==='browser.interaction').slice(0,16).map(({eventId,traceId,spanId,policyVersion})=>({eventId,traceId,spanId,policyVersion}));}}catch{}fs.writeFileSync(path.resolve('artifacts/interaction-evidence-native-error.json'),JSON.stringify({stage,name:error.name,message:error.message,checkpoint},null,2));console.error(`Interaction evidence fixture failed at ${stage}: ${error.name}`);app.exit(1);}});
|
||||
Reference in new issue
Block a user