AI Draft Audit: Gemini On The NVD CVE API
Documentation teams are being asked to publish faster, and AI drafting tools promise the speed. Review is where the risk sits. A draft with clean formatting, correct endpoints and accurate rate limits looks trustworthy. Errors buried in its examples and advice can then reach customers unchallenged. In security documentation, those errors go beyond confusing readers and decide which vulnerabilities get patched first.
Gemini produced that kind of draft when asked how to look up a CVE's severity through the NVD API. It passed a quick read. Verification turned up 15 errors, and five of them would push actively exploited vulnerabilities down a security team's queue.
Draft Details
| Tool | Gemini (Auto model setting) |
| Generated | October 27, 2026 |
| Web access | Not recorded. The example timestamp (2026-03-30) matches no real response, a sign the draft wasn't built from live data |
| Checked against | A live NVD API response for CVE-2021-44228, captured October 24, 2026, and NIST's official NVD documentation |
What The Draft Got Right
Several parts hold up against NIST's documentation. The endpoint, host and cveId parameter are accurate, as are the apiKey header and the rate limits of 5 and 50 requests per rolling 30 seconds. The CVSS score, vector string and severity bands for Log4Shell also match the live record.
Errors That Change Security Decisions
Each one would misdirect a patching decision or break code that follows the draft.
| # | The draft says | The reality | The consequence |
|---|---|---|---|
| 1 | Nothing about vulnStatus or NIST's April 2026 change, although its own example includes "vulnStatus": "Analyzed" |
Since April 2026, many CVEs carry no NIST score and sit in Deferred status |
Every piece of advice in the draft assumes a score exists |
| 2 | Read severity from cvssMetricV31[0] |
Deferred records may have no cvssMetricV31 block, and the first entry isn't guaranteed to be NIST's Primary score |
Code following this path crashes on unscored records or uses another organisation's score without any warning |
| 3 | Prioritize in bands from Critical down to Low | An unscored CVE fits none of the bands | Unscored vulnerabilities drop out of the process entirely |
| 4 | CISA KEV is a side note under Critical and High | KEV membership means attackers are already exploiting the flaw, whatever its score | A Medium-scored CVE in KEV would wait for a routine maintenance window |
| 5 | The Log4Shell example omits KEV fields | The live record includes cisaExploitAdd and cisaActionDue, showing CISA listed it the day it was published |
The strongest prioritization signal in the record is missing from the example |
Fabricated Data
Values the draft presents as real API output, none of which match the live record.
| # | The draft says | The live record says |
|---|---|---|
| 6 | "published": "2021-12-10T10:15:07.680" |
2021-12-10T10:15:09.143 |
| 7 | "lastModified": "2023-01-20T21:15:11.120" |
2026-08-11T19:33:44.513 |
| 8 | "timestamp": "2026-03-30T10:00:00.123" |
No such response exists |
| 9 | Affected versions "2.0-beta9 to 2.15.0r1" | "2.0-beta9 through 2.15.0 (excluding security releases 2.12.2, 2.12.3, and 2.3.1)". Version 2.15.0r1 doesn't exist |
| 10 | A field named impactMetric inside cvssData |
No such field. The real fields are confidentialityImpact, integrityImpact and availabilityImpact |
Here is the draft's example response, corrected against the live record. Red lines show what Gemini wrote, and green lines show what the API returns.
- "published": "2021-12-10T10:15:07.680",
+ "published": "2021-12-10T10:15:09.143",
- "lastModified": "2023-01-20T21:15:11.120",
+ "lastModified": "2026-08-11T19:33:44.513",
- "impactMetric": {}
+ "confidentialityImpact": "HIGH",
+ "integrityImpact": "HIGH",
+ "availabilityImpact": "HIGH"
+ "cisaExploitAdd": "2021-12-10",
+ "cisaActionDue": "2021-12-24"
Unverified Claims And Omissions
| # | The draft says | The issue |
|---|---|---|
| 11 | Rate limits apply "by IP address" | Not stated in NIST's documentation |
| 12 | Retry on HTTP 403 or 429 |
Not confirmed by NIST, whose documentation directs users to a response header named message when client errors occur |
| 13 | Example request uses CVE-2023-12345 |
No check that the ID exists. Examples should use real CVEs that have been verified |
| 14 | No guidance on protecting the API key | Keys should never be hardcoded or committed to a repository |
| 15 | Score ranges written as $9.0 - 10.0$ |
GitHub renders $...$ as math notation, so the ranges display incorrectly |
Corrected Guidance
Reading severity safely. Look for NIST's primary score first. If it's missing, fall back to a secondary score and record its source. A missing score should be treated as unknown, never as low.
def get_severity(cve):
metrics = cve.get("metrics", {}).get("cvssMetricV31", [])
primary = next((m for m in metrics if m.get("type") == "Primary"), None)
chosen = primary or (metrics[0] if metrics else None)
if chosen is None:
return None, None # Unscored: flag for manual review, never default to low
return chosen["cvssData"]["baseSeverity"], chosen["source"]
Prioritising in 2026. The score is the last thing to check. Exploitation status comes first and analysis status second, and severity bands apply only once a trustworthy score exists.
flowchart TD
A[New CVE record] --> B{cisaExploitAdd present?}
B -- Yes --> C[Urgent: actively exploited]
B -- No --> D{vulnStatus Analyzed or Modified?}
D -- Yes --> H[Apply severity bands with asset context]
D -- No --> E{Secondary score available?}
E -- Yes --> F[Use secondary score and record its source]
E -- No --> G[Manual review: treat as unknown, not low]
F --> H
How To Catch These Errors In Any AI Draft
- Compare every example value with a live response, field by field, since plausible-looking data is the most common hallucination.
- Check field and parameter names against the official documentation rather than other tutorials.
- Ask what has changed in the subject recently, and whether the draft reflects it.
- Flag any claim that sounds right but can't be traced to a primary source.
Sources
- NVD Developers: Start Here (NIST)
- NVD vulnerability status definitions (NIST)
- Live NVD API record for CVE-2021-44228
Gemini's original draft, unedited
# Developer Guide: Querying CVE Severity with the NVD API The National Vulnerability Database (NVD) API allows developers to programmatically retrieve vulnerability data, including Common Vulnerability Scoring System (CVSS) metrics and severity ratings. This section covers authentication, rate limiting, request structure, parsing responses, and vulnerability prioritization. --- ## 1. Authentication and API Keys While you can access the NVD API without an API key, doing so severely restricts your request rate and reliability. * **Without an API Key:** Limited to **5 requests in a rolling 30-second window** by IP address. * **With an API Key:** Increased to **50 requests in a rolling 30-second window**. ### How to Authenticate To use an API key, request one for free from the [NVD NIST website](https://nvd.nist.gov/developers/request-an-api-key). Pass your key in the HTTP request headers using the `apiKey` header:GET /rest/json/cves/2.0?cveId=CVE-2023-12345 HTTP/1.1
Host: services.nvd.nist.gov
apiKey: your-api-key-uuid-here
curl -X GET "https://services.nvd.nist.gov/rest/json/cves/2.0?cveId=CVE-2021-44228" \
-H "apiKey: your-api-key-uuid-here"
{
"resultsPerPage": 1,
"startIndex": 0,
"totalResults": 1,
"format": "NVD_CVE",
"version": "2.0",
"timestamp": "2026-03-30T10:00:00.123",
"vulnerabilities": [
{
"cve": {
"id": "CVE-2021-44228",
"sourceIdentifier": "security@apache.org",
"published": "2021-12-10T10:15:07.680",
"lastModified": "2023-01-20T21:15:11.120",
"vulnStatus": "Analyzed",
"descriptions": [
{
"lang": "en",
"value": "Apache Log4j2 2.0-beta9 to 2.15.0r1 vulnerable to JNDI features used in configuration..."
}
],
"metrics": {
"cvssMetricV31": [
{
"source": "nvd@nist.gov",
"type": "Primary",
"cvssData": {
"version": "3.1",
"vectorString": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H",
"baseScore": 10.0,
"baseSeverity": "CRITICAL",
"attackVector": "NETWORK",
"attackComplexity": "LOW",
"privilegesRequired": "NONE",
"userInteraction": "NONE",
"scope": "CHANGED",
"impactMetric": {}
},
"exploitabilityScore": 3.9,
"impactScore": 6.0
}
]
}
}
}
]
}