I just presented this at VulnCon 2026 (slides). Here's the tool and the data.
The blind spot
Your vulnerability scanner is excellent at finding CVEs. Trivy, Snyk, Grype — they do their job well.
But there's a category of risk they cannot see: packages that are no longer maintained.
No maintainer means no security patches. No bug fixes. No one watching. And because no one is looking — no CVEs get filed. Your scanner reports zero vulnerabilities, and you assume it's safe.
It's not safe. It's invisible.
Our team at FutureVuls analyzed 16,627 packages across 74 organizations running in production (published in Nikkei — Japan's largest business newspaper — March 2026):
| Status | % | Meaning |
|---|---|---|
| 🟢 Active | 40.6% | Someone is working on it |
| 🔵 Legacy-Safe | 10.9% | Dormant but stable |
| 🟡 Stalled | 34.6% | Activity declining |
| 🔴 EOL | 13.9% | End of life |
48.5% of production dependencies have lifecycle risk. Most of it is invisible.
(These figures come from the FutureVuls survey and its own classification rules. uzomuzo, introduced below, uses similar but not identical rules, so its numbers on the same data would differ.)
What does this look like in practice?
I scanned HashiCorp Vault — the tool you trust with your secrets. 209 dependencies.
$ uzomuzo scan --file vault-go.mod
STATUS PURL LIFECYCLE
✅ ok cloud.google.com/go/storage@v1.56.1 Active
✅ ok Azure/azure-sdk-for-go/sdk/azcore Active
🔴 replace aws/aws-sdk-go@v1.55.8 EOL-Confirmed
🔴 replace mitchellh/copystructure@v1.2.0 EOL-Confirmed
... (209 deps total)
── Summary ──────────────────────────────────
│ 209 deps | ✅ 186 ok | ⚠️ 11 caution | 🔴 11 replace
11 packages are EOL. In a secrets management tool. Most have zero CVEs.
The scary one: copystructure
Mitchell Hashimoto — Vault's creator — left HashiCorp in 2023. He publicly announced: "I very rarely write Go anymore." He archived 15 libraries at once, including copystructure.
Unlike mapstructure (which has a community fork), copystructure has no successor. Nobody picked it up.
Where is it used? vault/acl.go — the access control layer. It deep-copies the deny rules for each security check.
If this package were compromised — deny rules silently disappear. No error. No log. No crash. Your scanner will never find this because there is no CVE to find.
The risk is not a bug. It's being abandoned.
From detection to analysis — with an LLM
Finding the EOL package is step one. But what exactly happens if it's compromised? I used an LLM to trace the data flow and build the attack scenario:
mitchellh/copystructure — Risk: CRITICAL
Lifecycle: Archived. Maintainer left HashiCorp in 2023. CVEs: 0
Data flow:
IN: ACL DeniedParameters, MFA methods, ClientToken
OUT: Deep-copied structs for per-request isolation
Attack scenario:
1. acl.go: deny rules silently disappear
2. request.go: ClientToken shared across requests
3. policy.go: MFA bypass via shared ControlGroup
→ NO errors. NO logs. NO crashes.
Verdict: CRITICAL. Action: self-implement with ~100 lines of reflect-based code, replacing the ~500-line archived dependency.
This analysis — tracing what data flows through a package, constructing attack scenarios, assessing severity — takes weeks to do manually across 209 dependencies. With uzomuzo + LLM, it takes minutes. I automated this as the /diet-assess-risk skill.
How I built a tool to find this
I built uzomuzo — an open-source tool that detects unmaintained packages that SCA tools miss. The name comes from a Buddhist concept: uzōmuzō (有象無象) — "the visible and the invisible." That's exactly what your dependency tree is.
What makes it different
1. Seven lifecycle stages, not binary.
Other tools give you "maintained or not." uzomuzo gives you a spectrum:
| # | Signal | Result |
|---|---|---|
| 1 | Registry EOL? (npm deprecated, PyPI inactive) | Yes → 🔴 EOL-Confirmed |
| 2 | Archived / Disabled repository? |
Yes → 🟡 Stalled, marked · archived or · disabled
|
| 3 | EOL announced? | Yes → 🟠 EOL-Scheduled |
| 4 | No recent human commits + HIGH/CRITICAL advisory? | ⚫ EOL-Effective |
| No recent human commits + LOW/MEDIUM advisory? | 🟡 Stalled | |
| No recent human commits + no advisory? | 🔵 Legacy-Safe | |
| 5 | Recent human commits + recent publish or VCS-direct? | 🟢 Active |
| Recent human commits + no publish + low Scorecard? | 🟡 Stalled |
Each check is evaluated top to bottom — first match wins. "Stalled" and "dead" require different responses. Stalled — you watch. EOL — you replace.
2. Multiple signal sources.
It combines data from deps.dev (Scorecard, releases, advisories), GitHub API (archive status, commit history), and registry heuristics (PyPI classifiers, npm deprecated flag, Packagist abandoned).
The judgment is ecosystem-aware. Go delivers via git — commits are releases. npm requires a registry publish. Same commits, different verdict.
3. Two-axis evaluation: lifecycle × build integrity.
Lifecycle alone isn't enough. A package can be actively maintained but have no signed releases, no reproducible builds, no provenance. uzomuzo evaluates both axes — so you can distinguish "healthy and well-built" from "active but risky."
4. Not just detection — all the way to removal.
This is the big one. Every other tool stops at detection. Scorecard gives you a score. Trivy gives you a CVE. Then you're on your own.
uzomuzo goes from detect → prioritize → remove:
-
uzomuzo scan— find EOL packages (every CI build) -
uzomuzo diet— rank by removal priority: graph impact × health risk × coupling effort -
/diet-*Claude Code skills — assess risk, evaluate removal, execute safely
Try it now (30 seconds)
Install
go install github.com/future-architect/uzomuzo-oss/cmd/uzomuzo@latest
Or download a binary from GitHub Releases.
Scan your project
# Go project
uzomuzo scan --file go.mod
# Any language (via SBOM)
trivy fs . --format cyclonedx | uzomuzo scan --sbom -
# GitHub Actions
uzomuzo scan --file .github/workflows/ci.yml
# Single package
uzomuzo scan pkg:npm/express@4.18.2
CI gate
# Fail the build if any EOL dependency is found
trivy fs . --format cyclonedx \
| uzomuzo scan --sbom - --fail-on eol-confirmed
That's it. One pipeline command.
By default, an SBOM scan skips the dependencies your SBOM marks as transitive, since direct dependencies are the ones you can replace yourself. To check transitive dependencies too, including malicious versions that arrive through them, add --show-transitive.
I used it on my own project
I ran uzomuzo on my own project — vuls, an open-source vulnerability scanner with 391 dependencies. Then I removed what I found.
Scanner binary size: 106.6 MB → 34.1 MB (-68%). I reported my findings upstream — Grafana's maintainer closed the community PR and committed to replacing the archived Action internally using their own shared-workflows, and finished the migration in September 2026.
The full story of how I did it — in a follow-up post.
The blind spot is bigger than you think
I cross-checked SCA results against lifecycle status on real-world projects. The overlap was almost zero — most EOL packages had no CVEs at all. They are completely invisible to SCA tools.
"No CVE" doesn't mean "no vulnerability." It often means "no one is looking."
Full methodology and data — in a follow-up post.
Start today
-
30 seconds:
uzomuzo scan --file go.modoruzomuzo scan https://github.com/your/repo -
5 minutes: Add
--fail-on eol-confirmedto your CI pipeline -
Quarterly: Run
uzomuzo dietfor a prioritized removal plan
You will be surprised what you find.
Updated October 26, 2026: corrected the survey size (74 organizations, 16,627 packages), clarified that the 68% reduction is for the scanner-only build, removed a dependency count we could not trace to a primary record, and updated the lifecycle table: current versions label an archived repository Stalled and mark it · archived, rather than EOL-Confirmed; and noted that SBOM scans skip transitive dependencies unless you add --show-transitive. The scan outputs above were captured in April 2026 with an earlier version.
⭐ github.com/future-architect/uzomuzo-oss — Apache 2.0
Kota Kanbe — Creator of vuls (12K+ ⭐), presented at VulnCon 2026
Top comments (0)