Dependency research · 09

Nobody left to fix it

Every scanner tells you a dependency is vulnerable. None of them tell you whether anyone is still around to publish the fix. We measured how often nobody is.

11 Sep 2026 · 24 repositories · 8,943 components · 933 findings · 5 ecosystems

The number, and the argument against it, together

About one component in seven in this sample carries a signal that nobody is maintaining it: 1,202 of 8,943, or 13.4%. That figure is worth less than the split inside it, so here is the split before anything else.

Where the signal comes fromComponentsShare of signals
Somebody said so. A deprecation flag, an archived repository, or a published end-of-support date. 242 20.1%
We inferred it. Four years since the last release, and no repository activity either. 960 79.9%

Four signals in five are an inference from silence. That is the honest shape of this measurement, and it is where the fair objection lives: a library can go four years without a release because it is finished, not because it was abandoned. Small, single-purpose packages do exactly that, and treating them as a problem is how a maintenance signal turns into noise nobody reads.

We think the inference still earns its place, for one reason. The question a maintenance signal answers is not is this package bad. It is if an advisory lands against this tomorrow, is a fix coming. A finished library and an abandoned one give the same answer to that question, and it is the answer you need during an incident rather than a verdict on anybody's work.

The advice assumes somebody is home

The standard remediation for a vulnerable dependency is to upgrade it. That advice quietly assumes a maintainer exists, noticed, and shipped a release. For most packages it holds. For some it does not, and nothing in a normal findings report tells you which kind you are looking at.

request@2.75.0 — deprecated by its maintainer, four levels down. For years this was one of the most depended-upon packages in JavaScript. Its maintainers formally deprecated it, which is a deliberate statement rather than an inference. Nobody in this project chose it: it arrives underneath something else. CVE-2023-28155 against it has no fix version, because there will not be another release. There is no upgrade to recommend. The only real options are to replace whatever pulled it in, or to write down that you accept it.

In this project that package reaches the build, not the shipped artifact. Worth saying plainly, because a reader will check. It changes nothing about the argument: the same package in the same state sits in production trees elsewhere.

How we measured it

Twenty-four public repositories, pinned to fixed commits and rebuilt locally, across Maven, Gradle, npm, PyPI and Go. For every resolved component we asked three open sources what they knew about it: release history and deprecation from deps.dev, project activity from the OpenSSF Scorecard, and published support windows from endoflife.date. Every one of them is free, public, and available to you without writing any code.

Those facts feed a ladder of five rules, applied in order, first match winning. The order matters: a maintainer's own declaration should outrank anything we merely observed.

RuleKindComponents
01The package is deprecated. The maintainer marked it so.stated65
02The repository is archived. A deliberate act, not a guess.stated125
03This release line is past its published end of support, even where the project thrives.stated24
04Four years since the last release, and no sign of commits. Both halves required.inferred960
05This particular release is deprecated, though newer ones are not.stated28

Rule 4 deliberately requires both an old release and a quiet repository, because release age alone marks every finished library as abandoned. Where the Scorecard has no data the second half cannot be applied and the rule falls back to release age by itself. That affected 82 of its 960 components, so roughly 9% of the largest rule rests on the weaker evidence.

One note on counting, because two of our posts use different units. This page reports 933 findings, deduplicated by advisory and package. The companion measurement on findings that never ship reports 1,203 for the same corpus on the same day, counted once per manifest occurrence, because that is how a build gate sees them. Both are correct and they are not interchangeable. Component counts, which carry the headline here, are unaffected either way.

Which ecosystems carry it

The sample is 24 open-source repositories chosen for ecosystem coverage, so the mix here is ours rather than yours. The npm figure in particular reflects deep transitive trees in two large JavaScript projects.

EcosystemCarrying a signalComponentsShare
npm8525,65015.1%
Go2261,61414.0%
Gradle9075611.9%
Maven132375.5%
PyPI216863.1%

It is not just your test tooling

The obvious way to dismiss all of this is to assume abandoned packages cluster in build and test dependencies, where nobody much cares. We can check that, because every resolved dependency carries the scope it was declared under, and a component counts as shipping if any copy of it ships.

Reaches productionCarrying a signalComponentsShare
Ships6594,69814.0%
Build and test only3722,75013.5%
Undetermined1711,33212.8%

This cut covers the 8,780 components that carry a scope record anywhere in the corpus, which is why the column totals fall a little short of the 8,943 assessed for the headline rate. Undetermined means no scope was recorded anywhere for that component, and is shown rather than folded into either side.

