Go Module Supply Chain Security: Proxy, Checksums, govulncheck, and Private Modules
Problem
Go’s module system ships with two infrastructure components that most other language ecosystems lack: a transparent module proxy and a checksum database. Together they make supply chain attacks meaningfully harder. But “harder” is not “impossible,” and a surface-level understanding of these controls leads teams to trust them beyond their actual scope.
- The module proxy and checksum database protect against tampering after publication. They do not protect against malicious code that was in the module before it was published, a typosquatted module name, or a vanity import path that redirects to attacker-controlled source.
go.sumis only useful if it is committed and verified in CI. A missing or outdatedgo.sumfile, or a CI pipeline that allows updates without review, undermines the entire checksum chain.govulncheckis not run by default. Known vulnerabilities in dependencies are invisible unless you explicitly scan for them. Unlikenpm audit, which runs automatically,govulncheckmust be installed and invoked as a separate step.GONOSUMCHECKandGONOSUMDBbypass checksum verification entirely. They exist for legitimate use cases — private modules that cannot be published to sum.golang.org — but are routinely misconfigured in ways that create silent verification gaps for public modules.replacedirectives ingo.modcan silently redirect module resolution. Areplacethat points to a local path or an attacker-controlled repository will override the checksum database lookup for the replaced module. This is a vector for build-time compromise in projects that accept external contributions.go installwithout a version pin fetches the latest version.go install golang.org/x/tools/gopls@latestsilently resolves to whatever version is current at build time, bypassing reproducibility guarantees.
Go is widely used for security-critical infrastructure: container runtimes, admission webhooks, certificate managers, Kubernetes operators, and service meshes. Supply chain compromise in a Go module can have blast radius far beyond a typical application dependency.
Target systems: Go applications and services using Go modules (Go 1.11+); CI pipelines on GitHub Actions, GitLab CI, Tekton, or Jenkins; teams maintaining internal Go tooling or open-source Go projects.
Threat Model
-
Adversary 1 — Typosquatted module name: A developer adds
github.com/acme/utlisinstead ofgithub.com/acme/utils. An attacker who registered the typo module before the legitimate one exists provides a malicious package. The proxy serves it; the checksum database records the checksum of the malicious module — the checksum database only proves the module has not changed since it was first indexed, not that it is safe. -
Adversary 2 — Vanity import path hijack: A module uses a vanity import path like
go.company.com/sdk. The vanity redirect is controlled by a DNS record or a web server serving<meta>tags. If that server is compromised or the DNS record is hijacked, futurego getcommands for that module will resolve to attacker-controlled source. The checksum database protects against the resolved content changing after first fetch, but not against the initial resolution pointing to malicious source. -
Adversary 3 —
replacedirective injection: An attacker submits a pull request to an open-source project that includes a seemingly innocuousreplacedirective ingo.mod:replace golang.org/x/crypto => github.com/attacker/crypto v0.0.1. Code reviewers who do not readgo.moddiffs carefully merge the PR. CI builds now resolvegolang.org/x/cryptoto the attacker’s fork, which is excluded from checksum database verification if the attacker controls theGONOSUMDBconfiguration, or is silently trusted if thereplaceis accepted at face value. -
Adversary 4 — Known CVE in transitive dependency: A transitive Go module dependency has a published advisory in the Go vulnerability database. The project never runs
govulncheck. The vulnerability is exploited in production months after the advisory is published. Because Go statically links dependencies into binaries, the vulnerable code is present in every deployed binary compiled after the dependency was added. -
Adversary 5 — Private module leaked through public proxy: A developer imports an internal module (
go.internal.company.com/payments/v2) without configuringGOPRIVATE. Thegocommand attempts to look up the module throughproxy.golang.organd record the path insum.golang.org. The lookup fails (the module is not public), but the module path has now been disclosed to Google’s infrastructure. In extreme cases, a misconfigured module with a publicly routable path could be served by the proxy from an earlier poisoned fetch. -
Access level: Adversaries 1–3 require write access to a public package registry or DNS control. Adversary 4 requires only that the vulnerable dependency exists and is invoked. Adversary 5 is an information disclosure risk inherent to misconfigured
GOPRIVATE. -
Objective: Code execution during build or at runtime; credential theft through malicious module code; disclosure of internal module paths to external infrastructure.
Configuration
Step 1: Understand the Module Proxy and Checksum Database
The Go module system routes all dependency resolution through two infrastructure components by default.
proxy.golang.org is a transparent caching proxy. When you run go get github.com/some/module@v1.2.3, the go command fetches the module from the proxy rather than directly from GitHub. The proxy:
- Caches module content so it remains available even if the upstream repository is deleted or goes private.
- Provides a consistent, immutable source for a given module version once it has been fetched.
- Does not audit module content for malicious code.
sum.golang.org is the checksum database (also called a transparency log). It records the hash of every module version it has seen, in an append-only Merkle tree. When you download a module, the go command verifies the module’s hash against the entry in this log. This means:
- If a module version was tampered with after being indexed, the checksum mismatch causes a build failure.
- If a module version has never been indexed and is being fetched for the first time, the hash is recorded. A subsequent fetch of the same version must match.
- It does not prevent a malicious module from being indexed with its malicious content intact.
The go.sum file in your repository records the hashes of every module (and each module’s go.mod file) that your project depends on. This file is the local record of what your project has verified — it must be committed to version control and treated as a security artifact.
# View the current GOPROXY and GONOSUMDB settings.
go env GOPROXY
go env GONOSUMDB
go env GONOSUMCHECK
go env GOFLAGS
# Default values on a standard Go installation:
# GOPROXY=https://proxy.golang.org,direct
# GONOSUMDB= (empty — all modules go through sum.golang.org)
# GONOSUMCHECK= (empty)
# GOFLAGS= (empty)
The direct fallback in GOPROXY=https://proxy.golang.org,direct means that if the proxy does not have a module, Go falls back to fetching it directly from the VCS. This fallback bypasses the proxy’s caching guarantees. Consider whether you need it:
# Remove the direct fallback — require all modules to be available in the proxy.
# Appropriate for production CI where all dependencies should already be cached.
export GOPROXY=https://proxy.golang.org
# For airgapped or private-proxy environments, disable the public proxy entirely.
export GOPROXY=https://goproxy.internal.company.com
Step 2: go.sum — Commit, Verify, and Enforce
The go.sum file must be committed to the repository. Without it, the go command cannot verify that module content has not changed between fetches. This is not optional for production services or security-critical code.
# Verify that all modules in go.mod match their go.sum entries.
# This reads go.sum and re-fetches module checksums from the module cache
# or the checksum database, comparing against recorded hashes.
go mod verify
# Expected output when go.sum is intact and all modules are unmodified:
# all modules verified
# If a module's content has been tampered with locally (in the module cache):
# github.com/some/module v1.2.3: zip has been modified (...)
In CI, run go mod verify as an early step before any compilation. Add it alongside go mod tidy checks to ensure the go.sum file is complete and consistent:
# .github/workflows/security.yml — module integrity checks.
name: Module Integrity
on:
push:
branches: [main]
pull_request:
schedule:
- cron: "0 6 * * *"
jobs:
module-integrity:
name: go mod verify
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version-file: go.mod
cache: true
- name: Verify module checksums
run: go mod verify
- name: Check go.sum is up to date
# go mod tidy updates go.sum; if there are diffs after running it,
# the committed go.sum was not generated from the current go.mod.
run: |
go mod tidy
git diff --exit-code go.sum go.mod
The git diff --exit-code step catches the common case where a developer updated go.mod manually without running go mod tidy, leaving go.sum inconsistent with the actual dependency tree.
Step 3: GOFLAGS=-mod=readonly in CI
By default, the go command will update go.mod and go.sum during a build if they are missing entries. In CI, this behaviour is dangerous: it means a build can silently pull in new module versions and update the lockfiles without any review.
# Set -mod=readonly to prevent the go command from modifying go.mod or go.sum.
# With this flag, the build fails if go.mod or go.sum are out of date.
export GOFLAGS=-mod=readonly
# Or set it per-command without modifying the environment:
go build -mod=readonly ./...
go test -mod=readonly ./...
In CI, set GOFLAGS in the job environment rather than per-command to ensure it applies to all go invocations in the job, including indirect calls from build tools or Makefiles:
jobs:
build:
runs-on: ubuntu-latest
env:
GOFLAGS: "-mod=readonly"
GONOSUMCHECK: "" # Explicitly empty — never bypass checksum verification.
GOPROXY: "https://proxy.golang.org,direct"
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version-file: go.mod
cache: true
- run: go build ./...
- run: go test ./...
The explicit GONOSUMCHECK: "" line in the CI environment definition documents the intent — no modules bypass checksum verification — and prevents an accidentally set shell variable from propagating into the build.
Step 4: govulncheck — Vulnerability Scanning
govulncheck is the official Go vulnerability scanner, maintained by the Go security team. It queries the Go vulnerability database (https://vuln.go.dev/) and uses a call-graph analysis to determine whether a vulnerable function is actually reachable in your code. This call-graph analysis reduces false positives compared to SCA tools that flag any dependency that contains a CVE regardless of whether the vulnerable code path is invoked.
# Install govulncheck.
go install golang.org/x/vuln/cmd/govulncheck@latest
# Scan the entire module for vulnerabilities.
# --scan=package (default): report only vulnerabilities in packages your code imports.
# --scan=module: report all vulnerabilities in modules you depend on,
# even if you don't import the affected package.
govulncheck ./...
# Use --scan=module for a conservative policy that flags all known-vulnerable
# module versions, regardless of call reachability.
govulncheck --scan=module ./...
# Output JSON for downstream processing (SIEM, dashboards, policy engines).
govulncheck -json ./... | tee govulncheck-report.json
# Scan a compiled binary directly — useful for auditing pre-built tools
# without access to source.
govulncheck -mode=binary /usr/local/bin/some-go-tool
The difference between --scan=package and --scan=module matters for policy decisions:
| Flag | Reports | Use When |
|---|---|---|
--scan=package (default) |
Only vulnerabilities in packages your code directly imports and where the vulnerable call is reachable | General application development; minimising noise |
--scan=module |
All vulnerabilities in any version of any module in your dependency graph, whether or not the affected package is imported | Security-critical services; compliance requirements; conservative vulnerability management policy |
Integrate govulncheck into CI with a scheduled run to catch newly published advisories for existing dependencies:
jobs:
govulncheck:
name: govulncheck
runs-on: ubuntu-latest
permissions:
contents: read
security-events: write
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version-file: go.mod
cache: true
- name: Install govulncheck
run: go install golang.org/x/vuln/cmd/govulncheck@latest
- name: Run govulncheck (package scan)
run: govulncheck ./...
# govulncheck exits with code 3 when vulnerabilities are found,
# which fails this CI step and blocks the PR.
- name: Run govulncheck (module scan for policy enforcement)
# This step reports vulnerabilities even in packages you don't import.
# Use 'continue-on-error: true' here and handle the report downstream
# if you want to distinguish "reachable CVE" from "module-level CVE".
run: govulncheck --scan=module -json ./... | tee govulncheck-module.json
- name: Upload vulnerability report
if: always()
uses: actions/upload-artifact@v4
with:
name: govulncheck-report
path: govulncheck-module.json
For GitLab CI, the equivalent configuration:
govulncheck:
stage: security
image: golang:1.24-alpine
script:
- go install golang.org/x/vuln/cmd/govulncheck@latest
- govulncheck ./...
rules:
- if: $CI_PIPELINE_SOURCE == "push"
- if: $CI_PIPELINE_SOURCE == "schedule"
artifacts:
when: always
paths:
- govulncheck-report.json
expire_in: 30 days
Step 5: GONOSUMCHECK and GONOSUMDB — When to Use Them and the Security Risk
GONOSUMDB is a comma-separated list of module path prefixes for which the go command will not consult the checksum database. GONOSUMCHECK is similar but also suppresses checksum verification entirely (not just the database lookup). Both exist for a legitimate reason: modules that are not publicly accessible cannot be registered in sum.golang.org, so the checksum database lookup would always fail.
# GONOSUMDB: skip the checksum database for these module paths,
# but still verify against the local go.sum file.
export GONOSUMDB=go.internal.company.com,github.com/your-org
# GONOSUMCHECK: skip checksum verification entirely for these paths.
# WARNING: this means no tamper detection for matched modules.
# Use only for modules that are genuinely unreachable from sum.golang.org.
export GONOSUMCHECK=go.internal.company.com
# GOPRIVATE sets both GONOSUMDB and GONOPROXY for the listed prefixes.
# This is the recommended way to configure private module support.
export GOPRIVATE=go.internal.company.com,github.com/your-org/*
The security risk of misconfiguring these variables is that public modules can accidentally be excluded from checksum verification:
# DANGEROUS: this bypasses checksum verification for ALL modules under github.com.
# A typosquat or tampered module would be silently accepted.
export GONOSUMCHECK=github.com # Never do this.
# DANGEROUS: using a wildcard that accidentally covers public modules.
export GOPRIVATE=github.com/your-org # Intended: only your-org modules.
# But if an attacker registers github.com/your-org-fake, it is also excluded
# from checksum verification if the prefix matches.
The correct approach for private modules is to set GOPRIVATE to the most specific prefix that covers only your internal modules:
# Correct: specific internal domain only.
export GOPRIVATE=go.internal.company.com
# Correct: specific GitHub org, using the full prefix.
export GOPRIVATE=github.com/acme-internal-org
# Correct for multiple private prefixes.
export GOPRIVATE=go.internal.company.com,github.com/acme-internal-org
In CI, document every GONOSUMDB and GONOSUMCHECK setting and the justification for it. Review these settings in security audits. An undocumented bypass is a gap that will be discovered during an incident, not before.
Step 6: Private Module Security — Athens and Self-Hosted Proxies
For organisations with private Go modules, the recommended architecture is a self-hosted module proxy that:
- Authenticates requests from internal clients.
- Caches approved public modules (preventing dependency on
proxy.golang.orgavailability). - Hosts private modules without exposing them to public infrastructure.
Athens is the standard open-source self-hosted Go module proxy. It supports multiple storage backends (GCS, S3, Azure Blob, local disk) and integrates with private Git hosting.
# athens-config.toml — base Athens configuration.
# Deployed as a Kubernetes Deployment or Docker container.
GoEnv = "production"
GoBinaryEnv = ["GONOSUMDB=go.internal.company.com", "GOPRIVATE=go.internal.company.com"]
# StorageType controls where Athens stores cached modules.
StorageType = "gcs"
[Storage]
[Storage.GCS]
ProjectID = "your-gcp-project"
Bucket = "your-athens-bucket"
# NetworkMode controls how Athens fetches upstream modules.
# "strict" means Athens only serves modules it has already cached;
# "offline" means it never fetches from upstream.
# For production CI with a known dependency set, "strict" prevents
# new modules from being pulled without explicit caching.
NetworkMode = "strict"
# FilterFile controls which modules Athens will proxy.
# Modules not in the filter are rejected.
FilterFile = "/conf/filter.conf"
# Credentials for fetching from private Git hosting.
[GitConfig]
# Use SSH deploy keys or a service account token.
# filter.conf — allowlist of modules Athens will proxy.
# Prefix with + to allow, - to deny, D for direct (no proxy).
+ go.internal.company.com
+ github.com/acme-internal-org
+ github.com/google/
+ golang.org/x/
+ google.golang.org/
- * # Deny all other modules — require explicit allowlisting.
Configure your CI runners and developer workstations to use Athens:
# In CI environment or developer shell profile.
export GOPROXY=https://athens.internal.company.com,direct
export GONOSUMDB=go.internal.company.com
export GOPRIVATE=go.internal.company.com
The direct fallback in GOPROXY allows Athens to fetch from upstream on cache miss. Remove direct in a fully airgapped environment where only pre-approved modules should be accessible.
For private modules stored in a private Git host, Athens authenticates using .netrc or SSH keys configured in the proxy’s deployment environment. This means CI runners do not need individual credentials for every private module — they authenticate to Athens, which handles upstream authentication.
Step 7: replace Directive Security
replace directives in go.mod are a common source of security misconfiguration. They override module resolution for a named module, pointing to a different path or version. This is legitimately used for local development (replacing a module with a local fork) and for patching a transitive dependency that has a vulnerability before an upstream fix is available.
The security risk is that a replace directive can silently redirect module resolution away from the checksum-verified version to an unverified alternative:
# go.mod — DANGEROUS: replaces a standard library module with an untrusted fork.
module github.com/acme/myapp
go 1.24
require (
golang.org/x/crypto v0.22.0
)
// This replaces golang.org/x/crypto with an unverified fork.
// The checksum for the fork is recorded in go.sum, but no one has reviewed the fork.
replace golang.org/x/crypto => github.com/untrusted-author/crypto v0.0.1
Controls for replace directives:
# Audit all replace directives in your go.mod.
grep -A 100 "^replace" go.mod
# Check the replace directives in all modules in your build graph,
# including transitive dependencies.
go mod edit -json | python3 -c "
import json, sys
data = json.load(sys.stdin)
replaces = data.get('Replace', []) or []
if replaces:
for r in replaces:
print(f\"REPLACE: {r['Old']['Path']} => {r['New']['Path']}\")
else:
print('No replace directives found.')
"
In code review policies, require explicit approval from a security reviewer for any PR that adds or modifies a replace directive. Add a CI check that fails if replace directives point to non-canonical paths:
#!/usr/bin/env bash
# check-replace-directives.sh — fail if replace directives use non-org paths.
# Run as a CI step before go build.
ALLOWED_REPLACE_TARGETS="^(go\.internal\.company\.com|github\.com/acme-internal-org)"
if grep -qE "^replace" go.mod; then
replacements=$(grep -A 100 "^replace" go.mod | grep "=>" | awk '{print $NF}')
while IFS= read -r target; do
module_path=$(echo "$target" | sed 's/ v[0-9].*//')
if ! echo "$module_path" | grep -qE "$ALLOWED_REPLACE_TARGETS"; then
echo "ERROR: Unapproved replace target: $module_path"
echo "Replace directives to non-internal modules require security review."
exit 1
fi
done <<< "$replacements"
fi
echo "Replace directive check passed."
Step 8: Vanity Import Path Security
Vanity import paths (go.company.com/sdk rather than github.com/company/sdk) are resolved by the go command fetching the URL and reading a <meta name="go-import"> HTML tag. This resolution is a trust boundary: whoever controls the web server at go.company.com controls where go get go.company.com/sdk resolves.
Threat vectors for vanity import paths:
- DNS hijack: An attacker takes over the DNS record for
go.company.com. - TLS certificate compromise: An attacker obtains a fraudulent certificate for
go.company.comand performs a MITM attack on the metadata fetch. - Web server compromise: The server hosting the
<meta>tag redirect is compromised and the redirect is changed. - Domain expiry: The domain lapses and is re-registered by an attacker.
Hardening measures:
# Audit your vanity import paths and their current redirect targets.
# For each vanity path, fetch the meta redirect and verify it points to the expected VCS.
curl -s "https://go.company.com/sdk?go-get=1" | grep 'go-import'
# Expected: <meta name="go-import" content="go.company.com/sdk git https://github.com/acme/sdk">
# Monitor vanity path redirects in CI — alert if the resolved VCS URL changes.
For internal vanity paths hosted on infrastructure you control:
- Use infrastructure-as-code to manage the web server configuration, so redirect changes go through code review.
- Enable DNSSEC for the domain.
- Set a short TTL on the DNS records and monitor for unexpected changes.
- Consider replacing vanity paths with direct module paths for new modules — the indirection adds attack surface without much usability benefit for private projects.
Step 9: Go Toolchain Supply Chain — go install and Version Pinning
go install without a specific version installs the latest version of a tool:
# DANGEROUS: resolves to whatever is latest at the time of installation.
# The installed version can change silently between CI runs if the cache is cold.
go install golang.org/x/tools/gopls@latest
# CORRECT: pin to an exact version.
go install golang.org/x/tools/gopls@v0.16.1
For tools used in CI, pin the version explicitly and verify the expected binary hash after installation. In GitHub Actions, use a dedicated tools file to track tool versions:
//go:build tools
// +build tools
// tools.go — track tool dependencies with explicit versions.
// Run: go generate ./tools/... to install at pinned versions.
package tools
import (
_ "golang.org/x/tools/gopls"
_ "golang.org/x/vuln/cmd/govulncheck"
_ "mvdan.cc/gofumpt"
_ "github.com/golangci/golangci-lint/cmd/golangci-lint"
)
# go.mod includes the tool dependencies at pinned versions.
# Install all tools at their pinned versions from go.mod.
go install golang.org/x/tools/gopls@$(go list -m -f '{{.Version}}' golang.org/x/tools)
go install golang.org/x/vuln/cmd/govulncheck@$(go list -m -f '{{.Version}}' golang.org/x/vuln)
This pattern ensures that tool versions are tracked in go.sum alongside application dependencies, providing the same checksum guarantees for tooling as for production code.
Expected Behaviour
| Signal | Without Controls | With Controls |
|---|---|---|
| Known CVE in transitive dep | Silently present; discovered in incident or pentest | govulncheck ./... fails CI at PR time; advisory blocks merge |
| go.sum tampered with | Undetected; build succeeds with wrong dependency content | go mod verify fails CI; tampered module cache entry rejected |
replace directive added pointing to fork |
Merged without review; build silently uses unverified fork | CI check fails; PR requires explicit security reviewer approval |
| GONOSUMCHECK misconfigured to cover public modules | Public module checksums not verified; tampered module silently accepted | CI environment definition explicitly sets GONOSUMCHECK=; no bypass |
| go.sum missing from commit | Developer forgets to commit; CI regenerates it on each run | GOFLAGS=-mod=readonly fails CI; git diff --exit-code go.sum check fails |
| Private module path leaked to proxy.golang.org | Internal module path recorded in public transparency log | GOPRIVATE set correctly; no lookup sent to public infrastructure |
go install @latest in CI |
Tool version changes silently between runs; compromised release pulled | Tools pinned to exact versions in go.mod; versions tracked in go.sum |
Trade-offs
| Control | Benefit | Cost | Mitigation |
|---|---|---|---|
GOFLAGS=-mod=readonly in CI |
Prevents silent go.sum updates; enforces reviewed lockfile | Breaks CI if go.sum is legitimately out of date | Add a separate go mod tidy && git diff --exit-code check to catch this condition explicitly |
govulncheck --scan=module |
Reports all module-level CVEs, not just reachable ones | Higher volume of findings; may block builds on CVEs in unused packages | Use --scan=package for PR gates; --scan=module for nightly policy reporting |
| Athens self-hosted proxy | Full control over which modules are allowed; no dependency on proxy.golang.org | Operational overhead of running and maintaining Athens | Use a managed proxy service (Artifactory GoProxy, GCP Artifact Registry) if Athens maintenance is not feasible |
replace directive check in CI |
Prevents unreviewed module substitution | May block legitimate emergency patches | Allow specific approved replacements via CI configuration; require documented justification |
| Vanity path elimination | Removes DNS/web server as an attack vector in module resolution | Breaks existing import paths; requires go.mod updates across all consumers | Apply only to new modules; maintain existing vanity paths with infrastructure hardening |
Exact version pins for go install |
Reproducible tool installation; no silent version drift | Requires manual version updates for tool upgrades | Use Renovate or Dependabot to open PRs for tool version bumps automatically |
Failure Modes
| Failure | Symptom | Detection | Recovery |
|---|---|---|---|
| New advisory published for pinned dep | govulncheck fails in daily scheduled CI run |
Scheduled security job produces failure; alert to security channel | Assess the advisory; update the dep in go.mod and go.sum; regenerate binaries |
| Athens proxy unavailable | CI builds fail with module fetch errors | Build step fails with connection error to proxy URL | Fall back to GOPROXY=https://proxy.golang.org,direct temporarily; restore Athens from backup; ensure modules are cached |
GONOSUMCHECK set incorrectly in CI environment |
Public module checksums not verified; silent security gap | Manual audit of CI environment variables during security review | Audit all GONOSUMCHECK and GONOSUMDB values; document exceptions; set GONOSUMCHECK="" explicitly in CI |
go.sum conflict in merge |
Two PRs update different dependencies; merge conflict in go.sum |
Git merge conflict; CI fails | Run go mod tidy on the merge result; verify with go mod verify; commit the resolved go.sum |
replace directive to local path committed accidentally |
Build works in CI but only because of absolute path on CI runner; fails on other machines | go build fails on machines without the local path |
Remove the replace directive; use a published version or a properly referenced module |
| Vanity import path redirect changed | go get resolves to different repository than before; checksum mismatch if go.sum exists |
go mod verify or go build -mod=readonly fails with checksum error |
Investigate the meta redirect; if the redirect was legitimately changed, update go.sum after verifying the new source; if hijacked, rotate any credentials accessible from the compromised module |