Supply-Chain Gates (Phase 8 · Block A)
OmniRoute publishes npm + Docker artifacts. These gates provide provenance, inventory (SBOM) and CVE scanning, all OSS, plugged into release workflows. Advisory-first posture — they report now, promote to blocking after the 1st green release.
| Gate | Tool | Where | Blocks? | Output |
|---|---|---|---|---|
| SLSA provenance (npm) | npm --provenance (OIDC) | npm-publish.yml | only if publish fails | badge npmjs / npm audit signatures |
| SBOM npm | @cyclonedx/cyclonedx-npm | npm-publish.yml | only if generation fails | Release asset + artifact |
| SBOM image | anchore/sbom-action (syft) | docker-publish.yml (merge) | advisory | CycloneDX artifact |
| Trivy CVE (SARIF) | aquasecurity/trivy-action | docker-publish.yml (merge) | advisory | SARIF (HIGH+CRITICAL) → Security tab |
| Trivy CRITICAL gate | aquasecurity/trivy-action | docker-publish.yml (merge) | blocking | exit-code: '1' on fixable CRITICAL |
| osv vulnCount | osv-scanner (check:vuln-ratchet --ratchet) | ci.yml (quality-extended) | blocking | ratchets metrics.vulnCount (direction:down) |
| OpenSSF Scorecard | ossf/scorecard-action | scorecard.yml (cron) | advisory | SARIF → Security + badge |
The image CVE ratchet uses two steps in docker-publish.yml: the SARIF step
(HIGH,CRITICAL, exit-code: 0) keeps HIGH+CRITICAL visible in the Security tab
without blocking; the CRITICAL gate step (severity: CRITICAL, ignore-unfixed: true,
exit-code: 1) fails the release on a CRITICAL CVE with a fix available. ignore-unfixed
prevents blocking the release for a base-image CVE without an upstream patch.
⚠️ CVE Variance (blocking osv/Trivy gates)
osv and Trivy compare deps against CVE databases that continuously grow. A PR
that touches no dependencies can suddenly turn red because a new CVE was
disclosed in an existing dep (osv: measured vulnCount > baseline; Trivy: a new
fixable CRITICAL in the image). This is EXPECTED operational behavior of a blocking
CVE gate, not a product regression.
When osv or Trivy go red due to a newly disclosed CVE, the remedy is:
- Bump the affected dep (preferred) — upgrade to the patched version via
package.jsonoverrides(transitive deps) or rebuild the image on a patched base. - If there is no upstream fix:
- osv: re-baseline
metrics.vulnCountinconfig/quality/quality-baseline.json(npm run quality:ratchet -- --updatedoes not cover dedicated gates — edit the value by hand,direction:down) with a justification note + tracking issue. - Trivy: add an entry in
.trivyignore(CVE-ID per line) with a justification comment + tracking issue.ignore-unfixed: truealready covers CVEs without patches automatically.
- osv: re-baseline
Both gates gracefully SKIP (exit 0) when the tool is absent or the measurement fails (osv-scanner not in PATH, osv.dev/network unreachable, invalid JSON) — a measurement failure never blocks, only a measured regression blocks.
Backlog: Scorecard advisory → blocking
After the 1st green release with Scorecard reporting:
- Scorecard: score ratchet (freezes the measured score; cannot decrease).
Complements the Phase 7 gates (osv-scanner, gitleaks, actionlint+zizmor): zizmor audits the workflows themselves; Scorecard measures the repo posture in aggregate.