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

PropertyValue
LanguageGo
FrameworkStandard library HTTP
Port8087 (health check)
ExposedInternal only
DependenciesMySQL
Repositorytacosec/taco-enricher

Architecture

taco-enricher is a background worker that enriches vulnerability findings stored in MySQL. It runs two periodic tasks:

  1. Initial enrichment (every ENRICH_INTERVAL, default 30m) -- Processes findings with enrichment_status = 'pending'.
  2. Re-enrichment (every REENRICH_INTERVAL, default 24h) -- Refreshes all findings and updates only those where scores have drifted.

Key Packages

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

  1. Batch-fetch EPSS scores for all CVEs in the batch.
  2. Check CISA KEV catalog (in-memory).
  3. Query NVD for CVSS vector if score is missing.
  4. Compute risk score.

Non-CVE IDs (e.g., GHSA-xxxx-xxxx-xxxx)

  1. Query OSV.dev for CVSS data.
  2. EPSS is set to -1 (not applicable).
  3. KEV is always false (KEV only tracks CVE IDs).
  4. 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 score
  • epss_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 string
  • risk_score -- Computed risk score (0-10)
  • known_exploited -- CISA KEV flag
  • enrichment_status -- pending, enriched, or no_source

Environment Variables

VariableRequiredDefaultDescription
MYSQL_DSNYes--MySQL connection string
TACO_NVD_API_KEYNo--NVD API key (increases rate limit from 5/30s to 50/30s)
ENRICH_INTERVALNo30mInterval for initial enrichment of pending findings
REENRICH_INTERVALNo24hInterval for re-enrichment of all findings
HEALTH_PORTNo8087Health check HTTP port

Health Check

GET /healthz -- Returns 200 OK when MySQL is reachable. Returns 503 if MySQL is down.