The Subtleties of Vulnerability Scanning in Go Projects



This content originally appeared on DEV Community and was authored by Oleg Sydorov

Today, I want to talk about addressing vulnerabilities in our Go projects. As you may know, the standard tool for vulnerability checking is govulncheck, an official utility from the Go development team that leverages the Go Vulnerability Database. In addition to being used as a standalone tool, this utility is integrated into GoLand IDE starting from version 2023.1, enabling real-time scanning of the go.mod file. Of course, you can always run govulncheck manually as well.

At first glance, it seems straightforward — what could possibly go wrong? Imagine you finish working on your code, run govulncheck, and confidently commit your changes. Suddenly, your CI/CD pipeline fails to build or, worse, your IT security team escalates an issue. You’re puzzled: how is this possible?

The nuance here lies in the fact that govulncheck only checks and considers effective dependencies, i.e., those actually used by the code. In contrast, many scanners commonly integrated into pipelines — such as Trivy, Grype, or platforms like Sonatype IQ (Nexus Lifecycle) — often operate in a “paranoid” mode, scanning the entire dependency graph, including unused or transitive packages. This frequently leads to situations where seemingly clean code triggers multiple vulnerability alerts.

My code is clean!
My code is clean!

How is it possible?
How is it possible?

Let’s look at the detailed report (Sonatype OSS index example)

Sonatype detailed report
Let’s turn to our go.mod file.

module my-awesome-project

go 1.23.0

toolchain go1.23.9

require (
    github.com/aws/aws-cdk-go/awscdk/v2 v2.204.0
    github.com/aws/aws-cdk-go/awscdklambdagoalpha/v2 v2.204.0-alpha.0
    github.com/aws/aws-lambda-go v1.49.0
    github.com/aws/constructs-go/constructs/v10 v10.4.2
    github.com/aws/jsii-runtime-go v1.112.0
)

require (
    github.com/Masterminds/semver/v3 v3.4.0 // indirect
    github.com/cdklabs/awscdk-asset-awscli-go/awscliv1/v2 v2.2.244 // indirect
    github.com/cdklabs/awscdk-asset-node-proxy-agent-go/nodeproxyagentv6/v2 v2.1.0 // indirect
    github.com/cdklabs/cloud-assembly-schema-go/awscdkcloudassemblyschema/v44 v44.9.0 // indirect
    github.com/cdklabs/cloud-assembly-schema-go/awscdkcloudassemblyschema/v45 v45.2.0 // indirect
    github.com/fatih/color v1.18.0 // indirect
    github.com/mattn/go-colorable v0.1.14 // indirect
    github.com/mattn/go-isatty v0.0.20 // indirect
    github.com/yuin/goldmark v1.7.12 // indirect
    golang.org/x/lint v0.0.0-20241112194109-818c5a804067 // indirect
    golang.org/x/mod v0.26.0 // indirect
    golang.org/x/sync v0.16.0 // indirect
    golang.org/x/sys v0.34.0 // indirect
    golang.org/x/tools v0.35.0 // indirect
)

What do we see? Our project does not have the problems reported by the scanner! However, as soon as you follow go mod graph | grep ….some_bad_package command, the situation immediately changes: the secret becomes clear.

Let’s look at the detailed report again. Let’s take a closer look at the particular vulnerability that was discovered.


The scanner complains about version 2.2.240 and recommends replacing it with a version no lower than 2.2.242

Well, now let’s turn to the go.sum file. Maybe the answer lies here?

go.mod (unfixed)
go.mod (unfixed)

Excellent! So, the crime is solved.

But how can this be addressed? The solution is as follows: You need to use the replace directive to override vulnerable packages with safe versions directly in your go.mod file.

In our case (see the full list of the detected vulnerabilities above), we will apply to the go.mod file such a fix:

replace (
    github.com/cdklabs/awscdk-asset-awscli-go/awscliv1/v2 => github.com/cdklabs/awscdk-asset-awscli-go/awscliv1/v2 v2.2.244
    github.com/golang-jwt/jwt/v5 => github.com/golang-jwt/jwt/v5 v5.2.2
    golang.org/x/crypto => golang.org/x/crypto v0.38.0
    golang.org/x/net => golang.org/x/net v0.40.0
    golang.org/x/text => golang.org/x/text v0.25.0
)

I recommend exercising caution:

Ensure compatibility and correct versioning with your Go environment
Verify that your code still builds successfully after the change

After applying the fix, run go mod tidy and, if needed, go mod vendor to clean up and update your module dependencies accordingly.

The results were not long in coming:

go.mod (fixed)
go.mod (fixed)

Likewise, the rest of the packages such as crypto, text, net, etc. also have been fixed.

Sonatype report - no critical vulnerabilities found

Thus, as we’ve seen, the problem runs deeper and its resolution cannot be fully achieved using only standard methods and tools.

Wishing you successful development and clean code!


This content originally appeared on DEV Community and was authored by Oleg Sydorov