Files
safetynet/tests/windows-release-inventory.test.mjs
T

29 lines
4.0 KiB
JavaScript
Raw Normal View History

import test from 'node:test';
import assert from 'node:assert/strict';
import {mkdtemp,mkdir,writeFile,rm,symlink} from 'node:fs/promises';
import path from 'node:path';
import os from 'node:os';
import {generateKeyPairSync} from 'node:crypto';
import {createRequire} from 'node:module';
const {signManifest,verifyReleaseEnvelope}=createRequire(import.meta.url)('../packages/core/windows-trust.cjs');
import {buildReleaseInventory,verifyReleaseInventory} from '../scripts/windows-release-inventory.mjs';
async function fixture(t){const root=await mkdtemp(path.join(os.tmpdir(),'safetynet-release-inventory-'));t.after(()=>rm(root,{recursive:true,force:true}));for(const file of ['SafetyNet.exe','safetynet-service-host.exe','resources/app.asar','resources/broker/node.exe','resources/python/python.exe','resources/inspector/server.py','resources/semantic/model.bin']){const name=path.join(root,file);await mkdir(path.dirname(name),{recursive:true});await writeFile(name,'synthetic-public-'+file);}return root;}
test('complete actual staged inventory binds all components and excludes only external envelopes',async t=>{
const root=await fixture(t);await writeFile(path.join(root,'release-envelope.json'),'synthetic-public-signature');await writeFile(path.join(root,'deployment-envelope.json'),'synthetic-public-deployment');const files=await buildReleaseInventory(root);assert.equal(files.length,7);assert.deepEqual(files.map(file=>file.path),files.map(file=>file.path).sort());assert.ok(files.every(file=>/^[a-f0-9]{64}$/.test(file.sha256)));assert.equal(await verifyReleaseInventory(root,{manifest:{files}}),true);
});
test('missing scanner, same-size tampering and extra broker module cannot pass trusted inventory',async t=>{
const root=await fixture(t),files=await buildReleaseInventory(root);const target=path.join(root,'resources/broker/node.exe');const original=Buffer.from('synthetic-public-resources/broker/node.exe');await writeFile(target,Buffer.alloc(original.length,65));await assert.rejects(verifyReleaseInventory(root,{manifest:{files}}),{code:'windows-release-inventory-mismatch'});await writeFile(target,original);await writeFile(path.join(root,'resources/broker/extra.cjs'),'synthetic-public-extra');await assert.rejects(verifyReleaseInventory(root,{manifest:{files}}),{code:'windows-release-inventory-mismatch'});await rm(path.join(root,'resources/inspector/server.py'));await assert.rejects(buildReleaseInventory(root),{code:'windows-release-component-missing'});
});
test('actual junction cannot smuggle unsigned external files into release inventory',async t=>{
const root=await fixture(t);await symlink(path.join(root,'resources/python'),path.join(root,'resources/linked'),process.platform==='win32'?'junction':'dir');await assert.rejects(buildReleaseInventory(root),{code:'windows-release-path-unsafe'});
});
test('genuine canonical signed release verifies actual inventory despite JSON field order, then rejects tamper',async t=>{
const root=await fixture(t),vendor=generateKeyPairSync('ec',{namedCurve:'prime256v1'}),customer=generateKeyPairSync('ec',{namedCurve:'prime256v1'});
const manifest={format:'safetynet-windows-release-v1',releaseId:'12345678-1234-1234-1234-123456789abc',releaseSequence:1,serviceName:'SafetyNetNetworkBroker',deploymentTenant:'22345678-1234-1234-1234-123456789abc',deploymentPublicKey:customer.publicKey.export({type:'spki',format:'pem'}),files:await buildReleaseInventory(root)};
const signed=signManifest(manifest,vendor.privateKey.export({type:'pkcs8',format:'pem'}),'release'),verified=verifyReleaseEnvelope(signed,{publicKey:vendor.publicKey.export({type:'spki',format:'pem'})});
assert.notDeepEqual(Object.keys(manifest.files[0]),Object.keys(verified.manifest.files[0]));
assert.equal(await verifyReleaseInventory(root,verified),true);
const browser=verified.manifest.files.find(file=>file.path==='SafetyNet.exe');await writeFile(path.join(root,'SafetyNet.exe'),Buffer.alloc(browser.bytes,66));
await assert.rejects(verifyReleaseInventory(root,verified),{code:'windows-release-inventory-mismatch'});
});