Files
2026-05-27 23:29:53 +01:00

6.9 KiB

SLSA capability spec

Requirements

SLSA-01 Structured errors

The package SHALL expose a SlsaError base class extending SuaveplanError<SlsaCode>, a frozen SLSA_CODES registry built via defineCodes, and one subclass per code (InvalidProvenanceError, InvalidSbomError, VerificationFailedError, PolicyViolationError, RekorUploadError, RekorInclusionError, AttestationInvalidError, EnvironmentUnsupportedError). Every thrown error inside the package MUST be a subclass of SlsaError — no raw Error or string throws.

Verified by: src/errors/slsa-error.test.ts

SLSA-02 Zod schemas

Every wire-format type SHALL have a sibling Zod schema co-located in src/schemas/*. Types SHALL be derived via z.infer rather than hand-authored. parseProvenance(input) and parseSbom(input) SHALL accept either a JSON string or an object and throw InvalidProvenanceError / InvalidSbomError (with context.issues) on failure.

Verified by: src/schemas/parse.test.ts AND src/schemas/parse.extra.test.ts

SLSA-03 Canonical JSON

The package SHALL export a canonicalize(value): string helper that sorts object keys recursively and omits undefined. All hashing and DSSE payload encoding SHALL use this helper so digests are stable across producers.

Verified by: src/provenance/canonical-json.test.ts

SLSA-04 SHA-256 via @suaveplan/crypto-hash

The package SHALL NOT import from node:crypto. Hashing SHALL route through @suaveplan/crypto-hash's sha256 selector plus @suaveplan/crypto-utils's toHex / fromHex.

Verified by: src/provenance/digest.test.ts AND src/spec-conformance.test.ts::"src/ does not import node:crypto"

SLSA-05 Provenance generation

generateProvenance(input) SHALL produce an in-toto v1 statement with a SLSA v1.0 provenance predicate. Input SHALL be validated; missing or malformed fields SHALL raise InvalidProvenanceError with context.field. Invalid or reverse-ordered ISO 8601 timestamps SHALL also raise.

Verified by: src/provenance/provenance.test.ts AND src/provenance/provenance.extra.test.ts

SLSA-06 Provenance verification

verifyProvenance(statement, policy, opts?) SHALL run the baseline rule set (statement type, predicate type, subject present, subject digest, builder trusted, build timestamps) and append optional rules based on the policy shape (expectedSourceRepo, expectedSourceRepoPrefix, requireFinishedOn, maxBuildDurationMs, maxAgeMs). The result SHALL be a structured VerificationResult — the function SHALL NOT throw on policy failure.

Verified by: src/verification/verification.test.ts

SLSA-07 SBOM generation

generateSbom(input, opts?) SHALL produce a CycloneDX 1.5 SBOM. workspace:* and catalog:key dependency references SHALL be rewritten to * and catalog-key respectively. clock and serial SHALL be injectable for deterministic tests. Canonical serialisation and SHA-256 hashing SHALL be exposed via serializeSbom and hashSbom.

Verified by: src/sbom/sbom.test.ts

SLSA-08 Assessment

assessSlsaLevel(env) SHALL return the highest achievable SLSA Build level (L0-L3) given the supplied signals. When hasRekorInclusion is false, L3 SHALL be blocked even if isolated-build and hardened-provenance are set. detectEnvironment() SHALL populate the signals from SLSA_* / CI / GITLAB_CI / GITHUB_ACTIONS env vars.

Verified by: src/assessment/assessment.test.ts

SLSA-09 DSSE attestation envelope

encodeDssePae(payloadType, payload) SHALL produce the exact DSSEv1 <len> <type> <len> <body> byte sequence defined by the DSSE spec. wrapInDsse(statement, signatures) SHALL canonicalise and base64-encode the statement. verifyDsseEnvelope(envelope, keys) SHALL verify every signature against the supplied ed25519 keys, honouring per-signature keyid scoping, and return a structured DsseVerificationResult with per-signature records.

Verified by: src/attestation/dsse/dsse.test.ts

SLSA-10 Sigstore Rekor client

RekorClient SHALL support uploadHashedrekord (v0.0.1), uploadIntoto (v0.0.2 with auto-computed payload sha256), getEntryByUuid, and getEntryByLogIndex. The client SHALL accept an injectable fetch implementation and an AbortSignal on every request. Non-2xx responses SHALL raise RekorUploadError with the status code and response body attached. Rekor's { [uuid]: entry } response shape SHALL be flattened and Zod-validated.

Verified by: src/rekor/rekor-client.test.ts AND src/rekor/rekor-client.extra.test.ts

SLSA-11 Inclusion proof verification

verifyRfc6962InclusionProof SHALL apply the RFC 6962 leaf (0x00) and interior-node (0x01) domain separators, correctly carry unsibled nodes at odd tree levels, and compare the computed root case-insensitively against expectedRootHex. Out-of-range logIndex values SHALL raise RekorInclusionError.

Verified by: src/rekor/inclusion-proof.test.ts AND src/rekor/inclusion-proof.extra.test.ts

SLSA-12 Policy evaluator

evaluatePolicy(statement, envelope, rekorEntry, policy, opts?) SHALL compose verifyProvenance with DSSE signature verification, Rekor inclusion-proof presence, and min-SLSA-level assertion. The function SHALL NOT throw on policy failure — callers branch on result.valid.

Verified by: src/policy/policy.test.ts

SLSA-13 OTel audit events

The package SHALL export createSlsaOtelAdapter(tracer?) emitting slsa.provenance.generated, slsa.provenance.verified, slsa.rekor.upload, slsa.rekor.inclusion_verified, and slsa.policy.violation span events with attributes under the slsa.* namespace. When no active span and no tracer are present, events SHALL be silently discarded.

Verified by: src/otel/slsa-otel.test.ts AND src/provenance/provenance.test.ts::"invokes otel adapter when supplied" AND src/rekor/rekor-client.test.ts::"emits otel upload event"

SLSA-14 Feature-module layout

Every feature SHALL live in src/<category>/<feature>/<feature>.{ts,test.ts,md} with an optional <feature>.types.ts. A sibling .md ≥200 words is required for every non-test .ts except barrels and .types.ts.

Verified by: src/spec-conformance.test.ts::"every feature module has its sibling .md"

SLSA-15 Subpath exports

package.json exports SHALL include one entry per feature module (./provenance/provenance, ./verification/verification, ./sbom/sbom, ./assessment/assessment, ./attestation/dsse, ./rekor/rekor-client, ./rekor/inclusion-proof, ./policy/policy, ./errors, ./otel, ./schemas) to preserve tree-shaking.

Verified by: src/spec-conformance.test.ts::"package.json exports include every documented subpath"

SLSA-16 Coverage floor

Istanbul coverage SHALL meet the 100% floor on statements, branches, functions, and lines.

Verified by: src/spec-conformance.test.ts::"vitest config sets a coverage floor at or above 100" AND coverage gate