It does not cluster. The rate is flat. Whatever causes a package to be abandoned has nothing to do with whether you ship it. Your production dependencies are not the well-tended ones. They are tended exactly as well as your test tooling, which is to say nobody checked either.

The cost of that lands at the worst possible moment. When a critical advisory drops, the escalation runs the same way every time: someone is paged, a patch window opens, teams stop what they were doing. All of it assumes a fixed version exists. For about one component in seven, that assumption is being tested for the first time during the incident, by people who need an answer that afternoon.

The result that cuts against us

Findings concentrate more narrowly than components. 57 of 933 findings sit on a component carrying a maintenance signal, which is 6.1%. And of those, exactly three had no fix version published at all.

Three findings in the whole sample had nowhere to upgrade to, and none of the three ships. Two are build-and-test dependencies, one has no scope recorded. Everywhere else a newer release existed. We went looking for the acute case, a vulnerability in production with no fix available, and did not find it once.

On this sample the maintenance signal is a statement about a project's future, not about today's remedy. Anyone quoting these numbers should quote that too. It is the difference between estate hygiene, which this is, and urgent risk, which this is not.

The records

The examples below are the argument. Each names a real package at a real version in a real project, and each can be checked against the same public sources we used.

xerces:xercesImpl@2.8.1 — last release 2022-01, five advisories, the oldest from 2009. A Java XML parser sitting two levels under an enterprise service. Fixed versions exist and have for years. This is not a stuck dependency. It is an old one nobody has looked at, and the age of the pinned release is what makes that visible.

rsc.io/pdf@v0.1.1 — repository archived, advisory unrated, no fix. The author archived the repository, which is an explicit signal rather than a silence we interpreted. The advisory against it carries no severity rating and no fix version. A gate filtering on severity passes this without comment, which is a problem with severity as a filter rather than with this package.

org.eclipse.jetty:jetty-http@9.4.58 — thriving project, dead release line. Jetty is actively developed. The 9.4 line is not: its published support window closed on 14 August 2025. This is the case the other four rules miss entirely, and the reason end-of-support calendars are worth reading separately from activity signals.

hoek@2.16.3 — deprecated at the source, six levels down. In a dependency tree nobody assembled on purpose, and deprecated by its maintainer. It reaches the build but not the shipped artifact, which is a meaningful difference and one worth recording next to the finding rather than leaving the reader to work out.

A correction to the previous version

An earlier internal draft of this measurement reported that unmaintained components carry worse findings, with criticals running at more than three times the rate of lows. That was the headline. It does not survive a re-run, and we are retracting it.

SeverityEarlier draftRe-run, 10 Sep 2026
Critical17.9%4.9%
High14.2%6.7%
Medium8.5%6.6%
Low5.4%3.0%

Criticals now sit near the bottom of the range rather than the top, and the gradient is absent under both counting units we tried, so it is not an artefact of how findings were deduplicated. We cannot reconstruct the earlier run to explain the difference, which is itself the lesson. A measurement you cannot re-run is a number you should stop quoting.

What this does not prove

  • The sample is 24 pinned open-source repositories, chosen for ecosystem coverage. It is not anyone's enterprise estate, and a private codebase with vendored or internal packages would look different in ways we cannot predict from here.
  • Four signals in five are inferred, and 82 of those 960 rest on release age alone because the Scorecard had no activity data for them.
  • The end-of-support rule is a floor, not a measurement. It depends on a hand-curated map from package coordinates to products and found 24 components. A larger map would find more.
  • A maintenance signal is not a verdict about a package or the people who wrote it. Many of these are finished libraries doing exactly what they were built to do. The signal says a fix is unlikely to arrive, not that anything is wrong.
  • Scope tells you what ships, not what runs. Whether a dependency is ever actually loaded when the code executes would sharpen every number here, and it needs each project's test suite run under load tracing, which this study did not do.

The part we have not solved

Everyone agrees an abandoned dependency is a problem. Nobody agrees what you actually do on a Tuesday when it is four levels down, there is no fork, and the thing that pulled it in is a package you do need.

Some specific things we would like argued with. Is four years the right threshold, or does it depend entirely on the ecosystem? Should a deprecated direct dependency read differently from a deprecated transitive one, given only one of them was a decision anybody made? Is this worth surfacing at all on a package that never reaches your shipped artifact? And does the even split above match what you see, or does your estate concentrate this somewhere ours did not?

