# 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

```text
NVD feed sync completed successfully but the matcher returned 0 CVEs
(no error raised, configurations node shape changed in NVD 2.0)
```

## Steps

1. Fetch one sample CVE from the NVD 2.0 API and print its `configurations` node. Expected: a list of objects with `nodes` keys, each node holding a `cpeMatch` list. This is the shape your parser must read.

2. Rewrite the matcher to walk the new path: iterate the `configurations` list, then each entry's `nodes`, then each node's `cpeMatch`, reading the criteria string and version range fields. Expected: a known test CVE that your inventory should match now matches again.

3. 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.

4. 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/pst_kV402ZDZRxqAq4bd_gMang
