Go supply chain risk assessment — vulnerabilities, maintainer health, typosquatting, AI-generated code risk, CI/CD audit, SBOM, policy enforcement.
unisupply analyzes a Go project's full module dependency chain and produces a
supply chain risk assessment. It runs nine focused scanners — vulnerability
lookup, maintenance health, maintainer analysis, typosquatting detection,
resilience scoring, AI-generated code heuristics, CI/CD pipeline audit, build
file inspection, and Trust Index lookup — combines the in-tree scanner signals
into a weighted risk score per dependency, attaches the optional Trust Index
data to each report alongside that score, and renders the result as a colored terminal
summary, machine-readable JSON, an enterprise PDF report, or a CycloneDX /
SPDX SBOM. A built-in policy engine fails CI on configurable thresholds
(critical vulns, max age, blocked modules, unpinned actions, ...).
It is intended for engineering teams that need to make merge-time and release-time decisions about third-party Go code.
# Latest release (Go 1.25+ required)
go install github.com/unidoc/unisupply/cmd/unisupply@latest
# Or download a prebuilt binary from the Releases page
# https://github.com/unidoc/unisupply/releasesHomebrew and other package-manager distribution channels are tracked as
post-1.0 follow-ups; for now use go install or the release tarballs.
# Scan the current module
unisupply ./
# JSON output for CI ingestion
unisupply ./ --format json --output results.json
# Full PDF report (requires UNIDOC_LICENSE_API_KEY for PDF generation)
unisupply ./ \
--format pdf \
--output report.pdf \
--github-token "$GITHUB_TOKEN"
# Policy-enforced run — exits 2 on violation
unisupply ./ --policy-preset strictA typical text-format run against a small project looks like this (truncated for the README — your output will list every direct and transitive dependency):
unisupply v0.4.0 — Go Supply Chain Risk Assessment
by UniDoc (unidoc.io)
Project: github.com/example/app
Dependencies: 4 direct, 38 transitive (42 total, 113 graph edges)
═══════════════════════════════════════════════════
OVERALL SUPPLY CHAIN RISK SCORE: 26/100 (MEDIUM)
═══════════════════════════════════════════════════
HIGH RISK (3 dependencies)
────────────────────────────────────────
● golang.org/x/net Risk: 51/100 (transitive)
├─ 4 known vulnerabilities with available fixes
├─ Resilience: 70/100 (frequent cadence, 53 releases)
└─ Score: vuln=100×40% maint=0×25% depth=0×15% maintainer=0×10% maturity=0×10%
MEDIUM RISK (19 dependencies) [...]
LOW RISK (20 dependencies) [use --verbose to see details]
PACKAGES ELIGIBLE FOR MAINTENANCE TAKEOVER
────────────────────────────────────────
● widely-used/inactive-pkg Activity: inactive Bus factor: 1
Pass --verbose for full per-dependency breakdowns including the dependency
path that pulled the module in.
| Scanner | What it checks | Data source |
|---|---|---|
| Vulnerability | Known CVEs in dependencies | Go vuln DB (vuln.go.dev) |
| Maintenance | Last release, archive status, deprecation | Go Module Proxy |
| Maintainer | Contributors, bus factor, activity, org verification | GitHub API |
| Typosquatting | Levenshtein-similarity to ~75 well-known modules | Built-in list |
| Resilience | Release cadence, governance files, version scheme | GitHub API |
| AI-Generated | Fresh modules, few releases, generic names (heuristics) | Module metadata |
| CI/CD | Action pinning, permissions, secret exposure | .github/workflows/*.yml |
| Build files | Unpinned Docker images, curl | bash patterns |
Dockerfile, Makefile, *.sh |
| Trust Index | Curated trust scores | unitrust API (optional) |
The risk score is a weighted composite:
Risk Score (0–100) =
Vulnerabilities × 0.40
+ Maintenance × 0.25
+ Depth × 0.15
+ Maintainer Risk × 0.10
+ Maturity × 0.10
+ Typosquat Penalty (0–20)
+ AI-Gen Penalty (0–15)
+ Low-Resilience Penalty (0–6) // adds when resilience score < 30
Levels: LOW 0–25 · MEDIUM 26–50 · HIGH 51–75 · CRITICAL 76–100.
| Format | Flag | Use |
|---|---|---|
| Text (colored) | --format text (default) |
Interactive terminal use |
| JSON | --format json |
CI ingestion, dashboards, programmatic consumers |
--format pdf |
Enterprise reports (built with UniPDF + UniChart) | |
| CycloneDX SBOM | --format sbom-cyclonedx |
Standard CycloneDX 1.5 software bill of materials |
| SPDX SBOM | --format sbom-spdx |
SPDX 2.3 software bill of materials |
unisupply ships two presets and accepts a custom JSON policy file. Policy
violations cause the binary to exit 2, which is the convention CI systems
expect for a deliberate fail-fast.
# Built-in presets
unisupply ./ --policy-preset strict
unisupply ./ --policy-preset moderate
# Custom policy
unisupply ./ --policy ./security-policy.jsonA minimal custom policy (every field is optional — see
pkg/policy/engine.go for the full set):
{
"max_overall_score": 50,
"max_risk_score": 75,
"no_critical_vulns": true,
"no_archived": true,
"no_deprecated": true,
"no_typosquatting": true,
"no_unmaintained_months": 24,
"max_depth": 8,
"max_ci_score": 50,
"blocked_modules": ["github.com/suspicious/pkg"],
"allowed_modules": ["golang.org/x/", "github.com/unidoc/"]
}Notable fields:
no_known_vulns/no_critical_vulns— fail on any vuln, or only on high/critical-severity vulns.no_single_maintainer— fail if any direct dependency has bus factor ≤ 1.allowed_modules— when set, acts as a strict whitelist applied to direct dependencies only; transitive modules are not gated by this rule. Each entry matches by exact module path or by path-prefix ("golang.org/x/"matchesgolang.org/x/net); glob patterns are not supported.blocked_modules— applies to direct and transitive dependencies, with the same exact-or-prefix matching rule.max_ci_score— gate on the CI/CD scanner's overall risk score (requires--scan-ci).
unisupply can enrich a scan with curated trust data from a running
unitrust instance. The Trust Index is
UniDoc's curated database of Go module trustworthiness — it goes beyond what
public APIs and heuristics can tell you (vulnerability feeds, GitHub
contributor counts, release cadence) and adds vetted, human-reviewed
metadata: who actually maintains the package, what country and organization
they operate from, whether their identity is verified, the package's
stewardship status, and — when a module is known-risky — a recommended safer
alternative.
When --trust-index-url points at a reachable unitrust instance, every
discovered module (direct and transitive) is sent in a single batched
HTTP request to POST /api/v1/lookup. The returned data is folded into
each dependency's report alongside the in-tree scanner output. No per-module
calls, no fan-out — one round trip regardless of graph size.
# Hosted unitrust (production CI)
unisupply ./ \
--trust-index-url https://unitrust.unidoc.io \
--format json --output results.jsonFor every module that unitrust has data on, the report gains:
| Field | What it tells you |
|---|---|
trust_score |
Composite curated trust score (0–100) |
maintainer_trust |
Curated confidence in the maintainer's identity and track record |
resilience_score |
Project resilience as assessed by UniDoc, not just heuristics |
security_score |
Curated security posture (review history, hardening, response time) |
community_score |
Community health beyond raw star/fork counts |
maintainer_name |
Real maintainer name where known |
maintainer_org |
Sponsoring or employing organization |
maintainer_country |
Maintainer jurisdiction — relevant for compliance / sanctions checks |
maintainer_verified |
Whether UniDoc has verified the maintainer's identity |
stewardship_status |
actively_maintained, community, inactive, abandoned, … |
safer_alternative |
Recommended replacement module, if the entry is flagged as risky |
is_unidoc_maintained |
True for modules under UniDoc's own stewardship |
The full schema lives in
pkg/scanner/trustindex.go (TrustIndexEntry).
Modules unitrust has no entry for are reported with their normal scanner
output unchanged.
- CI gating — combine
--trust-index-urlwith--policy-preset strictto fail builds on known-risky modules whose risk isn't yet visible in the CVE feeds. - Procurement / vendor review — the maintainer name, country, and
verification flag are the fields enterprise reviewers actually need; they
are not derivable from
go.modalone. - Supply-chain incident response — the
safer_alternativefield short-circuits "what should we replace this with?" during a live incident.
- The lookup payload contains only module paths — the same information
that is already in your published
go.mod. Versions are not transmitted, and the request includes no source code, scan results, or other project-identifying data. - The whole feature is opt-in and additive. Leaving
--trust-index-urloff produces a fully self-contained scan —unisupplynever reaches out to unitrust by default and has no implicit endpoint. - Failures of the Trust Index call (network errors, non-200 responses) are surfaced as warnings; they do not abort the scan or alter risk scores derived from the in-tree scanners.
CLI (pflag)
│
├── Parse go.mod / go.sum pkg/parser/
├── Resolve dependency graph pkg/resolver/
├── Run 9 security scanners pkg/scanner/
│ ├── Vulnerability (govulncheck)
│ ├── Maintenance health
│ ├── Maintainer analysis (GitHub API)
│ ├── Typosquatting detection
│ ├── Resilience scoring
│ ├── AI-generated code risk
│ ├── CI/CD pipeline audit
│ ├── Trust Index lookup (unitrust, optional)
│ └── Build file scanning
├── Compute risk scores pkg/scorer/
├── Evaluate org policies pkg/policy/
└── Generate reports pkg/report/
├── Text (colored terminal)
├── JSON (machine-readable)
├── PDF (UniPDF)
└── SBOM (CycloneDX, SPDX)
The full flag set is always available via:
unisupply --helpThe most frequently used flags:
| Flag | Purpose |
|---|---|
-f, --format |
text, json, pdf, sbom-cyclonedx, sbom-spdx |
-o, --output |
Output file (default: stdout for text/json/sbom) |
--github-token |
GitHub API token (or GITHUB_TOKEN env) |
--trust-index-url |
unitrust endpoint for curated trust scores |
--policy-preset |
strict or moderate |
--policy |
Path to a custom policy JSON file |
--scan-workflows |
Audit .github/workflows/*.yml and *.yaml only |
--scan-ci |
Full CI/CD audit: workflows + Dockerfile / Makefile / scripts |
--min-risk |
Hide dependencies below the given score |
--direct-only |
Skip transitive dependencies |
-v, --verbose |
Per-dependency breakdown |
Environment variables:
| Variable | Purpose |
|---|---|
GITHUB_TOKEN |
Higher GitHub API rate limits and access to private repositories |
UNIDOC_LICENSE_API_KEY |
UniDoc license key (required for PDF report generation) |
- docs/scanners.md — scanner reference and the canonical risk-scoring formula
- SECURITY.md — vulnerability reporting and supported versions
- CONTRIBUTING.md — development setup and PR process
- CHANGELOG.md — release notes (Keep a Changelog 1.1)
- RELEASING.md — maintainer release procedure
- LICENSE — Apache License 2.0
Apache License 2.0 — see LICENSE for the full text.
Copyright © UniDoc ehf.