Skip to content
Supply Chain Security

Mapping Your SBOM to NIST NVD: A Vulnerability Triage Workflow

Go from SBOM to patched artifact: CVE lookup, CISA KEV prioritization, VEX documentation, and remediation tracking.

Intermediate 12 min read Updated May 2026

Having an SBOM is step one. Using it to find and fix vulnerabilities is where compliance becomes operational.

You've generated a CycloneDX SBOM. It lists 300 components. Then a CVE is announced. Your SBOM contains [email protected]. The vulnerability is CVE-2024-8999. Your patch timeline depends on which vulnerabilities matter most and how fast you can verify they're fixed.

This article covers the workflow: SBOM → NVD (National Vulnerability Database) → CISA KEV (Known Exploited Vulnerabilities) → patch decision → VEX (Vulnerability Exploitability Exchange) → evidence for auditors.

The SBOM-to-Patch Workflow

Step 1: Generate SBOM (if not already done)

# From artifact
cdxgen -t docker docker.io/myorg/api-gateway:v2.14.1 -o sbom.cdx.json

# Validate
cyclonedx-cli validate --input-file sbom.cdx.json --fail-on-errors

(See Generating CycloneDX SBOM in CI/CD for detailed setup.)


Step 2: Scan SBOM for Known Vulnerabilities

Use a vulnerability scanner that understands CycloneDX. Popular options:

Tool Approach Output
Grype Database-backed CVE lookup JSON with CVSS scores, fix versions
Trivy Fast, offline-capable JSON, SBOM with vulnerabilities embedded
Snyk SCA with fix recommendations JSON, web dashboard, PR suggestions
Dependabot (GitHub) Automated PR generation GitHub alerts, auto-PR for fixes
Dependency-Track (enterprise) Multi-project dashboard Web UI, API, email alerts

Recommended for compliance: Grype (free, no API key needed, accurate NVD mapping).

# Install Grype
curl -sSfL https://get.anchore.io/grype | sudo sh -s -- -b /usr/local/bin

# Scan SBOM
grype sbom:sbom.cdx.json --output json > vulns.json

Output example:

