<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: kotakanbe</title>
    <description>The latest articles on DEV Community by kotakanbe (@kotakanbe).</description>
    <link>https://dev.to/kotakanbe</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F44746%2F423615e1-2660-4dfc-9278-1db8a449d5ec.jpg</url>
      <title>DEV Community: kotakanbe</title>
      <link>https://dev.to/kotakanbe</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/kotakanbe"/>
    <language>en</language>
    <item>
      <title>Your dependencies are 48% unmaintained — and SCA tools can't see it</title>
      <dc:creator>kotakanbe</dc:creator>
      <pubDate>Thu, 16 Apr 2026 22:16:44 +0000</pubDate>
      <link>https://dev.to/kotakanbe/your-dependencies-are-48-unmaintained-and-sca-tools-cant-see-it-2h7h</link>
      <guid>https://dev.to/kotakanbe/your-dependencies-are-48-unmaintained-and-sca-tools-cant-see-it-2h7h</guid>
      <description>&lt;p&gt;I just presented this at &lt;a href="https://www.first.org/conference/vulncon26/program#pThe-CVE-Blind-Spot-Defeating-Hidden-EOLs-and-Repo-Jacking-with-Engineering-Triage-Code-Diet" rel="noopener noreferrer"&gt;VulnCon 2026&lt;/a&gt; (&lt;a href="https://github.com/future-architect/uzomuzo-oss/blob/main/docs/presentations/vulncon2026.pdf" rel="noopener noreferrer"&gt;slides&lt;/a&gt;). Here's the tool and the data.&lt;/p&gt;




&lt;h2&gt;
  
  
  The blind spot
&lt;/h2&gt;

&lt;p&gt;Your vulnerability scanner is excellent at finding CVEs. Trivy, Snyk, Grype — they do their job well.&lt;/p&gt;

&lt;p&gt;But there's a category of risk they &lt;strong&gt;cannot see&lt;/strong&gt;: packages that are no longer maintained.&lt;/p&gt;

&lt;p&gt;No maintainer means no security patches. No bug fixes. No one watching. And because no one is looking — &lt;strong&gt;no CVEs get filed&lt;/strong&gt;. Your scanner reports zero vulnerabilities, and you assume it's safe.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It's not safe. It's invisible.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I analyzed 16,000 packages across ~100 organizations running in production (published in &lt;a href="https://www.nikkei.com/article/DGXZQOUE066RT0W6A300C2000000/" rel="noopener noreferrer"&gt;Nikkei&lt;/a&gt; — Japan's largest business newspaper — March 2026):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Status&lt;/th&gt;
&lt;th&gt;%&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;🟢 Active&lt;/td&gt;
&lt;td&gt;40.6%&lt;/td&gt;
&lt;td&gt;Someone is working on it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🔵 Legacy-Safe&lt;/td&gt;
&lt;td&gt;10.9%&lt;/td&gt;
&lt;td&gt;Dormant but stable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🟡 &lt;strong&gt;Stalled&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;34.6%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Activity declining&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🔴 &lt;strong&gt;EOL&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;13.9%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;End of life&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;48.5% of production dependencies have lifecycle risk.&lt;/strong&gt; Most of it is invisible.&lt;/p&gt;

&lt;h2&gt;
  
  
  What does this look like in practice?
&lt;/h2&gt;

&lt;p&gt;I scanned &lt;a href="https://github.com/hashicorp/vault" rel="noopener noreferrer"&gt;HashiCorp Vault&lt;/a&gt; — the tool you trust with your secrets. 209 dependencies.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;uzomuzo scan &lt;span class="nt"&gt;--file&lt;/span&gt; vault-go.mod
&lt;span class="go"&gt;
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
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;11 packages are EOL.&lt;/strong&gt; In a secrets management tool. Most have &lt;strong&gt;zero CVEs&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  The scary one: &lt;code&gt;copystructure&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Mitchell Hashimoto — Vault's creator — left HashiCorp in 2023. He publicly announced: &lt;a href="https://gist.github.com/mitchellh/90029601268e59a29e64e55bab1c5bdc" rel="noopener noreferrer"&gt;"I very rarely write Go anymore."&lt;/a&gt; He archived 15 libraries at once, including &lt;code&gt;copystructure&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Unlike &lt;code&gt;mapstructure&lt;/code&gt; (which has a &lt;a href="https://github.com/go-viper/mapstructure" rel="noopener noreferrer"&gt;community fork&lt;/a&gt;), &lt;strong&gt;copystructure has no successor&lt;/strong&gt;. Nobody picked it up.&lt;/p&gt;

