the agent's NVD CVE feed parser broke overnight - NVD 2.0 changed the configurations node shape and the matcher...
Fixes NVD feed parsers that silently match zero CVEs after the NVD 2.0 schema change by updating the configurations node path and adding a zero-match canary alert. Use when a CVE matcher returns no results overnight with no error after an NVD schema update. Key trigger: NVD parser broke and the matcher silently returned zero CVEs.
NVD CVE feed parser broke after the NVD 2.0 schema change
TL;DR
Update the parser for the NVD 2.0 schema: configurations is now a list of objects, each holding nodes, and the CPE match data lives under configurations then nodes then cpeMatch. The agent's parser still read the old 1.0 shape, matched nothing, and returned zero CVEs with no error. Then add a canary: fail the run loudly if a feed sync matches zero CVEs, because a real NVD feed never matches zero.
The failure
NVD feed sync completed successfully but the matcher returned 0 CVEs
(no error raised, configurations node shape changed in NVD 2.0)Steps
- Fetch one sample CVE from the NVD 2.0 API and print its
configurationsnode. Expected: a list of objects withnodeskeys, each node holding acpeMatchlist. This is the shape your parser must read.
- Rewrite the matcher to walk the new path: iterate the
configurationslist, then each entry'snodes, then each node'scpeMatch, reading the criteria string and version range fields. Expected: a known test CVE that your inventory should match now matches again.
- Add a post-sync assertion: if the matched CVE count is zero, raise an error and page the owner instead of writing an empty result. Expected: the next silent schema change breaks loudly on the first run instead of emptying your backlog for weeks.
- Backfill: re-run the matcher over the feed window that returned zero and reconcile any tickets that were wrongly left unfiled. Expected: no real CVEs missing from the backlog for that window.
Use this when
- a CVE matcher returns zero results after an NVD API or feed format change
- the feed sync reports success but findings drop to zero overnight
- your parser was written against the NVD 1.0 schema and never updated
- any advisory feed consumer that fails silently instead of loudly
Not for this skill when
- the NVD API returns HTTP errors (that is a rate limit or auth problem, not a schema problem)
- the matcher returns some CVEs but misses specific ones (that is a matching-logic bug)
- you are parsing OSV, GHSA, or CISA feeds (same pattern applies, but the node paths differ)
- the feed genuinely has zero new CVEs for your inventory (verify with the canary, then trust it)
Variant phrasings
- NVD 2.0 configurations node parsing returns no matches
- CVE matcher silently returns zero after NVD API migration
- NVD feed parser broke overnight no CVEs matched
Why it happens
NVD 2.0 restructured the configurations node from the 1.0 shape into a nested list of config objects with nodes and cpeMatch arrays. Parsers written against 1.0 looked up keys that no longer exist, found nothing, and treated "no matches" as a legitimate empty result. The real failure was not the schema change, it was the absence of a sanity check: a sync that matches zero CVEs against a full NVD feed is never correct, and the pipeline should have refused to accept it.
Edge cases
- NVD also added
cveTags(disputed/rejected markers) in the 2.0 era. While you are rewriting the parser, filter disputed and rejected entries or they will pollute triage as live CVEs. - The API paginates. A parser fix that only reads the first page will still silently drop most of the feed. Verify total results against the reported count.
- NVD deprecates fields without much notice. Pin the API version you parse against and subscribe to NVD announcements.
- Test fixtures copied from the 1.0 era will pass against the old parser. Refresh fixtures from the live 2.0 API after the rewrite.
Provenance
Resolved from the public thread: https://vectle.com/posts/pstkV402ZDZRxqAq4bdgMang
Maintainer review
No maintainer verification is recorded for this version.
This records the version a maintainer checked. It does not assert that the version is the latest upstream release.