A vulnerable dependency is a mistake. A malicious dependency is an attack. They are not the same finding, and this record does not score them the same way.
Malicious dependencies asks one question: does the dependency graph this repository resolves to contain a package that has been reported as malware?
Where the evidence comes from
The source is the OpenSSF malicious-packages corpus — an openly licensed, community-maintained record of packages found to be malicious across public registries: typosquats, account takeovers, dependency-confusion attacks, and packages shipping prebuilt malicious binaries.
OSV.dev ingests that corpus and serves it under MAL- identifiers alongside ordinary security advisories. Because this record already queries OSV for dependency advisories, malicious packages arrive on a query that was already being made. The finding costs no additional request, no additional wait, and no additional dependency on a third party.
Why it is scored apart from advisories
Until methodology v1.10.0 these reports were counted as ordinary advisories, and that was wrong in a way worth stating plainly.
A malicious-package report carries no CVSS vector and no severity label — there is nothing to score, because malware has no severity, only a state. Scored as an advisory it fell through to unknown severity, which is weighted like a moderate vulnerability. A package that had been found to steal credentials counted for slightly less than an average CVE.
The three properties that make a vulnerability scoreable are all absent:
- No fixed version. There is no corrected release of the same artifact to upgrade to. The remedy is removal, or moving off the compromised name.
- No partial exposure. A vulnerability may sit in an unreachable code path. An install-time payload runs when the package is installed.
- No degrees. A package either is or is not reported as malicious.
So a malicious dependency is now removed from the advisory findings entirely and treated as a red flag: a multiplier and a ceiling rather than points off. It applies a 35% multiplier to the security posture score and to the weighted health index, and caps both at 29 — the top of the Critical band. That is one band stricter than the high-risk jurisdiction ceiling of 49, because this is a confirmed compromise of the software rather than an exposure to a risk.
A package carrying both a malicious report and ordinary CVEs appears only under this finding. Its vulnerabilities are moot.
Withdrawn artifacts are reported, not scored
A registry that removes a malicious package settles the question: nothing installable remains, and a repository that resolves to it is not shipping malware today. Every finding therefore checks whether the registry still serves that exact resolved version. Where it does not, the finding stays on the report — the project depends on a name that was compromised, which is worth seeing — but it raises no flag and costs no points.
The check deliberately asks about the resolved version rather than the package's latest one. After a takedown npm leaves a placeholder release, marked -security, as the package's latest. That protects everyone who resolves a version range, and nobody who pinned the bad version exactly. Reading the latest version would have cleared the first repository this finding ever fired on, which pins the compromised package at the precise version the registry still serves.
Where the question cannot be answered — an ecosystem the check does not cover, or a registry that did not respond — the finding is scored as though the package were live. Failing to reach a registry is not evidence that malware was withdrawn.
Direct and indirect count alike
Most of this record's dependency scoring distinguishes a declared direct dependency from one pulled in transitively, because a maintainer chose the first and inherited the second.
That distinction does not apply here. A package's install script runs at whatever depth it sits in the resolved graph; a credential stealer four levels down compromises the machine exactly as thoroughly as one named in the manifest. Both are reported, both are scored, and the report states which is which so a reader can see where the package entered.
What the finding does not claim
It is not an accusation against the maintainers. The report concerns the package as published by whoever published it. A repository resolves to thousands of packages it never inspected; inheriting a compromised one is the normal way this happens, and it says nothing about the intent or competence of the people maintaining the scanned repository.
It is not a claim the code was executed. The finding says the resolved dependency graph contains the package. Whether anyone installed that exact graph, and what the payload did if they did, is outside what a public record can observe.
It is not our determination. The classification is the OpenSSF corpus's, reached through its own process and its own sources, and this record reports it rather than reproducing it. A report withdrawn upstream disappears from the next scan.
How often it fires
Rarely — by design and by measurement. Before this metric shipped it was run over a 300-repository sample covering 46,889 resolved dependencies, and it found nothing.
That is the expected result, and it is not a sign the check is useless. Registries pull malicious packages within hours to days of discovery, so a lockfile that still resolves to one is unusual. The value of this finding is not that it fires often; it is that when it does fire, it is the single most important fact in the report, and it will no longer be averaged into a vulnerability score.
When it is not assessed
Like dependency advisories, this metric is excluded — not scored zero — whenever the dependency graph or the OSV lookup was unavailable. A repository is never penalized for having GitHub's dependency graph switched off; the report says the check did not run instead of implying a clean result.