Quick Reference
| When you need... |
Jump to |
| Run a full dependency audit |
S2 Gates -> S5 Checklist -> S9 Output |
| Scan for known CVEs |
S2 Gates -> S5.1 CVE Scanning |
| Triage a finding / decide urgency |
S6 — evidence tier, NOT a CVSS guess |
| Check license risk |
S5.2 + references/license-compliance.md |
| Plan a version upgrade |
S5.3 + references/upgrade-planning.md |
| Investigate supply chain risk |
S5.4 + references/supply-chain-security.md |
| Review go.mod hygiene |
S5.5 Module Hygiene |
| Actually apply a fix |
S1.3 Remediation Boundary |
1 Scope & Operating Mode
1.1 In scope
go.mod/go.sum analysis, CVE scanning via govulncheck (primary), license risk
triage, outdated dependency reporting, upgrade path planning, breaking change
assessment, supply chain posture (proxy, checksum DB, private modules),
+incompatible triage, module graph analysis.
1.2 Out of scope
Application code security (use security-review), micro-benchmark performance
(use go-benchmark), infrastructure provisioning, container image scanning,
runtime behavior analysis, and legal determinations about license
obligations — see S5.2: this skill produces evidence and escalation triggers,
never verdicts.
1.3 Remediation Boundary (NON-NEGOTIABLE)
An audit is read-only. It observes; it does not repair.
| Class |
Commands |
Allowed during audit |
| Read-only probe |
govulncheck, go list -mod=readonly, go mod graph/verify/why, go mod edit -json, go mod tidy -diff, go version -m, go env <VAR>…, go-licenses check/report/help, git status/diff/log |
Yes |
| Mutating |
go get, go mod tidy (without -diff), go mod edit -require, go work, go env -w, go list -mod=mod, go-licenses save, go install, cyclonedx-gomod -output |
No — emit as plan |
| Destructive |
git checkout, git restore, git reset, rm |
Never — not even to roll back |
allowed-tools pre-approves; it does not forbid. It keeps writes off the
auto-approved surface, while gate 2/11's snapshot detects a mutation that
happened anyway. Two writes wear the name of a read, and a coarse list lets
both through:
go env -w rewrites Go's persistent env file rather than printing it, and
go list -mod=mod lets package loading update go.mod/go.sum. Readonly is
the default since Go 1.16, but GOFLAGS can override it — so every
package-loading command here states -mod=readonly. Writes that produce a
deliverable (go-licenses save, SBOM, tool installs) are remediation: emitted
under S9.7, marked # EMIT in the references, never run. Guarded by
regression checks DA008/DA014/DA015 — static checks that keep writes off the
auto-approved surface; they cannot stop a write at runtime.
- Emit, do not execute. Every fix is delivered as a copy-pasteable command
block under S9.7, for a human to run. The skill never runs it.
- Never generate a rollback that discards uncommitted work.
git checkout go.mod go.sum overwrites unstaged edits with no recovery path. Require a
clean worktree instead — git status --porcelain go.mod go.sum empty before
any upgrade loop — and stop on failure rather than revert.
- Prove read-only-ness. Record
git status --porcelain go.mod go.sum at the
start and end. If it differs, a probe mutated the module files (e.g.
go mod download on Go < 1.18) — say so in S9.8 rather than silently
reporting post-mutation state.
- Switching to remediation requires the user to ask for it in this turn.
"Audit our dependencies" is not authorization to upgrade them.
2 Gates
Gates are checked in order. Each gate declares a class that determines what
failure does — this is the only thing that decides stop-vs-continue.
2.1 Gate classes
| Class |
Meaning |
On failure |
| BLOCK |
The audit's subject does not exist or is untrustworthy |
Stop. Emit no findings. Emit the reason + what would unblock, headed NOT AN AUDIT. |
| DEGRADE |
A capability is unavailable; the subject is fine |
Continue. Enter the matching S4 mode, list the lost coverage in S9.8. |
| WARN |
An observation worth reporting |
Continue at full scope. Record as a finding. |
A gate has exactly one class. There is no gate that both stops and degrades.
2.2 Gate table
Order matters. Gates 1–2 touch no Go tooling, so the read-only baseline is
captured before anything could disturb it.
| # |
Gate |
Check |
Class |
Failure action |
| 1 |
Module exists |
Glob("**/go.mod") — filesystem only |
BLOCK |
No Go module here — nothing to audit |
| 2 |
Baseline snapshot |
git status --porcelain go.mod go.sum — before any go command |
WARN |
Note uncommitted module edits; the audit reflects the worktree, not HEAD |
| 3 |
go.mod is well-formed |
go mod edit -json parses; module directive present |
BLOCK |
Malformed manifest — findings would be fiction |
| 4 |
Module graph resolvable |
go list -mod=readonly -m all succeeds |
DEGRADE |
-> no-graph mode (see below) |
| 5 |
Checksums available |
go.sum covers every non-replaced external requirement |
DEGRADE |
-> no-integrity mode (see below — absence alone is not a failure) |
| 6 |
Checksums verify |
go mod verify |
WARN |
Report as a P1 finding, do not stop — a tampered cache is exactly what an audit exists to surface |
| 7 |
govulncheck available |
govulncheck -version |
DEGRADE |
-> no-cve mode |
| 8 |
Vuln DB reachable |
govulncheck exits 0 or 3, not 1 |
DEGRADE |
-> no-cve mode (offline) |
| 9 |
License tool available |
go-licenses help lists subcommands |
DEGRADE |
-> no-license mode |
| 10 |
Tidy state |
go mod tidy -diff (Go 1.23+; skip below that) |
WARN |
Report untidy go.mod as a hygiene finding |
| 11 |
Closing snapshot |
git status --porcelain go.mod go.sum matches gate 2 |
WARN |
A probe mutated the module files — say so in S9.8 |
Four rationales, each replacing a worse rule:
Gate 2 precedes every go command. A baseline taken after go list cannot
prove the audit was read-only — go list is one of the things it would have to
exonerate.
Gate 4 is DEGRADE, not BLOCK. Gate 3 already caught a broken manifest; a
failure here is environmental (offline, proxy down, missing credentials, cold
cache) — a lost capability, not an untrustworthy subject.
Gate 5 is conditional, not "file exists". A missing go.sum is legal when
the module has no dependencies or every requirement is redirected by a local
replace. Decide by what is required, not by ls: external non-replaced
requirements with checksums missing -> DEGRADE; none, or all locally replaced
-> N/A, legitimately absent, not a finding; cannot tell (gate 4 already
degraded) -> DEGRADE, naming the unresolved graph as the cause.
Gate 6 is WARN. An integrity failure is the highest-value output this skill
can produce; stopping would suppress the finding the user most needs.
Gate 9 uses help, not --help. go-licenses --help prints only the
logging flags and never lists commands — a useless liveness probe.
2.3 Multi-module repositories
Gate 1 globs for every go.mod. If it finds more than one, the unit of
audit is the module, not the repository — running the gates once in the root
audits one module and reports it as though it covered all of them. When >1 is
found, before gate 3:
- Load
references/multi-module.md and follow it. Do not improvise.
- Snapshot all manifests at once, still before any
go command:
git status --porcelain -- '**/go.mod' '**/go.sum'
- List every module in S9.1; name any you skipped in S9.8.
Single-module repositories skip this — the gate table runs once.
2.4 Scope classification
| Mode |
Trigger |
Output contract |
| Quick |
"check for CVEs", one named concern |
S9 subset (9.1, 9.2, 9.3, 9.8, 9.9) |
| Standard |
"audit dependencies", pre-release check |
Full S9 |
| Deep |
"supply chain review", compliance audit |
Full S9 + provenance/SBOM |
3 Depth Selection
Quick
Single-concern scan. Load no reference files.
- Triggers: "run govulncheck", "any CVEs?", "check this dependency"
- Coverage: govulncheck scan + S6 triage + immediate remediation plan
- Output: the S9 subset above. Do not emit empty License/Supply-Chain
sections — omit them and say why in S9.8.
Standard (default)
Full audit across 5 domains. Load govulncheck-patterns.md,
license-compliance.md, upgrade-planning.md — one per domain this depth
covers. (supply-chain-security.md is Deep-only; multi-module.md loads on the
gate-1 trigger regardless of depth.)
- Triggers: pre-release audit, "audit our dependencies", quarterly review
- Coverage: CVE scan, license risk, outdated report, upgrade assessment, hygiene
- Force Standard if: multiple go.mod files, compliance requirements, CI integration
Deep
Comprehensive supply chain review. Load all references.
- Triggers: compliance audit, incident response, "supply chain review"
- Coverage: all Standard domains + provenance, SBOM, transitive license, proxy config
- Force Deep if: regulatory compliance, post-incident, new vendor onboarding
4 Degradation Modes
Each mode is entered by exactly one DEGRADE gate. Modes compose — record all
that apply.
| Mode |
Entered by |
Can still deliver |
MUST NOT claim |
no-graph |
Gate 4 |
Direct requirements read from go.mod |
Anything about indirect dependencies, or that the list is complete |
no-integrity |
Gate 5 |
Module list, versions, licenses, hygiene |
Reproducible-build or tamper-detection status |
no-cve |
Gate 7, 8 |
License, outdated, hygiene, supply chain posture |
Any CVE status — present, absent, or reachable |
no-license |
Gate 9 |
CVE, outdated, hygiene, supply chain posture |
License distribution or compliance posture |
no-reachability |
-scan was not symbol, or binary mode |
Which modules are affected |
That any finding is or is not reachable |
Mark every degraded output inline: # DEGRADED [<mode>]: <what is missing>
Two absolute rules:
- Never fabricate CVE findings.
- Never claim "no vulnerabilities" without a scan that completed. A
govulncheck exit code of 1 is a failed scan, not a clean one.
5 Dependency Audit Checklist
5.1 CVE Scanning
govulncheck ./... in source mode is primary — it traces the call graph,
so it reports whether your code can actually reach the vulnerable symbol.
- The
-scan level decides what "found" means — symbol (default) reports
reachable symbols, package imported packages, module required versions.
Lowering it raises noise and forfeits reachability.
- Exit code is the CI contract, and
-format json breaks it — text mode:
3 found at scan level, 2 invalid usage, 1 error, 0 clean. -json /
-format sarif / -format openvex exit 0 regardless of findings, so a
CI job gating on $? after them never fails.
- govulncheck reports no CVSS score — see S6.1. Priority comes from the
evidence tier, not from a severity number the tool never emitted.
- Test files are excluded by default —
-test defaults to false, so
test-only dependencies are not analyzed unless you pass -test.
- Transitive findings still need
go mod why -m <module> to establish which
direct dependency pulls them in — that is the module you actually upgrade.
5.2 License Risk Triage
This skill does not give legal advice and does not decide whether a license
is compatible with a project. It gathers the facts a lawyer needs and states
which facts trigger escalation. Every copyleft finding routes to legal review.
- Report the license, the path, and the trigger conditions — never a verdict.
Whether a copyleft obligation attaches turns on facts this skill cannot see:
distribution, linkage vs build-tool-only, licence version and exceptions,
modification, deployment model. Record the observable; escalate the rest.
- Use the scanner's own vocabulary —
go-licenses types are forbidden,
restricted, reciprocal, notice, permissive, unencumbered, unknown;
--disallowed_types defaults to forbidden,unknown. Reporting in the tool's
terms keeps the output auditable and version-stable.
- Distinguish shipped from not-shipped, and label the evidence grade.
Required (
go list -m all) < build-dependency (`go list -mod=readonly -deps
- A missing LICENSE file is the highest-signal license finding — no grant
of rights was located. An escalation trigger, not a legal conclusion.
- Escalate with the facts attached: module path, licence identifier and
version,
go mod why -m path, evidence grade for shipping (item 9), and
whether the project distributes binaries or runs a network service.
5.3 Upgrade Planning
- Semver signals intent, not a guarantee. Patch/minor are lower risk,
not safe, and
v0.x.y carries no compatibility promise at all. Read the
changelog; diff the API surface when there is none.
+incompatible is a silent major-version upgrade hazard — the module
published v2+ tags without a module-aware go.mod, so the toolchain treats
those versions as part of the same module as v1.x. MVS can therefore
upgrade v1.5.2 straight to v4.1.2+incompatible during a routine -u. Plan
migration to a /vN path.
go get -u upgrades far more than the target — it raises the target and
its dependencies. Use go get <module>@<version> for precise control, and
remember that even a precise go get can move other modules, because
minimal version selection re-solves the whole graph.
5.4 Supply Chain Security
- go.sum is an integrity anchor, not a lockfile. It records expected hashes;
it does not pin which version is selected — that is
go.mod + MVS. Commit
both; verify with go mod verify.
- GOPROXY affects availability and privacy, not checksum verification.
Validation is controlled by
GOSUMDB and disabled per pattern by
GOPRIVATE/GONOSUMDB — GOPROXY=direct still verifies.
GOPRIVATE for internal modules — stops internal module paths leaking to
the public proxy and checksum database. Shorthand for GONOPROXY +
GONOSUMDB.
- Deleted upstream tags break builds —
proxy.golang.org caches immutably,
so a cached version survives tag deletion. Prefer the proxy over direct.
5.5 Module Hygiene
- Check tidiness without mutating —
go mod tidy -diff (Go 1.23+) prints
the change and exits non-zero if non-empty. Below 1.23 report the check as
unavailable rather than running the mutating go mod tidy.
- Minimize
replace directives — each is technical debt, and a local-path
replace in a committed go.mod breaks every machine but the author's.
- Module-graph cycles are legal in Go and are not, by themselves, a defect.
Modules may require each other; only package import cycles are rejected by
the compiler. Report a cycle as a WARN-level design smell that widens upgrade
blast radius — never as a failed check.
go.work is normally not committed — it encodes one developer's local
layout. Exception: a single-repository workspace whose use directives are
all repo-relative. Check the paths before flagging it.
6 Triage & Priority Model
6.1 The tool gives you evidence, not a score
The Go vulnerability database does not publish CVSS scores, so govulncheck
never prints one. Its report carries the GO-YYYY-NNNN ID, aliases (CVE/GHSA),
summary, affected ranges, fixed version, and database_specific.review_status
(REVIEWED / UNREVIEWED). Both rules are mandatory:
- Never state a CVSS score sourced from govulncheck. It did not produce one.
- Any CVSS must be enriched from a named external source keyed on the alias —
"CVSS 9.8 (NVD, CVE-2023-44487)" — and recorded in S9.3. With no such lookup
the column reads
not retrieved, never a guess.
6.2 Evidence tiers
govulncheck groups findings into result sections. The section is the evidence.
| Section |
Meaning |
Tier |
=== Symbol Results === |
A vulnerable symbol is reachable from your call graph |
E1 Called |
=== Package Results === |
You import the affected package; no reachable symbol proven |
E2 Imported |
=== Module Results === |
The module is required at an affected version only |
E3 Required |
No vulnerabilities found. = zero findings at any tier.
6.3 Priority
| Priority |
Condition |
| P0 |
E1 Called, a fix version exists, and the call path is reachable from a network-facing entry point |
| P1 |
E1 Called (any other case); or go mod verify reported a checksum mismatch; or an unlicensed dependency is linked into a shipped binary |
| P2 |
E2 Imported; or a copyleft dependency linked into a shipped artifact and pending legal review; or a +incompatible direct dependency |
| P3 |
E3 Required only; minor-version drift; hygiene findings; EOL library with no current findings |
Escalation modifiers — apply, then state the reason:
- No fix version available — escalate one; remediation is a compensating
control, not an upgrade.
UNREVIEWED report — absence of a symbol-level finding is not proof of
unreachability. Hold at the tier reported and note the status.
- Reachability not established (
-scan module|package, binary mode, or
reflection/unsafe/plugin in the path) — no-reachability mode. Report the
tier obtained; never downgrade on absent evidence.
- Test-only dependency — de-escalate one, only after
go mod why -m confirms
no non-test path exists.
7 Anti-Examples
Each rule below is binding on its own. Worked WRONG/RIGHT pairs for all six are
in references/anti-examples.md — load it when an audit is about to do one of
these things, or when explaining why not.
| ID |
Anti-pattern |
Rule |
| AE-1 |
Assigning a CVSS score govulncheck never emitted |
Report CVSS: not retrieved, or cite the external database and alias it came from. Priority comes from the evidence tier. |
| AE-2 |
Turning a licence observation into a legal verdict |
Emit the escalation packet — module, licence, path, linkage, distribution — and route to legal. Never conclude. |
| AE-3 |
Gating CI on an exit code -json always sets to 0 |
Gate on the text-mode exit code (3 = found, 1 = broke), or parse findings from JSON with jq -s. |
| AE-4 |
Rolling back with a command that destroys work |
Never emit git checkout/restore/reset. Require a clean worktree up front and stop on failure. |
| AE-5 |
Claiming "no vulnerabilities" from a failed scan |
Only exit 0 with No vulnerabilities found. supports that claim. Exit 1 means no-cve, status UNKNOWN. |
| AE-6 |
Treating +incompatible as harmless |
It is the same module as v1.x to MVS, so -u can cross a major version silently. Track as P2 with a /vN migration plan. |
8 Dependency Audit Scorecard
Twelve checks in three tiers, applied after every audit —
load references/scorecard.md for the item list and score them there.
A check that could not run because of a DEGRADE gate scores N/A and leaves
both numerator and denominator; it never counts as a pass. Score each tier as a
ratio over its applicable items, because a fixed threshold breaks the moment an
item goes N/A:
critical = passed / applicable must be 1.00 (0 applicable -> tier N/A)
standard = passed / applicable must be >= 0.80
hygiene = passed / applicable must be >= 0.75
PASS iff every non-N/A tier meets its threshold.
Report ratio and raw counts: Standard 3/3 (1.00) — 2 items N/A. In a
multi-module audit the repository verdict is the worst module's, never an
average — an average lets a clean module mask a failing one.
9 Output Contract
Quick mode emits 9.1, 9.2, 9.3, 9.8, 9.9; Standard and Deep emit all nine. An
omitted section must be named in 9.8 with the reason — never silently dropped,
never emitted empty. Volume: P0/P1 fully detailed, P2 up to 10, P3 summary.
9.1 Audit Context
Every module audited (path + directory), Go version, direct/indirect counts,
worktree state, tool versions, scan timestamp.
9.2 Mode & Depth
Quick | Standard | Deep, plus every active degradation mode from S4 and the
gate that triggered it.
9.3 CVE Scan Results
Command (with -scan/-mode), exit code, and per finding: GO-ID, aliases,
module, evidence tier (E1/E2/E3), fixed version, review status, priority, and
CVSS with its source or not retrieved.
9.4 License Inventory
Per dependency: licence identifier, scanner classification, shipping evidence
grade (S5.2 item 9). Separate escalation table for copyleft/unknown/missing with
the item-11 facts attached. No verdicts.
9.5 Outdated Dependencies
Direct dependencies behind latest, grouped by major/minor/patch drift, with the
v0.x ones called out as unbounded-risk regardless of the size of the bump.
9.6 Supply Chain Posture
Actual go env values (GOPROXY, GOPRIVATE, GONOPROXY, GONOSUMDB,
GOSUMDB); go.sum status and go mod verify result; replace inventory.
9.7 Remediation Plan
Prioritized, as commands for the user to run — this skill does not run them.
Immediate (P0/P1), short-term (P2), backlog (P3). Each entry: module, current ->
target, evidence tier resolved, and precondition (clean worktree, green baseline).
9.8 Uncovered Risks
What this audit did NOT cover. Mandatory — never empty. Must include every
degradation mode, N/A scorecard item, omitted output section, module not audited,
and escalation handed to another party.
9.9 Machine-Readable Summary
{"summary":{"pass":true,"modes":["no-license"],
"tiers":{"critical":{"passed":3,"applicable":3,"ratio":1.0},
"standard":{"passed":4,"applicable":4,"ratio":1.0,"na":1},
"hygiene":{"passed":3,"applicable":4,"ratio":0.75}}},
"counts":{"p0":0,"p1":1,"p2":3,"p3":5},
"evidence":{"e1_called":1,"e2_imported":3,"e3_required":5},
"modules":{"direct":12,"indirect":47,"affected":4},
"scan":{"tool":"govulncheck","mode":"source","scan_level":"symbol","exit_code":3}}
Scorecard appended, ratios with raw counts and N/A totals:
Critical 3/3 (1.00) · Standard 4/4 (1.00, 1 N/A) · Hygiene 3/4 (0.75) — PASS
10 Reference Loading Guide
| Condition |
Load |
| CVE scanning (Standard+) |
references/govulncheck-patterns.md |
| License risk triage (Standard+) |
references/license-compliance.md |
| Upgrade planning, version migration (Standard+) |
references/upgrade-planning.md |
| Supply chain review (Deep) |
references/supply-chain-security.md |
| About to do — or explain — an S7 anti-pattern |
references/anti-examples.md |
More than one go.mod found (gate 1) |
references/multi-module.md |
| Scoring the audit (S8) |
references/scorecard.md |
Each reference has a table of contents — load the relevant sections, not the
whole file.
Tool-version note (G1): commands here are verified against govulncheck v1.1.4, go-licenses v2.0.1, and Go 1.26.1.
go mod tidy -diff additionally requires Go 1.23+.
Check govulncheck -version and the local go version before relying on a
flag. A govulncheck built against a different Go than the one on PATH fails
package loading with exit 1 — that is no-cve, and the fix is to rebuild
govulncheck, not to report a clean scan.
1---2name: go-dependency-audit3description: Go dependency audit specialist for CVE scanning (govulncheck), license risk triage, outdated dependency detection, upgrade impact analysis, and supply chain security. ALWAYS use when auditing go.mod dependencies, running govulncheck, checking license compatibility, planning dependency upgrades, or investigating supply chain risks in Go projects. Read-only by default — emits a remediation plan instead of mutating go.mod/go.sum. Complements security-review (code-level) with module-level supply chain analysis.4---56## Quick Reference78| When you need... | Jump to |9|-------------------------------------------|--------------------------------------------|10| Run a full dependency audit | S2 Gates -> S5 Checklist -> S9 Output |11| Scan for known CVEs | S2 Gates -> S5.1 CVE Scanning |12| Triage a finding / decide urgency | S6 — evidence tier, NOT a CVSS guess |13| Check license risk | S5.2 + `references/license-compliance.md` |14| Plan a version upgrade | S5.3 + `references/upgrade-planning.md` |15| Investigate supply chain risk | S5.4 + `references/supply-chain-security.md` |16| Review go.mod hygiene | S5.5 Module Hygiene |17| Actually apply a fix | S1.3 Remediation Boundary |1819---2021## 1 Scope & Operating Mode2223### 1.1 In scope2425go.mod/go.sum analysis, CVE scanning via govulncheck (primary), license risk26triage, outdated dependency reporting, upgrade path planning, breaking change27assessment, supply chain posture (proxy, checksum DB, private modules),28`+incompatible` triage, module graph analysis.2930### 1.2 Out of scope3132Application code security (use `security-review`), micro-benchmark performance33(use `go-benchmark`), infrastructure provisioning, container image scanning,34runtime behavior analysis, and **legal determinations about license35obligations** — see S5.2: this skill produces evidence and escalation triggers,36never verdicts.3738### 1.3 Remediation Boundary (NON-NEGOTIABLE)3940**An audit is read-only. It observes; it does not repair.**4142| Class | Commands | Allowed during audit |43|------------------|--------------------------------------------------------------------------|----------------------|44| Read-only probe | `govulncheck`, `go list -mod=readonly`, `go mod graph/verify/why`, `go mod edit -json`, `go mod tidy -diff`, `go version -m`, `go env <VAR>…`, `go-licenses check/report/help`, `git status/diff/log` | Yes |45| Mutating | `go get`, `go mod tidy` (without `-diff`), `go mod edit -require`, `go work`, **`go env -w`**, **`go list -mod=mod`**, `go-licenses save`, `go install`, `cyclonedx-gomod -output` | No — emit as plan |46| Destructive | `git checkout`, `git restore`, `git reset`, `rm` | Never — not even to roll back |4748`allowed-tools` pre-approves; it does not forbid. It keeps writes off the49auto-approved surface, while gate 2/11's snapshot *detects* a mutation that50happened anyway. Two writes wear the name of a read, and a coarse list lets51both through:52**`go env -w`** rewrites Go's persistent env file rather than printing it, and53**`go list -mod=mod`** lets package loading update `go.mod`/`go.sum`. Readonly is54the default since Go 1.16, but `GOFLAGS` can override it — so every55package-loading command here states `-mod=readonly`. Writes that produce a56deliverable (`go-licenses save`, SBOM, tool installs) are remediation: emitted57under S9.7, marked `# EMIT` in the references, never run. Guarded by58regression checks DA008/DA014/DA015 — static checks that keep writes off the59auto-approved surface; they cannot stop a write at runtime.60611. **Emit, do not execute.** Every fix is delivered as a copy-pasteable command62 block under S9.7, for a human to run. The skill never runs it.632. **Never generate a rollback that discards uncommitted work.** `git checkout64 go.mod go.sum` overwrites unstaged edits with no recovery path. Require a65 clean worktree instead — `git status --porcelain go.mod go.sum` empty before66 any upgrade loop — and stop on failure rather than revert.673. **Prove read-only-ness.** Record `git status --porcelain go.mod go.sum` at the68 start and end. If it differs, a probe mutated the module files (e.g.69 `go mod download` on Go < 1.18) — say so in S9.8 rather than silently70 reporting post-mutation state.714. Switching to remediation requires the user to ask for it in this turn.72 "Audit our dependencies" is not authorization to upgrade them.7374---7576## 2 Gates7778Gates are checked in order. Each gate declares a **class** that determines what79failure does — this is the only thing that decides stop-vs-continue.8081### 2.1 Gate classes8283| Class | Meaning | On failure |84|-------------|--------------------------------------------------|-------------------------------------------------------------------|85| **BLOCK** | The audit's subject does not exist or is untrustworthy | Stop. Emit no findings. Emit the reason + what would unblock, headed `NOT AN AUDIT`. |86| **DEGRADE** | A capability is unavailable; the subject is fine | Continue. Enter the matching S4 mode, list the lost coverage in S9.8. |87| **WARN** | An observation worth reporting | Continue at full scope. Record as a finding. |8889A gate has exactly one class. There is no gate that both stops and degrades.9091### 2.2 Gate table9293Order matters. Gates 1–2 touch no Go tooling, so the read-only baseline is94captured before anything could disturb it.9596| # | Gate | Check | Class | Failure action |97|----|-------------------------|----------------------------------------------------|---------|-------------------------------------------------------|98| 1 | Module exists | `Glob("**/go.mod")` — filesystem only | BLOCK | No Go module here — nothing to audit |99| 2 | Baseline snapshot | `git status --porcelain go.mod go.sum` — **before any `go` command** | WARN | Note uncommitted module edits; the audit reflects the worktree, not HEAD |100| 3 | go.mod is well-formed | `go mod edit -json` parses; `module` directive present | BLOCK | Malformed manifest — findings would be fiction |101| 4 | Module graph resolvable | `go list -mod=readonly -m all` succeeds | DEGRADE | -> `no-graph` mode (see below) |102| 5 | Checksums available | go.sum covers every non-replaced external requirement | DEGRADE | -> `no-integrity` mode (see below — absence alone is not a failure) |103| 6 | Checksums verify | `go mod verify` | WARN | **Report as a P1 finding, do not stop** — a tampered cache is exactly what an audit exists to surface |104| 7 | govulncheck available | `govulncheck -version` | DEGRADE | -> `no-cve` mode |105| 8 | Vuln DB reachable | `govulncheck` exits 0 or 3, not 1 | DEGRADE | -> `no-cve` mode (offline) |106| 9 | License tool available | `go-licenses help` lists subcommands | DEGRADE | -> `no-license` mode |107| 10 | Tidy state | `go mod tidy -diff` (**Go 1.23+**; skip below that) | WARN | Report untidy go.mod as a hygiene finding |108| 11 | Closing snapshot | `git status --porcelain go.mod go.sum` matches gate 2 | WARN | A probe mutated the module files — say so in S9.8 |109110Four rationales, each replacing a worse rule:111112- **Gate 2 precedes every `go` command.** A baseline taken after `go list` cannot113 prove the audit was read-only — `go list` is one of the things it would have to114 exonerate.115- **Gate 4 is DEGRADE, not BLOCK.** Gate 3 already caught a broken manifest; a116 failure here is environmental (offline, proxy down, missing credentials, cold117 cache) — a lost capability, not an untrustworthy subject.118- **Gate 5 is conditional, not "file exists".** A missing `go.sum` is legal when119 the module has no dependencies or every requirement is redirected by a local120 `replace`. Decide by what is required, not by `ls`: external non-`replace`d121 requirements with checksums missing -> DEGRADE; none, or all locally replaced122 -> **N/A**, legitimately absent, not a finding; cannot tell (gate 4 already123 degraded) -> DEGRADE, naming the unresolved graph as the cause.124125- **Gate 6 is WARN.** An integrity failure is the highest-value output this skill126 can produce; stopping would suppress the finding the user most needs.127- **Gate 9 uses `help`, not `--help`.** `go-licenses --help` prints only the128 logging flags and never lists commands — a useless liveness probe.129130### 2.3 Multi-module repositories131132Gate 1 globs for **every** `go.mod`. If it finds more than one, **the unit of133audit is the module, not the repository** — running the gates once in the root134audits one module and reports it as though it covered all of them. When >1 is135found, before gate 3:1361371. **Load `references/multi-module.md`** and follow it. Do not improvise.1382. **Snapshot all manifests at once**, still before any `go` command:139 `git status --porcelain -- '**/go.mod' '**/go.sum'`1403. **List every module in S9.1**; name any you skipped in S9.8.141142Single-module repositories skip this — the gate table runs once.143144### 2.4 Scope classification145146| Mode | Trigger | Output contract |147|--------------|--------------------------------------------|-----------------|148| **Quick** | "check for CVEs", one named concern | S9 subset (9.1, 9.2, 9.3, 9.8, 9.9) |149| **Standard** | "audit dependencies", pre-release check | Full S9 |150| **Deep** | "supply chain review", compliance audit | Full S9 + provenance/SBOM |151152---153154## 3 Depth Selection155156### Quick157Single-concern scan. Load no reference files.158- Triggers: "run govulncheck", "any CVEs?", "check this dependency"159- Coverage: govulncheck scan + S6 triage + immediate remediation plan160- **Output**: the S9 subset above. Do not emit empty License/Supply-Chain161 sections — omit them and say why in S9.8.162163### Standard (default)164Full audit across 5 domains. Load `govulncheck-patterns.md`,165`license-compliance.md`, `upgrade-planning.md` — one per domain this depth166covers. (`supply-chain-security.md` is Deep-only; `multi-module.md` loads on the167gate-1 trigger regardless of depth.)168- Triggers: pre-release audit, "audit our dependencies", quarterly review169- Coverage: CVE scan, license risk, outdated report, upgrade assessment, hygiene170- Force Standard if: multiple go.mod files, compliance requirements, CI integration171172### Deep173Comprehensive supply chain review. Load all references.174- Triggers: compliance audit, incident response, "supply chain review"175- Coverage: all Standard domains + provenance, SBOM, transitive license, proxy config176- Force Deep if: regulatory compliance, post-incident, new vendor onboarding177178---179180## 4 Degradation Modes181182Each mode is entered by exactly one DEGRADE gate. Modes compose — record all183that apply.184185| Mode | Entered by | Can still deliver | MUST NOT claim |186|-----------------|------------|--------------------------------------------------|-----------------------------------------------|187| `no-graph` | Gate 4 | Direct requirements read from `go.mod` | Anything about indirect dependencies, or that the list is complete |188| `no-integrity` | Gate 5 | Module list, versions, licenses, hygiene | Reproducible-build or tamper-detection status |189| `no-cve` | Gate 7, 8 | License, outdated, hygiene, supply chain posture | Any CVE status — present, absent, or reachable |190| `no-license` | Gate 9 | CVE, outdated, hygiene, supply chain posture | License distribution or compliance posture |191| `no-reachability` | `-scan` was not `symbol`, or binary mode | Which modules are affected | That any finding is or is not reachable |192193Mark every degraded output inline: `# DEGRADED [<mode>]: <what is missing>`194195Two absolute rules:196197- **Never fabricate CVE findings.**198- **Never claim "no vulnerabilities" without a scan that completed.** A199 govulncheck exit code of 1 is a *failed scan*, not a clean one.200201---202203## 5 Dependency Audit Checklist204205### 5.1 CVE Scanning2062071. **`govulncheck ./...` in source mode is primary** — it traces the call graph,208 so it reports whether your code can actually reach the vulnerable symbol.2092. **The `-scan` level decides what "found" means** — `symbol` (default) reports210 reachable symbols, `package` imported packages, `module` required versions.211 Lowering it raises noise and forfeits reachability.2123. **Exit code is the CI contract, and `-format json` breaks it** — text mode:213 `3` found at scan level, `2` invalid usage, `1` error, `0` clean. `-json` /214 `-format sarif` / `-format openvex` exit **0 regardless of findings**, so a215 CI job gating on `$?` after them never fails.2164. **govulncheck reports no CVSS score** — see S6.1. Priority comes from the217 evidence tier, not from a severity number the tool never emitted.2185. **Test files are excluded by default** — `-test` defaults to false, so219 test-only dependencies are not analyzed unless you pass `-test`.2206. **Transitive findings still need `go mod why -m <module>`** to establish which221 direct dependency pulls them in — that is the module you actually upgrade.222223### 5.2 License Risk Triage224225> **This skill does not give legal advice and does not decide whether a license226> is compatible with a project.** It gathers the facts a lawyer needs and states227> which facts trigger escalation. Every copyleft finding routes to legal review.2282297. **Report the license, the path, and the trigger conditions — never a verdict.**230 Whether a copyleft obligation attaches turns on facts this skill cannot see:231 distribution, linkage vs build-tool-only, licence version and exceptions,232 modification, deployment model. Record the observable; escalate the rest.2338. **Use the scanner's own vocabulary** — `go-licenses` types are `forbidden`,234 `restricted`, `reciprocal`, `notice`, `permissive`, `unencumbered`, `unknown`;235 `--disallowed_types` defaults to `forbidden,unknown`. Reporting in the tool's236 terms keeps the output auditable and version-stable.2379. **Distinguish shipped from not-shipped, and label the evidence grade.**238 Required (`go list -m all`) < build-dependency (`go list -mod=readonly -deps239 <main pkg>`, after discovering `main` packages — never assume `./cmd/...`) <240 binary (`go version -m <artifact>`, which reads the module list the build241 actually recorded). Say which grade you have; only the last supports the242 phrase "linked into the shipped binary".24310. **A missing LICENSE file is the highest-signal license finding** — no grant244 of rights was located. An escalation trigger, not a legal conclusion.24511. **Escalate with the facts attached**: module path, licence identifier and246 version, `go mod why -m` path, evidence grade for shipping (item 9), and247 whether the project distributes binaries or runs a network service.248249### 5.3 Upgrade Planning25025112. **Semver signals intent, not a guarantee.** Patch/minor are *lower risk*,252 not safe, and **`v0.x.y` carries no compatibility promise at all**. Read the253 changelog; diff the API surface when there is none.25413. **`+incompatible` is a silent major-version upgrade hazard** — the module255 published v2+ tags without a module-aware `go.mod`, so the toolchain treats256 those versions as part of the *same* module as v1.x. MVS can therefore257 upgrade v1.5.2 straight to v4.1.2+incompatible during a routine `-u`. Plan258 migration to a `/vN` path.25914. **`go get -u` upgrades far more than the target** — it raises the target and260 its dependencies. Use `go get <module>@<version>` for precise control, and261 remember that even a precise `go get` can move *other* modules, because262 minimal version selection re-solves the whole graph.263264### 5.4 Supply Chain Security26526615. **go.sum is an integrity anchor, not a lockfile.** It records expected hashes;267 it does not pin which version is selected — that is `go.mod` + MVS. Commit268 both; verify with `go mod verify`.26916. **GOPROXY affects availability and privacy, not checksum verification.**270 Validation is controlled by `GOSUMDB` and disabled per pattern by271 `GOPRIVATE`/`GONOSUMDB` — `GOPROXY=direct` still verifies.27217. **`GOPRIVATE` for internal modules** — stops internal module paths leaking to273 the public proxy and checksum database. Shorthand for `GONOPROXY` +274 `GONOSUMDB`.27518. **Deleted upstream tags break builds** — `proxy.golang.org` caches immutably,276 so a cached version survives tag deletion. Prefer the proxy over `direct`.277278### 5.5 Module Hygiene27928019. **Check tidiness without mutating** — `go mod tidy -diff` (Go 1.23+) prints281 the change and exits non-zero if non-empty. Below 1.23 report the check as282 unavailable rather than running the mutating `go mod tidy`.28320. **Minimize `replace` directives** — each is technical debt, and a local-path284 `replace` in a committed go.mod breaks every machine but the author's.28521. **Module-graph cycles are legal in Go and are not, by themselves, a defect.**286 Modules may require each other; only *package* import cycles are rejected by287 the compiler. Report a cycle as a WARN-level design smell that widens upgrade288 blast radius — never as a failed check.28922. **`go.work` is normally not committed** — it encodes one developer's local290 layout. Exception: a single-repository workspace whose `use` directives are291 all repo-relative. Check the paths before flagging it.292293---294295## 6 Triage & Priority Model296297### 6.1 The tool gives you evidence, not a score298299The Go vulnerability database does **not** publish CVSS scores, so govulncheck300never prints one. Its report carries the `GO-YYYY-NNNN` ID, aliases (CVE/GHSA),301summary, affected ranges, fixed version, and `database_specific.review_status`302(`REVIEWED` / `UNREVIEWED`). Both rules are mandatory:303304- **Never state a CVSS score sourced from govulncheck.** It did not produce one.305- Any CVSS must be enriched from a *named external source* keyed on the alias —306 "CVSS 9.8 (NVD, CVE-2023-44487)" — and recorded in S9.3. With no such lookup307 the column reads `not retrieved`, never a guess.308309### 6.2 Evidence tiers310311govulncheck groups findings into result sections. The section *is* the evidence.312313| Section | Meaning | Tier |314|---------------------------|--------------------------------------------------------|------|315| `=== Symbol Results ===` | A vulnerable symbol is reachable from your call graph | **E1 Called** |316| `=== Package Results ===` | You import the affected package; no reachable symbol proven | **E2 Imported** |317| `=== Module Results ===` | The module is required at an affected version only | **E3 Required** |318319`No vulnerabilities found.` = zero findings at any tier.320321### 6.3 Priority322323| Priority | Condition |324|----------|-----------|325| **P0** | E1 Called, a fix version exists, and the call path is reachable from a network-facing entry point |326| **P1** | E1 Called (any other case); **or** `go mod verify` reported a checksum mismatch; **or** an unlicensed dependency is linked into a shipped binary |327| **P2** | E2 Imported; **or** a copyleft dependency linked into a shipped artifact and pending legal review; **or** a `+incompatible` direct dependency |328| **P3** | E3 Required only; minor-version drift; hygiene findings; EOL library with no current findings |329330Escalation modifiers — apply, then state the reason:331332- **No fix version available** — escalate one; remediation is a compensating333 control, not an upgrade.334- **`UNREVIEWED` report** — absence of a symbol-level finding is not proof of335 unreachability. Hold at the tier reported and note the status.336- **Reachability not established** (`-scan module|package`, binary mode, or337 reflection/`unsafe`/plugin in the path) — `no-reachability` mode. Report the338 tier obtained; never downgrade on absent evidence.339- **Test-only dependency** — de-escalate one, only after `go mod why -m` confirms340 no non-test path exists.341342---343344## 7 Anti-Examples345346Each rule below is binding on its own. Worked WRONG/RIGHT pairs for all six are347in `references/anti-examples.md` — load it when an audit is about to do one of348these things, or when explaining why not.349350| ID | Anti-pattern | Rule |351|------|-------------------------------------------------|----------------------------------------------------------------------|352| AE-1 | Assigning a CVSS score govulncheck never emitted | Report `CVSS: not retrieved`, or cite the external database and alias it came from. Priority comes from the evidence tier. |353| AE-2 | Turning a licence observation into a legal verdict | Emit the escalation packet — module, licence, path, linkage, distribution — and route to legal. Never conclude. |354| AE-3 | Gating CI on an exit code `-json` always sets to 0 | Gate on the text-mode exit code (3 = found, 1 = broke), or parse findings from JSON with `jq -s`. |355| AE-4 | Rolling back with a command that destroys work | Never emit `git checkout`/`restore`/`reset`. Require a clean worktree up front and stop on failure. |356| AE-5 | Claiming "no vulnerabilities" from a failed scan | Only exit 0 with `No vulnerabilities found.` supports that claim. Exit 1 means `no-cve`, status UNKNOWN. |357| AE-6 | Treating `+incompatible` as harmless | It is the *same* module as v1.x to MVS, so `-u` can cross a major version silently. Track as P2 with a `/vN` migration plan. |358359---360361## 8 Dependency Audit Scorecard362363Twelve checks in three tiers, applied after every audit —364**load `references/scorecard.md`** for the item list and score them there.365366A check that could not run because of a DEGRADE gate scores **N/A** and leaves367both numerator and denominator; it never counts as a pass. Score each tier as a368ratio over its applicable items, because a fixed threshold breaks the moment an369item goes N/A:370371```372critical = passed / applicable must be 1.00 (0 applicable -> tier N/A)373standard = passed / applicable must be >= 0.80374hygiene = passed / applicable must be >= 0.75375PASS iff every non-N/A tier meets its threshold.376```377378Report ratio and raw counts: `Standard 3/3 (1.00) — 2 items N/A`. In a379multi-module audit the repository verdict is the **worst** module's, never an380average — an average lets a clean module mask a failing one.381382---383384## 9 Output Contract385386Quick mode emits 9.1, 9.2, 9.3, 9.8, 9.9; Standard and Deep emit all nine. An387omitted section must be named in 9.8 with the reason — never silently dropped,388never emitted empty. Volume: P0/P1 fully detailed, P2 up to 10, P3 summary.389390### 9.1 Audit Context391Every module audited (path + directory), Go version, direct/indirect counts,392worktree state, tool versions, scan timestamp.393394### 9.2 Mode & Depth395`Quick | Standard | Deep`, plus every active degradation mode from S4 and the396gate that triggered it.397398### 9.3 CVE Scan Results399Command (with `-scan`/`-mode`), exit code, and per finding: GO-ID, aliases,400module, evidence tier (E1/E2/E3), fixed version, review status, priority, and401CVSS **with its source** or `not retrieved`.402403### 9.4 License Inventory404Per dependency: licence identifier, scanner classification, shipping evidence405grade (S5.2 item 9). Separate escalation table for copyleft/unknown/missing with406the item-11 facts attached. No verdicts.407408### 9.5 Outdated Dependencies409Direct dependencies behind latest, grouped by major/minor/patch drift, with the410`v0.x` ones called out as unbounded-risk regardless of the size of the bump.411412### 9.6 Supply Chain Posture413Actual `go env` values (`GOPROXY`, `GOPRIVATE`, `GONOPROXY`, `GONOSUMDB`,414`GOSUMDB`); go.sum status and `go mod verify` result; `replace` inventory.415416### 9.7 Remediation Plan417Prioritized, **as commands for the user to run** — this skill does not run them.418Immediate (P0/P1), short-term (P2), backlog (P3). Each entry: module, current ->419target, evidence tier resolved, and precondition (clean worktree, green baseline).420421### 9.8 Uncovered Risks422What this audit did NOT cover. Mandatory — never empty. Must include every423degradation mode, N/A scorecard item, omitted output section, module not audited,424and escalation handed to another party.425426### 9.9 Machine-Readable Summary427```json428{"summary":{"pass":true,"modes":["no-license"],429 "tiers":{"critical":{"passed":3,"applicable":3,"ratio":1.0},430 "standard":{"passed":4,"applicable":4,"ratio":1.0,"na":1},431 "hygiene":{"passed":3,"applicable":4,"ratio":0.75}}},432"counts":{"p0":0,"p1":1,"p2":3,"p3":5},433"evidence":{"e1_called":1,"e2_imported":3,"e3_required":5},434"modules":{"direct":12,"indirect":47,"affected":4},435"scan":{"tool":"govulncheck","mode":"source","scan_level":"symbol","exit_code":3}}436```437438**Scorecard appended**, ratios with raw counts and N/A totals:439`Critical 3/3 (1.00) · Standard 4/4 (1.00, 1 N/A) · Hygiene 3/4 (0.75) — PASS`440441---442443## 10 Reference Loading Guide444445| Condition | Load |446|----------------------------------------------|----------------------------------------|447| CVE scanning (Standard+) | `references/govulncheck-patterns.md` |448| License risk triage (Standard+) | `references/license-compliance.md` |449| Upgrade planning, version migration (Standard+) | `references/upgrade-planning.md` |450| Supply chain review (Deep) | `references/supply-chain-security.md` |451| About to do — or explain — an S7 anti-pattern | `references/anti-examples.md` |452| More than one `go.mod` found (gate 1) | `references/multi-module.md` |453| Scoring the audit (S8) | `references/scorecard.md` |454455Each reference has a table of contents — load the relevant sections, not the456whole file.457458**Tool-version note (G1)**: commands here are verified against `govulncheck459v1.1.4`, `go-licenses v2.0.1`, and Go 1.26.1.460`go mod tidy -diff` additionally requires Go 1.23+.461Check `govulncheck -version` and the local `go` version before relying on a462flag. A govulncheck built against a *different* Go than the one on `PATH` fails463package loading with exit 1 — that is `no-cve`, and the fix is to rebuild464govulncheck, not to report a clean scan.