first commit

This commit is contained in:
Karl committed 2026-10-13 10:34:48 +02:00
commit ff482ebe23
251 files changed
+19126

No files matched your search

+77
View File
@@ -0,0 +1,77 @@
import test from 'node:test';
import assert from 'node:assert/strict';
import {mkdtempSync,rmSync} from 'node:fs';
import {tmpdir} from 'node:os';
import {join} from 'node:path';
import {randomUUID} from 'node:crypto';
import {openStore,digest} from '../apps/server/store.mjs';
import {createApp} from '../apps/server/app.mjs';
import {PolicyGroupSchema,applyPolicyGroups} from '../packages/core/policy-groups.mjs';
import {defaultPolicy,verifyPolicy,destinationAllowed} from '../packages/core/policy.mjs';
async function fixture(t,review=false){
const dir=mkdtempSync(join(tmpdir(),'safetynet-groups-')),store=openStore(dir),origin='http://127.0.0.1:4310';
const server=createApp(store,{publicUrl:origin,secure:false,requireIndependentPolicyApproval:review}).listen(0,'127.0.0.1');await new Promise(r=>server.once('listening',r));
t.after(async()=>{await new Promise(r=>server.close(r));store.db.close();rmSync(dir,{recursive:true,force:true});});
const ids=[randomUUID(),randomUUID(),randomUUID()];for(const [index,id] of ids.entries())store.db.prepare('INSERT INTO devices(id,name,platform,user,token_hash) VALUES(?,?,?,?,?)').run(id,`Device ${index}`,'win32','employee',digest(`device-credential-${index}-12345678901234567890`));
async function api(path,method='GET',body,subject='author',role='admin'){
const credential=`synthetic-group-session-${subject}-12345678901234567890`;store.db.prepare('INSERT OR REPLACE INTO sessions VALUES(?,?,?)').run(digest(credential),Date.now()+60000,JSON.stringify({sub:subject,role}));
const response=await fetch(`http://127.0.0.1:${server.address().port}${path}`,{method,headers:{cookie:`sn_session=${credential}`,origin,'content-type':'application/json'},body:body===undefined?undefined:JSON.stringify(body)});return {status:response.status,body:await response.json()};
}
const group=()=>PolicyGroupSchema.parse({id:'finance',name:'Finance',memberDeviceIds:ids.slice(0,2),restrictions:{blockedHosts:['blocked.example'],disableAI:true,hiddenSections:['agents'],requireDocumentScan:true,managedBookmarks:[{id:'finance_intranet',title:'Finance',url:'https://intranet.example/finance'}]},rollout:{stage:'pilot',deviceIds:[ids[0]]}});
const publish=(g,expectedVersion=store.get('policy').version)=>api(`/api/admin/policy-groups/${g.id}`,'PUT',{expectedVersion,group:g});
const approve=r=>api(`/api/admin/policy-reviews/${r.reviewId}/approve`,'POST',{payloadHash:r.payloadHash,expectedVersion:r.expectedVersion,approve:true},'reviewer');
return {store,api,ids,group,publish,approve};
}
test('group pilot applies only to approved members, all promotion and removal create new signed generations',async t=>{
const f=await fixture(t),g=f.group(),baseline=f.store.get('policy');baseline.ai.enabled=true;baseline.browser.allowDownloads=true;baseline.inspection.documentAnalysisEnabled=false;f.store.put('policy',baseline);
assert.equal((await f.publish(g)).status,200);
const pilot=verifyPolicy(f.store.envelope(f.ids[0]),f.store.publicKey()),control=verifyPolicy(f.store.envelope(f.ids[1]),f.store.publicKey());
assert.equal(pilot.version,2);assert.equal(control.version,2);assert.deepEqual(pilot.assignment,{deviceId:f.ids[0],groups:['finance']});assert.deepEqual(control.assignment.groups,[]);
assert.equal(pilot.ai.enabled,false);assert.equal(control.ai.enabled,true);assert.equal(pilot.inspection.documentAnalysisEnabled,true);assert.equal(destinationAllowed('https://blocked.example/',pilot),false);assert.equal(destinationAllowed('https://blocked.example/',control),true);
assert.equal(pilot.browser.bookmarks.managed[0].id,'finance_intranet');assert.equal(control.browser.bookmarks.managed.length,0);
const same=f.store.envelope(f.ids[0]);assert.deepEqual(verifyPolicy(same,f.store.publicKey()),pilot); // Stable content at one generation.
g.rollout={stage:'all',deviceIds:[]};assert.equal((await f.publish(g,2)).status,200);assert.equal(f.store.effectivePolicy(f.ids[1]).ai.enabled,false);assert.equal(f.store.effectivePolicy(f.ids[2]).ai.enabled,true);
assert.equal((await f.api('/api/admin/policy-groups/finance','DELETE',{expectedVersion:3})).status,200);assert.equal(f.store.effectivePolicy(f.ids[0]).ai.enabled,true);assert.equal(f.store.effectivePolicy(f.ids[0]).version,4);
});
test('restrictive merges preserve baseline, conflict order cannot change result, unapproved features rejected',()=>{
const p=defaultPolicy();p.browser.blockedHosts=['baseline.example'];p.browser.settings.hiddenSections=['passwords'];
const first=PolicyGroupSchema.parse({id:'a',name:'A',memberDeviceIds:['device'],rollout:{stage:'all'},restrictions:{blockedHosts:['group.example'],disableDownloads:true,hiddenSections:['agents']}}),second=PolicyGroupSchema.parse({...first,id:'b',restrictions:{blockedHosts:['second.example'],disableAI:true}});
assert.deepEqual(applyPolicyGroups(p,[first,second],'device'),applyPolicyGroups(p,[second,first],'device'));
const result=applyPolicyGroups(p,[first],'device');assert.deepEqual(result.browser.blockedHosts,['baseline.example','group.example']);assert.equal(result.browser.allowDownloads,false);assert.ok(result.browser.settings.hiddenSections.includes('passwords'));
assert.throws(()=>PolicyGroupSchema.parse({...first,restrictions:{allowDownloads:true}}));assert.throws(()=>PolicyGroupSchema.parse({...first,rollout:{stage:'pilot',deviceIds:['outsider']}}));
});
test('independent review covers exact membership and cohort, stale and revoked candidates fail atomically',async t=>{
const f=await fixture(t,true),g=f.group(),queued=await f.publish(g);assert.equal(queued.status,202);assert.equal(f.store.getRaw('policyGroups'),null);assert.equal(f.store.effectivePolicy(f.ids[0]).version,1);
const detail=await f.api(`/api/admin/policy-reviews/${queued.body.reviewId}`);assert.deepEqual(detail.body.operation.group,g);
assert.equal((await f.api(`/api/admin/policy-reviews/${queued.body.reviewId}/approve`,'POST',{payloadHash:queued.body.payloadHash,expectedVersion:1,approve:true})).status,403);
assert.equal((await f.approve(queued.body)).status,200);assert.equal(f.store.effectivePolicy(f.ids[0]).version,2);
const next=await f.publish({...g,name:'Reviewed change'});f.store.db.prepare('UPDATE devices SET revoked=1 WHERE id=?').run(f.ids[0]);assert.equal((await f.approve(next.body)).status,409);assert.equal(f.store.get('policy').version,2);assert.equal(f.store.getRaw('policyGroups')[0].name,'Finance');
});
test('permissions, preview, unknown devices, union limits and bookmark conflicts are enforced centrally',async t=>{
const f=await fixture(t),g=f.group();assert.equal((await f.api('/api/admin/policy-groups/finance','PUT',{expectedVersion:1,group:g},'auditor','auditor')).status,403);
assert.equal((await f.api('/api/admin/policy-groups/preview','POST',{deviceId:f.ids[0],group:g},'auditor','auditor')).body.policy.ai.enabled,false);assert.equal(f.store.getRaw('policyGroups'),null);
assert.equal((await f.publish({...g,memberDeviceIds:[...g.memberDeviceIds,'unknown']})).status,409);
f.store.db.prepare('INSERT INTO bookmarks VALUES(?,?,?)').run(f.ids[0],1,JSON.stringify([{id:'finance_intranet',title:'Personal',url:'https://personal.example/',folder:''}]));assert.equal((await f.publish(g)).status,409);
f.store.db.prepare('DELETE FROM bookmarks').run();assert.equal((await f.publish(g)).status,200);
const conflicting={...g,id:'other',restrictions:{managedBookmarks:[{id:'finance_intranet',title:'Other',url:'https://other.example/',folder:''}]}};assert.equal((await f.publish(conflicting)).status,409);assert.equal(f.store.get('policy').version,2);
const overview=(await f.api('/api/admin/policy-groups')).body;assert.equal(overview.devices[0].acknowledged,false);f.store.db.prepare('UPDATE devices SET policy_version=2 WHERE id=?').run(f.ids[0]);assert.equal((await f.api('/api/admin/policy-groups')).body.devices.find(d=>d.id===f.ids[0]).acknowledged,true);
});
test('restoring a generation restores reviewed group assignments at a new version',async t=>{
const f=await fixture(t),g=f.group();assert.equal((await f.publish(g)).status,200);
g.rollout={stage:'all',deviceIds:[]};assert.equal((await f.publish(g)).status,200);
assert.deepEqual(f.store.effectivePolicy(f.ids[1]).assignment.groups,['finance']);
const snapshot=(await f.api('/api/admin/policy/history/2')).body;assert.equal(snapshot.groups[0].rollout.stage,'pilot');
assert.equal((await f.api('/api/admin/policy/restore','POST',{sourceVersion:2,expectedVersion:3,approve:true})).status,200);
assert.equal(f.store.get('policy').version,4);assert.deepEqual(f.store.effectivePolicy(f.ids[1]).assignment.groups,[]);assert.deepEqual(f.store.effectivePolicy(f.ids[0]).assignment.groups,['finance']);
const diff=(await f.api('/api/admin/policy/diff?from=2&to=3')).body;assert.ok(diff.groupDiff.changes.length);
});
test('gateway and bookmark APIs enforce device effective group policy and global drafts reject assignment metadata',async t=>{
const f=await fixture(t),g=f.group(),p=f.store.get('policy');p.ai.enabled=true;f.store.put('policy',p);assert.equal((await f.publish(g)).status,200);
// Use the authenticated device API, not an isolated merge helper.
const server=createApp(f.store,{publicUrl:'http://127.0.0.1:4310',secure:false,aiBaseUrl:'https://127.0.0.1:1',aiKey:'synthetic-fixture-key',aiModel:''}).listen(0,'127.0.0.1');await new Promise(r=>server.once('listening',r));t.after(()=>new Promise(r=>server.close(r)));
const headers={authorization:'Bearer device-credential-0-12345678901234567890','content-type':'application/json'};
const ai=await fetch(`http://127.0.0.1:${server.address().port}/api/device/ai`,{method:'POST',headers,body:JSON.stringify({prompt:'Public content'})});assert.equal(ai.status,409);assert.match((await ai.json()).error,/Approved AI provider/);
const bookmark=await fetch(`http://127.0.0.1:${server.address().port}/api/device/bookmarks`,{method:'POST',headers,body:JSON.stringify({action:'add',expectedRevision:0,policyVersion:2,bookmark:{id:'test',title:'Blocked',url:'https://blocked.example/'}})});assert.equal(bookmark.status,403);
const draft=f.store.effectivePolicy(f.ids[0]);draft.version++;assert.equal((await f.api('/api/admin/policy','PUT',draft)).status,400);assert.equal(f.store.get('policy').version,2);
});