Date: 2025-01-30
Schema: https://raw.githubusercontent.com/dotnet/designs/refs/heads/main/accepted/2025/cve-schema/dotnet-cves-schema.json
Spec: https://raw.githubusercontent.com/dotnet/designs/refs/heads/main/accepted/2025/cve-schema/cve_schema.md
The CVE schema design philosophy is excellent - intentional denormalization for queryability is well-justified and aligns with the documented "jq battle-testing" approach. However, two critical bugs will cause validation failures with existing data.
Overall Grade: A- (would be A with mitigation field fix)
The intentional denormalization with index dictionaries is a feature, not a bug:
- 15% storage overhead buys massive query simplification
- Join indices (
cve_releases,release_cves,cve_commits) transform complex operations into O(1) lookups - Well-documented tradeoff in the spec
- Products: Core runtime components (dotnet-runtime-libraries, aspnetcore-runtime)
- Packages: NuGet packages and extensions (System.Formats.Nrbf)
- Aligns with modern platform architecture (runtimes + packages)
Single source of truth in commits{} dictionary with hash-based references eliminates duplication.
Problem: Schema has "additionalProperties": false but data contains mitigation field.
Evidence:
- 3 files contain mitigation field (2023/11, 2024/01, 2024/11)
- 5 total CVE entries use it
- Example from CVE-2024-43498, CVE-2024-43499
- The example file (cve.json) in the spec does NOT have mitigation
- Therefore:
mitigationis truly optional
Impact: Any validator will REJECT these valid CVE entries.
Fix (5 minutes) - Add as optional field:
"cves": {
"items": {
"properties": {
"id": {...},
"problem": {...},
"severity": {...},
"timeline": {...},
"cvss": {...},
"description": {...},
"mitigation": {
"description": "Mitigation steps for the vulnerability.",
"type": "array",
"items": {
"type": "string"
}
},
"platforms": {...},
"architectures": {...},
"cna": {...},
"references": {...}
},
"required": [
"id", "problem", "severity", "timeline", "cvss",
"description", "platforms", "architectures", "cna", "references"
// NOTE: mitigation is NOT in required - it's optional
],
"additionalProperties": false
}
}Initial Assessment: ❌ "Required fields with empty strings is bad schema design"
Corrected Understanding: ✅ Empty strings have semantic meaning
What's happening: Entries with empty version fields represent defensive backports:
{
"cve_id": "CVE-2025-21172",
"name": "dotnet-runtime-libraries",
"release": "6.0",
"commits": ["b3f8ef0..."],
"min_vulnerable": "", // No 6.0 versions were vulnerable
"max_vulnerable": "", // But a defensive patch was applied
"fixed": ""
}Real-world scenario (CVE-2025-21172):
- Vulnerability found in .NET 8.0 and 9.0
- Code inspection shows 6.0 has similar code pattern
- Fix backported to 6.0 defensively (preventative)
- But 6.0 was never actually vulnerable
- GitHub advisory doesn't list 6.0 as affected
- Empty strings = "commit exists but no vulnerable versions"
This is good design because:
- ✅ Tracks all fix commits for completeness
- ✅ Distinguishes "not vulnerable" from "missing data"
- ✅ Simple queries work:
if min_vulnerable == "" then not_vulnerable - ✅ No migration needed
- ✅ Common practice for defensive patching
Recommendation: Add documentation to schema:
{
"min_vulnerable": {
"description": "Minimum vulnerable version. Empty string indicates a defensive patch with no known vulnerable versions.",
"type": "string"
},
"max_vulnerable": {
"description": "Maximum vulnerable version. Empty string indicates a defensive patch with no known vulnerable versions.",
"type": "string"
},
"fixed": {
"description": "Version containing the fix. Empty string indicates a defensive patch with no known vulnerable versions.",
"type": "string"
}
}Problem: No way to track schema evolution or handle breaking changes.
Fix (5 minutes):
{
"properties": {
"schema_version": {
"description": "Schema version (semver)",
"type": "string",
"pattern": "^\\d+\\.\\d+\\.\\d+$"
},
"last_updated": {...},
...
},
"required": ["schema_version", "last_updated", "title", ...]
}Set initial version to "1.0.0" for all existing files.
The spec says "Aligns with CVSS specification standards" but doesn't enforce it.
Quick wins:
"last_updated": {"type": "string", "format": "date"}
"disclosed": {"type": "string", "format": "date"}
"fixed": {"type": "string", "format": "date"}
"severity": {
"type": "string",
"enum": ["critical", "high", "medium", "low", "informational"]
}
"id": {
"type": "string",
"pattern": "^CVE-\\d{4}-\\d{4,}$"
}
"cvss.version": {
"type": "string",
"enum": ["3.0", "3.1", "4.0"]
}Why allow null in arrays? Just omit them.
Current:
"items": {"type": ["string", "null"]}Better:
"items": {"type": "string"}Affected: description, platforms, architectures, references
The spec emphasizes jq battle-testing but the schema file has no examples.
Add to schema:
{
"$comment": "Common queries: .cves[].id | .release_cves[\"8.0\"] | .cve_commits[\"CVE-2024-38095\"]",
"description": "A set of CVEs with affected products...",
...
}| Priority | Issue | Impact | Effort | Blocker? |
|---|---|---|---|---|
| 🔴 P0 | Add mitigation field |
Validation fails | 5 min | YES |
| 🟡 P1 | Add schema versioning | Future-proofing | 5 min | NO |
| 🟢 P2 | Document empty string semantics | Clarity | 5 min | NO |
| 🟢 P2 | String format validation | Data quality | 20 min | NO |
| 🟢 P2 | Remove nullable array items | Cleaner data | 5 min | NO |
| 🟢 P3 | Add jq examples | Documentation | 10 min | NO |
- ✅ Add
mitigationfield to CVE object definition
- Add
schema_versionfield - Document empty string semantics for version fields
- Add string format validation (dates, CVE IDs, CVSS)
- Remove nullable array items
- Add jq query examples to schema as
$comment
After fixes, validate with:
# Install ajv-cli if needed
npm install -g ajv-cli
# Validate all CVE files against schema
for f in release-notes/archives/*/*/cve.json; do
ajv validate -s dotnet-cves-schema.json -d "$f" || echo "FAILED: $f"
doneThe two critical fixes require no data migration for Issue #1 (mitigation already exists in data) and minimal impact for Issue #2 (just remove from required array).
Files affected by migration:
- All 15 CVE JSON files already migrated from
extensions→packages - No further changes needed if schema is fixed