Skip to content

fix(ci): bump pinned Go toolchain to 1.26.6 for stdlib CVE fixes - #1832

Merged
cristim merged 1 commit into
mainfrom
fix/1829-govulncheck-go-1266
Aug 14, 2026
Merged

cristim merged 1 commit into
mainfrom
fix/1829-govulncheck-go-1266

Conversation

@cristim

@cristim cristim commented Aug 13, 2026 •

Copy link
Copy Markdown
Member

What

Bumps the pinned Go toolchain from 1.26.5 to 1.26.6 in the two workflows that pin it explicitly. 2 files, 3 lines.

File Line Change
.github/workflows/ci.yml 9 doc comment default: 1.26.5 -> 1.26.6
.github/workflows/ci.yml 26 GO_VERSION: '1.26.5' -> '1.26.6'
.github/workflows/pre-commit.yml 35 go-version: "1.26.5" -> "1.26.6"

Deliberately minimal: main is 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, each Found 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 Success fails downstream, so no PR can satisfy the merge gate.

Time-based, not change-based: govulncheck fetches the vulnerability DB at run time, so a commit that passed before these advisories were published fails afterwards.

go1.26.6 confirmed as the current stable patch release via the go.dev/dl JSON feed; there is no 1.26.7.

pre-commit.yml was a second stale pin

Issue #1829 identified ci.yml as the only pin. It is not: pre-commit.yml:35 carried the same 1.26.5 in 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 -scan narrowing, no continue-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.6 toolchain (installed via golang.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:

Module Result
. 7 vulns, exit 3
pkg 5 vulns, exit 3
providers/aws 5 vulns, exit 3
providers/azure 6 vulns, exit 3
providers/gcp 4 vulns, exit 3
tests/e2e clean, exit 0

Root 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/e2e specifically (it declares go 1.25, unlike the others): with 1.26.6 as the local toolchain and GOTOOLCHAIN=auto, go version there resolves go1.26.6 and govulncheck -version reports Go: go1.26.6, since auto only ever upgrades. Its ./... matches no packages because e2e_test.go is behind a //go:build e2e tag; that is pre-existing and identical on both toolchains.

Collateral on the newer toolchain (exit codes captured explicitly, not inferred from empty output):

Check Version Result
golangci-lint run --timeout=10m v2.10.1, exact CI pin 0 issues., exit 0
go vet ./... (root, as CI) go1.26.6 exit 0
gosec per-module, all 6 v2.28.0, exact CI pin exit 0 each
go build ./..., all 6 modules go1.26.6 exit 0 each
go test ./... (root) go1.26.6 exit 0, 32 packages ok, 0 failures

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, plus pre-commit, AWS Sanity and Azure 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 (plus Dockerfile.dev / Dockerfile.test) builds from golang:1.26.5-alpine3.24, so the production binary still contains the 7 vulnerable stdlib packages until that bump lands.
  • The go directives in go.mod / go.work / pkg / providers/* remain 1.26.5. database-migration.yml, aws_sanity.yml and azure_sanity.yml resolve their toolchain via go-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

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
@cristim cristim added triaged Item has been triaged priority/p1 Next up; this sprint severity/high Significant harm urgency/now Drop other things impact/internal Team-internal only effort/s Hours type/bug Defect labels Aug 13, 2026
@coderabbitai

coderabbitai Bot commented Aug 13, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9119dc07-641b-4644-9029-6e308c5e08b2

📥 Commits

Reviewing files that changed from the base of the PR and between d2f009e and 6f70742.

⛔ Files ignored due to path filters (1)
  • go.work is excluded by !**/*.work
📒 Files selected for processing (8)
  • Dockerfile
  • Dockerfile.dev
  • Dockerfile.test
  • go.mod
  • pkg/go.mod
  • providers/aws/go.mod
  • providers/azure/go.mod
  • providers/gcp/go.mod

📝 Walkthrough

Walkthrough

The repository updates Go from 1.26.5 to 1.26.6 across module requirements, pinned container images, and CI workflows.

Changes

Go toolchain update

