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-XXXX-NNNNN. 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:

ToolApproachOutput
GrypeDatabase-backed CVE lookupJSON with CVSS scores, fix versions
TrivyFast, offline-capableJSON, SBOM with vulnerabilities embedded
SnykSCA with fix recommendationsJSON, web dashboard, PR suggestions
Dependabot (GitHub)Automated PR generationGitHub alerts, auto-PR for fixes
Dependency-Track (enterprise)Multi-project dashboardWeb 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-XXXX-NNNNN",
    "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-XXXX-NNNNN",
  "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:

CategoryCISA KEV?CVSSAction
Critical Known ExploitYes9+Patch TODAY if possible, max 24h
Critical Not Yet ExploitedNo9+Patch within 1 week, test thoroughly
High Known ExploitYes7–8Patch within 48 hours
High Not ExploitedNo7–8Patch within 2 weeks
Medium/LowNo<7Patch 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-XXXX-NNNNN"
      },
      "timestamp": "2026-04-29T10:00:00Z",
      "components": [
        {
          "identifiers": {
            "purl": "pkg:npm/[email protected]"
          }
        }
      ],
      "status": "not_affected",
      "justification": "vulnerable_code_not_in_execute_path",
      "details": "Routing parameter handler not used in our application — all routes are static"
    }
  ]
}

VEX Statuses:

StatusMeaningExample
affectedVulnerability is exploitable in your use"We use dynamic routing, patch needed"
fixedYou've already patched"Upgraded to 4.18.2"
not_affectedComponent present but code path unreachable"Dependency is present but not called"
under_investigationAssessment in progress"Triage in progress, update tomorrow"

VEX Justifications (for not_affected status):

JustificationUse Case
component_not_presentThe vulnerable component is not included in the artifact
vulnerable_code_not_presentThe component is present, but the specific vulnerable code is not
vulnerable_code_not_in_execute_pathThe vulnerable code is present but never reached at runtime
vulnerable_code_cannot_be_controlled_by_adversaryThe vulnerable code is reachable but its inputs cannot be influenced by an attacker
inline_mitigations_already_existBuilt-in mitigations (e.g. input validation) prevent exploitation

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-XXXX-NNNNN" 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-XXXX-NNNNN"},
    "status": "not_affected",
    "justification": "vulnerable_code_not_in_execute_path",
    "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-XXXX-NNNNN",
  "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-XXXX-NNNNN\")" | 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|under_investigation",
      "justification": "component_not_present|vulnerable_code_not_present|vulnerable_code_not_in_execute_path|vulnerable_code_cannot_be_controlled_by_adversary|inline_mitigations_already_exist",
      "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-xxxx-nnnnn.json", "vex-cve-xxxx-nnnnp.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 →