Files
Portfolio/tests/unit/approval-closure.test.ts

243 lines
7.4 KiB
TypeScript

import { generateKeyPairSync, sign } from "node:crypto";
import { describe, expect, test } from "vitest";
import {
computeApprovalClosure,
verifyApprovalProjectionAttestation,
} from "../../src/security/approval-closure.ts";
import { type ApprovalPayload } from "../../src/security/approval-envelope.ts";
import {
canonicalJsonDigest,
canonicalValueBytes,
} from "../../src/security/canonical-digest.ts";
const h = (c: string) => c.repeat(64);
const keys = generateKeyPairSync("ed25519");
function envelope(payload: ApprovalPayload) {
return {
schema: "portfolio-approval-envelope/v1" as const,
payload,
signature: {
algorithm: "Ed25519" as const,
encoding: "base64url-nopad" as const,
value: sign(null, canonicalValueBytes(payload), keys.privateKey).toString(
"base64url",
),
},
};
}
function base(
recordClass: "content" | "asset",
id: string,
ledgerRevision: number,
previousLedgerHeadHash: string,
): ApprovalPayload {
return {
schema: "portfolio-approval-payload/v1",
keyId: "key",
issuerEpoch: 1,
authorizedRole: "approver",
evidenceFingerprints: [h("1")],
recordClass,
recordType: recordClass === "content" ? "profile" : "",
publicRecordIdOrAssetId: id,
approvedPublicRecordHashOrManifestRecordHash: h(
recordClass === "content" ? "2" : "3",
),
sanitizedOutputHashOrNull: recordClass === "asset" ? h("4") : null,
decision: "approve",
issuedAt: "2026-09-01T00:00:00Z",
expiresAt: "2026-10-01T00:00:00Z",
previousLedgerHeadHash,
ledgerRevision,
policyVersion: "p1",
};
}
function ledgerTrust(entries: ReturnType<typeof envelope>[]) {
let head = h("0");
entries.forEach((entry, index) => {
const envelopeDigest = canonicalJsonDigest(
"portfolio-approval-envelope-digest/v1",
entry,
);
head = canonicalJsonDigest("portfolio-approval-ledger-entry/v1", {
previousLedgerHeadHash: head,
ledgerRevision: index + 1,
envelopeDigest,
});
});
return {
now: "2026-09-02T00:00:00Z",
latestLedgerHeadHash: head,
latestLedgerRevision: entries.length,
trustedKeys: [
{
keyId: "key",
issuerEpoch: 1,
scope: "portfolio-approval-decision/v1" as const,
authorizedRoles: ["approver"],
publicKey: keys.publicKey,
},
],
ledgerProof: {
schema: "portfolio-approval-ledger-proof/v1" as const,
genesisHeadHash: h("0"),
entries,
},
};
}
describe("approval closure", () => {
test("computes exact sorted record, asset, and proof roots", () => {
const content = base("content", "profile-main", 1, h("0"));
const contentEnvelope = envelope(content);
const head1 = canonicalJsonDigest("portfolio-approval-ledger-entry/v1", {
previousLedgerHeadHash: h("0"),
ledgerRevision: 1,
envelopeDigest: canonicalJsonDigest(
"portfolio-approval-envelope-digest/v1",
contentEnvelope,
),
});
const asset = base("asset", "asset-main", 2, head1);
const assetEnvelope = envelope(asset);
const result = computeApprovalClosure({
contentRecords: [
{
recordType: "profile",
publicRecordId: "profile-main",
approvedPublicRecordHash: h("2"),
envelope: contentEnvelope,
},
],
assetRecords: [
{
assetId: "asset-main",
approvedManifestRecordHash: h("3"),
sanitizedOutputHash: h("4"),
envelope: assetEnvelope,
},
],
trust: ledgerTrust([contentEnvelope, assetEnvelope]),
});
const contentEnvelopeDigest = canonicalJsonDigest(
"portfolio-approval-envelope-digest/v1",
envelope(content),
);
const assetEnvelopeDigest = canonicalJsonDigest(
"portfolio-approval-envelope-digest/v1",
envelope(asset),
);
expect(result.approvedRecordRoot).toBe(
canonicalJsonDigest("portfolio-record-closure/v1", {
records: [["profile", "profile-main", h("2"), contentEnvelopeDigest]],
}),
);
expect(result.approvedAssetRoot).toBe(
canonicalJsonDigest("portfolio-asset-closure/v1", {
assets: [["asset-main", h("3"), h("4"), assetEnvelopeDigest]],
}),
);
expect(result.approvalProofSetDigest).toBe(
canonicalJsonDigest("portfolio-approval-proof-set/v1", {
envelopes: [
["asset", "", "asset-main", assetEnvelopeDigest],
["content", "profile", "profile-main", contentEnvelopeDigest],
],
}),
);
});
test("rejects duplicate IDs and stale protected ledger input", () => {
const content = base("content", "profile-main", 1, h("0"));
const record = {
recordType: "profile",
publicRecordId: "profile-main",
approvedPublicRecordHash: h("2"),
envelope: envelope(content),
};
const trust = ledgerTrust([record.envelope]);
expect(() =>
computeApprovalClosure({
contentRecords: [record, record],
assetRecords: [],
trust,
}),
).toThrow();
expect(() =>
computeApprovalClosure({
contentRecords: [record],
assetRecords: [],
trust: { ...trust, latestLedgerRevision: 0 },
}),
).toThrow();
});
test("verifies a scoped projection attestation against protected state and trusted time", () => {
const payload = {
schema: "portfolio-approval-projection-attestation/v1" as const,
keyId: "projection-key",
issuerEpoch: 4,
scope: "portfolio-approval-projection-attestation/v1" as const,
approvedRecordRoot: h("1"),
approvedAssetRoot: h("2"),
approvalProofSetDigest: h("3"),
inputDigests: {
PortfolioContent: h("4"),
PublicAssetManifest: h("5"),
PreparedAssetSet: h("6"),
SiteConfig: h("7"),
},
productionInputRoot:
"e19fd4083ba973074449e5ecc9364c2b339eadea32e61d8dd2f0d8e3d6e91c85",
liveLedgerHeadHash: h("8"),
liveLedgerRevision: 42,
policyVersion: "projection-policy-1",
issuedAt: "2026-09-02T00:00:00Z",
expiresAt: "2026-09-03T00:00:00Z",
earliestApprovalExpiry: "2026-09-04T00:00:00Z",
};
const projectionKeys = generateKeyPairSync("ed25519");
const attestation = {
schema: "portfolio-approval-projection-attestation-envelope/v1" as const,
payload,
signature: {
algorithm: "Ed25519" as const,
encoding: "base64url-nopad" as const,
value: sign(
null,
canonicalValueBytes(payload),
projectionKeys.privateKey,
).toString("base64url"),
},
};
expect(
verifyApprovalProjectionAttestation(attestation, {
now: "2026-09-02T01:00:00Z",
latestLedgerHeadHash: h("8"),
latestLedgerRevision: 42,
trustedKey: {
keyId: "projection-key",
issuerEpoch: 4,
scope: "portfolio-approval-projection-attestation/v1",
publicKey: projectionKeys.publicKey,
},
}).liveLedgerRevision,
).toBe(42);
expect(() =>
verifyApprovalProjectionAttestation(attestation, {
now: "2026-09-02T01:00:00Z",
latestLedgerHeadHash: h("9"),
latestLedgerRevision: 42,
trustedKey: {
keyId: "projection-key",
issuerEpoch: 4,
scope: "portfolio-approval-projection-attestation/v1",
publicKey: projectionKeys.publicKey,
},
}),
).toThrow();
});
});