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
- NIST NVD (National Vulnerability Database) — CVE lookup and scoring
- CISA KEV Catalog — Confirmed exploited vulnerabilities
- OpenVEX Specification — Vulnerability Exploitability eXchange format
- Grype Documentation — Vulnerability scanner for SBOMs
- OWASP Dependency-Track — SBOM management platform