How to Use govulncheck to Find Known Vulnerabilities

Run govulncheck ./... to scan your Go module for known vulnerabilities in the code and dependencies.

The vulnerability you didn't know you had

You merge a feature. Tests pass. The linter is silent. Two days later, a security alert pings the team: a dependency you use has a remote code execution flaw. Or worse, the flaw is in how you called a standard library function, and the exploit is already in the wild. You didn't write the bug, but your code calls the function that triggers it.

govulncheck catches this before the merge. It scans your module against the Go Vulnerability Database and reports only the vulnerabilities that affect your code. It doesn't just list every CVE in your dependency tree. It analyzes your code to see if the vulnerable code path is actually reachable.

What govulncheck actually does

govulncheck is a command-line tool that checks Go programs for known vulnerabilities. It fetches data from the Go Vulnerability Database, which aggregates vulnerability reports in the VEX format. The tool parses your code, builds a call graph, and traces data flow to determine if a vulnerability is exploitable in your application.

Unlike a simple dependency checker that flags any package with a known issue, govulncheck understands version ranges and code usage. If you import a library but never call the vulnerable function, govulncheck may suppress the report. If you are using a patched version of a package, the tool marks you as safe. It focuses on what matters: can an attacker exploit your running program?

The tool uses GO- identifiers for vulnerabilities. These map to CVEs but are curated specifically for the Go ecosystem. The database includes vulnerabilities in third-party modules and the standard library.

Running your first scan

Install govulncheck using go install. The tool lives in the golang.org/x/vuln repository, not in the core Go distribution.

# Installs the latest version of govulncheck to your GOPATH/bin
go install golang.org/x/vuln/cmd/govulncheck@latest

Run the tool from your module root. Pass ./... to scan all packages in the module.

# Scans the current module and all packages for known vulnerabilities
govulncheck ./...

The tool fetches the vulnerability database on first run and caches it locally. Subsequent runs use the cache until the data expires. If the network is unavailable, govulncheck falls back to the cached database but warns you that the data might be stale.

# Example output when a vulnerability is found
Vulnerability ID  Package               Severity  Description
GO-2023-1234      github.com/example/lib High      SQL injection in Query method

Call stack:
  main.HandleRequest
  github.com/example/lib.Query

The output lists the vulnerability ID, the affected package, severity, and a description. It also shows the call stack leading to the vulnerable function. This tells you exactly where in your code the risk originates.

govulncheck finds the holes. You plug them.

How the analysis works under the hood

govulncheck performs static analysis. It reads your source code and go.mod file to understand your dependencies. The tool builds a representation of your program's structure, including packages, functions, and calls.

The analysis happens in three phases. First, govulncheck resolves all dependencies and checks their versions against the vulnerability database. It identifies which packages have known issues and which versions are affected. Second, the tool analyzes your code to find calls to vulnerable functions. It traces the call graph to see if your code invokes the vulnerable function directly or indirectly. Third, govulncheck applies VEX data to refine the results. VEX entries can specify conditions under which a vulnerability is exploitable. The tool uses this information to filter out false positives.

For example, a vulnerability might only affect a function when a specific flag is enabled. If your code never sets that flag, govulncheck can suppress the report. The tool also checks for data flow. If a vulnerability requires user input to reach a sink, govulncheck traces whether untrusted data flows from a source like http.Request to the vulnerable sink.

Vulnerabilities are facts. Usage is context.

VEX format and reachability

The Go Vulnerability Database uses the VEX format, which stands for Vulnerability Exploitability eXchange. VEX is a JSON-based standard for describing vulnerabilities and their impact on products. Each VEX document contains statements about a vulnerability, the affected components, and the status.

A VEX statement includes the vulnerability ID, the package name, version ranges, and a status. The status can be affected, not_affected, fixed, or under_investigation. govulncheck uses these statuses to determine if your code is at risk. If the status is not_affected, the tool ignores the vulnerability for that package. If the status is fixed, the tool checks if your version includes the fix.

