Day 72 — govulncheck & supply chain
Day 72 — govulncheck & supply chain
Stage VII · ~3h
Goal: Run govulncheck on your module, understand call-graph reachability vs “CVE in go.mod”, remediate or document accepted risk, and capture a short supply-chain checklist for CI—on Go 1.26.
Why this day exists
Your code can be perfect while a dependency is not. Supply-chain hygiene is part of ship quality:
- Known vulnerable modules
- Pseudo-versions and abandoned libs
replaceforks nobody audits
- CI that never re-scans
govulncheck is the official Go vulnerability scanner, integrated with the Go vulnerability database. Day 74’s gate will expect a clean scan or an explicit accepted-risk note. Day 81’s security checklist will still cover your auth bugs—scanner ≠ secure app.
Theory 1 — What govulncheck does
go install golang.org/x/vuln/cmd/govulncheck@latest
govulncheck -version # if supported; else: note binary path + date
govulncheck ./...It analyzes your module’s code and reports vulnerabilities that are reachable in the call graph when possible—not merely present in the module graph. That reduces noise versus naïve “every CVE in the lockfile” tools.
Useful invocations
govulncheck ./...
govulncheck -show verbose ./...
# JSON for CI parsing when needed
govulncheck -json ./... > vuln.jsonBinary mode
go build -o bin/app ./cmd/service
govulncheck -mode=binary ./bin/appBinary mode helps when source and shipped artifact diverge (build tags, different main packages, stripped paths). Scan what you ship.
| Mode | Strength |
|---|---|
Source (./...) |
Maps findings to your call sites |
| Binary | Matches the artifact operators run |
Theory 2 — Reading a finding
A report typically includes:
- Vulnerability ID (e.g.
GO-20XX-XXXX)
- Affected module and version range
- Fixed version (if any)
- Whether your code calls into the vulnerable symbol/path
- Links to advisory detail
| Situation | Action |
|---|---|
| Reachable + fixed version available | Upgrade; re-test |
| In module graph but not reachable | Still prefer upgrade; document if blocked |
| No fix yet | Mitigate (avoid code path), monitor, careful replace/fork |
| Only test-only dependency | Still upgrade if easy; else note scope |
| False sense of security | Scanner ≠ review of your auth bugs |
Example narrative (write yours in SECURITY-SCAN.md)
ID: GO-2024-XXXX
Module: golang.org/x/net
Your version: v0.a.b
Fixed in: v0.x.y
Reachable: yes (HTTP/2 related import path)
Plan: go get golang.org/x/net@v0.x.y && go test ./...Theory 3 — Remediation workflow
go get example.com/vulnerable@v1.2.4
go mod tidy
go test ./...
govulncheck ./...If an upgrade breaks APIs:
- Read upstream changelog / release notes.
- Fix compile breaks in adapters first (prefer not to change domain).
- Run unit + smoke of main paths.
- Re-run
govulncheck.
- Update
SECURITY-SCAN.mdwith before/after versions.
Partial upgrades
go list -m all | grep vulnerable
go get golang.org/x/net@v0.x.y
go mod tidyAvoid go get -u ./... as a blind first move on a large graph—prefer targeted bumps for the advisory, then consider broader hygiene.
Toolchain and stdlib
go version
go env GOTOOLCHAINKeep the Go toolchain itself updated; stdlib CVEs happen too. Matching this volume’s Go 1.26 baseline means you already practice currency (Day 37 quality pass). If go.mod says go 1.26, CI should use 1.26.x as well.
Theory 4 — Reachability vs lockfile scanners
| Scanner style | Reports | Noise |
|---|---|---|
| Lockfile/CVE match | Any vulnerable version in graph | Higher |
| govulncheck (source) | Prefer reachable symbols | Lower |
| Image OS scanner (Trivy etc.) | Distro packages in container | Different surface |
Use both mindsets in production orgs. For this volume, govulncheck is the required Go-native bar; container image scan is stretch.
Your module code
→ imports package P@v1 (vulnerable func F)
→ if F is unreachable from your main graph, source mode may omit or deprioritize
Binary mode still encodes what was linked.
Theory 5 — CI placement
Recommended Stage VII pipeline shape:
test → vet → staticcheck → govulncheck → build → (image scan optional)
Example GitHub Actions step sketch:
- name: govulncheck
run: |
go install golang.org/x/vuln/cmd/govulncheck@latest
govulncheck ./...Policy options:
| Policy | When |
|---|---|
| Fail on any finding | Main branch / release tags |
| Fail on reachable only | Early adoption |
| Annotated exceptions | Time-boxed, with owner + expiry |
Script hook
Add to scripts/lint.sh or scripts/gate-vii.sh:
#!/usr/bin/env bash
set -euo pipefail
go test ./...
go vet ./...
staticcheck ./...
govulncheck ./...Theory 6 — Other supply-chain controls (awareness)
| Control | Role |
|---|---|
go.sum |
Integrity of downloaded modules |
| Checksum DB | Verified sums for public modules |
| Module proxy | Default proxy.golang.org; privacy via GOPRIVATE |
GOPRIVATE / GONOSUMDB |
Private modules not forced through public sum DB |
replace |
Local forks—audit like first-party code |
| SBOM / cosign | Org-level signing & inventory; optional stretch |
| Dependabot / Renovate | Automated upgrade PRs |
| Vendoring | vendor/ can go stale—re-scan after updates |
Private modules sketch
go env -w GOPRIVATE=github.com/yourorg/*
# ensure git credentials work in CITheory 7 — Accepted risk template
When you cannot upgrade today:
## Accepted risk
ID: GO-....
Module/version:
Reachable: yes/no
Why not fixed today:
Mitigation (feature flag, avoid path, WAF, etc.):
Owner:
Review by date:
Tracking issue:Accepted risk without expiry is how permanent holes form. Day 74 gate should reject eternal waivers without dates.
Worked example — clean scan document
SECURITY-SCAN.md:
# govulncheck — day72
Date: YYYY-MM-DD
Go: go1.26.x
govulncheck: <version or install date>
Module: example.com/myservice
## Commands
govulncheck ./...
go build -o bin/app ./cmd/service
govulncheck -mode=binary ./bin/app
## Result
No vulnerabilities found.
## Notes
- Last dependency review: ...
- replace directives: none | listed below
- CI: scripts/lint.sh includes govulncheckFinding + remediation example
## Finding
ID: GO-....
Module: golang.org/x/net @ v0.a.b
Fixed in: v0.x.y
Reachable: yes (import path ...)
## Remediation
go get golang.org/x/net@v0.x.y
go mod tidy
go test ./...
govulncheck ./... → clean
## Residual risk
None known.Lab 1 — Install and baseline scan
Suggested workspace: your primary service or CLI module.
- Confirm
go versionis 1.26.x.
- Install govulncheck; record version/date.
- Run
govulncheck ./...; save full output toSECURITY-SCAN.mdor attach summary.
- Build main artifact; run binary mode.
- If clean: still write the clean-scan document (evidence for Day 74).
go install golang.org/x/vuln/cmd/govulncheck@latest
govulncheck ./...
mkdir -p bin
go build -o bin/app ./cmd/...
govulncheck -mode=binary ./bin/appLab 2 — Remediate or accept
- If findings exist: prefer upgrade path.
- Re-test; re-scan.
- If blocked: fill accepted-risk template with review date.
- Search for
replaceingo.modand justify each.
grep -n replace go.mod || true
go list -m all > /tmp/modules.txt
# skim for pseudo-versions on critical path libsLab 3 — CI / script integration
- Add govulncheck to
scripts/lint.sh(or create it).
- Sketch Actions (or other CI) step in
SECURITY-SCAN.md.
- Run the script cold once.
chmod +x scripts/lint.sh
./scripts/lint.shLab 4 — JSON output stretch (optional but useful)
govulncheck -json ./... > vuln.json
# count findings with jq if installed
jq '.. | .osv? // empty | .id' vuln.json 2>/dev/null || trueDocument how CI could fail on non-empty findings.
Stretch — image scan awareness
# conceptual — tool must be installed
# trivy image myapp:devNote: OS packages in the container are outside govulncheck’s Go module graph. Distroless/static helps shrink that surface (Day 68).
Hour-by-hour suggestion
| Time | Focus |
|---|---|
| 0:00–0:20 | Install tool; baseline source scan |
| 0:20–1:00 | Binary scan; interpret findings |
| 1:00–2:00 | Upgrade / remediate / accepted risk |
| 2:00–2:30 | lint.sh + CI sketch |
| 2:30–3:00 | SECURITY-SCAN.md polish |
Common gotchas
| Gotcha | Fix |
|---|---|
| Old govulncheck DB / binary | Reinstall @latest; ensure network to vuln DB |
| Clean scan ≠ secure app | Still do Day 81 checklist |
| Upgrading everything blindly | Read changelogs for major jumps |
| Ignoring binary mode | Scan what you ship |
| Vendoring stale code | Re-scan after vendor updates |
| Private modules fetch fail | GOPRIVATE + CI credentials |
| Accepting risk forever | Expiry date + owner |
Only scanning ./cmd |
Prefer ./... then binary |
Committing vuln.json secrets |
Usually none—but don’t commit env dumps |
Checkpoint
govulncheck ./...run documented
- Binary mode run or explicit N/A with reason
- Findings remediated or explicitly accepted with review date
- Tool version + Go 1.26 recorded
- Script/CI hook sketched and executed once
- Can explain reachability vs lockfile-only scanners
replacedirectives reviewed
Commit
git add .
git commit -m "day72: govulncheck supply chain pass"Write three personal gotchas before continuing.
Tomorrow
Day 73 — Elective: Kubernetes client-go or deepen the service—choose deliberately and ship one coherent outcome. Bring SECURITY-SCAN.md into the Stage VII gate pack (Day 74).