In OSV exports from 19 October 2026, PyPI lists 13,941 vulnerability records but only 6,506 distinct CVE IDs. In total 93.7 percent of PyPI records share a CVE with at least one other record. If you count records, you count the same vulnerability twice. Deduplicate by alias before reporting.
Method
We downloaded eight OSV exports on 19 October 2026 and kept vulnerability advisories (excluding MAL- and withdrawn records). For each ecosystem we built the set of CVE IDs found in aliases and counted records that share a CVE ID with another record in the same ecosystem. Records without a CVE alias are not in this count.
Limit: two records can describe related but distinct flaws that share a CVE. We did not read each pair, so the figure is an upper bound on pure duplicates.
What the numbers say
| Ecosystem | Records | Distinct CVE IDs | Records sharing a CVE | Share |
|---|---|---|---|---|
| PyPI | 13,941 | 6,506 | 13,065 | 93.7% |
| Go | 9,464 | 4,793 | 8,061 | 85.2% |
| crates.io | 2,799 | 1,072 | 1,141 | 40.8% |
| Packagist | 7,147 | 6,181 | 213 | 3.0% |
| npm | 7,447 | 6,372 | 28 | 0.4% |
| Maven | 7,077 | 6,986 | 14 | 0.2% |
| RubyGems | 1,091 | 1,058 | 2 | 0.2% |
| NuGet | 1,142 | 1,090 | 0 | 0.0% |
The cause is visible in the ID prefixes. PyPI records are 7,587 PYSEC IDs and 6,343 GHSA IDs. Go records are 4,869 GHSA and 4,595 GO IDs. crates.io has 1,505 GHSA and 1,294 RUSTSEC. Two databases describe the same flaw, each with its own ID, and both appear in the export. In npm, Maven, NuGet and RubyGems, nearly all records are GitHub advisories, so duplication is negligible.
What to do with it
Three places where duplicates cause trouble.
Counts. A scan that reports 40 findings in a Python project may be reporting 20 vulnerabilities. Say which you mean.
Tickets. If each record opens its own issue, the same fix is requested twice. Group by alias set before creating work.
Suppressions. An exception for one ID may not cover the other. Store the exception against the whole alias group so one decision covers both.
A simple method: treat the aliases and id fields as edges in a graph, and the connected components as the real vulnerabilities. A few lines with a union find structure do it. In Python, Go and Rust ecosystems the count of components is close to half the count of records.
When you compare tools, compare on components too. A tool that deduplicates will look like it finds less, and may be finding the same set.
Safeguard's SBOM and dependency scanning reports findings by component and vulnerability, and the free scan at https://gold.safeguard.sh/scan shows a first pass on one repository.
Quick check
Why does PyPI have two IDs per flaw?
Two databases, the Python advisory database and GitHub's, each publish a record. OSV carries both, and their aliases point at the same CVE.
How should I deduplicate?
Group records that share any alias or ID into one set, and count, ticket and suppress per set.
Sources
- OSV data sources and per-ecosystem downloads, fetched 19 October 2026
- OSV schema, fetched 19 October 2026
Last reviewed: 19 October 2026.