Initial Commit
This commit is contained in:
138
openspec/archive/2026-04-19-enterprise-slsa/specs/slsa/spec.md
Normal file
138
openspec/archive/2026-04-19-enterprise-slsa/specs/slsa/spec.md
Normal file
@@ -0,0 +1,138 @@
|
||||
# SLSA capability spec — enterprise delta
|
||||
|
||||
## ADDED 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.
|
||||
|
||||
### 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 <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.
|
||||
|
||||
### 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/<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`.
|
||||
|
||||
### 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.
|
||||
Reference in New Issue
Block a user