Pipeline que mantiene al agente vuln-triage al día con las vulnerabilidades que de
verdad se están explotando. Sin dependencias externas (solo Python stdlib + SQLite).
RAG hermanos (esta carpeta):
knowledge/(técnica accionable, Capa 1+2) ·context/(contexto per-engagement, aislado por zona) ·triage/(POLÍTICA DE PROGRAMA — dataset curado y VERSIONADO de criterios de aceptación y clases do-not-report de HackerOne/Bugcrowd/Intigriti/YWH; ADVISORY, orienta avuln-triage/reporting, la política oficial del programa PREVALECE;python rag/triage/query_triage.py --stats).
No es un "RAG ingenuo" de embeddings sobre descripciones de CVE (impreciso para matching producto/versión). Es retrieval híbrido con priorización basada en riesgo, el estándar actual de vulnerability management:
| Señal | Qué aporta | Peso |
|---|---|---|
| CISA KEV | Explotación confirmada en el mundo real | 🔴 Máxima |
| Exploit público (ExploitDB / eip) | Existe PoC/exploit publicado | 🔴 Muy alta |
| VulnCheck KEV | KEV ampliada (más que la de CISA) | 🟠 Alta |
| EPSS (FIRST.org) | Probabilidad (0-1) de explotación a 30 días | 🟠 Alta |
| CVSS (CVE 5.0 / CNA, no NVD) | Severidad si se explota | 🟡 Media |
| SSVC (CISA Vulnrichment) | Decisión exploitation/automatable/impact | 🟡 Contexto |
| Relevancia textual | ¿pertenece al producto consultado? | Gate previo |
Regla de oro (FIRST): si hay evidencia de explotación, eso supera a cualquier score predictivo. Por eso el orden es KEV > exploit público > EPSS > CVSS.
El ranking es dos etapas: (1) relevancia — ¿este CVE es de este producto? (gate sobre vendor/product/name); (2) prioridad de explotación — KEV > exploit público > EPSS > CVSS.
El NVD está degradado: backlog masivo desde 2024, y desde el 15-abr-2026 NIST solo enriquece CVEs de alto riesgo (KEV/federal/crítico); su severidad es errónea ~88% de las veces (Inspector General). Por eso tomamos el CVSS del registro CVE 5.0 (lo publica el propio CNA) y el SSVC/KEV del contenedor CISA-ADP (Vulnrichment). Fuente pública, fiable y sin depender del NVD.
# 1. Poblar / refrescar el store (KEV + EPSS). Hazlo la primera vez y luego a diario.
python rag/refresh.py # rápido: solo enriquece EPSS nuevo
python rag/refresh.py --epss-all # completo: re-enriquece EPSS de todo (cambia a diario)
# 2. Consultar (lo que hace vuln-triage por dentro)
python rag/query_vulns.py --query "Fortinet FortiOS SSL VPN"
python rag/query_vulns.py --query "Apache Log4j" --json
python rag/query_vulns.py --query "Microsoft Exchange" --kev-only --min-epss 0.5 --limit 10Salida --json: lista results[] lista para mapear a finding.schema.json (cve, title,
severity, in_kev, epss, cwe, source_refs...), más metadatos de frescura del store.
| Fichero | Función |
|---|---|
db.py |
Esquema SQLite + migración + helpers (store vulns.db) |
ingest_kev.py |
Descarga el catálogo CISA KEV y lo upserta |
ingest_recent.py |
Frescura: CVE recién publicados (CVEDetector + OpenCVE) que aún no están en KEV |
enrich_cve5.py |
CVSS + SSVC desde CVE 5.0 (CNA + CISA-ADP), no NVD |
enrich_exploits.py |
Exploit público: ExploitDB (offline) + eip-mcp (opcional) |
enrich_epss.py |
Añade scores EPSS (lotes de 50, con reintentos) |
query_vulns.py |
Retrieval híbrido rankeado (lo llama el agente) |
refresh.py |
Orquesta KEV + recientes + CVE5 + exploits + MSF + Nuclei + EPSS para cron/Task Scheduler |
KEV va con meses de retraso respecto a la publicación. ingest_recent.py añade los CVE recientes que
aún no tenemos, desde feeds de frescura, y los marca con source_feed/published_date (in_kev=0). Luego
los enrichers les ponen CVSS/EPSS/exploit en el mismo refresco. Corre dentro de refresh.py (constante).
- CVEDetector (canal Telegram, preview web público
t.me/s/CVEDetector): sin auth. Su lado prosa también va al RAG semántico (Capa 2,rag/knowledge/ingest_feeds.py). - cvelistV5 (repo oficial MITRE,
deltaLog.json): sin auth, cobertura amplia de CVE recién publicados (es la fuente que OpenCVE agrega por debajo).enrich_cve5les rellena nombre/descripción/ producto desde el mismo registro CVE 5.0 que ya descarga (sin doble fetch). - OpenCVE (API v2 de
app.opencve.io): requiere credenciales →OPENCVE_USERNAME/OPENCVE_PASSWORD(nunca en el repo). Sin ellas se omite con aviso. - Anti-inyección: todo el contenido remoto es DATO inerte; nunca se ejecuta.
python rag/ingest_recent.py # CVEDetector + cvelistV5 (+ OpenCVE si hay claves)
python rag/ingest_recent.py --cvelistv5-limit 800 # más cobertura reciente de MITRE
OPENCVE_USERNAME=u OPENCVE_PASSWORD=p python rag/ingest_recent.py --opencve-pages 3vulns.db se genera; no se versiona ni se incluye en el paquete (caduca). Ejecuta
refresh.py para crearlo.
En Kali ya queda automático:
auto-deploy.shinstala el cron de ingesta pasiva en el crontab del usuario operador (vulnerabilidades a diario 06:00 — incluye CVE recientes; conocimiento los domingos 04:00; log enrag/.refresh.log). Idempotente (no duplica). Omítelo con--no-cron; con--semantic-ragel cron semanal usarefresh_kb.py --semantic. Para programarlo a mano:
Linux/macOS (cron):
0 6 * * 0-6 cd /ruta/data-attack && python rag/refresh.py --epss-all # vulns a diario
0 4 * * 0 cd /ruta/data-attack && python rag/knowledge/refresh_kb.py # conocimiento semanal
Windows (Task Scheduler):
schtasks /Create /SC DAILY /ST 06:00 /TN "cyberseg-rag-refresh" ^
/TR "python C:\ruta\cyberseg-agents\rag\refresh.py --epss-all"
El núcleo (KEV + CVSS de CVE5 + ExploitDB + EPSS) funciona sin claves y offline una vez sincronizado, ideal para la zona E2 aislada. La capa híbrida añade inteligencia de exploits más rica (VulnCheck KEV, InTheWild, Metasploit/Nuclei, madurez del exploit) vía eip-mcp (Exploit Intelligence Platform, MIT, self-hostable, sin API key).
Por qué híbrido y no MCP-en-vivo: (1) E2 suele estar aislada → un MCP externo no es alcanzable; el store local sí. (2) Opsec/NDA: consultar un servicio externo sobre el producto del target filtra a un tercero qué cliente auditas. (3) En 2026 se publicaron +40 CVEs contra implementaciones de MCP (RCE, command injection, path traversal); la NSA sacó guía. Por eso el MCP se usa self-hosted, en sandbox y solo en el lado con red, como motor que rellena el store local — nunca como dependencia en caliente de E2.
Activación:
# 1. Instala y arranca eip-mcp self-hosted (en el plano de control / E1, NO en E2):
pipx install eip-mcp # github.com/exploitintel/eip-mcp (MIT)
# 2. Apunta el enriquecedor a tu instancia y refresca:
EIP_API_URL=http://127.0.0.1:8000 python rag/enrich_exploits.py
# 3. (Opcional) Para exponerlo a vuln-triage como tool en línea: renombra
# .mcp.json.example -> .mcp.json (SOLO en el workspace del plano de control)Somos una empresa de seguridad: trata el MCP como código no confiable.
- Pin de versión y revisión del binario; no
latestciego. - Sandbox (contenedor/usuario sin privilegios) y sin exposición a red pública
(bind a
127.0.0.1). - Nunca cargues
.mcp.jsoncon eip en agentes de E2 ni en redes de cliente. - Si self-hospedas también la EIP API, mejor: cero dependencia externa.
NVD API 2.0 sigue existiendo (5 req/30s sin clave) pero su enriquecimiento está limitado
desde abr-2026 y su severidad es poco fiable. Usamos CVE 5.0 + CISA-ADP en su lugar. Si
aun así quieres NVD como respaldo, su clave gratuita está en
https://nvd.nist.gov/developers/request-an-api-key (guárdala en NVD_API_KEY, nunca en el repo).
El SQLite local es ideal para un operador. Para equipo y para integrarlo con tu infraestructura existente, replica el store en Supabase (Postgres + pgvector si añades búsqueda semántica). Patrón equivalente a tu Market Bot:
- Tabla
vulnsen Supabase con las mismas columnas quedb.py(PKcve_id). - Workflow n8n
WF_KEV_Ingest(Schedule diario 06:00):HTTP RequestGET al feed KEV →Code/Setpara normalizar →Supabaseupsert (on_conflict=cve_id,Prefer: resolution=merge-duplicates). - Workflow n8n
WF_EPSS_Enrich: por lotes de 50 CVE, GET ahttps://api.first.org/data/v1/epss?cve=...→SupabasePATCHepss/epss_percentile. - Retrieval para los agentes: función SQL
match_vulns(query text)o una RPC que replique el ranking dequery_vulns.py(relevancia × 10000 + KEV/EPSS/CVSS), expuesta vía MCP de Supabase o un endpoint que el agente llame.
Si añades búsqueda semántica: columna
embedding vector(1536)(text-embedding-3-small vía OpenRouter, como en tu bot), e híbrido = filtro estructurado (producto) +matchpor similitud sobre la descripción. Para CVE, el matching estructurado pesa más que el semántico: úsalo como complemento, no como sustituto.
- CISA KEV: https://www.cisa.gov/known-exploited-vulnerabilities-catalog
- CVEDetector (Telegram, frescura): https://t.me/s/CVEDetector
- MITRE cvelistV5 (frescura, sin auth): https://github.com/CVEProject/cvelistV5 (cves/deltaLog.json)
- OpenCVE (API v2, frescura, requiere cuenta): https://app.opencve.io/cve/ · https://docs.opencve.io/api/
- EPSS API: https://www.first.org/epss/api
- CVE 5.0 (MITRE CVE Services): https://cveawg.mitre.org/api/cve/{CVE-ID}
- CISA Vulnrichment (SSVC): https://github.com/cisagov/vulnrichment
- ExploitDB: https://gitlab.com/exploit-database/exploitdb
- eip-mcp (Exploit Intelligence Platform): https://github.com/exploitintel/eip-mcp
- VulnCheck NVD++ (alternativa al NVD): https://www.vulncheck.com/nvd2
- OSV.dev: https://osv.dev
- NVD API 2.0 (degradado, solo respaldo): https://nvd.nist.gov/developers/vulnerabilities