# SLSA capability spec — enterprise delta ## ADDED Requirements ### SLSA-01 Structured errors The package SHALL expose a `SlsaError` base class extending `SuaveplanError`, 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. ### 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. ### 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. ### 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`. ### 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. ### 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. ### 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`. ### 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. ### SLSA-09 DSSE attestation envelope `encodeDssePae(payloadType, payload)` SHALL produce the exact `DSSEv1 ` 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. ### 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. ### 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`. ### 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`. ### 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. ### SLSA-14 Feature-module layout Every feature SHALL live in `src///.{ts,test.ts,md}` with an optional `.types.ts`. A sibling `.md` ≥200 words is required for every non-test `.ts` except barrels and `.types.ts`. ### 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. ### SLSA-16 Coverage floor Istanbul coverage SHALL meet the 95% floor on statements, branches, functions, and lines. Current: 99.2 / 95.15 / 100 / 99.44.