Skip to content

fix(security): patch container image CVEs and osv-scanner exit handling - #288

Merged
santosr2 merged 3 commits into
mainfrom
fix/container-scan-findings
Sep 3, 2026
Merged

fix(security): patch container image CVEs and osv-scanner exit handling#288
santosr2 merged 3 commits into
mainfrom
fix/container-scan-findings

Conversation

@santosr2

@santosr2 santosr2 commented Sep 3, 2026

Copy link
Copy Markdown
Owner

What and why

Clears the container vulnerability findings reported on main and fixes two scanner-reporting bugs that were distorting what those findings meant.

A local Trivy scan of the rebuilt image takes the count from 68 to 3, across three root causes:

Cause Alerts Fix
Binary compiled against Go stdlib v1.25.0 46 build the scanned binary on the release toolchain
golang.org/x/crypto v0.53.0 (CVE-2026-56854, critical) 2 bump to v0.55.0
Alpine openssl 3.5.7-r0 (libssl3 + libcrypto3) 20 apk upgrade during image build

Why: the 46 stdlib findings came from the container test building with go-version-file: go.mod. That directive is the minimum supported Go and setup-go installs it exactly, so the scan was reading a stdlib version that no released artifact ever carries — releases already build on 1.26.x. The scan was measuring the wrong binary. The openssl findings are different: alpine:3.24.1 is the newest published tag and its digest already matches what is pinned, so no base-image bump can fix them; the patched package exists in the v3.24 repo but only arrives via an explicit upgrade.

Note that the stdlib fix is the build-toolchain change, not a go.mod floor bump. Raising the floor was tried and reverted: setup-go pins GOTOOLCHAIN=local and resolves 1.25.x to whatever patch a given runner image carries (macOS gave 1.25.12), so any floor above that fails the matrix with go.mod requires go >= X (running go Y; GOTOOLCHAIN=local) and cannot be satisfied by a toolchain download. The floor stays at 1.25.0.

Separately, the Security workflow's osv-scanner step used a blanket continue-on-error: true. osv-scanner exits 1 when it scans successfully and finds vulnerabilities, so the job went green while GitHub still recorded a red "Process completed with exit code 1" annotation. Worse, the blanket suppression also swallowed 127 (general error) and 128 (no packages found), so a moved lockfile would have been indistinguishable from a clean scan.

How to test

mise run check          # fmt + vet + lint + test — passes, 0 failures, 0 lint issues
mise run docker:build

# stdlib now current, was go1.25.0
go version -m bin/terratidy | head -1

# openssl now 3.5.8-r0, was 3.5.7-r0
docker run --rm --entrypoint sh terratidy-dev -c 'apk list --installed 2>/dev/null | grep -E "^lib(ssl|crypto)3"'

# 3 findings remain, all unreachable
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock aquasec/trivy:latest \
  image --scanners vuln terratidy-dev:latest

zizmor passes on both changed workflows. The exit-code branching was exercised against 0, 1, 127 and 128 under bash -e: 0 and 1 pass the step, 127 and 128 fail it.

Notes for reviewers

Why x/crypto stops at v0.55.0. v0.56.0 would clear two more advisories, but it declares go 1.26.0, which forces the module floor to 1.26 and drops 1.25.x from the test matrix. That is a support-policy decision rather than a security one, so it is left out of this PR. Both remaining advisories are in golang.org/x/crypto/ssh, which this module does not import.

The 3 remaining findings are all unreachable. govulncheck reports 0 vulnerabilities in called code; go mod why confirms neither x/crypto/ssh nor x/crypto/openpgp is needed by the main module. GO-2026-5932 (openpgp is unmaintained) has no fix version in any release and is a dismissal candidate. Note that the two ssh advisories also affected v0.53.0 — they were already latent and simply had not reached the Security tab yet, so they are not a regression introduced here.

apk upgrade has a cost. Image contents now track the build date rather than the pinned digest alone. The binary reproducibility check is unaffected since it only covers the Go build.

Not addressed here. release.yml carries the same inert severity: 'CRITICAL,HIGH' filter, but its scan uploads under refs/tags/* where the default-branch Security view never shows it. Fixing that properly means changing its ref and category handling, which is a separate change. The 4 Scorecard alerts are repo-settings and process items (branch protection, OpenSSF badge, approved-changeset ratio) and cannot be fixed from a PR — worth a look is Scorecard's "no status checks found to merge onto branch 'main'", which suggests required status checks may not be configured on the branch rule.

No tests added. The change is dependency versions, a base-image build step, and CI configuration; there is no new code path to cover. Verification is the Trivy scan above, which is what CI runs.

Checklist

  • I have read the CONTRIBUTING guidelines
  • My code follows the project's code style
  • I have added tests that prove my fix/feature works — n/a, see notes above
  • All checks pass (mise run check runs fmt, vet, lint, and test)
  • I have updated documentation as needed — no doc references to the changed versions; CLAUDE.md's "Go 1.25+" remains accurate
  • My commits follow Conventional Commits

🤖 Generated with Claude Code

@codecov

codecov Bot commented Sep 3, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

santosr2 and others added 3 commits September 3, 2026 22:30
Take golang.org/x/crypto to v0.55.0 for CVE-2026-56854, and raise the
dev toolchain to 1.26.8 so local builds compile against a stdlib with
the current patches rather than one several releases behind.

The base image is pinned by digest, so its package set is frozen while
security patches keep landing in the Alpine v3.24 repo. Upgrade packages
explicitly during the build to pick up openssl 3.5.8-r0.

x/crypto stops at v0.55.0 rather than v0.56.0 on purpose: v0.56.0
requires Go 1.26, which would drop 1.25 from the support matrix. The two
advisories that leaves are both in x/crypto/ssh, which this module does
not import.

The go.mod floor stays at 1.25.0 deliberately. Raising it to a current
patch would be satisfied by neither the preinstalled Go on the runners
nor a toolchain download, because setup-go pins GOTOOLCHAIN=local and
resolves 1.25.x to whatever patch that runner image happens to carry.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
The scan reads the stdlib version stamped into the binary, but the build
used go.mod's floor, so it reported CVEs against a Go patch release that
never ships. Build on the release toolchain instead, with check-latest so
new stdlib patches need no manual bump.

Also drop the severity filter: it does not apply to SARIF output unless
limit-severities-for-sarif is set, so it read as if it were narrowing the
upload while doing nothing.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Exit 1 means the scan succeeded and found vulnerabilities, which should
not fail the job since the SARIF upload is how those surface. A blanket
continue-on-error also swallowed 127 and 128, so a moved lockfile would
have looked exactly like a clean scan while the upload failed on a
missing file. Handle the codes separately.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
@santosr2
santosr2 force-pushed the fix/container-scan-findings branch from b40a253 to 8f801df Compare September 3, 2026 22:30
@santosr2
santosr2 merged commit e182bc4 into main Sep 3, 2026
55 of 56 checks passed
@santosr2
santosr2 deleted the fix/container-scan-findings branch September 3, 2026 23:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant