Safeguard's software composition analysis (SCA, generally available) supports Go modules by reading go.mod and go.sum, resolving the full transitive module graph, and matching every module version against vulnerability advisories enriched with EPSS and CISA KEV. Go reachability analysis is generally available for both static and dynamic analysis, so findings in code your program never calls drop down the queue. When a fixed version exists, Griffin can open a fix pull request, but autonomous remediation ships switched off, with every category set to Manual.
The rest of this post covers what Safeguard's docs say it reads for Go, how the module system shapes what a scanner can know, how to scan from a laptop and from CI, how Go prioritisation works, what a good Go fix pull request contains, and where Safeguard stops.
Key takeaways
- SCA for Go modules (GA). Safeguard's CLI support matrix lists Go Modules, with
go.modas the manifest andgo.sumas the lock file, at full support. The legacy dep tool (Gopkg.toml,Gopkg.lock) is listed with limited support. - Deep dependency scanning (GA). The docs say deep scanning resolves
go.modandgo.sumand walks the resolved graph up to 100 levels, recording the path from your module to each vulnerable one. - Vulnerability management (GA). Findings carry CVE or GHSA identifiers, CVSS, EPSS, KEV status, the affected module and version, and the fixed version where one is known.
- Reachability analysis (GA). The docs' language table marks Go as GA for static and dynamic reachability and describes it as "module-aware, high-fidelity call paths".
- Autonomous remediation with Griffin fix pull requests (GA, ships off). The AI Remediate docs list Go modules with full support. Nothing is applied until you enable it; every category starts at Manual.
- Policies and gates (GA) decide whether a build fails.
safeguard scannever fails on findings by itself. - License compliance (GA) records licenses per component and writes a
license.jsonfrom CLI scans. - CLI and local runner (GA) let you scan on your own machine and send only results to the platform. SAST (GA) also runs in the default CI mode, but this post covers SCA only.
- Gold Open Source (GA) is a free, no login lookup for affected and fixed versions of a CVE or package.
The problem: Go dependencies are pinned, not safe
Go gets a lot right about dependency management. The Go team's own write up on supply chain security makes the point that every Go build is "locked": the version of every dependency is fully determined by go.mod, and since Go 1.16 build commands fail rather than silently change go.mod. The go.sum file records a cryptographic hash for every module the build uses, and the public checksum database gives the whole ecosystem an append only, verifiable record of those hashes.
None of that tells you whether the version you pinned has a known vulnerability. Pinning means you get the same code every time. If that code contains a CVE, you get the CVE every time too, until someone changes the requirement. A typical Go service pulls in dozens to hundreds of modules once you count everything below its direct requirements, and the module that carries the vulnerable function is often three or four hops away from anything your team chose on purpose.
This is the gap SCA fills. It reads what your build will use, matches it against vulnerability data, and tells you what to change.
What standards and regulators expect
Several requirement sets turn "know what is in your Go binaries and fix what is vulnerable" from good practice into an obligation.
- NIST SP 800-218, the Secure Software Development Framework (SSDF) version 1.1. Practice PW.4.1 asks producers to acquire and maintain well-secured software components from third parties. PW.4.4 asks them to verify that acquired open source components comply with your requirements throughout their lifecycle, with the example of regularly checking for publicly known vulnerabilities. RV.1.1 covers gathering vulnerability information, including monitoring vulnerability databases, and RV.2.2 covers planning risk responses for each vulnerability. PS.3.2 asks you to collect, protect, maintain and share provenance data for all components, which is where an SBOM comes in.
- CISA BOD 26-04, "Prioritizing Security Updates Based on Risk" (10 June 2026). For US federal agencies this directive sets remediation timelines from four factors: whether the asset is publicly exposed, whether the CVE is on CISA's KEV catalog, whether exploitation can be automated, and whether exploitation gives partial or total control. The timelines live in the directive's Table 1. BOD 26-04 explicitly revokes BOD 22-01, the 2021 directive that created the KEV catalog, so treat 22-01 deadlines as history only.
- The CISA KEV catalog itself. CISA describes the catalog as the authoritative source of vulnerabilities exploited in the wild and tells organisations to use it as an input to prioritisation.
- FIRST EPSS. EPSS publishes, every day, a probability between 0 and 1 that a published CVE will be exploited in the next 30 days. It is a ranking input, not a severity score.
You do not need to be a federal agency for these to matter. Customer security questionnaires, procurement rules and audits increasingly borrow the same ideas: inventory your components, check them against known vulnerabilities, prioritise exploited ones, and keep evidence.
How Go modules shape what a scanner can see
Before looking at Safeguard's flow, it helps to be precise about what the two Go files contain, because the details explain both what SCA does well and where its limits are.
go.mod: the requirement graph
go.mod declares your module path, the Go language version, and require directives for the modules you depend on. The Go modules reference explains that at go 1.17 or higher, go.mod includes an explicit requirement for every module that provides any package transitively imported by a package or test in the main module. Requirements that no package in your module imports directly carry an // indirect comment and sit in their own block.
That change matters for scanners. Before 1.17, go.mod often listed only direct dependencies plus a few indirect ones that minimal version selection needed. From 1.17 onward, the file is close to a complete list of the modules your build can load, which makes it a much better input for SCA.
module example.com/payments
go 1.22
require (
github.com/example/router v1.8.2
golang.org/x/net v0.25.0
)
require (
golang.org/x/text v0.15.0 // indirect
)
go.mod can also carry replace, exclude and retract directives. A replace swaps the contents of a module version for another version or a local directory, and only applies in the main module. An exclude stops a version from being loaded. A retract lets an author mark their own published versions as ones nobody should depend on.
go.sum: hashes, not a lock file in the npm sense
go.sum is often called Go's lock file, and Safeguard's support matrix lists it in the lock file column. It is worth knowing how it differs from a lock file in other ecosystems. Per the Go modules reference, each line has three fields: a module path, a version (possibly ending in /go.mod), and a hash. A line whose version ends in /go.mod holds the hash of that module's go.mod file only; otherwise the hash covers the module's zip contents.
golang.org/x/net v0.25.0 h1:<base64 SHA-256 of the module zip>
golang.org/x/net v0.25.0/go.mod h1:<base64 SHA-256 of its go.mod>
Two consequences follow:
go.sumcan list several versions of the same module. The go command may need thego.modfiles of multiple versions to run minimal version selection, so their hashes end up ingo.sumeven though only one version is built.go.sumcan hold stale entries. The reference notes it may contain hashes for versions that are no longer needed, for example after an upgrade, untilgo mod tidyremoves them.
So go.sum tells you which module contents the build trusts. It does not on its own say which single version of each module the build selects. Minimal version selection, run over the requirement graph in go.mod files, decides that.
Minimal version selection
Go picks versions with minimal version selection (MVS). The reference describes it as a walk over a directed graph where each vertex is a module version and each edge is a minimum required version from a require directive, adjusted by exclude and replace in the main module. For each module, the build uses the highest of the minimum versions anything in the graph asks for.
This is why a Go upgrade can be surprisingly contained. Raising one requirement raises the floor for that module and possibly a few of its own requirements. It does not float every other dependency to its newest release.
Where reachability fits for Go
Go is a good language for reachability analysis. It is statically typed and compiled, and the Go project's own vulnerability tooling, govulncheck, analyses source code or a binary's symbol table to narrow reports to vulnerabilities your code may actually call. The Go vulnerability database at vuln.go.dev publishes its reports in the OSV format with symbol level detail.
Govulncheck's documentation is also candid about the limits that apply to any static analysis of Go: calls made through package reflect are not visible to static analysis, use of unsafe can cause false negatives, and binary mode cannot produce call stacks because compiled Go binaries do not carry detailed call information. Keep those limits in mind when you read any reachability verdict, Safeguard's included.
What Safeguard reads for Go
Safeguard's docs describe Go support in four places. Taken together they are consistent.
| Docs page | What it says about Go |
|---|---|
| Software Composition Analysis | Go modules is in the supported ecosystems list; manifests and lockfiles are both honoured "so resolution matches what actually installs" |
| CLI support matrix | Go Modules: manifest go.mod, lock file go.sum, full support. Dep (legacy): Gopkg.toml, Gopkg.lock, limited support |
| Deep Dependency Scanning | "Go modules: resolves go.mod and go.sum" |
| CI/CD Integration | safeguard ci scan --local reads dependencies from lockfiles and manifests, with go.sum named in the list |
Two more entries in the support matrix are relevant to Go teams. It lists Linux ELF executables, Windows .exe files and macOS applications as supported binary file types, and it lists the major container registries. Go services usually ship as a static binary inside a container image, so those paths matter. The docs do not describe what Safeguard extracts from a Go binary specifically (for example, the module list that Go embeds and that the standard library's runtime/debug package exposes as build information), so test binary and image scans on your own artifacts before you rely on them as the only source of truth.
What the docs do not mention for Go is just as important. They say nothing specific about go.work workspace files, the vendor directory and vendor/modules.txt, private module settings such as GOPRIVATE, or how replace directives pointing at local directories are treated. Treat each of those as untested until you have scanned a repository that uses them and checked the result.
How Safeguard handles Go SCA, step by step
There are three ways to get a Go repository into Safeguard: connect it in the web app, scan it with the CLI on your own machine, or scan it in CI. All three produce the same kind of findings in the same findings model. Pick by where your code is allowed to go. The repository connection post covers that choice in depth.
Before you start: tidy the module
This step is about Go, not Safeguard, and it makes every scanner more accurate.
- Run
go mod tidyand commit the result. This adds missing requirements and hashes and removes stalego.sumentries, so the files the scanner reads match what you build. - Run
go mod verifyto confirm the module cache matches the recorded hashes. - Run
go list -m alland keep the output. It prints the build list that MVS selected. You will use it later to sanity check the SBOM Safeguard produces.
Path A: connect the repository in the web app
- In Safeguard, open Integrations from the sidebar and stay on the Add Project/Product tab. You can filter the cards by Source Code (SCM).
- Click Connect on the GitHub, GitLab, Bitbucket or generic Git card. Public repositories need only a URL; private ones need credentials. The Bitbucket walkthrough in the Generate SBOM docs shows the pattern: a username and access token or app password, then Verify Credentials.
- On the review step, pick the repositories, set the Branch/Tag for each, and set the Project Name and Version (the version defaults to 1.0.0 and is auto-detected from git tags in generation options).
- Review the generation options. Include Dev Dependencies controls whether development dependencies are included. Go has no separate dev dependency list in
go.mod, but test only imports can still pull in modules, so decide deliberately. Deep Analysis enables Griffin's deeper analysis. - Click Connect. The configuration appears under Project Configurations, and the project appears under Projects with its findings counts.
- Turn on extra triggers if you want them. Safeguard already rescans every project daily for newly disclosed vulnerabilities. The docs list On Push, Scheduled and On Release as additional triggers.
The docs give rough timings: under a minute for projects with fewer than 100 dependencies, two to five minutes for 100 to 500, and five to fifteen minutes beyond that.
Path B: scan on your own machine with the CLI
The Safeguard CLI (GA) is a single static binary; the docs note there is no runtime to install alongside it. Install it and open the interactive terminal:
curl -fsSL https://cli.safeguard.sh/install | bash
safeguard
Inside the terminal, type /login to authenticate and then /scan . to scan the folder you are in. Sign in uses an OAuth device code flow that opens your browser. A scan from the interactive terminal while signed in uploads the SBOM and findings to the platform; per the docs, your source never leaves the machine.
You can also scan directly from the command line, which writes results as JSON files under ./output by default:
cd ~/src/payments
safeguard scan --dir .
Each scan writes a folder containing vuln.json, license.json and collection.json, among other files. Two flags are worth knowing for Go:
# Resolve transitive dependencies to a chosen depth
safeguard scan --dir . --resolve-transitive --transitive-depth 100
# Run only selected scanners, write results elsewhere
safeguard scan --dir . --scanners <list> --output out/
The usage docs say --resolve-transitive resolves transitive dependencies through deps.dev, a public open source dependency service. That means module names and versions are looked up outside your network. If your module paths are private or sensitive, agree with your security team whether that lookup is acceptable before you add the flag. For most Go modules at go 1.17 or later, go.mod already lists every module the build can load, so the extra lookup matters less than it does in some other ecosystems.
The docs also describe --org-namespace for naming the namespaces of your own private packages. Their example is an npm style scope. Whether a Go module path prefix such as github.com/yourcompany is treated the same way is not stated, so check it on a real scan.
You can scan a Git repository without cloning it yourself, or a container image that holds your Go binary:
safeguard scan --repo https://github.com/org/repo
safeguard scan --image myregistry.azurecr.io/myapp:v1 \
--registry-username "$REGISTRY_USER" \
--registry-password "$REGISTRY_PASSWORD"
The CLI relies on open source scanners that the interactive terminal downloads the first time it runs. On a machine that has never opened the terminal, such as a fresh CI container, the docs say to put those tools on the PATH or point TOOLS_DIR at a directory that holds them. Run safeguard doctor if anything looks wrong; it checks environment, sign in, tools and policy without changing anything.
Path C: scan in CI
The CI/CD docs describe one step: safeguard ci scan. Each push becomes a new version of the Safeguard project, named after the branch and commit.
- In Safeguard, create a secret key under Settings › Developer with the scopes
scans:triggerandscans:read. You can lock it to one project. - Store it in your CI as a secret named
SAFEGUARD_API_KEY. - Add the step. For GitHub Actions the docs give this workflow, saved as
.github/workflows/safeguard.yml:
name: Safeguard
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
scan:
runs-on: ubuntu-latest
steps:
- name: Safeguard scan
env:
SAFEGUARD_API_KEY: ${{ secrets.SAFEGUARD_API_KEY }}
SAFEGUARD_INSTALL_DIR: ${{ runner.temp }}/safeguard-bin
run: |
set -eu
curl -fsSL https://cli.safeguard.sh/install | bash
"$SAFEGUARD_INSTALL_DIR/safeguard" ci scan --wait --fail-on critical
- Push. The run shows the scan result and a link to it in Safeguard.
There are two scan modes, and the choice matters for Go teams with private module hosts:
| Mode | Command | Needs | Scans |
|---|---|---|---|
| On Safeguard (default) | safeguard ci scan | The repository connected as an integration, or public on a major hosted Git service | SAST (GA), SBOM, dependency vulnerabilities |
| On the runner | safeguard ci scan --local | A checkout in the job; no integration | SBOM, dependency vulnerabilities |
With --local, only results leave the runner. The docs say it reads dependencies from lockfiles and manifests and name go.sum among them. For GitHub Actions, add actions/checkout@v4 before the step and --local to the command.
To fail the build, add --wait and then either --fail-on with a severity (critical, high, medium or low, applied to dependency vulnerabilities) or --fail-on-gate with a gate name, id or all. The docs are explicit that SAST findings are not part of --fail-on yet. Pin the CLI with SAFEGUARD_VERSION so a new release does not change a build under you.
If you would rather scan on the build machine and gate yourself, the docs show this pattern:
safeguard scan --dir . --output out/
jq -e '[.data.matches[].vulnerability.severity | ascii_downcase]
| index("critical") | not' out/*/vuln.json
safeguard scan exits 0 when the scan ran, whatever it found; 1 when the command failed; and 2 when results were written but the vulnerability scan did not actually run. There is no severity threshold flag on safeguard scan. Treat exit code 2 as a failure in your script, or a broken scan will look like a clean one.
Reading Go findings in Safeguard
Once the project is scanned, open it and go to the Vulnerabilities tab. The SCA docs list what each finding carries:
| Field | What it tells you for a Go module |
|---|---|
| Component and version | The module path and the version the build uses, for example golang.org/x/net v0.25.0 |
| CVE or GHSA identifiers | The advisory ids that name the vulnerability |
| Severity with EPSS and KEV | CVSS based severity plus exploit probability and whether it is known exploited |
| Transitive path | The chain from your module to the vulnerable one |
| Reachability verdict | Reachable, conditionally reachable, unreachable or unknown, where available |
| Fixed version | The safe upgrade target, where one exists |
The Vulnerabilities docs list the data sources Safeguard aggregates: NVD, GitHub Security Advisories, OSV, CISA KEV, vendor security bulletins and security researcher disclosures. The overview splits findings into Critical, High, Medium, Low and Exploitable, the last meaning known active exploitation.
The transitive path is the most useful field for Go
In the ESSCM UI you can click a finding to see the full chain that pulls the vulnerable module in. For Go this answers the first question every reviewer asks: did we choose this module, or did something else? If the vulnerable module is marked // indirect in your go.mod, the path shows which direct requirement is responsible. That decides the fix. Sometimes you raise the indirect requirement directly; sometimes you upgrade the direct dependency that needs it.
Filters that work for a Go backlog
The reachability docs list filters for the Vulnerabilities table. These combine well:
reachable:trueshows only findings with a real call path.kev:trueshows only KEV listed CVEs.epss:>0.3shows only CVEs with an exploit probability above 30 percent.env:production reachable:true kev:trueis what the docs call the P0 backlog.
You can also filter by component name, which helps when one shared module (a networking or crypto library, for instance) accounts for many findings across services.
How prioritisation works for Go
Safeguard's prioritisation engine combines four signals into one score from 0 to 100: reachability, exploitability (EPSS and KEV, plus whether a public proof of concept exists), runtime context, and business context. The weights are configurable per tenant under Settings › Prioritization. The docs give the default bands:
| Signals | Default priority |
|---|---|
| Reachable, KEV listed, production | 90 to 100 (P0) |
| Reachable, EPSS above 0.5, production | 75 to 89 (P1) |
| Reachable, high CVSS, staging | 50 to 74 (P2) |
| Unreachable, any severity | 0 to 30 (backlog) |
Go reachability in detail
The docs list three kinds of reachability analysis:
- Static reachability builds a call graph from source, lockfile and bytecode and checks whether any path leads to the vulnerable function. For Go, the docs mark static reachability GA.
- Dynamic reachability records which modules and functions are loaded and invoked in running workloads, through a runtime collector, and flags never executed paths as cold. The docs mark this GA for Go too. It needs that collector running in your environment, so scope it with Safeguard before you plan around it.
- Configuration reachability parses framework configuration to see whether code is wired in at all. The docs' examples are from other ecosystems; they do not name a Go framework.
Each finding is classified as reachable, conditionally reachable, unreachable or unknown. Unknown covers cases the analyser cannot decide, such as reflection, dynamic dispatch or native code, and the docs say these are marked conservatively as potentially reachable rather than suppressed. For Go, that means code paths that run through reflect, through interface values the analyser cannot resolve, or through cgo should come back as unknown, not as safe.
The docs are also honest that reachability is weakest for data driven vulnerabilities, where the danger is in what input reaches a function rather than whether the function is called, and for paths that only run under configurations the analyser cannot enumerate. For those, lean on KEV, EPSS and runtime observation. For a wider treatment of how the three signals combine, see prioritise vulnerabilities with reachability, EPSS and KEV in Safeguard, and for Go specifically, reachability analysis for Go modules.
How this maps to BOD 26-04
If you work for, or sell to, a US federal agency, BOD 26-04's four factors line up with signals Safeguard already holds. KEV status is a field on every finding. Asset exposure corresponds to the runtime context labels (internet facing, internal, air-gapped) that feed the score. Exploit automation and technical impact are published by CISA through its Vulnrichment program; Safeguard's docs do not say they ingest those two fields, so you still need to bring them into your triage yourself. Use Safeguard's priority as an input to your BOD 26-04 timeline, not as a replacement for it.
Fixing Go findings
When a fixed version exists, Safeguard's AI Remediate (part of autonomous remediation, GA) works through five stages according to the docs: analysis, solution generation, impact assessment, pull request creation and validation. The docs list Go modules with full support.
The three ways to start a fix
- Single vulnerability. Open the finding, click AI Remediate, review the proposed change and click Create Pull Request.
- Bulk. Select several findings, click AI Remediate Selected. Griffin batches compatible fixes and you create one pull request per repository.
- Page level. On the Vulnerabilities page, click AI Remediate at the top. Griffin analyses all fixable vulnerabilities and shows a plan to approve.
From the CLI, safeguard fix updates vulnerable dependencies; run safeguard fix --help for the flags in your build. The docs say --help is authoritative when it and the docs disagree.
Switched off until you turn it on
Autonomous remediation ships switched off. Every category starts at Manual, and nothing is applied automatically until an administrator enables it. Settings live under Settings › AI Remediate: Auto-Create PRs, Require Approval, Test Integration, Breaking Change Policy and Branch Naming. A sensible start for a Go estate is Require Approval on, Test Integration on, and a breaking change policy that sends anything rated Major or Critical to a human. The autonomous remediation post covers the eight categories and the strategies behind them.
What a good Go fix pull request looks like
The docs say AI Remediate upgrades vulnerable packages to fixed versions, handles transitive dependency conflicts, updates lock files and checks for breaking changes, rating each fix Safe, Minor, Major or Critical. Pull requests include a title with the CVE reference, a description of the vulnerability, a summary of changes, breaking change warnings, test results if configured, and links to advisories.
For Go, review every such pull request against this checklist, whoever opened it:
- Both files changed. A real Go upgrade changes the
requireline ingo.modand the matching hashes ingo.sum. A diff that touches only one of them will failgo buildunder the default read only module mode. - The indirect block is right. If the fix raises an indirect requirement, the module should stay in the indirect block with its
// indirectcomment, unless your code now imports it directly. - The major version did not change by accident. In Go, a new major version from v2 onward is a different module path (with a
/v2suffix, for example). A fix that changes the import path is a code change, not a version bump, and deserves a closer look. - No surprise replace. A fix should not add a
replacedirective to dodge a version. If one appears, ask why. go mod tidyis clean. Run it on the branch. If it changes anything, the pull request was incomplete.- Tests and the binary both build. Run
go test ./...and build the release binary. Then rescan the branch and confirm the finding is gone.
Where no fixed version exists, the reachability docs say Griffin can propose a safe downgrade. Treat a downgrade with extra care in Go, because MVS may pull the version back up if another module in the graph requires a higher minimum.
License data for Go modules
License compliance is GA. Safeguard's docs describe license detection from package metadata, SPDX identifiers, full text matching of LICENSE, LICENCE, COPYING and NOTICE files, source file headers, and binary extraction in container layers. When signals disagree, Safeguard raises a license discrepancy finding.
One Go specific point: go.mod has no license field, and the docs' list of metadata sources names other ecosystems' manifests, not go.mod. For Go modules, license identification therefore depends on the other routes, chiefly the license files that ship inside the module. Check a sample of your Go components in the license view and confirm the licenses match what you expect.
From there, the documented features apply as for any ecosystem:
- Classification into permissive, weak copyleft, strong copyleft, network copyleft, commercial, public domain and unknown.
- A YAML
LicensePolicywith allow, deny and require review lists, scoped by asset labels so a distributed CLI tool and a hosted service can have different rules. - Enforcement through guardrails at CI time, at admission and at pull request time.
- Attribution generation (a NOTICE file, a licenses folder and an SPDX document) for redistributed artifacts. The docs say there is no CLI command for this yet.
A CLI scan writes a license.json next to its other results. For Go teams that ship a static binary to customers, the attribution output matters: every module compiled into the binary is redistributed. See open source license obligations, OpenChain and attribution for the obligations side.
Gates: deciding what blocks a Go build
Policies and gates are GA. Policies evaluate SBOMs and generate findings; gates are the checkpoints that enforce them. You create policies under Settings › Policies with a visual builder or YAML. Conditions include vulnerability severity, CVSS score, EPSS score, license, component age and maintenance status.
Each policy's action decides what a failure does to the gate:
| Action | Effect |
|---|---|
| Block | The gate fails |
| Warn | The gate passes, with a warning naming the policy |
| Notify | The gate passes and a notification is sent |
The CLI does not evaluate policies itself; the docs are explicit that policies and gates run in the platform. To fail a CI job on a gate, use safeguard ci scan --wait --fail-on-gate <gate> with policy outcomes switched on for your workspace. The reachability docs also show policies that use reachability directly, such as blocking production when a reachable or conditionally reachable finding is on KEV. If you want to write policies in plain English rather than YAML, the plain English policies post walks through that flow.
A good first Go policy set is short: block on reachable KEV listed findings in production services, warn on reachable critical findings, notify on everything else. Tighten from there once the backlog is under control.
Decision table: which Safeguard route for which Go situation
| Your situation | Route | What you still own |
|---|---|---|
| Repositories on a hosted Git service and you are happy to connect them | Integrations in the web app, plus safeguard ci scan in CI | Choosing branches, reviewing fix pull requests |
| Code must not leave your machines | CLI with /scan . or safeguard ci scan --local | Keeping the scanner tools available on fresh CI runners |
| Private module host, firewalled Git server | safeguard ci scan --local on a runner that can fetch your modules | Module proxy and GOPRIVATE configuration for the build itself |
| Go service ships as a container image | safeguard scan --image on the built image | Base image updates, and checking what the binary scan reports |
| You need federal style prioritisation | Vulnerabilities tab filters and priority score as inputs | Exploit automation and technical impact inputs, and BOD 26-04 timelines |
| A fixed version exists | AI Remediate pull request, with Require Approval on | Merging, release, deployment |
| No fixed version exists | Safe downgrade proposal, or the fork and patch options below | Risk acceptance with an expiry, compensating controls |
| You want a quick public check of one module | Gold Open Source lookup, free and without login | Acting on the result |
How Safeguard compares with running govulncheck alone
Govulncheck is the Go project's own tool, it is free, and every Go team should know it. It is not a competitor to SCA in a platform; the two answer different questions.
| Question | govulncheck | Safeguard SCA (GA) |
|---|---|---|
| Does this module or binary call a known vulnerable Go function? | Yes, at symbol level, from the Go vulnerability database | Yes, through reachability analysis (GA for Go) |
| Which of 200 services carry this module version? | No, one module at a time | Yes, across every project in the tenant |
| Is the CVE on KEV, and what is its EPSS? | Not part of its output | Yes, on every finding |
| Can it fail a build on a policy? | You script it | Gates with Block, Warn and Notify |
| Does it open a fix pull request? | No | Yes, once autonomous remediation is enabled |
| Does it produce an SBOM? | No | Yes, SPDX and CycloneDX |
| License data? | No | Yes |
Plenty of teams run govulncheck in developer workflows and Safeguard at the portfolio level. If the two disagree on a finding, read the advisory: the Go database is curated for Go modules with symbol level data, while Safeguard aggregates several sources. Our earlier post on running govulncheck in production covers the tool itself.
What Safeguard does not do
Be clear about the edges before you roll this out.
- It does not change how Go fetches modules. Safeguard reads
go.modandgo.sumand reports on them. It does not replace the module proxy, the checksum database,GOPRIVATEorGOFLAGSsettings. Those protect the integrity of what you download; SCA tells you whether what you downloaded has known vulnerabilities. You need both. Our post on Go module security with sumdb, GOPROXY and private modules covers the fetch side. - It does not document Go workspaces, vendoring or local replaces. The docs are silent on
go.work,vendor/modules.txtandreplacedirectives that point at local paths. Test a repository that uses them before you assume coverage. - It does not promise Go standard library or toolchain findings. Vulnerabilities in the Go standard library depend on the toolchain version you build with, which
go.modexpresses through thegoandtoolchainlines. Safeguard's docs do not say whether those are matched. Keep your toolchain current and track Go security releases separately until you have confirmed otherwise. - It does not decide reachability with certainty. Reflection,
unsafe, cgo and data driven vulnerabilities defeat static analysis. Safeguard marks undecidable cases as unknown and does not suppress them, but an unreachable verdict is still a model of your code, not a proof. - It does not fail a build from
safeguard scan. That command exits 0 whatever it finds. Failing a build needssafeguard ci scanwith--fail-onor--fail-on-gate, or your own script over the output files. - It does not merge or ship fixes. Autonomous remediation ships off, every category Manual. Even when enabled, your team reviews, merges, releases and deploys.
- It does not include SAST results in the CI severity gate yet. SAST is generally available, and the docs say its findings are not part of
--fail-on. - It does not query findings by priority from the CLI. The reachability docs say there is no CLI command for that yet. Use the web app, Griffin's natural language search, or the connector in Claude.
Common mistakes
- Scanning an untidy module. Stale
go.sumentries and missing requirements make any scanner less accurate. Rungo mod tidyfirst. - Reading
go.sumas the build list. It can hold several versions of one module and stale ones too. Usego list -m allto see what MVS actually selected, and compare it with the SBOM. - Treating exit code 0 as a pass.
safeguard scanreturns 0 whatever it finds and 2 when the vulnerability scan did not run. Check for 2 and gate on findings properly. - Suppressing everything marked unreachable. Unreachable findings still drop to the backlog, not to zero. Keep KEV listed unreachable findings on a watch list, and remember that unknown means "not proven safe".
- Accepting a fix that changes the import path. A move to a new major version changes the module path. That is a code change, and it needs review like one.
- Forgetting fork pull requests in CI. GitHub gives fork pull requests no secrets, so the scan step fails there. The docs suggest scanning pushes only, or using
pull_request_targetwith great care, never running the fork's code in that job. - Using deps.dev resolution on private modules without asking. The
--resolve-transitiveflag looks dependencies up on a public service. Agree that with your security team first. - Scanning only source when you ship images. A Go service in a container also carries the base image. Scan the image you deploy, not only the repository.
- Turning on Auto-Create PRs on day one. Start with Require Approval and Test Integration on, and watch the success rate before you widen anything.
FAQ
Does Safeguard support go.mod and go.sum?
Yes. Safeguard's CLI support matrix lists Go Modules with go.mod as the manifest and go.sum as the lock file at full support, and the deep dependency scanning docs say Safeguard resolves both. SCA is generally available.
Does Safeguard support the old dep tool?
Partly. The support matrix lists dep (Gopkg.toml and Gopkg.lock) as limited support. If you still have dep projects, migrating them to modules will give better results in Safeguard and in the Go toolchain itself.
Is reachability analysis available for Go in Safeguard?
Yes. The reachability docs list Go as GA for both static and dynamic reachability. Dynamic reachability needs Safeguard's runtime collector in your workloads, so confirm that part in a scoped demo before you depend on it.
How deep does Safeguard go into Go transitive dependencies?
The deep dependency scanning docs say Safeguard walks the resolved graph up to 100 levels and records the full path from your module to each vulnerable one. From the CLI, --resolve-transitive --transitive-depth 100 sets the depth for a local scan.
Can Safeguard open pull requests to fix vulnerable Go modules?
Yes, through AI Remediate, part of autonomous remediation (GA). The docs list Go modules with full support. It ships switched off with every category set to Manual, so a person starts each fix or approves the pull request until you change that.
Does Safeguard fail my Go build on a critical CVE?
Only if you ask it to. Add safeguard ci scan --wait --fail-on critical to your pipeline, or use --fail-on-gate with a policy gate. The plain safeguard scan command never fails on findings.
Does my Go source code leave my machine when I scan with the CLI?
The docs say a signed in scan from the interactive terminal uploads the SBOM and findings, and that your source never leaves the machine. In CI, safeguard ci scan --local sends only results. The default CI mode is different: Safeguard clones the repository itself.
Should I still run govulncheck?
Many teams do. Govulncheck is the Go project's own tool and works at symbol level against the Go vulnerability database. Safeguard adds a portfolio view, EPSS and KEV, gates, license data and fix pull requests. Running both is reasonable, and disagreements are worth reading.
Does Safeguard find vulnerabilities in the Go standard library?
The docs do not say. Standard library vulnerabilities depend on the Go toolchain you build with. Until you have confirmed Safeguard's behaviour on your own projects, track Go security releases and keep your toolchain current separately.
Where do I check a single Go module for free?
Gold Open Source at gold.safeguard.sh is free and needs no login. Search a CVE or a module to see affected and fixed versions. See using Gold Open Source in your workflow.
To try any of this, download the CLI at https://safeguard.sh/download and read the docs at https://docs.safeguard.sh/docs .
Most vulnerable Go modules get fixed the ordinary way: a maintainer tags a new version and you raise your requirement. When a fix is late, or never comes, the options below are what is left, and they apply to Go modules and to the container images your Go binaries ship in.
Fix it without waiting for upstream
Whatever the vulnerability, most of the risk sits in two gaps. The first is the time between a fix being published and that fix actually running in your production builds. The second, and worse, is the time before any fix exists at all, while a maintainer works out a patch and a release schedule. Waiting through both gaps is a habit, not a requirement. Safeguard gives teams three ways to close them.
1. Autonomous remediation: pull requests, not tickets
When a fixed version exists, Safeguard's Griffin models open a tested, policy gated pull request that moves the affected dependency to the safe version. You can run it on one repository or in bulk across every project that carries the vulnerable version. Remediation categories include dependencies, licenses, security posture and findings, and each one is set separately. Reachability analysis ranks the vulnerable code paths your application actually calls first, so the first pull requests you merge are the ones that matter most. Autonomous remediation ships switched off, with every category set to Manual, so your team decides how much runs on its own and what still needs a human approval.
2. Gold Open Source: the safe version in seconds, free
gold.safeguard.sh is free and needs no login. Search any CVE or package to see affected and fixed versions, CVSS, EPSS, CISA KEV status and a recommended safe version, or use the "Am I affected?" checker. The same data is available through a free read API, README status badges and RSS threat feeds.
3. Gold Registry: start from components that are already fixed
Gold Registry provides hardened, drop in replacements for upstream packages and container images. Every Gold artifact is held continuously to zero exploitable critical or high CVEs and a completed license check, with signed build provenance (signed with Safeguard's key and verifiable with cosign, SLSA Build Level 3 as the design target) and a public evidence page you can hand to an auditor. An artifact loses its Gold badge if it regresses. Teams can point their own package directory at the registry so hardened equivalents resolve without changing how developers install anything.
When upstream has no fix yet, or will not ship one, Safeguard first works to get the fix accepted upstream. If the issue is severe or upstream will not move, Safeguard builds a fork, fixes the vulnerability there and publishes a hardened Gold build, so you are not stuck on a maintainer's release schedule. You can also request a hardened, compatibility tested build of a specific package. Ask which packages it covers before you plan around it.
| Your situation | What to use |
|---|---|
| A fixed version exists | Autonomous remediation opens the upgrade pull request; Gold Open Source confirms the safe version |
| You need to know today whether you are affected | Gold Open Source lookup and "Am I affected?" checker, free and without login |
| You want vulnerable versions to stop shipping | Safeguard policies and gates in CI |
| Upstream has no fix yet, or will not ship one | A custom Gold request; fork and patch |
| You want to start clean on new projects | Pull hardened components and images from Gold Registry |
One honest limit: when the vulnerable code lives in a vendor appliance, a hosted service or an operating system runtime you do not build yourself, the fix still has to come from that vendor or distribution. The steps above apply to the open source dependencies and container images you build and ship, which is where most teams carry the largest and least tracked share of their exposure.
See how Safeguard would help
The first demo is a real Safeguard CLI session. It installs the CLI, opens the interactive terminal and asks it to fix a lodash CVE in a JavaScript repository; the assistant reads the finding and advisory, asks before any command that changes something, edits the manifest, runs the install and tests, prints a summary and pushes nothing, then shows /permissions, /model and /scan .. The repository in the demo is JavaScript, not Go. The same flow applies to a Go module: the fix would change go.mod and go.sum, and the commands would be go get, go mod tidy and go test ./....
Click through it at your own pace, or open it full screen.
The second demo sets up the local runner on your own machine, approves it with its device code, and then scans from the terminal. This is the route for Go teams whose code or private modules cannot leave their own machines: the scan runs where the modules already are, and only results go to the platform.
Click through it at your own pace, or open it full screen.
The third demo connects Claude to Safeguard: you add Safeguard from Claude's connector directory, where it has a community listing, sign in with OAuth, and choose which tools Claude can use. Once connected, you can ask Claude which of your projects carry a given Go module version or which Go findings are reachable, within the tools your administrator has enabled.
Click through it at your own pace, or open it full screen.
Quick check
Why is go.sum not, on its own, a list of the module versions your Go build uses?
Because go.sum can hold hashes for several versions of the same module, which the go command needs to run minimal version selection, and stale hashes left over after an upgrade until go mod tidy removes them. Minimal version selection over the go.mod requirement graph decides which single version is built; go list -m all shows the result.
A Go finding in Safeguard is marked unknown for reachability. Should you suppress it?
No. Safeguard's docs say unknown means the analyser could not decide, for example because of reflection or dynamic dispatch, and such findings are treated as potentially reachable rather than suppressed. Triage them with KEV, EPSS and runtime context instead.
Sources
- Safeguard docs: Software Composition Analysis
- Safeguard docs: CLI support matrix
- Safeguard docs: Deep Dependency Scanning
- Safeguard docs: Vulnerability Prioritization (Reachability Analysis)
- Safeguard docs: Generate SBOM
- Safeguard docs: Vulnerabilities
- Safeguard docs: AI Remediate
- Safeguard docs: License Compliance
- Safeguard docs: Policies and Gates
- Safeguard docs: Safeguard CLI
- Safeguard docs: CLI usage
- Safeguard docs: Slash commands
- Safeguard docs: CI/CD Integration
- Safeguard docs: GitHub Actions
- Go Modules Reference
- Go blog: How Go mitigates supply chain attacks
- Go: Vulnerability management
- Go: Vulnerability database
- govulncheck command documentation
- Go: Managing dependencies
- Go standard library: runtime/debug
- NIST SP 800-218, Secure Software Development Framework version 1.1
- NIST SP 800-218 full text (PDF)
- CISA BOD 26-04: Prioritizing Security Updates Based on Risk
- CISA Known Exploited Vulnerabilities Catalog
- FIRST: Exploit Prediction Scoring System
- OpenSSF: OSV schema
- CycloneDX specification overview
Last reviewed: 25 October 2026. Product capabilities change; check docs.safeguard.sh for the current state.