26 lines
4.4 KiB
JavaScript
26 lines
4.4 KiB
JavaScript
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import vm from 'node:vm';
|
|
import {readFileSync} from 'node:fs';
|
|
import {createRequire} from 'node:module';
|
|
const require=createRequire(import.meta.url),source=readFileSync(new URL('../apps/browser/main.cjs',import.meta.url),'utf8');
|
|
const shipped=source.slice(source.indexOf('async function reconcileAgentPolicy()'),source.indexOf('async function pairIndividualAgent'));
|
|
function fixture(){const events=[],grants=[{id:'reader',name:'Reader',generation:2,enabled:true,expiresAt:new Date(Date.now()+60000).toISOString(),scopes:['read'],allowedHosts:[]}];const service={revoke:()=>events.push('revoke'),close:async()=>events.push('close'),reconcilePrincipals:g=>events.push(g)};const context=vm.createContext({agentGrants:require('../apps/browser/agent-grants.cjs'),policy:{agents:{identityMode:'individual',enabled:true,grants},browser:{settings:{hiddenSections:[],lockedSections:[]}}},device:{},saveDevice:()=>events.push('save'),agentService:service,agentServiceMode:'individual',ready:false,fs:{unlinkSync:()=>{}},agentFile:()=>'/synthetic',featureAllowed:()=>false,managedSettings:()=>({})});vm.runInContext(shipped,context);return {context,events,service,run:context.reconcileAgentPolicy};}
|
|
test('accepted policy reconciles individual grants while temporary readiness pause preserves peer service',async()=>{const f=fixture();await f.run();assert.equal(f.context.agentService,f.service);assert.equal(f.events[0],'save');assert.equal(f.events[1][0].generation,2);assert.equal(f.events.includes('revoke'),false);f.context.policy.agents.grants[0].generation=1;await f.run();assert.equal(f.events.at(-1)[0].enabled,false);assert.equal(f.context.device.agentGenerationFloor.reader,2);});
|
|
test('mode changes and centrally hidden agents revoke old broker before reissue',async()=>{for(const mode of ['legacy','hidden']){const f=fixture();if(mode==='legacy')f.context.policy.agents.identityMode='legacy';else f.context.policy.browser.settings.hiddenSections=['agents'];await f.run();assert.ok(f.events.includes('revoke'));assert.ok(f.events.includes('close'));assert.equal(f.context.agentService,null);}});
|
|
|
|
test('stored library proposal cannot be applied by an actor excluded from its destinations',async()=>{
|
|
const helper=source.slice(source.indexOf('function assertAgentLibraryDestinations('),source.indexOf('function getLibraryOrganizer('));
|
|
const callback=source.split(/\r?\n/).find(line=>line.includes(' assertCurrent:(snapshot,')).trim().slice('assertCurrent:'.length,-1);
|
|
let actor={principalId:'planner'},commits=0;
|
|
const policy={version:1},identity={},snapshot={policy,identity,epoch:'stable',bookmarkRevision:0,bookmarks:[{id:'b',url:'https://excluded.example/private'}],tabs:[{id:'t',url:'https://excluded.example/work'}]};
|
|
const context=vm.createContext({agentActorStorage:{getStore:()=>actor},actorDestinationAllowed:url=>actor.principalId==='planner'||new URL(url).hostname==='allowed.example',requireLibraryAI:()=>{},policy,device:identity,nativeLibraryEpoch:()=> 'stable',personalBookmarks:{revision:0}});
|
|
vm.runInContext(helper+'\n globalThis.checkSnapshot='+callback+';',context);
|
|
const manager=require('../apps/browser/library-organization.cjs').createLibraryOrganization({capture:async()=>snapshot,assertCurrent:context.checkSnapshot,inspect:async()=>{},propose:async()=>({proposalId:'shared-proposal',plan:{bookmarks:[{id:'b',folder:'Work'}],tabs:[{id:'t',group:'Work'}]}}),commit:async()=>{commits++;return {version:1};},applyTabs:()=>{},audit:async()=>{}});
|
|
await manager.plan();actor={principalId:'restricted-applier'};
|
|
await assert.rejects(manager.apply({proposalId:'shared-proposal',confirm:true}),/Agent destination denied/);
|
|
assert.equal(commits,0);
|
|
});
|
|
|
|
test('individual network gates refuse ownerless traffic even after tab authority is cleared',()=>{const shippedOwner=source.slice(source.indexOf('function agentRequestOwnerAllowed(source)'),source.indexOf('function actorDestinationAllowed'));const c=vm.createContext({policy:{agents:{identityMode:'individual'}}});vm.runInContext(shippedOwner,c);assert.equal(c.agentRequestOwnerAllowed(null),false);assert.equal(c.agentRequestOwnerAllowed({guarded:false}),false);assert.equal(c.agentRequestOwnerAllowed({guarded:true,security:{agentAuthority:null}}),true);c.policy.agents.identityMode='legacy';assert.equal(c.agentRequestOwnerAllowed(null),true);assert.ok(source.includes('||!agentRequestOwnerAllowed(source)||'));});
|