Repository navigation
fix(ci): bump pinned Go toolchain to 1.26.6 for stdlib CVE fixes - #1832
Conversation
govulncheck started failing repo-wide on 7 Go standard library advisories, all reported as present in go1.26.5 and fixed in go1.26.6: GO-2026-6218 (net/url), GO-2026-6091 (html/template), GO-2026-6090 (crypto/tls), GO-2026-6089 (net/http), GO-2026-6088 (encoding/xml), GO-2026-5972 (encoding/asn1) and GO-2026-5026 (net/http). The failure is time-based rather than change-based: govulncheck fetches the vulnerability database at run time, so the same commit that passed before these advisories were published now fails. With CI Success gated on the Security Scanning job, no PR could satisfy the merge gate. Bump the pinned toolchain in both workflows that pin it explicitly. pre-commit.yml carried the same 1.26.5 pin in a double-quoted form, so it needs the same bump to keep both workflows on one toolchain. The go directives in the six go.mod files are deliberately left at 1.26.5 / 1.25. The directive is a minimum, and setup-go installing 1.26.6 supplies the 1.26.6 standard library regardless. Verified locally against the CI-pinned tool versions on a real go1.26.6 toolchain: govulncheck v1.1.4 reproduces all 7 advisories on 1.26.5 (exit 3) and reports every one of the six modules clean on 1.26.6 (exit 0). golangci-lint v2.10.1, go vet, gosec v2.28.0 and go build all pass on the newer toolchain. Closes #1829
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (8)
📝 WalkthroughWalkthroughThe repository updates Go from 1.26.5 to 1.26.6 across module requirements, pinned container images, and CI workflows. ChangesGo toolchain update
Estimated code review effort: 1 (Trivial) | ~2 minutes Mergeability Score: ⚪ Minimal · up to This localized toolchain-pin update has no actionable merge-blocking risk remaining and is merge-ready after normal checks and review. Possibly related issues
Possibly related PRs
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
|
@coderabbitai full review Context for the review: this is a 3-line CI unblock. Scope is deliberately narrow: Two known gaps, both recorded rather than hidden: the shipped container image still builds on go1.26.5 ( Requesting a full review rather than an incremental one because the earlier attempt on this PR was rate-limited. |
|
🧠 Learnings used✅ Action performedFull review finished. |
d2f009e to
6f70742
Compare
6f70742 to
d2f009e
Compare
…dcoded pin pre-commit.yml was the last workflow carrying a literal `go-version`. aws_sanity, azure_sanity and database-migration already use `go-version-file: go.mod`, so this removes one of the independent places the Go version had to be bumped by hand. That arrangement is what produced issue #1833: #1832 bumped some of them and CI went green while the shipped artifact stayed on the old toolchain. The root go.mod `go` directive is 1.26.6, so the resolved version is unchanged by this commit.
…e binary (#1833) #1832 bumped the toolchain CI reads, which turned govulncheck green while the image we deploy was still built on go1.26.5. This bumps what actually ships. The base image moves to golang:1.26.6-alpine3.24, digest re-resolved and re-pinned (the 1.26.6-alpine3.24 tag was re-pushed on 2026-08-16 onto a rebuilt image, so an earlier digest had gone stale; the pin is what caught that). The go directives in go.work and all five module files move to 1.26.6, which also fixes the workflows that resolve their toolchain via go-version-file rather than GO_VERSION. The image shipped two Go binaries and the base bump only fixed one. /usr/local/bin/migrate was an upstream prebuilt release carrying go1.25.4, with all seven advisories from this issue plus 62 more, and entrypoint.sh runs it against the database on every container start with DB_AUTO_MIGRATE=true by default. The vulnerable symbols included URL parsing and the TLS handshake, i.e. the paths that reach the database. It is now built from source on the pinned toolchain, the way the Makefile already did. Verified by reading the built artifact rather than inferring from the tag: on both arm64 and amd64, /app/cudly and /usr/local/bin/migrate both report go1.26.6, and both report zero standard-library findings under govulncheck. migrate goes from 69 vulnerabilities across 12 modules to none. pre-commit.yml now resolves its Go version from go.mod instead of hardcoding it, removing one of the places that had to be bumped by hand. Residual, tracked rather than hidden: #1836 (nothing in the pipeline scans the image we ship, which is why this was invisible), #1837 (/app/cudly still carries 2 dependency advisories, not stdlib), #1838 (Dockerfile.dev still downloads a prebuilt migrate). Closes #1833
…ce (#1836) Nothing in the pipeline inspected the container image we ship. govulncheck ran in source mode over the six modules, Trivy ran with scan-type fs and config, and docker-build built the image, ran --version against it, and discarded it. A vulnerable binary baked into the image was therefore unreachable by the pipeline by construction. That gap hid two consecutive rounds of CVE work. #1832 bumped the Go toolchain CI reads and govulncheck went green while the image still built on go1.26.5. #1835 bumped the base image and /app/cudly came out clean, but the image shipped a second Go binary: a prebuilt golang-migrate release carrying go1.25.4 with 69 vulnerabilities across 12 modules, executed on every container start by entrypoint.sh with DB_AUTO_MIGRATE=true by default. A new job runs govulncheck -mode=binary over every Go binary in the final stage after docker-build, and is wired into ci-success so it gates. It fails on any advisory with a published fixed version at any severity, and tolerates but always prints advisories with no published fix, so it does not land permanently red on GO-2026-5932 (x/crypto/openpgp, introduced: "0", no fixed event, unreachable here) while still failing on the classes that actually occurred. The scanner was itself fail-open twice, and both were closed before merge. Its jq filter returned an empty array with exit 0 for an empty stream, for {}, and for a well-formed object with no config, all of which the classifier read as zero findings; the existing guards only fired on a parse error and none of those inputs is one, so a govulncheck run that silently produced nothing would have certified the image clean. The parser now requires the stream to open with a config message. That check then accepted any non-empty protocol_version, which would have reproduced the same hole on a tool upgrade emitting a different schema, so it is pinned to v1.0.0 with the expected and found values reported on mismatch. Verified both directions with recorded fixtures: red on a known-bad image carrying the prebuilt migrate binary, green on the current image, empty and malformed and wrong-schema streams all exit 2 rather than clean, an unfixable-only stream is tolerated, and a fixable advisory fails. Suite is 9 passed, 0 failed, asserting message content rather than exit codes alone. Trivy scan-type image was deliberately not added. The pinned runtime base alpine:3.21.3 carries fixable CRITICAL and HIGH OS advisories today, including a libssl3 heap overflow from a large X.509 certificate, so a gating step would land red on day one for something this change does not fix and a non-gating one would print an unactionable wall on every PR. Ordering matters: bump the base first, then add the OS-package gate. Deferred and tracked: #1842 (bump the alpine runtime base, then add Trivy image scanning), #1837 (the unfixable x/crypto advisory this check tolerates by design). Closes #1836
…e binary (#1833) #1832 bumped the toolchain CI reads, which turned govulncheck green while the image we deploy was still built on go1.26.5. This bumps what actually ships. The base image moves to golang:1.26.6-alpine3.24, digest re-resolved and re-pinned (the 1.26.6-alpine3.24 tag was re-pushed on 2026-08-16 onto a rebuilt image, so an earlier digest had gone stale; the pin is what caught that). The go directives in go.work and all five module files move to 1.26.6, which also fixes the workflows that resolve their toolchain via go-version-file rather than GO_VERSION. The image shipped two Go binaries and the base bump only fixed one. /usr/local/bin/migrate was an upstream prebuilt release carrying go1.25.4, with all seven advisories from this issue plus 62 more, and entrypoint.sh runs it against the database on every container start with DB_AUTO_MIGRATE=true by default. The vulnerable symbols included URL parsing and the TLS handshake, i.e. the paths that reach the database. It is now built from source on the pinned toolchain, the way the Makefile already did. Verified by reading the built artifact rather than inferring from the tag: on both arm64 and amd64, /app/cudly and /usr/local/bin/migrate both report go1.26.6, and both report zero standard-library findings under govulncheck. migrate goes from 69 vulnerabilities across 12 modules to none. pre-commit.yml now resolves its Go version from go.mod instead of hardcoding it, removing one of the places that had to be bumped by hand. Residual, tracked rather than hidden: #1836 (nothing in the pipeline scans the image we ship, which is why this was invisible), #1837 (/app/cudly still carries 2 dependency advisories, not stdlib), #1838 (Dockerfile.dev still downloads a prebuilt migrate). Closes #1833
…ce (#1836) Nothing in the pipeline inspected the container image we ship. govulncheck ran in source mode over the six modules, Trivy ran with scan-type fs and config, and docker-build built the image, ran --version against it, and discarded it. A vulnerable binary baked into the image was therefore unreachable by the pipeline by construction. That gap hid two consecutive rounds of CVE work. #1832 bumped the Go toolchain CI reads and govulncheck went green while the image still built on go1.26.5. #1835 bumped the base image and /app/cudly came out clean, but the image shipped a second Go binary: a prebuilt golang-migrate release carrying go1.25.4 with 69 vulnerabilities across 12 modules, executed on every container start by entrypoint.sh with DB_AUTO_MIGRATE=true by default. A new job runs govulncheck -mode=binary over every Go binary in the final stage after docker-build, and is wired into ci-success so it gates. It fails on any advisory with a published fixed version at any severity, and tolerates but always prints advisories with no published fix, so it does not land permanently red on GO-2026-5932 (x/crypto/openpgp, introduced: "0", no fixed event, unreachable here) while still failing on the classes that actually occurred. The scanner was itself fail-open twice, and both were closed before merge. Its jq filter returned an empty array with exit 0 for an empty stream, for {}, and for a well-formed object with no config, all of which the classifier read as zero findings; the existing guards only fired on a parse error and none of those inputs is one, so a govulncheck run that silently produced nothing would have certified the image clean. The parser now requires the stream to open with a config message. That check then accepted any non-empty protocol_version, which would have reproduced the same hole on a tool upgrade emitting a different schema, so it is pinned to v1.0.0 with the expected and found values reported on mismatch. Verified both directions with recorded fixtures: red on a known-bad image carrying the prebuilt migrate binary, green on the current image, empty and malformed and wrong-schema streams all exit 2 rather than clean, an unfixable-only stream is tolerated, and a fixable advisory fails. Suite is 9 passed, 0 failed, asserting message content rather than exit codes alone. Trivy scan-type image was deliberately not added. The pinned runtime base alpine:3.21.3 carries fixable CRITICAL and HIGH OS advisories today, including a libssl3 heap overflow from a large X.509 certificate, so a gating step would land red on day one for something this change does not fix and a non-gating one would print an unactionable wall on every PR. Ordering matters: bump the base first, then add the OS-package gate. Deferred and tracked: #1842 (bump the alpine runtime base, then add Trivy image scanning), #1837 (the unfixable x/crypto advisory this check tolerates by design). Closes #1836
What
Bumps the pinned Go toolchain from
1.26.5to1.26.6in the two workflows that pin it explicitly. 2 files, 3 lines..github/workflows/ci.ymldefault: 1.26.5->1.26.6.github/workflows/ci.ymlGO_VERSION: '1.26.5'->'1.26.6'.github/workflows/pre-commit.ymlgo-version: "1.26.5"->"1.26.6"Deliberately minimal:
mainis red repo-wide and every open PR is blocked behind this, so this PR is scoped to the smallest change that unblocks the queue. The remaining work is sequenced separately (see "Follow-up" below).Why
Security Scanning->Run govulncheck CVE scanner (all modules)fails on every branch with 7 Go standard library advisories, eachFound in: <pkg>@go1.26.5/Fixed in: <pkg>@go1.26.6: GO-2026-6218 (net/url), GO-2026-6091 (html/template), GO-2026-6090 (crypto/tls), GO-2026-6089 (net/http), GO-2026-6088 (encoding/xml), GO-2026-5972 (encoding/asn1), GO-2026-5026 (net/http).CI Successfails downstream, so no PR can satisfy the merge gate.Time-based, not change-based:
govulncheckfetches the vulnerability DB at run time, so a commit that passed before these advisories were published fails afterwards.go1.26.6confirmed as the current stable patch release via thego.dev/dlJSON feed; there is no 1.26.7.pre-commit.ymlwas a second stale pinIssue #1829 identified
ci.ymlas the only pin. It is not:pre-commit.yml:35carried the same1.26.5in a double-quoted form, which the issue's search pattern (go-version: ') did not match. It is bumped here so both workflows resolve one toolchain.Nothing suppressed
No ignore list, no
-scannarrowing, nocontinue-on-error, no allowlist. These are real advisories against the running standard library, and the bump resolves them at the root.Verification
Against a real
go1.26.6toolchain (installed viagolang.org/dl/go1.26.6) using the exact CI-pinned tool versions, over the CI module loop. pkg providers/aws providers/azure providers/gcp tests/e2e.govulncheck v1.1.4 before (go1.26.5) - reproduces the CI failure exactly:
.pkgproviders/awsproviders/azureproviders/gcptests/e2eRoot reports precisely the 7 IDs above. Vuln DB stamp
2026-08-13 21:43:54 UTC, consistent with the pass-then-fail timeline in #1829.After (go1.26.6): all six modules
No vulnerabilities found., exit 0 each.tests/e2especifically (it declaresgo 1.25, unlike the others): with 1.26.6 as the local toolchain andGOTOOLCHAIN=auto,go versionthere resolvesgo1.26.6andgovulncheck -versionreportsGo: go1.26.6, since auto only ever upgrades. Its./...matches no packages becausee2e_test.gois behind a//go:build e2etag; that is pre-existing and identical on both toolchains.Collateral on the newer toolchain (exit codes captured explicitly, not inferred from empty output):
golangci-lint run --timeout=10m0 issues., exit 0go vet ./...(root, as CI)gosecper-module, all 6go build ./..., all 6 modulesgo test ./...(root)Real CI on this exact commit passed end to end, including
Run govulncheck CVE scanner (all modules) => success,Run gosec Security Scanner => success,Run golangci-lint => success,Run go vet => success, pluspre-commit,AWS SanityandAzure Sanity.Follow-up (not in this PR)
This PR fixes the CI gate. It does not fix the shipped artifact, and that gap is recorded rather than hidden:
Dockerfile(plusDockerfile.dev/Dockerfile.test) builds fromgolang:1.26.5-alpine3.24, so the production binary still contains the 7 vulnerable stdlib packages until that bump lands.godirectives ingo.mod/go.work/pkg/providers/*remain1.26.5.database-migration.yml,aws_sanity.ymlandazure_sanity.ymlresolve their toolchain viago-version-file: go.mod, so those five steps keep installing 1.26.5.That work is prepared and verified on a separate branch, to be sequenced after this merges so the unblock is not coupled to a slower, riskier change.
Closes #1829