When a KEV entry lists more than one CWE, count weaknesses by entry (how many entries mention it) and state the denominator. Counting labels instead gives slightly different shares, and the shares will not add to 100 percent by entry. Pick one method, name it, and keep it for the whole post.
The cwes field is a list
In the KEV JSON each vulnerability has a cwes array. In release 2026.10.16 (1,748 entries, fetched 19 October 2026), the author found:
| CWEs on the entry | Entries |
|---|---|
| 0 | 175 |
| 1 | 1,465 |
| 2 | 101 |
| 3 | 6 |
| 4 | 1 |
That is 1,573 entries with at least one CWE, carrying 1,689 labels in total across 189 distinct values in the field. If you ignore the lists and just read the first item, you silently drop 116 labels, and if you drop rows with no list you silently drop 175 entries (10 percent of the catalog).
Two honest ways to count
Entry basis: for each CWE, count the entries that mention it. Denominator is 1,748 entries (or 1,573 if you restrict to entries that have a CWE, and say so). Shares can sum to more than 100 percent because an entry with two CWEs counts twice.
Label basis: count every label. Denominator is 1,689. Shares sum to 100 percent, but the unit is now "label", which is harder to explain to a reader.
In the snapshot, CWE-20 appears on 119 entries. That is 6.8 percent of all 1,748 entries and 7.0 percent of the 1,689 labels. The difference is small here, but it grows when a field has many multi-label rows.
A checklist you can paste into a methodology
- Say whether the unit is entries or labels.
- Say what the denominator is, with its number.
- Say how you treated empty lists. Report the count.
- Do not add percentages from an entry basis table and call the total 100 percent.
- Do not call the top CWE "the most common cause" of exploitation. It is the most common label among entries CISA listed.
For the empty case in more depth, see what it means when a KEV entry has no CWE.
Why this matters for link-worthy posts
Readers who rebuild your table will hit the same file. If their count of CWE-20 is 119 and yours is 130, they will assume an error. A one line note ("entry basis, denominator 1,748") removes the argument. This is the cheapest credibility you can buy in a data post.
A last caution. CWE is a classification of the weakness, and NVD or the CNA assigns it. Different people can assign different CWEs to one flaw, and the KEV list inherits whatever is recorded. Treat weakness shares as a description of the catalog's labels, not as a measure of how attackers choose targets.
A worked example
Take a made up entry with two CWEs, such as one memory weakness and one input validation weakness. On an entry basis it adds one to each CWE's count. On a label basis it adds one to each as well, but it now contributes two labels to the denominator. In the real 2026.10.16 file, 101 entries had two CWEs, six had three and one had four. Those 108 multi-label entries are why the label total (1,689) is larger than the count of entries that have any CWE (1,573).
Now consider CWE-78, which appeared on 111 entries. On an entry basis the share of all 1,748 entries is 6.4 percent. On a label basis it is 111 of 1,689, or 6.6 percent. The ranking order among top CWEs stays the same here, but a close pair could swap places under a different method. That is exactly the kind of thing a reader will notice if you do not say which method you used.
Steps to reproduce the table
- Load the vulnerabilities array.
- For each row, read the cwes list. It may be empty.
- Build a counter of CWE values across all lists for the label basis.
- Build a second counter that counts each CWE once per entry for the entry basis. If no entry lists the same CWE twice, the two counters will match, but build both and compare.
- Divide by 1,748 for entries and by 1,689 for labels.
import json, collections
rows = json.load(open("kev.json"))["vulnerabilities"]
labels = collections.Counter(c for r in rows for c in r["cwes"])
print(sum(labels.values()), len(labels), labels.most_common(3))
For the 2026.10.16 release this printed 1,689 labels, 189 distinct values, and the top three CWE-20, CWE-78 and CWE-787 with 119, 111 and 105. Treat those as the check values for your own parse.
Presenting the result
| Presentation | Good for | Watch out for |
|---|---|---|
| Top ten table with counts and shares | Readers who want to rebuild it | Say the basis and denominator |
| Bar chart of counts | Quick comparison | Use counts on the axis, not unlabeled shares |
| Share of entries with no CWE | Showing the data gap | Report the 175 as its own bar |
When you describe the weaknesses, name the family in plain words and give the CWE number once. A reader who has never opened the CWE list will follow "out-of-bounds write (CWE-787)" better than a bare identifier.
Try it yourself
Click through it at your own pace, or open it full screen. Look at the kinds of weaknesses a code scan reports and compare them with the CWE names that appear in KEV.
Quick check
Should I drop entries with no CWE?
Only if you say so and give the count. Dropping them quietly changes every share in your table.
Can percentages from an entry basis table sum above 100?
Yes, when some entries have two or more CWEs. That is expected, and worth a footnote.
Will the top CWEs change if I switch from entries to labels?
The shares shift slightly, and close rankings could swap. State the basis so readers can see which one produced your table.
Sources
- CISA KEV JSON feed, catalogVersion 2026.10.16, https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json (fetched 19 October 2026; counts computed by the author).
- CISA, Known Exploited Vulnerabilities Catalog, https://www.cisa.gov/known-exploited-vulnerabilities-catalog (fetched 19 October 2026).
Last reviewed: 19 October 2026.