Implement managed browser security, individual agents and observability
This commit is contained in:
1 parent
ff482ebe23
commit
ea448de2d4
172 files changed
+5404
-347
No files matched your search
@@ -0,0 +1,47 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import {generateKeyPairSync} from 'node:crypto';
|
||||
import channel from '../packages/core/policy-channel.cjs';
|
||||
import browser from '../apps/browser/management-channel.cjs';
|
||||
|
||||
const ed=generateKeyPairSync('ed25519'),pq=generateKeyPairSync('ml-dsa-87');
|
||||
const publicKey=ed.publicKey.export({type:'spki',format:'pem'}),signatureKey=pq.publicKey.export({type:'spki',format:'pem'});
|
||||
const payload={eventId:'4d113278-cabd-4e6d-b4be-022220f7a505',type:'download',payload:{name:'confidential-synthetic-only.xlsx'}};
|
||||
function fixture({legacy=false,result={status:201,body:{id:'synthetic-ack'}},mutateResponse,mutateChallenge}={}){
|
||||
const device={server:'https://synthetic.invalid',id:'device-a',token:'synthetic-token',publicKey,...(!legacy?{channelSignatureKey:signatureKey}:{}),privateEnrollmentFlag:true};
|
||||
const calls=[];let item,saved=0;
|
||||
const request=async(server,route,body,token)=>{
|
||||
assert.equal(server,device.server);assert.equal(token,device.token);calls.push({route,body});
|
||||
if(route==='/api/device/channel/identity')return channel.identity(ed.privateKey,pq.privateKey,signatureKey);
|
||||
if(route==='/api/device/channel/evidence/challenge'){item=channel.evidenceChannel.challenge(device.id,ed.privateKey,pq.privateKey);return mutateChallenge?mutateChallenge(item.bundle):item.bundle;}
|
||||
assert.equal(route,'/api/device/channel/evidence');assert.equal(JSON.stringify(body).includes(payload.payload.name),false);
|
||||
const context=channel.evidenceChannel.receive(body,item.bundle,item.privateKey);
|
||||
try{assert.deepEqual(context.payload,payload);const response=channel.evidenceChannel.response(result,item.bundle,context);return mutateResponse?mutateResponse(response):response;}finally{context.responseKey.fill(0);}
|
||||
};
|
||||
return {device,calls,request,save:actual=>{assert.equal(actual,device);assert.equal(actual.privateEnrollmentFlag,true);saved++;},saved:()=>saved};
|
||||
}
|
||||
test('browser evidence uses real PQ encryption and authenticated acknowledgement; legacy pin migration preserves enrollment state',async()=>{
|
||||
const f=fixture({legacy:true});assert.deepEqual(await browser.protectedEvidenceRequest({...f,payload}),{id:'synthetic-ack'});assert.equal(f.saved(),1);assert.equal(f.device.channelSignatureKey,signatureKey);
|
||||
assert.deepEqual(f.calls.map(c=>c.route),['/api/device/channel/identity','/api/device/channel/evidence/challenge','/api/device/channel/evidence']);
|
||||
await browser.protectedEvidenceRequest({...f,payload});assert.equal(f.saved(),1);
|
||||
});
|
||||
test('authenticated evidence rejection preserves status without disclosing server error content',async()=>{
|
||||
const f=fixture({result:{status:403,body:{error:'synthetic confidential server detail'}}});
|
||||
await assert.rejects(browser.protectedEvidenceRequest({...f,payload}),error=>error.status===403&&error.message==='Protected evidence submission refused');
|
||||
});
|
||||
test('tampered evidence response and unauthenticated status never become a trusted acknowledgement',async()=>{
|
||||
for(const mutateResponse of [r=>({...r,status:403}),r=>({...r,encrypted:{...r.encrypted,tag:Buffer.alloc(16).toString('base64url')}})]){
|
||||
const f=fixture({mutateResponse});await assert.rejects(browser.protectedEvidenceRequest({...f,payload}),error=>error.status===undefined);
|
||||
}
|
||||
for(const result of [{status:201,body:[]},{status:200,body:null},{status:201,body:{},extra:true},{status:199,body:{}}])await assert.rejects(browser.protectedEvidenceRequest({...fixture({result}),payload}));
|
||||
});
|
||||
test('wrong pin and cross-purpose challenge are refused before evidence transmission',async()=>{
|
||||
const wrong=fixture();wrong.device.channelSignatureKey=generateKeyPairSync('ml-dsa-87').publicKey.export({type:'spki',format:'pem'});
|
||||
await assert.rejects(browser.protectedEvidenceRequest({...wrong,payload}));assert.equal(wrong.calls.length,1);
|
||||
const cross=fixture({mutateChallenge:()=>channel.challenge('device-a',ed.privateKey,pq.privateKey).bundle});
|
||||
await assert.rejects(browser.protectedEvidenceRequest({...cross,payload}));assert.equal(cross.calls.length,1);
|
||||
});
|
||||
test('transport failure has no plaintext evidence fallback',async()=>{
|
||||
const f=fixture(),request=async(...args)=>{if(args[1]==='/api/device/channel/evidence')throw Error('Synthetic offline transport');return f.request(...args);};
|
||||
await assert.rejects(browser.protectedEvidenceRequest({...f,request,payload}));assert.equal(f.calls.some(c=>c.route==='/api/device/evidence'),false);
|
||||
});
|
||||
Reference in new issue
Block a user