fix: bind final release authority and expiry

This commit is contained in:
2026-09-02 12:07:38 +03:00
parent a8fa09e21e
commit 90137c388c
7 changed files with 123 additions and 17 deletions

View File

@@ -7,7 +7,7 @@ import { describe, expect, it } from 'vitest';
import { canonicalValueBytes } from '../../src/security/canonical-digest.ts';
// @ts-expect-error The production release checker is an executable ESM module.
import { verifySupplyChainEvidence } from '../../scripts/supply-chain-check.mjs';
import { verifySupplyChainEvidence, verifySupplyChainEvidenceForTest } from '../../scripts/supply-chain-check.mjs';
const sha256 = (value: string | Buffer) => createHash('sha256').update(value).digest('hex');
@@ -87,10 +87,11 @@ async function evidenceFixture(overrides: Record<string, unknown> = {}) {
}
const verifyFixture = (fixture: Awaited<ReturnType<typeof evidenceFixture>>, extra: Record<string, unknown> = {}) =>
verifySupplyChainEvidence({
verifySupplyChainEvidenceForTest({
projectRoot: fixture.root,
evidenceDir: fixture.evidence,
toolTrustPolicyPath: fixture.trustPolicyPath,
expectedCommitSha: 'b'.repeat(40),
requireTrackedTriage: false,
...extra,
});
@@ -151,4 +152,15 @@ describe('supply-chain evidence', () => {
} });
await expect(verifyFixture(unscanned)).rejects.toThrow(/repository|secret/u);
});
it('keeps production authority and time non-caller-selectable', async () => {
const fixture = await evidenceFixture();
await expect(verifySupplyChainEvidence({
projectRoot: fixture.root,
evidenceDir: fixture.evidence,
toolTrustPolicyPath: fixture.trustPolicyPath,
now: '2026-09-02T12:00:00Z',
today: '2026-09-02',
})).rejects.toThrow(/production supply-chain request|unknown fields/u);
});
});

View File

@@ -252,6 +252,24 @@ describe("fenced atomic Nginx release transaction", () => {
await expect(lstat(join(root, "current"))).rejects.toThrow();
});
test("rechecks trusted time at the final pointer publication boundary", async () => {
const { fixture, root, archive } = await setup(
"portfolio-final-expiry-",
);
const input = transactionInput(root, archive, "tx-a", fixture);
await prepareReleaseForTest(input);
const times = ["2026-09-02T01:00:00Z", fixture.expiresAt];
await expect(
activateReleaseForTest({
...input,
expectedCurrentManifestDigest: null,
expectedFence: 1,
testClock: () => times.shift() ?? fixture.expiresAt,
}),
).rejects.toThrow(/expired.*publication/i);
await expect(lstat(join(root, "current"))).rejects.toThrow();
});
test("rollback requires a fresh signed manifest that binds the selected old artifact closure", async () => {
const { temp, fixture, root, archive } = await setup("portfolio-rollback-");
const inputA = transactionInput(root, archive, "tx-a", fixture);