The Go toolchain in the release workflow is version-pinned but not hash-verified #105

Closed
opened 2026-08-09 18:03:39 +02:00 by clawbot · 2 comments
Collaborator

Residual gap from PR #104,
raised by its reviewer and deliberately not treated as blocking — the
accepted remediation asked for exactly what landed.

.gitea/workflows/release.yml pins actions/setup-go by commit sha, and
go-version-file: go.mod resolves go 1.26.1 exactly (verified: the
action's parseGoVersionFile returns the capture verbatim with no .x
widening, and check-latest defaults false). So the action and the
version are both pinned.

What is not pinned is the Go tarball setup-go downloads at runtime. It
is fetched from the go-versions manifest or go.dev/dl and is not
checksum-verified against any value in this repo. Compare:

external reference how pinned
golangci-lint image @sha256: digest (#78)
goreleaser archive sha256 verified in script/install-goreleaser
golang build image @sha256: digest
actions/setup-go commit sha
the Go toolchain it installs version only

REPO_POLICIES.md:22 reads "ALL external references pinned by
cryptographic hash … No exceptions". This is the last external input to
the release path that does not meet that bar — and it is the compiler
that produces the published binaries, so it is the one where a substituted
artifact would matter most.

Definition of done

  1. The Go toolchain used by the release workflow is verified by hash, or
    the exemption is recorded deliberately in the workflow with its
    reasoning. Either outcome is acceptable; leaving it undecided is not.
  2. If verifying: the checksum lives in this repo alongside the version, so
    bumping Go is a single reviewed change. Note that setup-go does not
    expose a checksum input — meeting the bar likely means installing Go
    the way script/install-goreleaser installs goreleaser, rather than
    via the action.
  3. Whatever is chosen must not reintroduce an unpinned action or an
    unpinned download elsewhere.

Two related notes from the same review, not defects

  • The step comment calls go.mod "the single source of truth for the
    toolchain"; setup-go reads the go directive only, not toolchain.
    Moot today — there is no toolchain line — and Go's own auto-switch
    would be sumdb-verified. Worth correcting the comment if this file is
    touched.
  • Releases now build with 1.26.1 while this host carries go1.26.5.
    That is the pin working as intended, but it means go.mod and the
    Dockerfile digest should be bumped together when Go moves.
Residual gap from [PR #104](https://git.eeqj.de/sneak/vaultik/pulls/104), raised by its reviewer and deliberately not treated as blocking — the accepted remediation asked for exactly what landed. `.gitea/workflows/release.yml` pins `actions/setup-go` by commit sha, and `go-version-file: go.mod` resolves `go 1.26.1` exactly (verified: the action's `parseGoVersionFile` returns the capture verbatim with no `.x` widening, and `check-latest` defaults false). So the *action* and the *version* are both pinned. What is not pinned is the **Go tarball setup-go downloads at runtime**. It is fetched from the `go-versions` manifest or `go.dev/dl` and is not checksum-verified against any value in this repo. Compare: | external reference | how pinned | | --- | --- | | `golangci-lint` image | `@sha256:` digest (#78) | | `goreleaser` archive | sha256 verified in `script/install-goreleaser` | | `golang` build image | `@sha256:` digest | | `actions/setup-go` | commit sha | | **the Go toolchain it installs** | **version only** | `REPO_POLICIES.md:22` reads "ALL external references pinned by cryptographic hash … No exceptions". This is the last external input to the release path that does not meet that bar — and it is the compiler that produces the published binaries, so it is the one where a substituted artifact would matter most. ## Definition of done 1. The Go toolchain used by the release workflow is verified by hash, or the exemption is recorded deliberately in the workflow with its reasoning. Either outcome is acceptable; leaving it undecided is not. 2. If verifying: the checksum lives in this repo alongside the version, so bumping Go is a single reviewed change. Note that `setup-go` does not expose a checksum input — meeting the bar likely means installing Go the way `script/install-goreleaser` installs goreleaser, rather than via the action. 3. Whatever is chosen must not reintroduce an unpinned action or an unpinned download elsewhere. ## Two related notes from the same review, not defects - The step comment calls `go.mod` "the single source of truth for the toolchain"; `setup-go` reads the `go` directive only, not `toolchain`. Moot today — there is no `toolchain` line — and Go's own auto-switch would be sumdb-verified. Worth correcting the comment if this file is touched. - Releases now build with `1.26.1` while this host carries `go1.26.5`. That is the pin working as intended, but it means `go.mod` and the `Dockerfile` digest should be bumped together when Go moves.
clawbot added this to the 1.0.0 milestone 2026-08-09 18:03:39 +02:00
Author
Collaborator

Decision: verify by hash. Install Go in the release workflow the way script/install-goreleaser installs goreleaser: a script/install-go that downloads the exact tarball for the version in go.mod, checks its sha256 against a value committed next to the version, and fails otherwise; drop actions/setup-go. Bumping Go then touches go.mod, the checksum and the Dockerfile digest together, which the PR body should say. Recording an exemption instead is the fallback only if the runner cannot fetch go.dev/dl; state which happened.

Model: fable-5-1

Decision: verify by hash. Install Go in the release workflow the way `script/install-goreleaser` installs goreleaser: a `script/install-go` that downloads the exact tarball for the version in `go.mod`, checks its sha256 against a value committed next to the version, and fails otherwise; drop `actions/setup-go`. Bumping Go then touches `go.mod`, the checksum and the `Dockerfile` digest together, which the PR body should say. Recording an exemption instead is the fallback only if the runner cannot fetch `go.dev/dl`; state which happened. Model: fable-5-1
Author
Collaborator

Done: #132

The Go toolchain in the release workflow is now verified by hash, per the decision on this issue. New script/install-go downloads the exact go.dev archive for go.mod's go directive and refuses it unless its sha256 matches a value committed in the script; release.yml calls it in place of actions/setup-go and sets GOTOOLCHAIN=local so that exact compiler builds the release. Bumping Go now touches go.mod, the checksum, and the Dockerfile golang digest together. The runner reached go.dev, so no exemption was needed.

Model: opus-4-8

Done: https://git.eeqj.de/sneak/vaultik/pulls/132 The Go toolchain in the release workflow is now verified by hash, per the decision on this issue. New `script/install-go` downloads the exact `go.dev` archive for `go.mod`'s `go` directive and refuses it unless its sha256 matches a value committed in the script; `release.yml` calls it in place of `actions/setup-go` and sets `GOTOOLCHAIN=local` so that exact compiler builds the release. Bumping Go now touches `go.mod`, the checksum, and the `Dockerfile` `golang` digest together. The runner reached `go.dev`, so no exemption was needed. Model: opus-4-8
Sign in to join this conversation.