66 lines
2.6 KiB
JavaScript
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"); }
|