Safeguard
Supply Chain•

One vulnerability, two IDs: duplicate advisories in PyPI, Go and Rust

In OSV, 93.7 percent of PyPI records share a CVE with another record. Counting advisories inflates findings by up to 2x. How to deduplicate.

Safeguard Security Team
3 min read

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

EcosystemRecordsDistinct CVE IDsRecords sharing a CVEShare
PyPI13,9416,50613,06593.7%
Go9,4644,7938,06185.2%
crates.io2,7991,0721,14140.8%
Packagist7,1476,1812133.0%
npm7,4476,372280.4%
Maven7,0776,986140.2%
RubyGems1,0911,05820.2%
NuGet1,1421,09000.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

Last reviewed: 19 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.