import test from 'node:test';import assert from 'node:assert/strict';import {mkdtempSync,writeFileSync,rmSync} from 'node:fs';import {tmpdir} from 'node:os';import {join} from 'node:path';import {execFileSync} from 'node:child_process';import {randomBytes,randomUUID} from 'node:crypto'; import channel from '../packages/core/policy-channel.cjs';import browser from '../apps/browser/management-channel.cjs';import {openStore,digest} from '../apps/server/store.mjs';import {createApp} from '../apps/server/app.mjs'; async function fixture(t){ const dir=mkdtempSync(join(tmpdir(),'safetynet-pq-evidence-')),keyFile=join(dir,'evidence.key');writeFileSync(keyFile,randomBytes(32),{mode:0o600});if(process.platform==='win32')execFileSync(join(process.env.SystemRoot,'System32/WindowsPowerShell/v1.0/powershell.exe'),['-NoProfile','-NonInteractive','-Command',"$ErrorActionPreference='Stop';$sid=[System.Security.Principal.WindowsIdentity]::GetCurrent().User;$acl=New-Object System.Security.AccessControl.FileSecurity;$acl.SetOwner($sid);$acl.SetAccessRuleProtection($true,$false);$acl.AddAccessRule((New-Object System.Security.AccessControl.FileSystemAccessRule($sid,'FullControl','Allow')));Set-Acl -LiteralPath $env:TEST_PQ_EVIDENCE_KEY -AclObject $acl"],{windowsHide:true,env:{SystemRoot:process.env.SystemRoot,TEST_PQ_EVIDENCE_KEY:keyFile},stdio:'pipe'}); const store=openStore(dir),p=store.get('policy');p.evidence={enabled:true,captureDownloads:true,captureInteractions:true};store.put('policy',p);const token='synthetic-pq-evidence-device-token-123456789',otherToken='synthetic-pq-evidence-other-token-123456789',id='device-a';store.db.prepare('INSERT INTO devices(id,token_hash,revoked) VALUES(?,?,0)').run(id,digest(token));store.db.prepare('INSERT INTO devices(id,token_hash,revoked) VALUES(?,?,0)').run('device-b',digest(otherToken)); const config={publicUrl:'http://127.0.0.1',secure:false,evidenceKeyFile:keyFile,requirePQManagement:true},server=createApp(store,config).listen(0,'127.0.0.1');await new Promise(r=>server.once('listening',r));t.after(async()=>{server.closeAllConnections();await new Promise(r=>server.close(r));store.db.close();assert.ok(dir.startsWith(join(tmpdir(),'safetynet-pq-evidence-')));rmSync(dir,{recursive:true,force:true});}); const base=`http://127.0.0.1:${server.address().port}`,wire=[];const api=async(route,body,credential=token)=>{wire.push({route,body});const r=await fetch(base+route,{method:body?'POST':'GET',headers:{authorization:`Bearer ${credential}`,...(body?{'content-type':'application/json'}:{})},body:body?JSON.stringify(body):undefined});return {status:r.status,body:await r.json()};}; const eventId=randomUUID();store.event(id,'clipboard.copy',{eventId,policyVersion:1,interaction:{action:'copy',outcome:'completed',sessionId:'00000000-0000-4000-8000-000000000001'}});const payload={eventId,type:'interaction',payload:{pageUrl:'https://intranet.example/file',action:'copy',sessionId:'00000000-0000-4000-8000-000000000001',outcome:'completed',captureSource:'guarded-clipboard',sensitiveField:false}}; const device={id,token,server:base,publicKey:store.publicKey(),channelSignatureKey:store.get('channelSigningKeys').publicKey};const make=async()=>{const challenge=await api('/api/device/channel/evidence/challenge',{});assert.equal(challenge.status,200);channel.evidenceChannel.verifyChallenge(challenge.body,id,device.publicKey,device.channelSignatureKey);return {bundle:challenge.body,context:channel.evidenceChannel.prepare(challenge.body,payload)};};return {store,api,wire,payload,device,make}; } test('real central PQ evidence storage rejects plaintext and supports authenticated idempotent retry',async t=>{ const f=await fixture(t);assert.equal((await f.api('/api/device/evidence',f.payload)).status,403); const request=async(_base,route,body,credential)=>{const response=await f.api(route,body,credential);if(response.status>=400)throw Object.assign(Error('Transport refused'),{status:response.status});return response.body;}; const created=await browser.protectedEvidenceRequest({device:f.device,request,save:()=>{},payload:f.payload});assert.match(created.id,/^[0-9a-f-]{36}$/);const again=await browser.protectedEvidenceRequest({device:f.device,request,save:()=>{},payload:f.payload});assert.equal(again.id,created.id);assert.equal(again.replayed,true); assert.equal(f.store.db.prepare('SELECT count(*) n FROM protected_evidence').get().n,1);assert.equal(f.store.db.prepare("SELECT count(*) n FROM events WHERE kind='evidence.recorded'").get().n,1); assert.equal(JSON.stringify(f.wire.filter(r=>r.route.includes('/channel/'))).includes(f.payload.payload.pageUrl),false);assert.equal(f.store.db.prepare('SELECT metadata FROM events').all().some(r=>r.metadata.includes(f.payload.payload.pageUrl)),false); const row=f.store.db.prepare('SELECT ciphertext FROM protected_evidence').get();assert.equal(Buffer.from(row.ciphertext).includes(Buffer.from(f.payload.payload.pageUrl)),false); await assert.rejects(browser.protectedEvidenceRequest({device:f.device,request,save:()=>{},payload:{...f.payload,payload:{...f.payload.payload,bytes:17}}}),e=>e.status===409); }); test('challenge consumption covers successful replay, invalid ciphertext and cross-device substitution',async t=>{ const f=await fixture(t);let a=await f.make();assert.equal((await f.api('/api/device/channel/evidence',a.context.request,'synthetic-pq-evidence-other-token-123456789')).status,403); let response=await f.api('/api/device/channel/evidence',a.context.request);assert.equal(response.status,200);assert.equal(channel.evidenceChannel.finish(response.body,a.bundle,a.context).status,201);assert.equal((await f.api('/api/device/channel/evidence',a.context.request)).status,403);a.context.responseKey.fill(0); a=await f.make();const altered=structuredClone(a.context.request);altered.encrypted.tag=Buffer.alloc(16).toString('base64url');assert.equal((await f.api('/api/device/channel/evidence',altered)).status,403);assert.equal((await f.api('/api/device/channel/evidence',a.context.request)).status,403);a.context.responseKey.fill(0); }); test('central disable after issuance returns encrypted denial and revoked credentials cannot upload',async t=>{ const f=await fixture(t),a=await f.make(),p=f.store.get('policy');p.evidence.enabled=false;f.store.put('policy',p);const response=await f.api('/api/device/channel/evidence',a.context.request);assert.equal(response.status,200);assert.equal(channel.evidenceChannel.finish(response.body,a.bundle,a.context).status,403);assert.equal(f.store.db.prepare('SELECT count(*) n FROM protected_evidence').get().n,0);assert.equal((await f.api('/api/device/channel/evidence/challenge',{})).status,403);a.context.responseKey.fill(0); p.evidence.enabled=true;f.store.put('policy',p);const b=await f.make();f.store.db.prepare('UPDATE devices SET revoked=1 WHERE id=?').run(f.device.id);assert.equal((await f.api('/api/device/channel/evidence',b.context.request)).status,401);b.context.responseKey.fill(0); }); test('expired evidence challenge never decrypts or stores payload and remains one-use',async t=>{ const now=Date.now();t.mock.timers.enable({apis:['Date'],now});const f=await fixture(t),a=await f.make();t.mock.timers.setTime(now+60001);assert.equal((await f.api('/api/device/channel/evidence',a.context.request)).status,403);assert.equal((await f.api('/api/device/channel/evidence',a.context.request)).status,403);assert.equal(f.store.db.prepare('SELECT count(*) n FROM protected_evidence').get().n,0);a.context.responseKey.fill(0); });