browsec/trustlab/test/investigation-bundle.test.js

66 lines
2.6 KiB
JavaScript

import assert from "node:assert/strict";
import { createHash } from "node:crypto";
import test from "node:test";
import { createInvestigationBundle, parseInvestigationBundle } from "../src/investigation-bundle.js";
import { exploreCertificateDer } from "../src/der-explorer.js";
test("investigation bundles round-trip original DER and normalized evidence", async () => {
const derBytes = bytes("30023000");
const der = exploreCertificateDer(derBytes);
const certificate = {
subject: "CN=Offline Test",
sha256: sha256(derBytes),
isCa: false,
keyUsages: [],
selfSigned: false,
derBase64: Buffer.from(derBytes).toString("base64"),
der,
};
const report = {
observedAt: "2026-08-16T00:00:00.000Z",
facts: {
connectionId: "offline-test",
hostname: "offline.test",
port: 443,
validation: "failure",
errors: ["unknown-issuer"],
failure: { code: "unknown-issuer", check: "trust-anchor", summary: "Unknown", certificateSha256: certificate.sha256 },
presentedChain: [certificate],
constructedChain: [certificate],
tls: {},
},
findings: [],
caveats: [],
};
const bundle = createInvestigationBundle(report);
const parsed = await parseInvestigationBundle(JSON.stringify(bundle));
assert.equal(parsed.evidence.facts.hostname, "offline.test");
assert.equal(parsed.evidence.facts.presentedChain[0].derBase64, certificate.derBase64);
assert.equal(parsed.privacy.containsPrivateKeys, false);
});
test("bundle import rejects altered DER-derived authority facts and secret-bearing declarations", async () => {
const derBytes = bytes("30023000");
const der = exploreCertificateDer(derBytes);
const base = {
format: "org.browsec.investigation",
version: 1,
privacy: { containsPrivateKeys: false, containsTlsSessionSecrets: false },
evidence: {
facts: {
connectionId: "tampered", hostname: "offline.test", port: 443, validation: "failure", errors: [],
presentedChain: [{ subject: "x", sha256: sha256(derBytes), isCa: true, keyUsages: [], selfSigned: false, derBase64: Buffer.from(derBytes).toString("base64"), der }],
constructedChain: [], tls: {},
},
findings: [], caveats: [],
},
};
await assert.rejects(parseInvestigationBundle(JSON.stringify(base)), /CA assertion/);
base.privacy.containsPrivateKeys = true;
await assert.rejects(parseInvestigationBundle(JSON.stringify(base)), /no-secrets declaration/);
});
function bytes(hex) { return Uint8Array.from(hex.match(/../g).map((pair) => Number.parseInt(pair, 16))); }
function sha256(value) { return createHash("sha256").update(value).digest("hex"); }