Trivy, the popular security scanner, can look inside your Go binaries to find vulnerable dependencies, even if the source code isn’t available.
Let’s see it in action. Imagine you have a Go binary named my-app that you suspect might have some outdated or vulnerable libraries. You can run Trivy directly against this binary:
trivy fs --scanners vuln --security-checks vuln my-app
Trivy will then analyze the binary, decompressing and inspecting its internal structure to identify the Go modules and their versions used. It’s like opening up the compiled executable and reading the go.mod file that was used to build it, but without needing the source.
The problem this solves is significant: often, you’ll end up with compiled binaries from third parties, or you might have legacy binaries where the original build environment is long gone. Without source code, traditional dependency scanning tools are useless. Trivy bridges this gap by understanding the Go build artifacts embedded within the binary itself.
Internally, Trivy uses a combination of techniques. For Go binaries, it leverages the fact that Go binaries often contain embedded information about the modules and versions they were built with. Trivy can extract this information. It first identifies the binary as a Go executable. Then, it looks for specific sections or metadata within the binary that list the dependencies. Once it has a list of module paths and versions (e.g., github.com/gin-gonic/gin v1.7.0), it compares these against its vast vulnerability database to flag any known CVEs.
The exact levers you control are primarily through the trivy fs (filesystem) command. The --scanners vuln flag tells Trivy to focus its analysis on vulnerability detection. The --security-checks vuln flag further refines this to specifically look for known vulnerabilities in dependencies. You can also scan directories containing multiple binaries, or even container images, with similar commands.
What’s often overlooked is that Trivy can also identify vulnerabilities in your dependencies of dependencies. When it lists a vulnerable module, it’s not just reporting the direct dependency you might have added; it’s tracing the entire dependency tree. This means if your main.go imports A, and A imports B, and B imports a vulnerable C, Trivy will flag the vulnerability in C, even though you never directly specified C in your go.mod.
The next step is to understand how to interpret and act on these findings, particularly when dealing with transitive dependencies.