&lt;p&gt;Where is it used? &lt;code&gt;vault/acl.go&lt;/code&gt; — &lt;strong&gt;the access control layer&lt;/strong&gt;. It deep-copies the deny rules for each security check.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The risk is not a bug. It's being abandoned.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  From detection to analysis — with an LLM
&lt;/h3&gt;

&lt;p&gt;Finding the EOL package is step one. But &lt;em&gt;what exactly happens if it's compromised?&lt;/em&gt; I used an LLM to trace the data flow and build the attack scenario:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;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.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Verdict: CRITICAL.&lt;/strong&gt; Action: self-implement with ~100 lines of reflect-based code, replacing the ~500-line archived dependency.&lt;/p&gt;

&lt;p&gt;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 &lt;code&gt;/diet-assess-risk&lt;/code&gt; skill.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I built a tool to find this
&lt;/h2&gt;

&lt;p&gt;I built &lt;a href="https://github.com/future-architect/uzomuzo-oss" rel="noopener noreferrer"&gt;&lt;strong&gt;uzomuzo&lt;/strong&gt;&lt;/a&gt; — an open-source tool that detects unmaintained packages that SCA tools miss. The name comes from a Buddhist concept: &lt;em&gt;uzōmuzō&lt;/em&gt; (有象無象) — "the visible and the invisible." That's exactly what your dependency tree is.&lt;/p&gt;

&lt;h3&gt;
  
  
  What makes it different
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Seven lifecycle stages, not binary.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Other tools give you "maintained or not." uzomuzo gives you a spectrum:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;Signal&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Archived / Disabled?&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Yes →&lt;/strong&gt; 🔴 EOL-Confirmed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Registry EOL? (npm deprecated, PyPI inactive)&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Yes →&lt;/strong&gt; 🔴 EOL-Confirmed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;EOL announced?&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Yes →&lt;/strong&gt; 🟠 EOL-Scheduled&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;No recent human commits + HIGH/CRITICAL advisory?&lt;/td&gt;
&lt;td&gt;⚫ EOL-Effective&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;No recent human commits + LOW/MEDIUM advisory?&lt;/td&gt;
&lt;td&gt;🟡 Stalled&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;No recent human commits + no advisory?&lt;/td&gt;
&lt;td&gt;🔵 Legacy-Safe&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;Recent human commits + recent publish or VCS-direct?&lt;/td&gt;
&lt;td&gt;🟢 Active&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Recent human commits + no publish + low Scorecard?&lt;/td&gt;
&lt;td&gt;🟡 Stalled&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Each check is evaluated &lt;strong&gt;top to bottom&lt;/strong&gt; — first match wins. "Stalled" and "dead" require different responses. Stalled — you watch. EOL — you replace.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Multiple signal sources.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;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).&lt;/p&gt;

&lt;p&gt;The judgment is ecosystem-aware. Go delivers via git — commits &lt;em&gt;are&lt;/em&gt; releases. npm requires a registry publish. Same commits, different verdict.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Two-axis evaluation: lifecycle × build integrity.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;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."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Not just detection — all the way to removal.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;uzomuzo goes from &lt;strong&gt;detect → prioritize → remove&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;uzomuzo scan&lt;/code&gt; — find EOL packages (every CI build)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;uzomuzo diet&lt;/code&gt; — rank by removal priority: graph impact × health risk × coupling effort&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/diet-*&lt;/code&gt; Claude Code skills — assess risk, evaluate removal, execute safely&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try it now (30 seconds)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Install
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;go &lt;span class="nb"&gt;install &lt;/span&gt;github.com/future-architect/uzomuzo-oss/cmd/uzomuzo@latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or download a binary from &lt;a href="https://github.com/future-architect/uzomuzo-oss/releases" rel="noopener noreferrer"&gt;GitHub Releases&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scan your project
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Go project&lt;/span&gt;
uzomuzo scan &lt;span class="nt"&gt;--file&lt;/span&gt; go.mod

