Files
safetynet/tests/dpapi-helper-native.test.mjs
T

39 lines
3.9 KiB
JavaScript

import test from 'node:test';
import assert from 'node:assert/strict';
import {createRequire} from 'node:module';
import {mkdtemp,writeFile,access,rm,mkdir} from 'node:fs/promises';
import path from 'node:path';
import os from 'node:os';
import {randomUUID} from 'node:crypto';
import {defaultPolicy} from '../packages/core/policy.mjs';
const {createCapabilityCrypto}=createRequire(import.meta.url)('../apps/browser/dpapi.cjs');
const {createFixtureDiagnostics}=createRequire(import.meta.url)('./fixtures/broker-native-diagnostics.cjs');
const {createSyntheticAudit}=createRequire(import.meta.url)('./fixtures/broker-durable-audit.cjs');
const enabled=process.platform==='win32'&&process.env.SAFETYNET_NATIVE_DPAPI_HELPER==='1';
test('actual isolated private Python performs Windows DPAPI despite poisoned Python environment',{skip:!enabled},async t=>{
const root=process.cwd(),temporary=await mkdtemp(path.join(os.tmpdir(),'safetynet-dpapi-injection-'));
t.after(()=>rm(temporary,{recursive:true,force:true}));const marker=path.join(temporary,'startup-hook-ran');
await writeFile(path.join(temporary,'sitecustomize.py'),`from pathlib import Path\nPath(${JSON.stringify(marker)}).write_text('synthetic startup hook')\n`);
const convert=createCapabilityCrypto({root,environment:{...process.env,PYTHONPATH:temporary,PYTHONHOME:path.join(temporary,'missing-runtime'),PYTHONINSPECT:'1',UNLISTED_SECRET:'synthetic-inherited-secret'}});
const payload=Buffer.from('synthetic private capability');
const audit=createSyntheticAudit(t),diagnostics=await createFixtureDiagnostics({enabled:true,policy:defaultPolicy(),audit});let diagnosticProof,timings;
t.after(async()=>{if(!diagnosticProof)await diagnostics.finish();});
const started=performance.now();let encrypted,decrypted;
await diagnostics.run({bytes:0},async()=>{
encrypted=await diagnostics.stage('browser.credential',{operation:'credential.issue',component:'agent'},()=>convert('protect',payload));
decrypted=await diagnostics.stage('browser.credential',{operation:'credential.claim',component:'agent'},()=>convert('unprotect',encrypted));
// Synthetic fsync event supplies correlation only; no browser upload occurs.
diagnostics.persist({eventId:randomUUID(),kind:'upload.authorized',ruleId:'broker-approved',policyVersion:1,request:{bytes:0,detectedType:'text'}});
});
const traceId=audit.events()[0].traceId,actual=await diagnostics.trace(traceId),parent=actual.spans.find(value=>value.name==='browser.request');
timings=actual.spans.filter(value=>value.name==='browser.credential').map(value=>({name:value.name,parentSpanId:value.parentSpanId,durationMs:Number(BigInt(value.endTimeUnixNano)-BigInt(value.startTimeUnixNano))/1e6}));
assert.equal(timings.length,2);assert.ok(timings.every(value=>value.parentSpanId===parent.spanId&&value.durationMs>0));
diagnosticProof=await diagnostics.finish();assert.equal(diagnosticProof.failedExports,0);assert.equal(diagnosticProof.lifetimeFailedExports,0);
assert.equal(decrypted,payload.toString());assert.notEqual(encrypted,decrypted);assert.equal(Buffer.from(encrypted,'base64').includes(payload),false);
await assert.rejects(access(marker),{code:'ENOENT'});payload.fill(0);
const maximum=Buffer.alloc(32768,97),maximumEncrypted=await convert('protect',maximum);
assert.ok(Buffer.byteLength(maximumEncrypted)>32768);assert.equal(await convert('unprotect',maximumEncrypted),maximum.toString());maximum.fill(0);
const directory=path.join(root,'artifacts/dpapi-helper');await mkdir(directory,{recursive:true});
await writeFile(path.join(directory,'proof.json'),JSON.stringify({kind:'actual-windows-current-user-dpapi',passed:true,isolatedPrivatePython:true,poisonedPythonEnvironmentIgnored:true,startupHookAbsent:true,plaintextStored:false,maximumInputBytes:32768,maximumCiphertextBytes:Buffer.byteLength(maximumEncrypted),maximumRoundtripPassed:true,roundtripMs:performance.now()-started,diagnostics:{...diagnosticProof,timings,syntheticCorrelationEvent:true},serviceCredentialStoreProven:false},null,2));
});