Safeguard
Industry Analysis•

Why do two KEV statistics posts disagree about the same year?

Two KEV counts for one year can both be right: one buckets by dateAdded, the other by CVE ID year. Here is how to tell which one you are reading.

Safeguard Security Team
6 min read

Two KEV statistics for the same year usually disagree because they count different things: the year CISA added the entry (dateAdded) versus the year in the CVE ID. A third cause is snapshot date. Always check which field was used for the year and when the data was downloaded.

Two years hide inside every KEV row

Each KEV entry has a dateAdded field, the day it joined the catalog. It also has a CVE ID such as CVE-2021-44228, whose middle number is a year. That year comes from the ID, which is assigned when the identifier is reserved, so it is not a reliable publication date and it is not the year of exploitation.

Both are legitimate ways to bucket the catalog. They answer different questions. The first asks "how much did CISA add in 2022?" The second asks "how many of the flaws in the catalog carry a 2022 ID?"

The same file, bucketed both ways

Using the release 2026.10.16 (1,748 entries, fetched 19 October 2026), the author counted both ways:

YearBy dateAddedBy CVE ID year
2021311216
2022555132
2023187166
2024186164
2025245189
2026264177

The column totals are different shapes. Bucketed by dateAdded, 2022 has 555 entries. Bucketed by ID year, it has 132. A writer who used one method and a reader who assumed the other will each think the other is wrong. The 2026 row is also partial, since the snapshot ends on 16 October.

Note that the two columns are not meant to match for the older years. Entries with IDs from 2002 to 2020 sit in the ID-year buckets for those years but were added to the catalog later.

Steps to reconcile a disagreement

  1. Find the snapshot. Look for a catalogVersion or fetch date. If neither is stated, the number cannot be checked.
  2. Find the bucketing field. If the text says "added in", it should be dateAdded. If it says "from 2022", ask whether that means ID year.
  3. Check the denominator. Entries, unique CVEs and distinct vendors are different counts. In this snapshot entries and unique CVE IDs were both 1,748, but that is not guaranteed for other datasets.
  4. Check the cutoff. Does the year end on 31 December, or at the snapshot date?
  5. Recompute. A few lines of code settle most disputes; see reproduce a KEV analysis in thirty lines of Python.

How to label your own chart

State the field in the axis title: "Entries by dateAdded year" rather than "KEV entries by year". Put the catalogVersion in the caption. If you show both, show them in separate charts, since mixing them on one axis confuses readers.

For a related measure that depends on both dates, see how old CVEs are when CISA adds them.

A worked reconciliation

Imagine two posts. Post A says "KEV added 555 entries in 2022". Post B says "only 132 KEV entries are from 2022". Using the 2026.10.16 file, both are correct. Post A bucketed by dateAdded and Post B by the year in the CVE ID. Neither is wrong, but a reader comparing them would conclude that one author made an arithmetic mistake.

Now add a third post that says "KEV had 311 entries added in 2021". That also matches dateAdded. If a fourth post says "216 CVEs from 2021 are in KEV", that matches ID year. Four posts, four true statements, and two different fields.

Snapshot date is the third cause

Even with the same field, posts from different dates disagree because the catalog grows. The 2026 row in the table is partial: the snapshot is from the 2026.10.16 release, so anything added after 16 October is missing. A post written in January would show a larger 2026 figure for the same year. When you compare posts, check the catalogVersion or fetch date first. See how to cite a KEV number.

A short script that shows both

import json, collections
rows = json.load(open("kev.json"))["vulnerabilities"]
by_added = collections.Counter(r["dateAdded"][:4] for r in rows)
by_id = collections.Counter(r["cveID"][4:8] for r in rows)
for y in sorted(by_added):
    print(y, by_added[y], by_id.get(y, 0))

Run it on your own saved copy and you will see the same two columns as the table above, assuming you use the same release. If your numbers differ, compare the catalogVersion before suspecting a bug.

Which one should you chart?

Choose the field that matches your claim, and put it in the chart title.

Claim you want to makeField to use
How active was CISA's cataloging in a yeardateAdded
How old are the flaws in the catalogCVE ID year, with a caveat
How fast do flaws reach the catalogBoth, as a difference
Which vendors dominate recent additionsdateAdded, filtered to a window

If you need a true publication date for the flaw, the ID year is a poor substitute. Use a source that states a published date and cite it.

Writing a sentence that cannot be misread

Weak: "KEV had 555 entries in 2022."

Better: "CISA added 555 entries to KEV during 2022, counted by dateAdded in release 2026.10.16."

The second version names the actor, the unit, the field and the release. Anyone can check it, and no one will mistake it for a count of 2022 vulnerabilities.

Try it yourself

Click through it at your own pace, or open it full screen. Filter the list and watch how the totals change with each field you choose, which is the same choice you make when you bucket by year.

Quick check

Which bucketing is right?

Neither is wrong. Use dateAdded to describe catalog activity and ID year to describe the age profile of the flaws. Say which you used.

Is the CVE ID year the year the flaw was disclosed?

Not reliably. It reflects when the identifier was assigned. If you need a publication date, use a source that states one and cite it.

Why do older years look so different between the two columns?

Entries can carry an ID from one year and be added to the catalog much later. The two columns count different events, so large gaps are normal for early years.

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.