Skip to content

Bump golang.org/x/mod to v0.40.0 for CVE-2026-56864 and CVE-2026-56865 - #797

Merged
jeremy merged 1 commit into
mainfrom
wt/lane-xmod
Aug 19, 2026
Merged

Bump golang.org/x/mod to v0.40.0 for CVE-2026-56864 and CVE-2026-56865#797
jeremy merged 1 commit into
mainfrom
wt/lane-xmod

Conversation

@jeremy

@jeremy jeremy commented Aug 19, 2026

Copy link
Copy Markdown
Member

Trivy (Go SDK) fails on golang.org/x/mod v0.38.0, which go/go.mod carries as
an indirect dependency. This bumps it to v0.40.0. Two manifest lines and their
go.sum/go.work.sum hashes; nothing else moves.

The two advisories, and why v0.39.0 is not enough

CVE Go advisory Vulnerable package Fixed in
GOSUMDB serves content outside the transparency log CVE-2026-56864 GO-2026-6180 golang.org/x/mod/sumdb (Client.Lookup) v0.40.0
GOPROXY forges up to two sumdb tiles, bypassing GOSUMDB CVE-2026-56865 GO-2026-6179 golang.org/x/mod/sumdb/tlog (tileHashReader.ReadHashes) v0.40.0

Source: the OSV API (https://api.osv.dev/v1/vulns/GO-2026-6179, …/GO-2026-6180).
Both records give golang.org/x/mod a single affected range of [0, 0.40.0)
introduced at 0, fixed at 0.40.0. Trivy independently agrees: its failing run
prints 0.40.0 in the Fixed Version column for both.

So the smaller move is not available. v0.39.0 carries neither fix and would
leave the gate red; v0.40.0 is the first version that clears both.

Reachability: code-generation toolchain only, and not even there

Worth stating plainly, because the CVSS reads alarming and the actual exposure is
narrow in two independent ways.

1. It is not in the published module's runtime surface. golang.org/x/mod
enters the graph solely through the tool directive at the bottom of go/go.mod:

$ go mod why -m golang.org/x/mod
github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen
github.com/oapi-codegen/oapi-codegen/v2/pkg/codegen
golang.org/x/mod/modfile

go list -deps ./... — the packages consumers actually compile — returns no
x/mod package at all. Nothing an SDK consumer imports links it.

2. The vulnerable packages are not imported by anything, including the
generator.
Both advisories are scoped to sumdb and sumdb/tlog. The whole
module graph resolves exactly four x/mod packages:

$ go list -deps all | grep golang.org/x/mod
golang.org/x/mod/internal/lazyregexp
golang.org/x/mod/semver
golang.org/x/mod/module
golang.org/x/mod/modfile

$ go list -deps github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen | grep -c sumdb
0

modfile parses go.mod files; the sumdb client is a separate package that
nothing here links. The threat model behind these CVEs — a malicious GOPROXY or
GOSUMDB tampering with module fetches — is cmd/go's, and it is addressed for us
by the toolchain, not by this dependency.

This is therefore a manifest-hygiene fix, not an exploitable-path fix. It is
still worth landing: the gate is a merge blocker for five open PRs, and arguing
reachability at every scan is more expensive than moving one line.

Why govulncheck and Trivy disagree, and which one to believe here

govulncheck ./... is byte-identical before and after this change — I diffed the
two runs and they match exactly. It never reported these CVEs in the first place,
so it cannot confirm their removal:

$ diff <(grep -v '^go: downloading' govuln-before.log) \
       <(grep -v '^go: downloading' govuln-after.log) && echo IDENTICAL
IDENTICAL

Both runs report the same 4 stdlib findings from the local go1.26.5 toolchain
(all fixed in go1.26.6, all unrelated to this change), plus "2 vulnerabilities
in modules you require, but your code doesn't appear to call these" — and even
those two are stdlib, per -show verbose. Scanning the generator's own call graph
(govulncheck github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen) likewise
reports nothing from x/mod.

That is not govulncheck failing; it is govulncheck being right. It is call-graph
aware, so an unreachable sumdb is correctly silent — which is the same finding
as the section above, arrived at by a second instrument. Trivy's fs scan reads
go.mod as a manifest with no reachability analysis, so it flags every module
version in the graph. Trivy is the gate, so Trivy is the check that has to go
green
, and govulncheck is reported here only so the "before/after" is not
mistaken for evidence it cannot supply.

Trivy, before and after

A local green proves nothing unless the same DB is shown to catch the bug, so
here is the matched pair against one freshly downloaded DB (UpdatedAt 2026-08-19T13:00:00Z), with the base manifest taken from origin/main:

BASE (origin/main go.mod)                    REAL_EXIT=1
Total: 2 (HIGH: 2, CRITICAL: 0)
golang.org/x/mod  CVE-2026-56864  HIGH  fixed  v0.38.0  0.40.0
                  CVE-2026-56865

THIS BRANCH                                  REAL_EXIT=0
go.mod  gomod  0

Zero generated churn

The repo's pattern is to bump the leaf tool-dep rather than the oapi-codegen
generator, precisely so no emitted file moves. It holds here:

$ git status --porcelain
 M go.work.sum
 M go/go.mod
 M go/go.sum

Confirmed more strongly than by git status alone — make go-check-generated-drift
regenerates client.gen.go into a temp directory under the new toolchain and
diffs it: no drift. The generator emits identical bytes.

Two notes on the diff being three files rather than two:

  • x/tools v0.48.0 → v0.49.0 is MVS-forced, not a second bump. go mod graph
    has the edge golang.org/x/mod@v0.40.0 golang.org/x/tools@v0.49.0; x/mod v0.40.0
    requires it, so the selection is not optional.

  • go.work.sum is a real consequence, not scan residue. I checked by reverting
    to pristine origin/main and running make go-check: the tree stayed clean. Under
    the bump the same command rewrites go.work.sum, so it is committed —
    scripts/assert-lockfiles-unchanged covers go.work.sum and would otherwise fail
    the first CI job to build Go.

    A warm module cache hid one line of that, and CI caught it. The first push
    committed a go.work.sum generated on this machine, where make go-check
    converged across two runs. CI's Go Tests job still failed the guard: on a cold
    cache the generated-client drift check needs golang.org/x/text v0.41.0 h1:
    (x/tools v0.49.0 pulls x/text v0.41.0, and re-emitting the client links it),
    while a warm cache had it already extracted and never recorded the hash. Rerunning
    every step of that job under an empty GOMODCACHE reproduced the failure exactly,
    and the amended commit passes the same cold sequence with
    Dependency manifests match the committed tree. Worth naming rather than quietly
    amending: local convergence is not a cold-cache proof, and the guard is what
    distinguished them.

go.sum is tidied — origin/main is tidy (go mod tidy on it is a no-op), so
this keeps that property rather than leaving superseded h1: lines behind.

Why this is its own PR: four green Security checks that are stale, not clean

The advisories were published 2026-08-13 but only entered Trivy's DB on
2026-08-19, which puts a hard line through that day's CI:

PR Security run Result
#790 2026-08-19 06:07Z ✅ success
#791 2026-08-19 06:20Z ✅ success
#793 2026-08-19 06:29Z ✅ success
#796 2026-08-19 06:43Z ✅ success
#794 2026-08-19 17:40Z failure

Nothing about #794 introduced this. Its run is simply the first one on the far
side of the DB update — the failure names golang.org/x/mod v0.38.0, which every
one of those branches carries identically from main.

The four greens are stale, not clean, and will go red on any re-run. I can
narrow the window further from an accident: my own machine's Trivy DB was built
2026-08-19T00:49:40Z, and scanning the unfixed base manifest with it returned
a confident Total: 0. Refetching the DB (built 13:00Z) turned the identical
input red. So the advisory landed in Trivy's DB between 06:43Z and 13:00Z
after all four greens, before #794's red — and a stale DB reproduces the false
green on demand.

Hence a separate, Go-only PR: fixing this inside any one of the five would make
that PR's diff carry an unrelated dependency bump, and would still leave the
other four blocked.

Verification

All gates run with REAL_EXIT=$? written into a log and grepped back, not via
; echo after a pipe.

Gate Result
LC_ALL=C make go-check REAL_EXIT=0
make go-check-drift REAL_EXIT=0 — 250/250 operations, 100%
make go-check-wrapper-drift REAL_EXIT=0 — 92 pairs, 1252 generated fields
make go-check-generated-drift REAL_EXIT=0 — no drift
scripts/assert-lockfiles-unchanged --verify-clean REAL_EXIT=0 (post-commit)
Trivy, fresh DB, this branch REAL_EXIT=0

The API Compatibility job (apidiff -m, added in #776) runs on this PR; a
dependency bump touching no source file should be API-identical, and that job is
the proof rather than my assertion.

Copilot AI balanced review requested due to automatic review settings August 19, 2026 17:55
@github-actions github-actions Bot added the go label Aug 19, 2026

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Bumps Go tooling dependencies to address two x/mod vulnerabilities.

Changes:

  • Updates x/mod to v0.40.0.
  • Transitively updates x/tools and x/net.
  • Refreshes dependency checksums; the CI workspace checksum remains incomplete.

Tip

If you aren't ready for review, convert to a draft PR.
Click "Convert to draft" or run gh pr ready --undo.
Click "Ready for review" or run gh pr ready to reengage.

Reviewed changes

Copilot reviewed 2 out of 3 changed files in this pull request and generated 1 comment.

File Description
go/go.mod Updates Go tooling dependencies.
go/go.sum Refreshes module checksums.
go.work.sum Updates workspace checksums.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread go.work.sum
Trivy (Go SDK) fails on both advisories against the v0.38.0 the module
carries as an indirect dependency. Both are fixed in v0.40.0 — v0.39.0
carries neither fix, so this is the smallest move that clears the gate.

The dependency reaches the graph only through the oapi-codegen tool
directive, and only as golang.org/x/mod/modfile. Neither vulnerable
package is imported anywhere in the module: `go list -deps all` resolves
four x/mod packages — lazyregexp, semver, module, modfile — and sumdb and
sumdb/tlog are not among them.

MVS carries x/tools v0.48.0 -> v0.49.0 along with it, because x/mod
v0.40.0 requires it. No generated file moves; go-check-generated-drift
regenerates the client and diffs clean.

go.work.sum gains the hashes the workspace needs to verify the new
selection. Its x/text v0.41.0 h1: line is only reached on a cold module
cache — the generator needs it when re-emitting the client — so it was
found by rebuilding under an empty GOMODCACHE, the way CI runs.
Copilot AI review requested due to automatic review settings August 19, 2026 18:08

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 3 changed files in this pull request and generated no new comments.

@jeremy
jeremy merged commit 338ec40 into main Aug 19, 2026
46 checks passed
@jeremy
jeremy deleted the wt/lane-xmod branch August 19, 2026 21:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants