Full C++23 framework tree: runtime, observability, security, data, app, realtime, supervisor, clients, CLI, example plus enterprise tenancy, audit, operability, governance, supplychain. Postgres via docker compose for tests.
151 lines
16 KiB
Markdown
151 lines
16 KiB
Markdown
# 15 - Supply Chain and Trust
|
|
|
|
File: plans/15-supply-chain.md
|
|
Scope: plan.md sections 4 (wolfSSL exception), 9 (autonomous releases). Topics: SBOM generation, signed release artifacts, dependency policy and wolfSSL exception handling, vulnerability-scan pipeline hook, provenance and release attestation.
|
|
|
|
## 1. Purpose and philosophy
|
|
|
|
MoloApp ships almost no supply chain: stdlib plus OS syscalls plus wolfSSL as the sole third-party dependency. This file makes that claim verifiable. Every release carries a machine-readable SBOM, a signed provenance statement describing where, when and how it was built, and detached signatures over all artifacts, so a consumer can answer "what is inside this binary and who built it" without trusting the download server.
|
|
|
|
Philosophy:
|
|
|
|
- Minimal surface, maximal proof. One allowed exception (wolfSSL) is pinned, hashed and attested; everything else in-house is versioned by git commit.
|
|
- Proof over promise. SBOM, provenance and signatures are generated by the release pipeline in 10-workflow-example.md, never hand-edited.
|
|
- Standards first. SPDX and CycloneDX for SBOM, in-toto plus SLSA for provenance, Sigstore/cosign for signing, NIST SSDF for practice grouping.
|
|
- Closed by default. A new dependency, an unsigned artifact, or a failing vuln gate blocks the release. Overrides are explicit, logged and expire.
|
|
|
|
## 2. Best practices (from research)
|
|
|
|
1. SLSA provenance is verifiable information about where, when and how an artifact was produced, defined as an in-toto attestation predicate with predicateType `https://slsa.dev/provenance/v1`, where the build platform attests it produced artifacts by executing a buildDefinition (Source: https://slsa.dev/spec/v1.0/provenance, retrieved 2026-10-23; triangulated by https://slsa.dev/spec/v1.0/about, retrieved 2026-10-23).
|
|
2. The in-toto attestation framework is the envelope: a Statement binds subjects (artifact digests) to a predicateType plus predicate, and SLSA provenance is one predicate type inside it (Source: https://github.com/in-toto/attestation, retrieved 2026-10-23; triangulated by SLSA provenance spec above).
|
|
3. SPDX (Linux Foundation, ISO/IEC 5962:2021 for SPDX 2.2.1) and CycloneDX (OWASP, ECMA-424) are the two interoperable machine-readable SBOM formats satisfying the NTIA minimum elements: supplier name, component name, version, other unique identifiers, dependency relationship, SBOM author, timestamp (Source: https://spdx.dev/learn/overview/, retrieved 2026-10-23; Source: https://cyclonedx.org/specification/overview/, retrieved 2026-10-23; NTIA element list triangulated by https://github.com/spdx/ntia-conformance-checker, retrieved 2026-10-23). MoloApp emits SPDX JSON as canonical and CycloneDX JSON as secondary.
|
|
4. Sigstore cosign keyless blob signing (`cosign sign-blob` without `--key`) obtains a short-lived certificate from Fulcio via OIDC identity and records the signature in the Rekor transparency log, producing a `.sigstore.json` bundle verified against the expected signer identity (`--certificate-identity`) and OIDC issuer (Source: https://docs.sigstore.dev/cosign/signing/signing_with_blobs/, retrieved 2026-10-23; triangulated by https://docs.sigstore.dev/about/overview/, retrieved 2026-10-23).
|
|
5. NIST SP 800-218 v1.1 (SSDF, Feb 2022) defines outcome-based secure development practices in four groups: Prepare the Organization (PO), Protect the Software (PS), Produce Well-Secured Software (PW), Respond to Vulnerabilities (RV); it mandates outcomes, not tools (Source: https://csrc.nist.gov/pubs/sp/800/218/final, retrieved 2026-10-23).
|
|
6. Triangulated release rule from (1)+(4)+(5): a trustworthy release needs all three of SBOM (PW.4.1 lineage), signed provenance from the build platform (PS.3, SLSA Build L2+), and a vulnerability response hook (RV) that blocks or discloses known CVEs before ship. Missing any one fails the gate.
|
|
|
|
## 3. Structure
|
|
|
|
Directories (one class or concern per file, deep over flat):
|
|
|
|
```
|
|
tools/supply/ release-time only, never linked into the framework
|
|
SbomGenerator.hpp/.cpp molo::supply::sbom::Generator (inventory assembly)
|
|
SpdxEmitter.hpp/.cpp molo::supply::sbom::SpdxEmitter (SPDX 2.3 JSON)
|
|
CycloneDxEmitter.hpp/.cpp molo::supply::sbom::CycloneDxEmitter (CycloneDX 1.6 JSON)
|
|
SbomCheck.hpp/.cpp molo::supply::sbom::ConformanceCheck (NTIA elements)
|
|
include/molo/supply/
|
|
src/supply/
|
|
policy/DependencyPolicy.hpp molo::supply::policy::DependencyPolicy (allow-list)
|
|
policy/WolfPin.hpp/.cpp molo::supply::policy::WolfPin (version, digest, license receipt)
|
|
attest/ProvenanceBuilder.hpp molo::supply::attest::ProvenanceBuilder (SLSA v1 predicate)
|
|
attest/Attestor.hpp/.cpp molo::supply::attest::Attestor (in-toto Statement envelope)
|
|
attest/Signer.hpp/.cpp molo::supply::attest::Signer (cosign wrapper, keyless first)
|
|
attest/Verify.hpp/.cpp molo::supply::attest::Verifier (bundle + digest verify)
|
|
scan/VulnHook.hpp/.cpp molo::supply::scan::VulnHook (SBOM in, verdict out)
|
|
scan/OsdvClient.hpp/.cpp molo::supply::scan::OsvClient (OSV API query, capped)
|
|
sbom/ generated per release, committed as tag asset only
|
|
specs/supply.md normative spec mirror (owned by 10-workflow-example.md template)
|
|
```
|
|
|
|
Namespaces: `molo::supply::sbom`, `::policy`, `::attest`, `::scan`. No other namespace signs, attests or scans. Signing keys never enter `src/`; only digests and bundle paths cross into `tools/release/` from 10-workflow-example.md.
|
|
|
|
## 4. Rules (hard, numbered)
|
|
|
|
R15-1: wolfSSL is the sole third-party dependency. Any second dependency blocks the build until this spec is amended and the SBOM schema covers it.
|
|
R15-2: Every tagged release ships four artifacts minimum: binary or tarball, `sbom.spdx.json`, `sbom.cdx.json`, `provenance.intoto.jsonl` plus one `.sigstore.json` bundle per signed file. Missing file blocks the tag.
|
|
R15-3: SPDX JSON is canonical and must pass NTIA minimum-element conformance; CycloneDX JSON must describe the same component set (same name/version/digest triples).
|
|
R15-4: Provenance predicateType is exactly `https://slsa.dev/provenance/v1` inside an in-toto Statement; subject digests are SHA-256 over released bytes.
|
|
R15-5: Signing is Sigstore keyless first (OIDC identity of the release workflow); long-lived keys are forbidden unless a human-approved exception with expiry exists.
|
|
R15-6: The vuln-scan hook runs on the final SBOM before signing; a CRITICAL with a fix available blocks release, others attach as disclosed advisories.
|
|
R15-7: wolfSSL is pinned by exact version plus SHA-256 of the vendored tarball plus license receipt path; any mismatch fails the build loudly.
|
|
R15-8: Reproducibility: same git commit plus same toolchain record plus same wolfSSL tarball yields byte-identical SBOM component section (timestamps only in metadata, never in digests).
|
|
R15-9: No network fetch during normal build. The only network allowed is the release pipeline calling OSV, Fulcio and Rekor, each behind timeout, retry cap and offline fail-closed behavior.
|
|
R15-10: Verification is public and offline-capable: `molo verify` checks digests, bundle chain and provenance subject match using only release assets plus Sigstore roots.
|
|
R15-11: All caps in section 6 are static constants. No silent growth of SBOM size, scan window or signature count.
|
|
R15-12: This file never duplicates sibling scope: no TLS config (03-security.md), no git/tag mechanics (10-workflow-example.md), no CLI UX (09-cli-ops.md), no audit log schema (12-audit-compliance.md). It provides SBOM, policy verdicts, provenance and bundles those siblings consume.
|
|
|
|
## 5. Development guidelines
|
|
|
|
- Build order follows section 7: policy pin first, then SBOM, then scan hook, then provenance, then signing, then verify. Each step lands with its spec delta and template output checked in as golden files.
|
|
- Generators are pure functions over (git commit, toolchain file, wolfSSL pin, file manifest). No timestamps inside hashed content; timestamps live only in SBOM metadata and provenance `buildMetadata.finishedOn`.
|
|
- Treat the SBOM as code: diff it in review, fail CI on unexpected component delta, keep golden SBOMs for the last three releases in `sbom/golden/`.
|
|
- Wrap external CLIs (cosign) behind `Signer` with a fake for tests; never shell out from framework code, only from `tools/supply/`.
|
|
- Log every policy decision with reason code (allow, pin-mismatch, vuln-block, exception-expired) through 02-observability.md; denials are loud, never silent skips.
|
|
- License hygiene: wolfSSL commercial license receipt path is recorded in the SBOM supplier field and provenance materials; receipt missing blocks release.
|
|
|
|
## 6. Specifications (concrete behaviors, caps, formats)
|
|
|
|
Dependency policy:
|
|
|
|
- Allow-list file `supply.allow.json`: exactly one entry `{name: wolfssl, version: X.Y.Z, sha256: hex, source: vendored-tarball-url-or-path, license: commercial, receipt: path}`. Unknown entry or version drift fails `make supply-check`.
|
|
- Toolchain record `supply.toolchain.json`: compiler id plus version, linker, CMake or Make version, build flags hash. Max 100 entries, max 64 KB file.
|
|
|
|
SBOM:
|
|
|
|
- Formats: SPDX 2.3 JSON (`sbom.spdx.json`) canonical, CycloneDX 1.6 JSON (`sbom.cdx.json`) secondary. Both UTF-8, LF, sorted keys, 2-space indent.
|
|
- Components: `moloapp` (supplier MoloApp, version from VERSION, id git commit), `wolfssl` (supplier wolfSSL Inc, pinned version, externalRefs: url plus purl `pkg:generic/wolfssl@X.Y.Z`), `toolchain` (supplier recorded, version string). Each carries SHA-256 checksum, license declared plus concluded, downloadLocation or NOASSERTION with justification.
|
|
- NTIA fields mandatory on every component: supplier, name, version, unique id (SPDXID plus purl/CPE where known), relationship (DEPENDS_ON from moloapp), author (`MoloApp release pipeline`), timestamp (RFC 3339 UTC).
|
|
- Caps: max 64 components, max 256 relationships, SBOM JSON max 1 MB per format, component name max 256 chars, version max 64 chars.
|
|
|
|
Vuln-scan hook:
|
|
|
|
- Input: canonical SPDX JSON. Query OSV API `https://api.osv.dev/v1/query` per component (purl plus version), timeout 10 s per request, max 64 queries per release, 1 retry, offline yields `unknown` verdict which blocks signing until a human override with 7-day expiry.
|
|
- Verdicts: `clean`, `disclosed` (non-critical or no fix, attached as `advisories.json`), `blocked` (critical CVSS >= 9.0 with fix, or wolfSSL CVE with fix regardless of score). Blocked stops the pipeline before provenance generation.
|
|
|
|
Provenance and signing:
|
|
|
|
- Provenance: in-toto Statement v1, `predicateType: https://slsa.dev/provenance/v1`, subjects are `{name, digest: {sha256: hex}}` per released file, `buildDefinition.buildType` is `https://moloapp.dev/build/v1`, `externalParameters` holds git commit, toolchain hash, wolfSSL pin digest; `runDetails.builder.id` is the release workflow identity URI; `runDetails.metadata.invocationId` is the pipeline run id.
|
|
- Signing: `cosign sign-blob --yes --bundle <file>.sigstore.json <file>` per artifact; verify with `cosign verify-blob --bundle --certificate-identity <workflow-id> --certificate-oidc-issuer https://token.actions.githubusercontent.com <file>`. SHA-256SUMS file signed as one blob additionally.
|
|
- Layout per tag `vX.Y.Z`: `molo-X.Y.Z.tar.gz`, `SHA256SUMS`, `sbom.spdx.json`, `sbom.cdx.json`, `advisories.json`, `provenance.intoto.jsonl`, plus `.sigstore.json` per file. Max 32 signed files per release.
|
|
- `molo verify <dir>`: recompute SHA-256, check bundle chain, check provenance subjects match digests, check SBOM component digests match wolfSSL pin; exit 0 verified, 1 mismatch (first mismatch quoted), 2 usage error. No network required.
|
|
|
|
## 7. Execution steps (in build order)
|
|
|
|
E15-1: DependencyPolicy plus WolfPin: allow-list schema, pin check in `make supply-check`, golden fixtures (good pin, drifted version, bad digest).
|
|
E15-2: SbomGenerator plus SpdxEmitter: inventory assembly, canonical JSON, sorted output, NTIA conformance self-check.
|
|
E15-3: CycloneDxEmitter plus equivalence test: same triples as SPDX, golden pair diff in CI.
|
|
E15-4: VulnHook plus OsvClient: query, verdict mapping, advisories.json writer, offline fail-closed path.
|
|
E15-5: ProvenanceBuilder plus Attestor: SLSA v1 predicate, in-toto Statement envelope, subject digest binding.
|
|
E15-6: Signer plus Verifier: cosign keyless wrapper, bundle layout, offline `molo verify` command core.
|
|
E15-7: Pipeline wiring into 10-workflow-example.md release order (suite green, SBOM, scan, provenance, sign, tag) and 09-cli-ops.md `molo release` surface.
|
|
E15-8: Hardening: golden SBOMs for three releases, tamper drills (bit-flip, pin drift, expired exception), docs and living-example supply page.
|
|
|
|
## 8. Validation (how to verify each step)
|
|
|
|
V15-1: Pin fixtures: correct tarball passes, one-byte change fails with digest quoted, version bump without allow-list edit fails, missing license receipt fails.
|
|
V15-2: SPDX golden test: regenerate twice, byte-identical except timestamp; NTIA checker passes; component count is exactly 3 (moloapp, wolfssl, toolchain) on a clean tree.
|
|
V15-3: Equivalence test: extract (name, version, sha256) triples from both SBOMs, sets equal; CycloneDX validates against its JSON schema.
|
|
V15-4: Scan matrix: clean SBOM yields clean; fixture with known-fixed critical yields blocked; fixture with no-fix advisory yields disclosed plus advisories.json entry; network cut yields unknown and signing refused.
|
|
V15-5: Provenance test: subjects match `sha256sum` of release files; predicateType exact; builder id equals workflow identity; tampered artifact fails subject match.
|
|
V15-6: Sign and verify round-trip against local Sigstore stub plus one manual live run: valid bundle verifies, wrong identity fails, bit-flipped file fails, offline verify works with cached roots.
|
|
V15-7: End-to-end dry run on fixture repo: full order executes, a blocked verdict stops before signing, a green run produces all files in the layout with caps respected.
|
|
V15-8: Drill suite: expired exception blocks, oversized SBOM (>1 MB) rejected, 33rd signed file rejected, living-example supply page shows SBOM plus verify instructions on a small-phone viewport.
|
|
|
|
## 9. References (sibling files this depends on or feeds)
|
|
|
|
- Depends on 03-security.md: wolfSSL version and TLS posture, hash and RNG primitives for digests, secret redaction in SBOM and provenance.
|
|
- Depends on 09-cli-ops.md: `molo release`, `molo verify`, `molo version` UX, embedder manifest hash reuse, exit-code mapping.
|
|
- Depends on 10-workflow-example.md: release pipeline order, tag gating, VERSION and CHANGELOG linkage, spec and test templates.
|
|
- Depends on 02-observability.md: structured logs and metrics for policy verdicts and scan outcomes.
|
|
- Feeds 12-audit-compliance.md: SBOM, provenance, bundles and advisories as audit evidence; retention pointers.
|
|
- Feeds 14-api-governance.md: SBOM and provenance as governed release contracts (formats versioned, breaking changes major).
|
|
- Feeds 11-tenancy.md and 13-operability.md: per-tenant build identity inputs and SBOM exchange notes (formats defined here, transport there).
|
|
- Never duplicates: 10-workflow-example.md owns git mechanics, 09-cli-ops.md owns CLI flags, 12-audit-compliance.md owns audit retention.
|
|
|
|
## 10. Open questions
|
|
|
|
Q1: SLSA Build L3 (hardened builder, non-falsifiable provenance) in v1 or L2 first with a documented L3 path? Proposal: L2 now, L3 tracked as gap.
|
|
Q2: OSV only, or OSV plus a second feed (wolfSSL security list, CISA KEV) for the vuln hook? Proposal: OSV plus wolfSSL advisory check, KEV later.
|
|
Q3: Rekor transparency proof required at verify time, or bundle signature plus identity sufficient for v1? Proposal: bundle sufficient, checkpoint proof optional flag.
|
|
Q4: SPDX 3.0 migration timeline? Proposal: stay on SPDX 2.3 JSON until tooling converges, track 3.0 as gap.
|
|
|
|
## 11. Sources (exact URLs with retrieval dates)
|
|
|
|
- SLSA Provenance specification v1.0 (predicateType, model, purpose): https://slsa.dev/spec/v1.0/provenance (retrieved 2026-10-23)
|
|
- SLSA About page (SSDF link, supply-chain rationale): https://slsa.dev/spec/v1.0/about (retrieved 2026-10-23)
|
|
- in-toto Attestation Framework repository (Statement, predicate model): https://github.com/in-toto/attestation (retrieved 2026-10-23)
|
|
- SPDX Project overview (formats, ISO standard): https://spdx.dev/learn/overview/ (retrieved 2026-10-23)
|
|
- CycloneDX Specification Overview (object model, ECMA-424): https://cyclonedx.org/specification/overview/ (retrieved 2026-10-23)
|
|
- Sigstore cosign Signing Blobs (keyless sign-blob, bundles): https://docs.sigstore.dev/cosign/signing/signing_with_blobs/ (retrieved 2026-10-23)
|
|
- NIST SP 800-218 SSDF v1.1 project page (PO, PS, PW, RV practice groups): https://csrc.nist.gov/pubs/sp/800/218/final (retrieved 2026-10-23)
|