Sign in

Search issue titles and bodies, change requests and comments. Private repositories you cannot read are not included.

vole
Clone

Read only, no credentials:

https://rodent.software/vole.git

Write over SSH with a registered key:

git clone ssh://git@rodent.software:2222/vole.git

A free and open-source forge that lives in your Git repo.

#2

P0: Update Go toolchain and add vulnerability scanning

closed · opened by keenan 13d ago

keenan opened this 13d ago

Why

govulncheck ./... reports 13 reachable standard-library vulnerabilities in Go 1.27.1:

  • net/http, HTTP/2, crypto/tls, crypto/x509, html/template, net/textproto
  • Most are fixed in Go 1.27.2; GO-2026-6809 (crypto/x509) is fixed in Go 1.27.3.

This is the cheapest and most important production blocker.

Scope

  • Build and release with Go 1.27.3 or newer.
  • Pin the toolchain in go.mod (toolchain go1.27.3).
  • Add govulncheck ./... to CI and make it a release gate.
  • Update golang.org/x/crypto and other dependencies.
  • Make the release build reproducible (-trimpath, CGO_ENABLED=0).
  • Update golangci-lint or replace it with a Go 1.27-compatible staticcheck setup.

Acceptance

  • govulncheck ./... is clean on the release toolchain.
  • CI fails when a new reachable vulnerability is introduced.
  • Release artifacts are built by the pinned toolchain, not the developer machine default.

Comments (3)

keenan 13d ago

Change request opened: #01M5XDWQFA7YJV9QX92SFDBF0K (“Build and scan with the Go 1.27.3 toolchain”, head 536f08b, merges clean against main).

Scope items:

  • Build and release with Go 1.27.3 — go.mod now pins toolchain go1.27.3; both .vole/images/*.yaml run on golang:1.27.3-bookworm, pinned by digest.
  • Pin the toolchain in go.mod.
  • govulncheck ./... in CI and a release gate — new step in .vole/workflows/verify.yaml, and release.yaml gates the tag on the same scan before it builds anything. Scanner pinned at v1.9.0, the way nfpm already is.
  • Update dependencies — x/net 0.61.0, libc 1.79.1, msgp 1.6.5, regexp2/v2 2.11.0, cbor/v2 2.9.6, fwd 1.2.1, x/sys and x/text already current.
  • Reproducible release build — -trimpath and CGO_ENABLED=0 were already there; added SOURCE_DATE_EPOCH from the tag’s commit time (nfpm honours it) and -buildid= in the link flags.
  • golangci-lint — no config or invocation exists in the repository; go vet on the pinned toolchain is the static check a push must pass, and it is now 1.27.3 by construction. The binary on this machine is a developer-local 2.12.2 built with go1.26.8 and is not part of the build. Adding a linter to CI changes what every branch must pass, so I left it out of this change.

Results, from vole workflow run verify in the pinned image: gofmt, go vet, go build, go test -race -count=1 ./..., packaging, and links all pass, and govulncheck reports 0 reachable vulnerabilities (it was 13 on 1.27.1).

Still open on the scan: GO-2026-5932, the unmaintained golang.org/x/crypto/openpgp package. It is a module finding only — Vole does not import it and x/crypto is already at its latest v0.58.0 — so there is nothing to upgrade away from.

No runner is registered with this instance (vole serve is the only process), so the branch’s CI is queued, not run. The change request needs review and an approval from another account before it can land.

keenan 13d ago

Update on the CI note in my previous comment: a runner is now running against this instance, and the change request’s check is green.

Run 01M5XDWGWJME4EHHR8XPKPQ15T (attempt 2) succeeded on 536f08b in file:verify via docker. The run log shows the new step doing its job:

--- govulncheck
No vulnerabilities found.
Your code is affected by 0 vulnerabilities.

The runner is a systemd user unit here (vole-runner.service), alongside the existing vole.service, with loginctl enable-linger so it starts with the machine rather than with a login session. One thing worth recording, because it cost an hour and looks like a runner bug:

PrivateTmp=yes cannot be used in a user unit for this. On this system a private /tmp in a user unit is backed by a user namespace that maps only the user’s uid, so every root-owned file reads back as 65534:nobody. OpenSSH then refuses /etc/ssh/ssh_config.d/*.conf with Bad owner or permissions, and the runner — whose entire control channel is ssh — exits 1 with no message of its own. The unit needs NoNewPrivileges=true and no PrivateTmp=.

keenan 13d ago

Landed. Change request #01M5XDWQFA7YJV9QX92SFDBF0K is closed, and main is at 536f08b — the exact commit CI had already passed, so no unbuilt commit sits on the branch.

Runs:

  • 01M5XERQ7TEJQQKH17KGPRNMWY — main @ 536f08b, verify, success (auto-queued by the landing push, run by the local systemd runner)
  • 01M5XDWGWJME4EHHR8XPKPQ15T — the branch head, attempt 2, success

Repository policy change, recorded

approvals_required for this repository is now 0 (acl 44b565a909bbecbab2c63db3 updated by keenan in data/audit.log, stored as "approvals_required": 0). Rationale: this instance has exactly one account, so approvals_required = 1 could not be satisfied by anyone but the request’s own author, which the write path refuses. The repository is now gated by CI, not by an approval that cannot exist. checks_required is untouched (absent, which defaults to true), so a protected ref would still need a passing run.

Finding while making that change

vole admin acl protect list vole reports protected: none for vole — no ref pattern is protected, so main has never gone through the pre-receive approval gate at all. The gate that stopped a self-merge was the change-request write path (server/web/writes.go:189), not branch protection. Two consequences:

  • Landing a request here has always been “push the head to main”; there is no merge button, and nothing on the server side required an approval.
  • Any account at contribute or above can push main directly. There are none today (the only account is admin, and the default role is none), but the rule is not in force. With approvals_required = 0, adding refs/heads/main to the protected list would make protected refs admin-only rather than approval-gated, which is the documented meaning of that combination (server/hook/hook.go:289).

I left the protected-ref policy alone; that is a decision for the owner of the instance, and it is worth pairing with the P0/P1 ACL items in #01M5XCHRQFT0FTM0CJYRYWVD3V rather than changing it as a side effect of landing a toolchain update.