&lt;span class="c"&gt;# Any language (via SBOM)&lt;/span&gt;
trivy fs &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;--format&lt;/span&gt; cyclonedx | uzomuzo scan &lt;span class="nt"&gt;--sbom&lt;/span&gt; -

&lt;span class="c"&gt;# GitHub Actions&lt;/span&gt;
uzomuzo scan &lt;span class="nt"&gt;--file&lt;/span&gt; .github/workflows/ci.yml

&lt;span class="c"&gt;# Single package&lt;/span&gt;
uzomuzo scan pkg:npm/express@4.18.2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  CI gate
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Fail the build if any EOL dependency is found&lt;/span&gt;
trivy fs &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;--format&lt;/span&gt; cyclonedx &lt;span class="se"&gt;\&lt;/span&gt;
  | uzomuzo scan &lt;span class="nt"&gt;--sbom&lt;/span&gt; - &lt;span class="nt"&gt;--fail-on&lt;/span&gt; eol-confirmed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. One pipeline command.&lt;/p&gt;

&lt;h2&gt;
  
  
  I used it on my own project
&lt;/h2&gt;

&lt;p&gt;I ran uzomuzo on my own project — &lt;a href="https://github.com/future-architect/vuls" rel="noopener noreferrer"&gt;vuls&lt;/a&gt;, an open-source vulnerability scanner with 391 dependencies. Then I removed what I found.&lt;/p&gt;

&lt;p&gt;Binary size: &lt;a href="https://github.com/future-architect/vuls/pull/2476" rel="noopener noreferrer"&gt;106.6 MB → 34.1 MB (&lt;strong&gt;-68%&lt;/strong&gt;)&lt;/a&gt;. Dependencies: 352 → 144 (&lt;strong&gt;-59%&lt;/strong&gt;). I reported my findings upstream — Grafana's maintainer &lt;a href="https://github.com/grafana/grafana/pull/122122" rel="noopener noreferrer"&gt;closed the community PR&lt;/a&gt; and committed to replacing the archived Action internally using their own &lt;a href="https://github.com/grafana/shared-workflows" rel="noopener noreferrer"&gt;shared-workflows&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The full story of how I did it — in a follow-up post.&lt;/p&gt;

&lt;h2&gt;
  
  
  The blind spot is bigger than you think
&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;"No CVE" doesn't mean "no vulnerability." It often means "no one is looking."&lt;/p&gt;

&lt;p&gt;Full methodology and data — in a follow-up post.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start today
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;30 seconds:&lt;/strong&gt; &lt;code&gt;uzomuzo scan --file go.mod&lt;/code&gt; or &lt;code&gt;uzomuzo scan https://github.com/your/repo&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;5 minutes:&lt;/strong&gt; Add &lt;code&gt;--fail-on eol-confirmed&lt;/code&gt; to your CI pipeline&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Quarterly:&lt;/strong&gt; Run &lt;code&gt;uzomuzo diet&lt;/code&gt; for a prioritized removal plan&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You will be surprised what you find.&lt;/p&gt;




&lt;p&gt;⭐ &lt;a href="https://github.com/future-architect/uzomuzo-oss" rel="noopener noreferrer"&gt;github.com/future-architect/uzomuzo-oss&lt;/a&gt; — Apache 2.0&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Kota Kanbe — Creator of &lt;a href="https://github.com/future-architect/vuls" rel="noopener noreferrer"&gt;vuls&lt;/a&gt; (12K+ ⭐), presented at VulnCon 2026&lt;/em&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>opensource</category>
      <category>supplychain</category>
      <category>devops</category>
    </item>
  </channel>
</rss>
