import test from 'node:test'; import assert from 'node:assert/strict'; import fs from 'node:fs'; import os from 'node:os'; import path from 'node:path'; import grants from '../apps/browser/agent-grants.cjs'; const now=Date.now(),expiry=new Date(now+60000).toISOString(); const policy=()=>({expiresAt:expiry,agents:{enabled:true,identityMode:'individual',scopes:['read','navigate'],allowedHosts:['corp.example'],grants:[{id:'worker.a',name:'Reader',enabled:true,expiresAt:expiry,scopes:['read'],allowedHosts:['corp.example'],generation:2}]}}); const authorize=changes=>grants.authorizeGrant({policy:policy(),ready:true,principalId:'worker.a',generation:2,action:'read',url:'https://corp.example/',hostMatches:(a,b)=>a===b,now,...changes}); test('active grant discovery excludes expired and disabled grants and exposes no mutable policy reference',()=>{ const p=policy();p.agents.grants.push({...p.agents.grants[0],id:'disabled',enabled:false},{...p.agents.grants[0],id:'expired',expiresAt:new Date(now-1).toISOString()}); const active=grants.activeGrants(p,now);assert.deepEqual(active.map(g=>g.id),['worker.a']);active[0].scopes.push('navigate');assert.deepEqual(p.agents.grants[0].scopes,['read']); assert.deepEqual(grants.activeGrants(p,now+60001),[]); }); test('individual authorization intersects global and principal rights and destinations',()=>{ assert.equal(authorize().allowed,true); for(const changes of [{principalId:'worker.b'},{generation:1},{action:'navigate'},{ready:false},{url:'https://evil.example/'},{url:'file:///secret'},{now:now+60001}])assert.equal(authorize(changes).allowed,false); const p=policy();p.agents.scopes=[];assert.equal(authorize({policy:p}).allowed,false); p.agents.scopes=['read'];p.agents.grants[0].scopes=[];assert.equal(authorize({policy:p}).allowed,false); p.agents.grants[0].scopes=['read'];p.agents.grants[0].allowedHosts=['other.example'];assert.equal(authorize({policy:p}).allowed,false); }); test('legacy mode never accepts individual credentials and individual mode has no shared bypass',()=>{ assert.equal(authorize({principalId:undefined,generation:undefined}).allowed,false); const p=policy();p.agents.identityMode='legacy';assert.equal(authorize({policy:p}).allowed,false); assert.equal(authorize({policy:p,principalId:undefined,generation:undefined}).allowed,true); delete p.agents.identityMode;assert.equal(authorize({policy:p,principalId:undefined,generation:undefined}).allowed,false); }); test('generation floor survives removal and restart and disables restored older credentials',()=>{ const first=grants.reconcileGenerationFloor(policy().agents.grants,{}); const removed=grants.reconcileGenerationFloor([],JSON.parse(JSON.stringify(first.floor))); const restored=grants.reconcileGenerationFloor([{...policy().agents.grants[0],generation:1}],removed.floor); assert.equal(restored.grants[0].enabled,false);assert.equal(restored.floor['worker.a'],2); assert.equal(grants.reconcileGenerationFloor([{...policy().agents.grants[0],generation:3}],restored.floor).grants[0].enabled,true); }); test('protected credential publication is atomic, bounded to hashed names and stale-safe',async()=>{ const dir=fs.mkdtempSync(path.join(os.tmpdir(),'agent-grants-')); try{ const filePath=grants.descriptorPath(dir,'worker.a');assert.match(path.basename(filePath),/^agent-[a-f0-9]{64}\.json$/); await grants.persistConnection({filePath,connection:{token:'synthetic-secret'},protect:async()=> 'opaque-dpapi',isCurrent:()=>true}); const prior=fs.readFileSync(filePath,'utf8');assert.equal(prior.includes('synthetic-secret'),false); let current=true; await assert.rejects(grants.persistConnection({filePath,connection:{token:'other'},protect:async()=>{current=false;return 'new';},isCurrent:()=>current}),/context changed/); assert.equal(fs.readFileSync(filePath,'utf8'),prior);assert.equal(fs.readdirSync(dir).length,1); const linked=path.join(dir,'linked.json');fs.linkSync(filePath,linked); await assert.rejects(grants.persistConnection({filePath,connection:{},protect:async()=>'',isCurrent:()=>true}),/Unsafe credential/); }finally{fs.rmSync(dir,{recursive:true,force:true});} });