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