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 tox.Parsereaches 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/randwherecrypto/randbelongs, unchecked errors on security calls, permissive file modes. Also available as a golangci-lint linter; suppress with//nolint:gosec // justificationand audit the suppressions. - Fuzzing is security testing: native
go test -fuzzon 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.summismatches fail the build. What they do not stop: typosquatted import paths and malicious-by-design packages — review new dependencies, pin versions, keepgo.modminimal. - Scan the shipped artefact:
trivyorgrypeinspect 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, orgo version -mfor 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
- Testing in Go — strategies & tooling map — parent map of the strategy portfolio.
- Property-based testing & fuzzing — the fuzzing technique this note points at trust boundaries.
- govulncheck — the tool note: modes, flags, database.
- Go security — building secure Go code, the flip side of testing it.
- AppSec fundamentals — the SCA/SAST/DAST vocabulary these tools instantiate.
- Security testing in Java — dependency-check/SpotBugs/ZAP as the JVM mirror, without reachability analysis.