taco-enricher
The CVE enrichment service. Enhances vulnerability findings with exploit intelligence from public security APIs.
The CVE enrichment service. Enhances vulnerability findings with exploit intelligence from public security APIs.
Overview
| Property | Value |
|---|---|
| Language | Go |
| Framework | Standard library HTTP |
| Port | 8087 (health check) |
| Exposed | Internal only |
| Dependencies | MySQL |
| Repository | tacosec/taco-enricher |
Architecture
taco-enricher is a background worker that enriches vulnerability findings stored in MySQL. It runs two periodic tasks:
- Initial enrichment (every
ENRICH_INTERVAL, default 30m) -- Processes findings withenrichment_status = 'pending'. - Re-enrichment (every
REENRICH_INTERVAL, default 24h) -- Refreshes all findings and updates only those where scores have drifted.
Key Packages
| Package | Purpose |
|---|---|
enrichment/ | Service (API clients for CISA KEV, EPSS, NVD, OSV.dev) and Worker (background enrichment loops) |
config/ | Environment variable loading |
Enrichment Sources
CISA KEV (Known Exploited Vulnerabilities)
- URL:
https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json - Caching: Full catalog loaded into memory on startup. Refreshed every 24 hours.
- Data: Boolean flag -- whether a CVE is in the KEV catalog.
FIRST EPSS (Exploit Prediction Scoring System)
- URL:
https://api.first.org/data/v1/epss?cve=<cve_ids> - Batch support: Multiple CVE IDs can be queried in a single request (comma-separated).
- Data: EPSS probability score (0-1) and percentile.
- Note: Only works for CVE IDs. Non-CVE identifiers get EPSS = -1 (sentinel).
NVD (National Vulnerability Database)
- URL:
https://services.nvd.nist.gov/rest/json/cves/2.0?cveId=<cve_id> - Rate limit: 1 request per 6 seconds without API key, 1 per 0.6 seconds with key.
- Data: CVSS v3.1/v3.0 vector string and base score.
- Fallback: Only queried when the existing CVSS score is 0.
OSV.dev
- URL:
https://api.osv.dev/v1/vulns/<id> - Used for: Non-CVE identifiers (GHSA, ALAS, ALPINE, MAL, etc.)
- Data: CVSS vector and base score from severity array.
Enrichment Paths
CVE IDs (e.g., CVE-2024-1234)
- Batch-fetch EPSS scores for all CVEs in the batch.
- Check CISA KEV catalog (in-memory).
- Query NVD for CVSS vector if score is missing.
- Compute risk score.
Non-CVE IDs (e.g., GHSA-xxxx-xxxx-xxxx)
- Query OSV.dev for CVSS data.
- EPSS is set to -1 (not applicable).
- KEV is always false (KEV only tracks CVE IDs).
- Compute risk score (EPSS contribution is zero).
Risk Score Formula
risk = cvss_base * (1 + epss_score) * kev_multiplier
normalized = risk / 4.0
final = min(10.0, round(normalized, 1))
Where:
cvss_base: 0-10 CVSS base scoreepss_score: 0-1 EPSS probability (0 for non-CVE)kev_multiplier: 2.0 if known exploited, 1.0 otherwise- Maximum possible raw score: 10 * 2 * 2 = 40, normalized to 10
Drift Detection
During re-enrichment, the worker compares new scores against stored values. A finding is only updated if:
- EPSS score drifted by more than 0.001
- Risk score drifted by more than 0.001
- CVSS score was 0 and now has a value
This minimizes unnecessary database writes.
Enrichment Columns Updated
The worker updates these columns on the findings table:
epss_score-- EPSS probability (0-1 for CVEs, -1 for non-CVEs)epss_percentile-- EPSS percentile (0-1 for CVEs, -1 for non-CVEs)cvss_score-- CVSS base score (0-10)cvss_vector-- CVSS vector stringrisk_score-- Computed risk score (0-10)known_exploited-- CISA KEV flagenrichment_status--pending,enriched, orno_source
Environment Variables
| Variable | Required | Default | Description |
|---|---|---|---|
MYSQL_DSN | Yes | -- | MySQL connection string |
TACO_NVD_API_KEY | No | -- | NVD API key (increases rate limit from 5/30s to 50/30s) |
ENRICH_INTERVAL | No | 30m | Interval for initial enrichment of pending findings |
REENRICH_INTERVAL | No | 24h | Interval for re-enrichment of all findings |
HEALTH_PORT | No | 8087 | Health check HTTP port |
Health Check
GET /healthz -- Returns 200 OK when MySQL is reachable. Returns 503 if MySQL is down.