rgoussu@goussu: ~/library/go/testing
~/library/go/testing cat security-testing.md

Security testing — govulncheck, gosec, scanning

# Security testing for Go — reachability-aware govulncheck, gosec static rules, fuzzing at trust boundaries, dependency and image scanning, and CI wiring.

Conceptsaved 2026-08-09 #go#testing#security#supply-chain#tooling

Overview

Security testing for a Go service spans four layers: known vulnerabilities in dependencies (govulncheck), insecure patterns in your own code (gosec), robustness of trust boundaries (fuzzing), and the artefact you actually ship (image and secrets scanning). Go's standout is govulncheck — instead of flagging every vulnerable module in go.mod, it does static call-graph analysis and reports only vulnerabilities whose affected functions your code can actually reach, cutting the false-positive noise that makes teams ignore SCA reports.

Key points

  • govulncheck is the centerpiece: backed by the Go vulnerability database (vuln.go.dev), reachability-aware at source level (govulncheck ./...), symbol-level findings ("your call to x.Parse reaches GO-2024-XXXX"); binary mode (govulncheck -mode=binary) checks a compiled artefact's module versions. Details in govulncheck.
  • gosec (securego/gosec) runs static AST rules over your own code: hardcoded credentials (G101), command/SQL injection via concatenation (G204/G201), weak crypto primitives, math/rand where crypto/rand belongs, unchecked errors on security calls, permissive file modes. Also available as a golangci-lint linter; suppress with //nolint:gosec // justification and audit the suppressions.
  • Fuzzing is security testing: native go test -fuzz on every parser, decoder and input validator at a trust boundary — memory safety spares Go from classic overflows, but panics (DoS), infinite loops and validation bypasses remain; see Property-based testing & fuzzing.
  • Module supply chain is strong by default: GOPROXY (proxy.golang.org) + GOSUMDB (sum.golang.org) give tamper-evident, verified module content — go.sum mismatches fail the build. What they do not stop: typosquatted import paths and malicious-by-design packages — review new dependencies, pin versions, keep go.mod minimal.
  • Scan the shipped artefact: trivy or grype inspect container images — base-image CVEs plus Go binary analysis (both read the module metadata embedded in Go binaries via buildinfo). A scratch/distroless base shrinks the surface to your binary and CA certs.
  • Secrets scanning: gitleaks (or GitHub secret scanning / trufflehog) on the repo and its history; pre-commit hook plus CI job — a leaked key in an old commit is still leaked.
  • Defence in depth, one CI: each layer catches what the others miss; wire them all into the same pipeline as the functional tests.

Details

Layered pipeline

Layer Tool Catches Cadence
Dependency vulns (reachable) govulncheck CVEs your code can hit Every PR + nightly (DB moves)
Own-code patterns gosec / golangci-lint Injection, weak crypto, bad modes Every PR
Trust-boundary robustness go test -fuzz Panics, validation bypass Seeds per PR, fuzz nightly
Image / artefact trivy or grype Base-image + module CVEs in the shipped image On build + registry re-scan
Secrets gitleaks Committed credentials Pre-commit + every PR

CI wiring

  • Fail PRs on reachable vulns only (govulncheck's default exit behaviour); track unreachable ones in the nightly report rather than blocking.
  • Pin scanner versions but let vulnerability databases update per run — a green scan against a stale DB is theatre.
  • Emit SARIF (gosec, trivy and govulncheck all can) so findings land in the code-review UI instead of a log nobody reads.
  • Generate an SBOM at build (syft, or go version -m for the module list) so later CVE announcements can be matched against already-shipped versions.

Examples

$ govulncheck ./...
Vulnerability #1: GO-2025-3421 in golang.org/x/net@v0.19.0
    Your code calls http2.ReadFrame, which is vulnerable.
    Fixed in: golang.org/x/net@v0.23.0
$ go get golang.org/x/net@latest && govulncheck ./...
No vulnerabilities found.

$ gosec -quiet ./...
[G204] Subprocess launched with a potential tainted input or cmd arguments
    > exec.Command("sh", "-c", userArg)

$ trivy image --scanners vuln,secret registry.local/app:1.4.2

Related