Files
safetynet/scripts/agent-grants-history-admin-smoke.cjs
T

22 lines
14 KiB
JavaScript
Raw Normal View History

'use strict';
const {app,BrowserWindow,session}=require('electron'),assert=require('node:assert/strict'),fs=require('node:fs');app.disableHardwareAcceleration();app.enableSandbox();app.setPath('userData',process.env.SAFETYNET_ADMIN_SMOKE_PROFILE);let window;const deadline=setTimeout(()=>app.exit(1),55000);const evaluate=s=>window.webContents.executeJavaScript(s,true);
async function waitFor(s){for(let n=0;n<200;n++){if(await evaluate(s))return;await new Promise(r=>setTimeout(r,60));}const probe=await evaluate(`(async()=>{let error;try{await load()}catch(e){error={name:e.name,message:String(e.message).slice(0,300)}}return {workspaceHidden:document.getElementById('workspace').hidden,missingScopes:agentScopes.filter(scope=>!document.getElementById('agent-scope-'+scope)),error};})()`);fs.writeFileSync('artifacts/visual-admin-load-diagnostic.json',JSON.stringify(probe,null,2));throw Error('UI state timeout: '+s+' | '+await evaluate(`document.getElementById('status').textContent`));}
async function identity(value){await session.defaultSession.cookies.set({url:process.env.SAFETYNET_ADMIN_SMOKE_URL,name:'sn_session',value:value+'-synthetic-fixture-session-capability',httpOnly:true,sameSite:'strict'});await window.loadURL(process.env.SAFETYNET_ADMIN_SMOKE_URL);await waitFor(`!document.getElementById('workspace').hidden&&document.getElementById('reviews-status').textContent.includes('loaded')`);}
async function selectReview(){await evaluate(`document.querySelector('#reviews-rows button').click()`);await waitFor(`!document.getElementById('independent-review-detail').hidden`);}
async function approve(version){assert.equal(await evaluate(`document.getElementById('independent-review-approve').disabled`),true);await evaluate(`document.getElementById('independent-review-confirm').checked=true;document.getElementById('independent-review-confirm').dispatchEvent(new Event('change'));document.getElementById('independent-review-approve').click()`);await waitFor(`document.getElementById('policy-version').textContent==='v${version}'`);}
async function capture(file,section){await evaluate(`document.getElementById('${section}').scrollIntoView();document.getElementById('console-theme').value='dark';document.getElementById('console-theme').dispatchEvent(new Event('change'))`);await new Promise(r=>setTimeout(r,250));fs.mkdirSync('artifacts',{recursive:true});fs.writeFileSync(file,(await window.webContents.capturePage()).toPNG());}
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);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');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);
for(let n=0;n<100&&proofTraceIds.some(row=>row.outcome==='pending');n++)await new Promise(r=>setTimeout(r,50));assert.equal(proofTraceIds.some(row=>row.outcome==='pending'),false,'API requests must settle before telemetry shutdown');
fs.writeFileSync('artifacts/agent-grants-admin-request-trace-ids.json',JSON.stringify(proofTraceIds));console.log('GRANT_ADMIN_OK');clearTimeout(deadline);window.destroy();app.exit(0);
}).catch(error=>{console.error('GRANT_ADMIN_FAILED',error.message);try{fs.writeFileSync('artifacts/visual-admin-startup-diagnostics.json',JSON.stringify({message:error.message},null,2));}catch{}clearTimeout(deadline);window?.destroy();app.exit(1);});