Skip to content

Latest commit

 

History

History
273 lines (211 loc) · 7.63 KB

File metadata and controls

273 lines (211 loc) · 7.63 KB

CVE Schema Quality Review

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

Executive Summary

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)


✅ What's Working Well

1. Query-Optimized Design

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

2. Clear Product/Package Model

  • Products: Core runtime components (dotnet-runtime-libraries, aspnetcore-runtime)
  • Packages: NuGet packages and extensions (System.Formats.Nrbf)
  • Aligns with modern platform architecture (runtimes + packages)

3. Commit Normalization

Single source of truth in commits{} dictionary with hash-based references eliminates duplication.


🔴 Critical Issues (Must Fix)

Issue #1: Missing mitigation Field (BREAKS VALIDATION)

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: mitigation is 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
  }
}

✅ Not Issues (Initially Misunderstood)

Issue #2: Empty Version Fields - ACTUALLY CORRECT

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):

  1. Vulnerability found in .NET 8.0 and 9.0
  2. Code inspection shows 6.0 has similar code pattern
  3. Fix backported to 6.0 defensively (preventative)
  4. But 6.0 was never actually vulnerable
  5. GitHub advisory doesn't list 6.0 as affected
  6. 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"
  }
}

🟡 Medium Priority Issues

Issue #3: No Schema Versioning

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.


🟢 Quality Improvements (Nice to Have)

Issue #4: Missing String Format Validation

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"]
}

Issue #5: Nullable Array Items

Why allow null in arrays? Just omit them.

Current:

"items": {"type": ["string", "null"]}

Better:

"items": {"type": "string"}

Affected: description, platforms, architectures, references

Issue #6: Missing jq Examples in Schema

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 Summary

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

🎯 Recommendations

Immediate (Before Publishing Schema)

  1. ✅ Add mitigation field to CVE object definition

Next Release

  1. Add schema_version field
  2. Document empty string semantics for version fields
  3. Add string format validation (dates, CVE IDs, CVSS)
  4. Remove nullable array items

Nice to Have

  1. Add jq query examples to schema as $comment

Test Command

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"
done

Migration Impact

The 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