If you re-run any of this against your own code and get something different, we would rather hear it than not. hello@depproof.com.

We print the date, not a verdict

depproof runs the ladder above across every component in a codebase and shows the evidence behind each answer: a release date, an archive status, a maintainer's own words, a published support window. It distinguishes a package its author retired from one that has simply gone quiet, because those call for different decisions. And it states how much of the estate it was able to assess, so silence is never mistaken for health.

What it will not do is fail your build over any of it. That is a position, not a gap. There is no version to move to, so a gate would turn a fact you need into a blocked pipeline you cannot clear, and teams would route around it within a week. The label reads last release 2019-03 rather than unmaintained for the same reason: a date needs no defending, while a verdict invites an argument with a reader who knows things about that package we do not.

Which leaves the judgement where it belongs. We will tell you what is true about every dependency you have, in the same place you already read the rest of your estate, and you decide what it is worth.

Related reading: end-of-life dependencies and the risk with no CVE · findings that never ship · why zero findings is not zero risk

Questions people ask

How many dependencies are unmaintained?

In our measurement of 24 pinned public repositories across five ecosystems, 1,202 of 8,943 resolved components — 13.4%, or about one in seven — carried at least one signal that nobody is maintaining them. Read that figure with its split: 242 of those signals are a statement by the maintainer or the project, such as a deprecation flag, an archived repository or a published end-of-support date. The other 960 are our inference from four years of silence. The first group is a fact about the package. The second is a judgement you are entitled to disagree with.

How can you tell if a dependency is abandoned?

There are five signals worth checking, and they are not equally strong. A maintainer deprecation flag and an archived source repository are deliberate acts, so they outrank everything else. A published end-of-support date for a release line is a statement by the project, and it applies even when the project itself is thriving. Below those sits inference: a long gap since the last release combined with no repository activity. We use four years and require both halves, because release age alone marks finished libraries as abandoned. All of it is checkable against public sources, principally deps.dev, the OpenSSF Scorecard and endoflife.date.

Is an unmaintained dependency a security risk?

Not by itself, and the measurement argues against overstating it. An unmaintained component with no known vulnerability is not exposing you to anything today. What it changes is your response time later: if an advisory lands tomorrow, no upstream fix is coming, so your options are to replace it, fork and patch it, or record that you accept it. In this sample exactly three findings had no fix version published at all, and none of the three reached the shipped artifact. We went looking for the acute case, a production vulnerability with nowhere to upgrade to, and did not find it once.

Should a build fail on unmaintained dependencies?

No, and we do not offer it as a gate. A build gate assumes a clearing action exists. For a vulnerability that action is the upgrade, which is why gating on severity works at all. For an abandoned package there is no version to move to, so the gate produces a blocked pipeline nobody can clear, and teams route around it within a week. Maintenance status belongs in the inventory and in the ranking, where it informs a decision, rather than in a control that stops work it cannot help anyone finish.

Are abandoned dependencies mostly in test and build tooling?

No, and this was the most useful result in the study. Splitting components by whether any copy of them reaches the shipped artifact, 14.0% of shipping components carried a maintenance signal against 13.5% of build-and-test-only ones. The rate is essentially flat. Whatever causes a package to be abandoned has nothing to do with whether you ship it, so the common assumption that this is a test-tooling problem does not survive contact with the data.

How do you check if an npm package is still maintained?

Start with the deprecation flag, because npm deprecation is a deliberate act by the maintainer rather than something inferred: a deprecated package reports it on install and in the registry metadata. Then check whether the source repository has been archived, which is the same kind of explicit signal. Only after those should you look at the last publish date, and treat a long gap as a question rather than an answer, since plenty of small packages are simply finished. deps.dev exposes release history and deprecation for npm, PyPI, Maven, Go and Cargo without writing any code.

Cite this

About one component in seven carries a signal that nobody is maintaining it.
1,202 of 8,943 · 13.4% · 242 stated, 960 inferred · 24 pinned public repositories · 10 Sep 2026

That rate is the same whether the component ships or not.
659 of 4,698 shipping, 14.0% · 372 of 2,750 build-and-test, 13.5% · same sample

depproof, “Nobody left to fix it: measuring dependencies with no maintainer,” Dependency research 09, 10 September 2026. Measured against 24 repositories pinned to fixed commits using deps.dev, the OpenSSF Scorecard and endoflife.date; method and limits stated above. Figures, tables and charts may be reproduced with attribution and a link back — no permission needed.