Layer / File(s) Summary
Module version requirements
go.mod, pkg/go.mod, providers/*/go.mod
The root, package, AWS, Azure, and GCP modules now require Go 1.26.6.
Container toolchain images
Dockerfile, Dockerfile.dev, Dockerfile.test
Build, development, and test images now use pinned Go 1.26.6 Alpine images and matching refresh instructions.
Workflow toolchain configuration
.github/workflows/ci.yml, .github/workflows/pre-commit.yml
CI documentation, the shared GO_VERSION, and pre-commit setup now specify Go 1.26.6.

Estimated code review effort: 1 (Trivial) | ~2 minutes

Mergeability Score: ⚪ Minimal · up to 6f707

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

  • LeanerCloud/CUDly#857 — Both changes update the Go base image reference and digest in Dockerfile.dev.
🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The PR updates CI pins but also changes six go.mod directives and Dockerfiles, which #1829 explicitly excludes and tracks separately. Revert the six go.mod and Dockerfile changes, or link the separate issue that authorizes those updates.
Out of Scope Changes check ⚠️ Warning The go.mod and Dockerfile updates are outside #1829, which limits the fix to CI toolchain pins and excludes those changes. Remove the unrelated go.mod and Dockerfile changes from this PR.
✅ Passed checks (3 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the repository-wide Go toolchain update and its purpose of fixing standard-library CVEs.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/1829-govulncheck-go-1266

Comment @coderabbitai help to get the list of available commands.

@cristim

cristim commented Aug 14, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai full review

Context for the review: this is a 3-line CI unblock. main is currently red repo-wide because govulncheck reports 7 Go standard library advisories against the pinned go1.26.5 (GO-2026-6218, 6091, 6090, 6089, 6088, 5972, 5026), all fixed in go1.26.6. Every open PR is blocked behind it, so this one is being prioritised.

Scope is deliberately narrow: GO_VERSION in ci.yml and the double-quoted go-version pin in pre-commit.yml. Nothing is suppressed, and no go.mod directive is touched here.

Two known gaps, both recorded rather than hidden: the shipped container image still builds on go1.26.5 (Dockerfile:18 and siblings), and the three workflows using go-version-file: go.mod may still resolve 1.26.5. Both are tracked in #1833 and deliberately excluded so this unblock can land quickly.

Requesting a full review rather than an incremental one because the earlier attempt on this PR was rate-limited.

@coderabbitai

coderabbitai Bot commented Aug 14, 2026 •

Copy link
Copy Markdown
Contributor

@cristim Full review requested. I used the repository guidance for rate-limited review recovery.


🧠 Learnings used
Learnt from: cristim
Repo: LeanerCloud/CUDly PR: 0
File: :0-0
Timestamp: 2026-06-26T17:21:01.385Z
Learning: For the LeanerCloud/CUDly repository review workflow, when a previous CodeRabbit review pass was skipped or failed to produce findings due to a rate-limit event, use a full review request on the pull request rather than the incremental review form.

Learnt from: cristim
Repo: LeanerCloud/CUDly PR: 0
File: :0-0
Timestamp: 2026-06-26T17:23:32.317Z
Learning: In this repository, if a CodeRabbit review was throttled or hit a rate limit, the correct recovery is to request `coderabbitai full review` rather than `coderabbitai review`, because incremental review can silently skip the affected in-flight commit and report a false-clean result.

Learnt from: cristim
Repo: LeanerCloud/CUDly PR: 0
File: :0-0
Timestamp: 2026-08-06T08:28:24.968Z
Learning: In the LeanerCloud/CUDly repository, request CodeRabbit reviews at most once per hour across the repository because the adaptive quota is shared across open pull requests. When a PR head was pushed while automatic review was quota-exhausted and was not retried, use a full review rather than an incremental review so the missed commits are reviewed.

Learnt from: cristim
Repo: LeanerCloud/CUDly PR: 0
File: :0-0
Timestamp: 2026-08-05T05:27:02.254Z
Learning: For the LeanerCloud/CUDly repository, pace CodeRabbit review requests at one request per hour across the repository. The review quota is adaptive and shared per developer and organization; burst requests can exhaust the quota and tighten the limit. When a PR head was pushed while quota was exhausted, request a full review because an incremental review skips the unreviewed commits.

Learnt from: cristim
Repo: LeanerCloud/CUDly PR: 0
File: :0-0
Timestamp: 2026-08-04T08:23:48.546Z
Learning: For the LeanerCloud/CUDly repository, CodeRabbit review quota is shared per developer across the organization and adapts to recent review volume. Space review requests to approximately one per hour. When an automatic review did not run because quota was exhausted, use a full review instead of an incremental review so CodeRabbit inspects the missed commits.
✅ Action performed

Full review finished.

@cristim
cristim force-pushed the fix/1829-govulncheck-go-1266 branch from d2f009e to 6f70742 Compare August 14, 2026 00:14
@cristim cristim changed the title fix(ci): bump pinned Go toolchain to 1.26.6 for stdlib CVE fixes fix(security): bump Go toolchain to 1.26.6 everywhere it is pinned Aug 14, 2026
@cristim
cristim force-pushed the fix/1829-govulncheck-go-1266 branch from 6f70742 to d2f009e Compare August 14, 2026 00:22
@cristim cristim changed the title fix(security): bump Go toolchain to 1.26.6 everywhere it is pinned fix(ci): bump pinned Go toolchain to 1.26.6 for stdlib CVE fixes Aug 14, 2026
@cristim
cristim merged commit 929ee11 into main Aug 14, 2026
37 checks passed
cristim added a commit that referenced this pull request Aug 16, 2026
…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.
cristim added a commit that referenced this pull request Aug 17, 2026
…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
cristim added a commit that referenced this pull request Aug 18, 2026
…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
cristim added a commit that referenced this pull request Sep 27, 2026
…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
cristim added a commit that referenced this pull request Sep 27, 2026
…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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

effort/s Hours impact/internal Team-internal only priority/p1 Next up; this sprint severity/high Significant harm triaged Item has been triaged type/bug Defect urgency/now Drop other things

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(ci): govulncheck fails repo-wide on 7 stdlib CVEs fixed in go1.26.6, blocking every merge

1 participant