Every one of the 6 GitHub Actions pipelines now builds a local, unpushed image first, scans it with Trivy for `CRITICAL` severity CVEs, and only proceeds to the real multi-platform build-and-push if it's clean. An image with a critical, fixable vulnerability now never reaches Docker Hub at all — a true gate, not a post-push alert Image Updater could still pick up.
Vulnerability scanning in CI is cheap to add and a genuine DevSecOps signal worth having regardless — but it stopped being theoretical the moment it ran for real. On its very first pass, the gate found actual, fixable CRITICAL CVEs in 4 of the 6 apps.
`aquasecurity/trivy-action` scans a locally-built `amd64` image (`ignore-unfixed: true`, so an unpatchable CVE can't permanently block deploys) before the shared build cache is reused for the real push — the scan step only costs the marginal work Docker's cache doesn't already cover. Getting the action pinned took two tries: GitHub Actions tags need the `v` prefix, and the next attempt (`v0.28.0`) turned out to depend internally on a `setup-trivy` tag that no longer exists on that repo — a stale release with a broken transitive dependency. `v0.36.0` fixed both. What it caught, fixed the same day: - **Apercu, BotWhy, and x's frontends** all shared a stale `nginx:1.21` (Debian 11, 2021) base image — 22 CRITICAL CVEs across `curl`, `libxml2`, `libgnutls30`, and more. Fixed by moving to `nginx:1.30.4-alpine`, which also shrinks the CVE surface for this whole class of issue going forward. - **PopRoom's backend** had 3 CRITICAL CVEs in `perl-base` on `python:3.12-slim` — patched upstream in Debian but not yet baked into Docker Hub's current build of that tag. Fixed with an explicit `apt-get upgrade` in the Dockerfile to pull the already-patched packages at build time. - **The sacenpapier hub itself** had the most serious finding: a CRITICAL unauthenticated RCE in Next.js 16.2.1, fixed in 16.3.3. A second flagged CVE in `tar` turned out to live inside `node:22-alpine`'s global npm install, which the standalone runner stage never actually calls — removing `npm`/`npx` from that stage entirely closed it and shrank the image.
`x`'s backend had its own finding — a `python-jose` CVE affecting JWT handling, [written up separately](/updates/2026-09-14-python-jose-cve-fix) since auth-path fixes deserve their own scrutiny. All 6 repos are now confirmed green under the new gate.