[
  {
    "id": "CVE-2024-8999",
    "severity": "critical",
    "cvss": {"score": 9.8, "vector": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H"},
    "component": {
      "name": "express",
      "version": "4.18.0",
      "purl": "pkg:npm/[email protected]"
    },
    "fixedInVersions": ["4.18.2"],
    "description": "Expression language injection in routing parameters"
  }
]

Step 3: Enrich with CISA KEV (Known Exploited Vulnerabilities)

Not all vulnerabilities are being exploited. CISA KEV is a list of confirmed exploited CVEs — the ones attackers are actively using. Prioritize these.

CISA publishes the KEV catalog at: https://www.cisa.gov/known-exploited-vulnerabilities-catalog

Current size (catalogVersion 2026.05.04): 1,587 confirmed exploited vulnerabilities (live count published in the count field of the JSON feed below)

How to use:

# Download CISA KEV
curl -s https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json | jq . > kev.json

# Cross-reference with your vulnerabilities
node tools/enrich-with-kev.js vulns.json kev.json > vulns-with-kev.json

Enhanced output:

{
  "id": "CVE-2024-8999",
  "severity": "critical",
  "cvss": 9.8,
  "cisa_kev": {
    "is_exploited": true,
    "date_added": "2024-09-15",
    "days_since_publication": 8,
    "vendor": "Express.js",
    "product": "express"
  },
  "remediation_priority": "CRITICAL - Patch within 24 hours"
}

Priority guidance:

Category CISA KEV? CVSS Action
Critical Known Exploit Yes 9+ Patch TODAY if possible, max 24h
Critical Not Yet Exploited No 9+ Patch within 1 week, test thoroughly
High Known Exploit Yes 7–8 Patch within 48 hours
High Not Exploited No 7–8 Patch within 2 weeks
Medium/Low No <7 Patch in next release or 30 days

Step 4: Assess Exploitability (VEX — Vulnerability Exploitability eXchange)

Some vulnerabilities won't affect your software even if you have the component.

Example: express 4.18.0 has a vulnerability in a routing parameter handler. But your application doesn't use dynamic routing — you use static routes only. The vulnerability is not exploitable in your case.

VEX (Vulnerability Exploitability eXchange) documents this formally:

{
  "@context": "https://openvex.dev/ns/v0.2.0",
  "tooling": "vex-tool/1.0",
  "statements": [
    {
      "vulnerability": {
        "id": "CVE-2024-8999"
      },
      "timestamp": "2026-04-29T10:00:00Z",
      "components": [
        {
          "identifiers": {
            "purl": "pkg:npm/[email protected]"
          }
        }
      ],
      "status": "not_affected",
      "justification": "code_not_reachable",
      "details": "Routing parameter handler not used in our application — all routes are static"
    }
  ]
}

VEX Statuses:

Status Meaning Example
affected Vulnerability is exploitable in your use "We use dynamic routing, patch needed"
fixed You've already patched "Upgraded to 4.18.2"
not_affected Component present but code path unreachable "Dependency is present but not called"
under_investigation Assessment in progress "Triage in progress, update tomorrow"

VEX Justifications (for not_affected status):

Justification Use Case
code_not_present Component installed but not bundled in artifact
code_not_reachable Code present but unreachable (dead code, not exported)
requires_configuration Vulnerability only exploitable with specific config
protected_at_runtime Runtime protection (WAF, input validation) prevents exploitation
protected_at_perimeter Network-level protection (firewall rules) mitigates

Step 5: Patch and Verify

For affected or fixed status:

# Update dependency
npm upgrade [email protected]

# Generate new SBOM
cdxgen -t docker docker.io/myorg/api-gateway:v2.14.1-patched -o sbom-new.cdx.json

# Scan new SBOM
grype sbom:sbom-new.cdx.json --output json > vulns-new.json

# Verify CVE is gone
grep "CVE-2024-8999" vulns-new.json || echo "✓ CVE patched"

For not_affected status:

Document the justification:

# Create VEX document
cat > cve-2024-8999-vex.json << 'EOF'
{
  "@context": "https://openvex.dev/ns/v0.2.0",
  "statements": [{
    "vulnerability": {"id": "CVE-2024-8999"},
    "status": "not_affected",
    "justification": "code_not_reachable",
    "details": "Express routing handler not used; all routes hardcoded",
    "timestamp": "2026-04-29T10:00:00Z",
    "components": [{"identifiers": {"purl": "pkg:npm/[email protected]"}}]
  }]
}
EOF

# Sign VEX with your org key
gpg --sign --detach-sign --armor cve-2024-8999-vex.json

Step 6: Track Remediation

Create a remediation record:

{
  "cve": "CVE-2024-8999",
  "component": "express",
  "current_version": "4.18.0",
  "affected": true,
  "decision": "patch",
  "patch_version": "4.18.2",
  "estimated_patch_date": "2026-05-05",
  "vex_status": "affected",
  "owner": "[email protected]",
  "tracking_id": "SEC-2026-0042",
  "date_discovered": "2026-04-29",
  "date_patched": "2026-05-05",
  "date_deployed": "2026-05-06",
  "cisa_kev": true,
  "days_to_remediate": 7,
  "sla_compliance": "pass (SLA: 7 days for non-KEV, any duration for KEV)"
}

Store in database for audit reporting.


Tools for Vulnerability Triage

Grype + Jq (DIY Approach)

#!/bin/bash
# Complete vulnerability triage script

SBOM=$1
COMPONENT=$2

echo "=== Vulnerability Triage for $COMPONENT ==="

# Scan SBOM
grype sbom:$SBOM --output json > vulns.json

# Filter to target component
echo "Vulnerabilities in $COMPONENT:"
jq ".[] | select(.component.name == \"$COMPONENT\") | {id, severity, cvss: .cvss.score, fixedInVersions}" vulns.json

# Check CISA KEV
echo "CISA KEV Status:"
curl -s https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json | \
  jq ".[] | select(.cveID == \"CVE-2024-8999\")" | jq .

# Create VEX template
echo "Create VEX statement:"
cat > vex-template.json << 'EOF'
{
  "@context": "https://openvex.dev/ns/v0.2.0",
  "statements": [
    {
      "vulnerability": {"id": "CVE-????-????"},
      "status": "not_affected|affected|fixed",
      "justification": "code_not_reachable|...",
      "details": "[YOUR ASSESSMENT]",
      "timestamp": "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
    }
  ]
}
EOF

echo "VEX template created: vex-template.json"

OWASP Dependency-Track (Enterprise)

If you need a web dashboard:

# Docker
docker run -d -p 8080:8080 \
  -e ADMIN_PASSWORD=changeme \
  dependencytrack/apiserver:latest

# Upload SBOM via UI or API
curl -X POST https://localhost:8080/api/v1/bom \
  -H "X-API-Key: $DTRACK_KEY" \
  -F "projectName=api-gateway" \
  -F "projectVersion=2.14.1" \
  -F "[email protected]"

# View vulnerabilities in web UI
open https://localhost:8080

Reporting and Compliance Evidence

For auditors, create a quarterly report:

{
  "period": "Q1 2026",
  "total_cves_discovered": 47,
  "cves_by_severity": {
    "critical": 3,
    "high": 12,
    "medium": 20,
    "low": 12
  },
  "cve_sources": {
    "cisa_kev": 5,
    "nvd_only": 42
  },
  "remediation_metrics": {
    "patched_within_sla": 44,
    "patched_out_of_sla": 2,
    "marked_not_affected_with_vex": 1,
    "still_pending": 0,
    "sla_compliance_rate": "95.7%"
  },
  "sla_by_severity": {
    "critical": {"target_days": 1, "actual_avg_days": 0.5, "pass": true},
    "high": {"target_days": 7, "actual_avg_days": 3.2, "pass": true},
    "medium": {"target_days": 30, "actual_avg_days": 15.1, "pass": true}
  },
  "artifacts": {
    "sbom": "sbom-q1-2026.cdx.json",
    "vulnerability_scan": "grype-q1-2026.json",
    "vex_documents": ["vex-cve-2024-8999.json", "vex-cve-2024-9000.json"],
    "remediation_records": "remediation-log-q1-2026.json"
  }
}

Checklist: SBOM → Patch Workflow

  • SBOM generated and validated for artifact
  • Grype or equivalent SCA tool scans SBOM
  • Results enriched with CISA KEV data
  • Each vulnerability assessed: affected, fixed, or not_affected
  • VEX documents created for not_affected justifications
  • Patch priority assigned based on CVSS + CISA KEV
  • Patches scheduled and tracked
  • New SBOM generated after patching
  • Verification scan confirms CVE fixed
  • Remediation record stored for audit
  • Quarterly compliance report generated

References

This article is part of the Supply Chain Security knowledge series (5 articles) Browse all Supply Chain Security articles →
Related Use Case

Software Compliance — Your last compliance vendor

Don't fake the evidence. Trust it.

Explore Use Case →