VEX also supports "affects" ranges. A vulnerability might affect versions >=1.0.0 <1.2.0. govulncheck parses these ranges and compares them against your go.mod. If you are on v1.2.0 or higher, the tool considers you safe. This precision prevents the noise of flagging every dependency that has ever had a vulnerability.

The database includes VEX data for standard library packages. If a Go release introduces a vulnerability, the VEX data specifies the affected Go versions. govulncheck checks your go.mod directive to see if you are using a vulnerable Go version.

Trust the VEX data. Verify the call stack.

Realistic scenario: CI integration

Integrating govulncheck into your CI pipeline ensures every pull request is scanned for vulnerabilities. The tool fails with a non-zero exit code if it finds issues, blocking the merge. This shifts security left and prevents vulnerable code from reaching production.

Here's a GitHub Actions workflow that runs govulncheck on every push and pull request.

jobs:
  security:
    runs-on: ubuntu-latest
    steps:
      # Checkout the repository code
      - uses: actions/checkout@v4
      # Setup the Go environment
      - uses: actions/setup-go@v5
        with:
          go-version: '1.21'
      # Install govulncheck tool
      - run: go install golang.org/x/vuln/cmd/govulncheck@latest
      # Run scan and fail if vulnerabilities are found
      - run: govulncheck ./...

The workflow checks out the code, sets up Go, installs govulncheck, and runs the scan. The govulncheck ./... command exits with code 1 if vulnerabilities are detected, causing the job to fail. You can configure the workflow to run on a schedule to catch new vulnerabilities in dependencies over time.

For larger projects, you might want to generate a JSON report for integration with security dashboards. govulncheck supports the --json flag.

# Outputs results in JSON format for tooling integration
govulncheck --json ./... > vuln-report.json

The JSON output contains structured data about each vulnerability, including IDs, packages, call stacks, and metadata. You can parse this file to aggregate metrics or feed it into a vulnerability management system.

Don't wait for the audit. Scan on every commit.

Pitfalls and limitations

govulncheck is powerful, but it has boundaries. The tool relies on the Go Vulnerability Database, which depends on community contributions and vendor reports. New vulnerabilities might not appear immediately. The database updates periodically, so there is a lag between a disclosure and availability in the tool.

The analysis is static. govulncheck cannot prove that runtime sanitization prevents exploitation. If your code calls a vulnerable function but sanitizes input before the call, govulncheck might still report the vulnerability because it cannot verify the sanitization logic at compile time. You need to review the call stack and confirm that your defenses are effective.

Private modules can cause issues. govulncheck needs access to module metadata to resolve versions and analyze code. If your project uses private modules behind authentication, the tool might fail to fetch metadata. You need to configure authentication using GOPRIVATE and credentials so govulncheck can access the private repository.

# Sets GOPRIVATE to include your private domain
export GOPRIVATE=github.com/my-org/*

Network failures can interrupt scans. govulncheck fetches the database from the internet. If the network is down or the database server is unreachable, the tool errors out. The error message indicates the failure.

The tool reports failed to fetch vulnerability database: network error if it cannot reach the database.

You can work around this by pre-fetching the database in your CI environment or using a local mirror. The tool caches the database, so offline runs use the last known data.

govulncheck does not replace other security tools. It focuses on known vulnerabilities. It does not detect logic flaws, business logic errors, or misconfigurations. Use it alongside static analysis tools and dynamic testing for comprehensive coverage.

The worst vulnerability is the one you assume doesn't exist.

When to use govulncheck

Use govulncheck when you want to verify that your code does not use known vulnerable functions. Use govulncheck when you need to check both your code and dependencies against a curated database. Use govulncheck when you want actionable reports with call stacks pointing to the exact usage. Use gosec when you need pattern-based static analysis for common Go anti-patterns and security best practices. Use dependabot or renovate when you want automated pull requests to update dependencies to patched versions. Use go vet when you want built-in checks for suspicious constructs without external tools.

govulncheck finds the holes. You plug them.

Where to go next