Repository navigation
sec(build): runtime base alpine:3.21.3 ships fixable CRITICAL/HIGH OS advisories, blocking an image-level Trivy gate #1842
Description
Surfaced while implementing #1836 (scan the shipped image). Deliberately kept out of that PR, and filed here so it is not lost.
What
The pinned runtime base alpine:3.21.3 carries fixable CRITICAL and HIGH OS-package advisories today. Measured directly:
$ trivy image --severity CRITICAL,HIGH --ignore-unfixed --scanners vuln alpine:3.21.3
libssl3 CVE-2026-31789 CRITICAL heap buffer overflow from large X.509 certificate
libssl3 CVE-2025-15467 HIGH -> 3.3.6-r0
libssl3 CVE-2026-28387 HIGH -> 3.3.7-r0 (+ CVE-2025-69421, CVE-2026-28388/9/90)
musl CVE-2026-40200 HIGH 1.2.5-r9 -> 1.2.5-r11
zlib CVE-2026-22184 HIGH 1.3.1-r2 -> 1.3.2-r0
--ignore-unfixed is in that command, so every one of these has a published fix available.
The libssl3 CRITICAL is the notable one: a heap buffer overflow triggered by a large X.509 certificate, in a service that terminates TLS and connects to databases over TLS.
Why it is filed separately
#1836 adds govulncheck -mode=binary over the Go binaries in the shipped image, which catches the class that actually bit us twice: a stale Go toolchain (#1833) and a prebuilt third-party binary (#1835). It does not cover OS packages.
Trivy scan-type: image would cover them, and was deliberately not added in #1836 for a specific reason: gating on it would land red on day one for a problem that PR does not fix, and a non-gating version would print an unactionable wall on every PR, which is the "looks like coverage" pattern #1836 exists to avoid.
So the ordering is: bump the base, then add the OS-package scan. Doing it the other way round produces a red or ignored check.
Fix direction
- Bump the runtime base to a current
alpine:3.xpatch release that clears the fixable set above, re-pinning by digest. Never drop the@sha256:pin to make the bump easy, and never guess a digest (a real digest went stale mid-flight during sec(build): build the shipped image on go1.26.6 to clear the stdlib CVEs #1835 because the upstream tag was re-pushed; the pin is what caught it). - Re-measure with the same command and confirm the fixable set is empty.
- Then add Trivy
scan-type: imageto CI, gating, alongside the govulncheck binary scan from sec(ci): nothing scans the container image we ship, only source; a baked-in vulnerable binary is invisible by construction #1836.
Verification bar
Both directions. Prove the new scan fails on a known-bad image (for example the current alpine:3.21.3 base) and passes on the bumped one. A scan step that always passes is worse than none, because it reads as coverage.
Allowlist on success: a check written as != "failure" reports success for cancelled and skipped.
Related: #1836 (Go binary scanning in the image), #1833 and #1835 (the two rounds this class of gap hid).
Activity
sec(ci): scan the Go binaries in the shipped image, not just the sour…
cristim commentedon Aug 18, 2026
Reopening: this was closed automatically on 2026-08-18T06:16:22Z by the merge of #1841, whose body contains the sentence "This PR does not resolve #1842." GitHub's closing-keyword parser matches
resolve #1842and ignores the negation, so the issue was closed despite the text saying the opposite. Same failure mode that closed #1837.No alpine base bump has landed.
Dockerfileline 120 on main is stillFROM alpine:3.21.3@sha256:a8560b36e8b8210634f77d9f7f9efd7ffa463e380b75e2e74aff4511df3ef88c. The work described here is still outstanding.sec(ci): scan the Go binaries in the shipped image, not just the sour…