ci: hard-fail PR and release scans on unacknowledged HIGH/CRITICAL CVEs (#484)

* ci: hard-fail PR and release scans on unacknowledged HIGH/CRITICAL CVEs

Make Trivy a real gate instead of an advisory signal:

- PR CI (docker-validate) no longer uses `continue-on-error: true` on the
  Trivy step, so any HIGH/CRITICAL finding not in `.trivyignore` fails the PR.
- The release pipeline (docker-publish.yml) now builds an amd64-only scan
  image into the local daemon before the multi-arch push-build, re-runs
  Trivy against that exact artifact, and only proceeds to the push if the
  scan passes. This closes the gap where a CVE landed between PR merge and
  release-time rebuild.
- New `.trivyignore` at repo root is the single source of truth for
  acknowledged CVEs across both workflows; it starts empty so the first CI
  run surfaces the full list, which we then populate with justifications.

* ci: populate .trivyignore with initial HIGH/CRITICAL acknowledgements

First CI run on the hard-fail Trivy policy surfaced 7 unacknowledged
findings. Each has been reviewed and justified inline:

- 6 CVEs in the statically-linked Go modules inside docker-compose v5.1.1
  (github.com/docker/docker, buildkit, otel/sdk x2, grpc). These are
  transitively bundled and cannot be bumped without an upstream Compose
  rebuild. The grpc CVE is already explicitly acknowledged in the
  Dockerfile rationale block at Dockerfile:108-111.
- 1 CVE in picomatch 4.0.3 bundled inside the npm CLI that ships with
  node:22-alpine. npm is only invoked at build time against our own
  package.json, so the ReDoS vector is not reachable.

Every entry has a revisit trigger (next Compose release or next Alpine
node bump).
This commit is contained in:
Anso
2026-04-10 14:01:02 -04:00
committed by GitHub
parent 3b12f2678f
commit fdb476a84a
3 changed files with 123 additions and 3 deletions
+3 -3
View File
@@ -123,15 +123,15 @@ jobs:
cache-to: type=gha,mode=max
- name: Scan image for vulnerabilities (Trivy)
# Hard-fails the PR on any HIGH or CRITICAL finding that is not listed
# in .trivyignore at the repo root. To accept a CVE, add it to that
# file with a human-readable justification comment, not here.
uses: aquasecurity/trivy-action@57a97c7e7821a5776cebc9bb87c984fa69cba8f1 # v0.35.0
with:
image-ref: sencho:pr-test
exit-code: '1'
severity: 'CRITICAL,HIGH'
format: 'table'
# Findings surface as a visible warning on the job without blocking the PR.
# Base-image CVEs are often outside our control - review manually before ignoring.
continue-on-error: true
# ---------------------------------------------------------------------------
# E2E Tests (PRs only, skipped for release-please PRs)
+33
View File
@@ -63,6 +63,39 @@ jobs:
type=semver,pattern={{version}},enable=${{ startsWith(github.ref, 'refs/tags/v') }}
type=semver,pattern={{major}}.{{minor}},enable=${{ startsWith(github.ref, 'refs/tags/v') }}
# Build an amd64-only variant into the local daemon first so Trivy can
# scan the exact release artifact before it is tagged and pushed. This
# keeps vulnerable releases out of the `latest` and semver tags that
# users actually pull. The scan-build inherits layers from the shared
# buildcache so the amd64 leg of the subsequent push-build is mostly a
# cache hit; we intentionally do NOT write `cache-to` here because the
# push-build below writes a strictly better (multi-arch, mode=max)
# cache entry moments later. The tag lives in the `localhost/` namespace
# so a future `push: false` -> `push: true` mistake cannot publish it.
# Scanning only amd64 is a defensible proxy for the multi-arch release:
# distro package CVEs are arch-agnostic at the manifest level, and
# arch-specific container-relevant CVEs are extraordinarily rare.
- name: Build release image for pre-publish scan (amd64, loaded)
uses: docker/build-push-action@v7
with:
context: .
push: false
load: true
platforms: linux/amd64
tags: localhost/sencho:release-scan
cache-from: type=registry,ref=saelix/sencho:buildcache
- name: Re-scan release image for vulnerabilities (Trivy)
# Gates the release on the same HIGH/CRITICAL policy as the PR scan.
# Entries in .trivyignore at the repo root are honored so the acknowledged
# CVE list is the single source of truth across PR CI and release CI.
uses: aquasecurity/trivy-action@57a97c7e7821a5776cebc9bb87c984fa69cba8f1 # v0.35.0
with:
image-ref: localhost/sencho:release-scan
exit-code: '1'
severity: 'CRITICAL,HIGH'
format: 'table'
- name: Build and push Docker image
id: build
uses: docker/build-push-action@v7