Dependency health: malicious and deprecated packages
On this page
In plain terms
A vulnerability scanner reports flaws in the packages you use. It says nothing about the packages themselves, and two facts about a package change how urgent its flaws are:
- Malicious. The package was published to do harm: it steals credentials, installs a backdoor, or impersonates a popular package under a similar name. Everything it contains is the problem, not one function.
- Deprecated. The publisher has marked this version as no longer supported, often with a reason and a replacement. A flaw found in it next month may never be fixed.
Neither is in your code, which is why no scanner reading your code can report either.
Why it matters
A CVE in a maintained package and the same CVE in one its publisher has walked away from look identical in a scan.
Maintained
A fix ships in a later version. Upgrade, and the finding closes.
Deprecated
No fix is coming, and the next flaw found in it will not get one either. Replace it, often with the package its publisher names as the successor.
A malicious package is a different kind of problem again. It does not need a vulnerability to hurt you, because running its install script or importing it is the attack.
How it works
Both statements come from named parties, and both reach Draugr through deps.dev, Google's index of open source packages.
| Malicious | Deprecated | |
|---|---|---|
| Who says so | the OSSF Malicious Packages Project, an OpenSSF project | the package's own publisher |
| How | researchers and automated detectors contribute reports, published in the OSV vulnerability format | the publisher marks a version deprecated in its registry, for example npm deprecate, or a Deprecated: comment on a Go module |
| Covers | every version of the package | the version marked |
| When it is wrong | a confirmed false positive is withdrawn from the dataset and kept on record, not deleted | the publisher can lift the mark |
deps.dev indexes Cargo, Go, Maven, npm, NuGet, PyPI and RubyGems. Operating-system packages inside a container image are not among them.
Why health scores are not in the list
Scorecards that grade a project on activity, review practice and release cadence are useful for
choosing a dependency. They are a poor basis for ranking one of its flaws. Research on OpenSSF
Scorecard found reported vulnerability counts rose with the aggregate score, explaining 9% to 12%
of the variation, and the Maintained check scores PyYAML zero because it reads a 90-day activity
window and PyYAML is stable rather than abandoned.
A statement somebody can be named for, this package is hostile or this version is no longer supported, is evidence. A score is a heuristic about the project around it.
Common pitfalls
| The pitfall | Why it bites |
|---|---|
| Unflagged is not safe | the dataset lists packages somebody reported. A new malicious package is unlisted until somebody does |
| Deprecated is not vulnerable | it says who maintains the package, not whether a flaw in it can run in your code |
| Replacing is not upgrading | a deprecated package often names a different package as its successor, so the fix is a code change rather than a version bump |
config.dependencyHealth. A finding in a malicious package is ranked critical; a finding in a deprecated version rises one band, and the publisher's reason travels into the report. Absence moves nothing: a package nobody has flagged is ranked as it would be with the signal off. Draugr sends deps.dev the names and versions of the packages a scan found and nothing else. See dependency health in the Saga reference.
Keep learning
- Vulnerability prioritization, where every signal combines
- EPSS & KEV, the signals about the flaw rather than the package
- SCA, the scan that finds the packages in the first place