feat: verify canonical portfolio release provenance

This commit is contained in:
2026-09-02 09:41:01 +03:00
parent 1cec658baf
commit 672ba80d9b
12 changed files with 3536 additions and 0 deletions

View File

@@ -0,0 +1,34 @@
import { describe, expect, test } from 'vitest';
import {
byteDigest,
canonicalJsonBytes,
canonicalJsonDigest,
parseCanonicalJson,
} from '../../src/security/canonical-digest.ts';
describe('canonical digest primitives', () => {
test('matches an independently fixed RFC 8785 byte and digest vector', () => {
const bytes = canonicalJsonBytes('portfolio-test/v1', { z: 1, a: 'é' });
expect(new TextDecoder().decode(bytes)).toBe(
'{"schema":"portfolio-test/v1","value":{"a":"é","z":1}}',
);
expect(canonicalJsonDigest('portfolio-test/v1', { z: 1, a: 'é' })).toBe(
'2d38a0324bcda2075f853db9972f6ce34ecb8072a85ec579b7c0e89be38df76a',
);
expect(byteDigest(new TextEncoder().encode('abc'))).toBe(
'ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad',
);
});
test.each([
['duplicate keys', '{"a":1,"a":2}'],
['non-canonical whitespace', '{ "a":1}'],
['alternate number encoding', '{"a":1.0}'],
['non-NFC string', '{"a":"e\\u0301"}'],
['unpaired surrogate', '{"a":"\\ud800"}'],
])('rejects %s at JSON ingest', (_label, source) => {
expect(() => parseCanonicalJson(source)).toThrow();
});
});