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"); }