Safeguard
Vulnerability Analysis•

NVD Enrichment for CVEs Published 1 Sep to 15 Oct 2026: The Numbers

Of 23,469 CVEs published 1 Sep to 15 Oct 2026, NVD lists 5,915 as Analyzed and 6,119 with CPE data. All 37 KEV CVEs from the window were analyzed.

Safeguard Security Team
7 min read

In April 2026, NIST changed how the National Vulnerability Database handles new CVEs. It now prioritizes enrichment (CPE product lists, NVD severity scores) for CVEs in CISA's KEV catalog, software used in the federal government, and critical software as defined by Executive Order 14028. Everything else is still listed, but at the lowest priority.

We wanted to see what that looks like six months later. We pulled every CVE published between 1 September and 15 October 2026 from the NVD API on 18 October and counted the status of each. Of 23,469 non-rejected records, 5,915 are Analyzed and 6,119 have CPE configuration data.

How we counted

We queried the NVD CVE API 2.0 on 18 October 2026 between 08:09 and 08:15 UTC, with a publication date filter. Sleep between pages and retry any HTTP 429.

for i in $(seq 0 2000 22000); do
  curl -sS -o "p_$i.json" "https://services.nvd.nist.gov/rest/json/cves/2.0?\
pubStartDate=2026-09-01T00:00:00.000Z&pubEndDate=2026-10-15T23:59:59.999Z\
&resultsPerPage=2000&startIndex=$i"
  sleep 8
done
import json, glob, collections

cves = {}
for p in glob.glob("p_*.json"):
    for v in json.load(open(p))["vulnerabilities"]:
        cves[v["cve"]["id"]] = v["cve"]
live = [c for c in cves.values() if c["vulnStatus"] != "Rejected"]
N = len(live)

def nvd_score(c):
    return any(m.get("source") == "nvd@nist.gov"
               for ms in c.get("metrics", {}).values() for m in ms)
def any_score(c):
    return any(c.get("metrics", {}).values())
def has_cpe(c):
    return bool(c.get("configurations"))

for s, k in collections.Counter(c["vulnStatus"] for c in live).most_common():
    print(s, k, round(100 * k / N, 1))
print("CPE configurations:", sum(map(has_cpe, live)))
print("CVSS from NVD itself:", sum(map(nvd_score, live)))
print("no CVSS or SSVC metric from anyone:", sum(not any_score(c) for c in live))

for lo, hi in [("01", "09"), ("10", "19"), ("20", "30")]:
    for m in ("2026-09", "2026-10"):
        part = [c for c in live if f"{m}-{lo}" <= c["published"][:10] <= f"{m}-{hi}"]
        if part:
            print(m, lo, hi, len(part),
                  round(100 * sum(c["vulnStatus"] == "Analyzed" for c in part) / len(part), 1),
                  round(100 * sum(map(has_cpe, part)) / len(part), 1))

"CPE configurations" means the record has a non-empty configurations array, the product applicability data most scanners match on. "CVSS from NVD itself" means at least one metric with source equal to nvd@nist.gov.

What's affected

Status of the 23,469 non-rejected CVEs published 1 September to 15 October 2026, as of 18 October:

NVD statusCVEsShare
Deferred9,57140.8%
Analyzed5,91525.2%
Awaiting Analysis4,63619.8%
Received2,2589.6%
Undergoing Analysis9153.9%
Modified1740.7%

Other counts across the same set:

MeasureCVEs
Has CPE configuration data6,119
Has a CVSS metric from NVD itself1,134
Has no CVSS or SSVC metric from any source1,618

Most records still carry a score from somewhere. The CNA or CISA's ADP container usually supplies CVSS or SSVC, which fits NIST's April statement that it will "no longer routinely provide a separate severity score" when the CNA already has.

Older records in the window are better covered, as you'd expect:

PublishedCVEsAnalyzedHas CPE
1 to 9 Sep4,39550.9%52.8%
10 to 19 Sep6,03921.7%22.7%
20 to 30 Sep4,50219.5%20.2%
1 to 9 Oct3,54023.4%24.1%
10 to 15 Oct4,99313.3%13.3%

Status depends heavily on who published the CVE. A second pass maps each record's source to a name (the src_*.json files come from the NVD Source API, see sources) and checks the KEV CVEs:

import json, glob, collections

cves = {v["cve"]["id"]: v["cve"] for p in glob.glob("p_*.json")
        for v in json.load(open(p))["vulnerabilities"]}
name = {i: s["name"] for p in glob.glob("src_*.json")
        for s in json.load(open(p))["sources"] for i in s.get("sourceIdentifiers", [])}
live = [c for c in cves.values() if c["vulnStatus"] != "Rejected"]

by = collections.defaultdict(collections.Counter)
for c in live:
    by[name.get(c["sourceIdentifier"], c["sourceIdentifier"])][c["vulnStatus"]] += 1
for n in sorted(by, key=lambda n: -sum(by[n].values()))[:10]:
    print(n, sum(by[n].values()), dict(by[n]))

kev = [v["cveID"] for v in json.load(open("kev.json"))["vulnerabilities"]
       if "2026-09-01" <= v["dateAdded"] <= "2026-10-16"]
print(collections.Counter(cves[c]["vulnStatus"] for c in kev if c in cves))
SourceCVEsAnalyzedDeferredOther statuses
VulnCheck2,8932052,151537
Microsoft Corporation2,4471,4770970
kernel.org2,3832002,363 (2,258 Received)
GitHub, Inc.2,2183521,251615
VulDB1,286171,25415
Patchstack1,04001,0400
WPScan82908290
Chrome772715057
MITRE72478486160
Wordfence71607160

The prioritization is visible. 38 of the KEV CVEs added between 1 September and 16 October were also published in this window. 37 are Analyzed with CPE data, and the 38th, CVE-2026-21589, has since been rejected.

How to check if you're exposed

The exposure is any tool that matches vulnerabilities to your software using NVD CPE data alone. For a CVE with no CPE configuration, that tool has nothing to match on and reports nothing. Test it with your own findings list:

python3 -I -c '
import json, glob
mine = set(open("my_cves.txt").read().split())
for p in glob.glob("p_*.json"):
    for v in json.load(open(p))["vulnerabilities"]:
        c = v["cve"]
        if c["id"] in mine and not c.get("configurations"):
            print(c["id"], c["vulnStatus"])
'

Then do the reverse test. Take a library you know is affected by a recent GitHub CNA CVE, and check whether your scanner flags it. If it doesn't, find out which data source it relies on.

What to do now

  1. Ask your vendors where their matching data comes from. CPE-only matching misses most new CVEs in this window. Package advisories (GHSA, OSV) carry package names and version ranges instead.
  2. Use the CNA's own data. The CVE record from the CNA often has affected versions and a CVSS score even when NVD has not enriched it.
  3. Don't read "Deferred" as "not important." WordPress plugin CNAs had every record deferred. If you run WordPress, those still apply to you.
  4. Request enrichment when it matters. NIST says users can email nvd at nist.gov to request enrichment of a lowest priority CVE.
  5. Track the kernel separately. 2,258 kernel.org records sat at Received. Patch by kernel version, not by waiting for CPE data.

Why it matters

NVD's change was announced and the reasons were stated plainly. The effect on downstream tooling is the part teams haven't caught up with. Most CVEs published in this window have no NVD product data, and the gap is widest for the newest records.

That doesn't make NVD less useful for KEV CVEs, which were all enriched here. It means a vulnerability program built on one data source will see a shrinking slice of new CVEs.

Try it yourself

Generate or import an SBOM for one service and compare two lists: findings matched by package identifier and findings matched by CPE. The difference is the coverage you'd lose with CPE alone. The demo below shows importing an SBOM and analyzing it against package advisories.

Click through it at your own pace, or open it full screen.

Quick check

How many CVEs published from 1 September to 15 October 2026 had NVD CPE data on 18 October?

6,119 of 23,469 non-rejected records.

Were KEV CVEs from this window enriched?

Yes. All 37 non-rejected KEV CVEs published in the window were Analyzed and had CPE data.

Which status did most kernel.org CVEs have?

Received: 2,258 of 2,383.

Sources

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.