From 2dda097a111fb480aec935fa241a75c843450624 Mon Sep 17 00:00:00 2001 From: Luis Gutierrez Lopez Date: Tue, 11 Aug 2026 18:13:00 +0200 Subject: [PATCH] Ajuste REPO --- INFORME.sh | 2 + check_CVEs.py | 223 -------------------------------------------------- 2 files changed, 2 insertions(+), 223 deletions(-) delete mode 100644 check_CVEs.py diff --git a/INFORME.sh b/INFORME.sh index 8ead6e2..3091313 100644 --- a/INFORME.sh +++ b/INFORME.sh @@ -4,4 +4,6 @@ echo "Revisando vulnerabilidades de productos mediante ENISA" echo "Transformando informe en formato Markdown en PDF" pandoc -V lang=es -V geometry:a4paper -V geometry:margin=2cm --pdf-engine=xelatex -V monofont="DejaVu Sans Mono" --metadata author="Luis Gutiérrez López (Software Libre)" informe_vulnerabilidades.md -o informe_vulnerabilidades.pdf echo +echo "Informe Finalizado" +echo diff --git a/check_CVEs.py b/check_CVEs.py deleted file mode 100644 index 416eea6..0000000 --- a/check_CVEs.py +++ /dev/null @@ -1,223 +0,0 @@ -#!/usr/bin/env python3 - -import time -import json -import yaml -import requests -from datetime import datetime, timedelta, timezone - -BASE_URL = "https://euvdservices.enisa.europa.eu/api/search" -HEADERS = { - "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) EUVD-Auditor/1.0" -} - -def fetch_fast_enisa_vulnerabilities(vendor, product, from_score=7.0, days_back=30): - vulnerabilities = [] - from_date = (datetime.now() - timedelta(days=days_back)).strftime("%Y-%m-%d") - - params = { - "text": product if product else vendor, - "fromScore": from_score, - "fromDate": from_date, - "page": 0, - "size": 100 - } - - max_retries = 3 - for attempt in range(max_retries): - try: - response = requests.get(BASE_URL, headers=HEADERS, params=params, timeout=15) - - # Si se supera el rate limit, esperar y reintentar - if response.status_code == 429: - wait_time = (attempt + 1) * 5 - print(f" ⚠️ Rate limit (429) alcanzado para {product}. Esperando {wait_time}s...") - time.sleep(wait_time) - continue - - response.raise_for_status() - data = response.json() - - items = data.get("items", data.get("content", [])) if isinstance(data, dict) else data - vulnerabilities.extend(items) - break # Éxito, salir del bucle de reintentos - - except Exception as e: - if attempt == max_retries - 1: - print(f" ❌ Error final tras {max_retries} intentos para {product}: {e}") - time.sleep(2) - - return vulnerabilities - -def generate_markdown_report(total_consolidados, total_analizadas, total_aplicables, - total_no_aplicables, total_revision, resumen_objetivos, - aplicables_severidad, output_file="informe_vulnerabilidades.md"): - """ - Genera el informe ejecutivo en Markdown utilizando el formato de Bloques / Fichas Ejecutivas, - ideal para lectura clara y conversión a PDF mediante Pandoc. - """ - now_iso = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%S.%f")[:-3] + "Z" - - md_content = f"""# Informe Ejecutivo de Vulnerabilidades - -**Generado en:** {now_iso} | **Fuentes:** ENISA - -**Grupo de Software Libre - Área de Plataformas** - -* **Informes consolidados:** {total_consolidados} -* **Vulnerabilidades analizadas:** {total_analizadas} -* **Aplicables:** {total_aplicables} -* **No aplicables:** {total_no_aplicables} -* **Revisión manual:** {total_revision} - -## Resumen por objetivo - -| Fuente | Producto | Versión | Analizadas | Aplicables | No aplicables | Revisión manual | -|---|---|---|---|---|---|---| -""" - - for obj in resumen_objetivos: - md_content += f"| {obj['Fuente']} | {obj['Producto']} | {obj['Versión']} | {obj['Analizadas']} | {obj['Aplicables']} | {obj['No aplicables']} | {obj['Revisión manual']} |\n" - - md_content += """ -## Detalle de Vulnerabilidades Aplicables -""" - - if not aplicables_severidad: - md_content += "\n*No se encontraron vulnerabilidades aplicables con los criterios seleccionados.*\n" - else: - for app in aplicables_severidad: - # Determinar nivel y badge según la puntuación CVSS - score_val = app['BaseScore'].split()[0] if app['BaseScore'] else "0.0" - try: - score_num = float(score_val) - except ValueError: - score_num = 0.0 - - if score_num >= 9.0: - badge = "[**CRÍTICA**] - " - elif score_num >= 7.0: - badge = "[**ALTA**] - " - elif score_num >= 4.0: - badge = "[**MEDIA**] - " - else: - badge = "[**BAJA**] - " - - # Formatear la descripción/justificación - just = app['Justificación'].strip() - - md_content += f""" - -### {badge} {app['ID']} - {app['CVE']} - -* **Producto / Versión:** `{app['Producto']}` (v`{app['Versión']}`) | **Fuente:** {app['Fuente']} -* **BaseScore:** {app['BaseScore']} -* **Regla versión:** {app['Regla versión']} -* **Justificación / Descripción:** - > {just} -""" - - with open(output_file, "w", encoding="utf-8") as f: - f.write(md_content) - - print(f"📝 Informe Markdown generado en '{output_file}'") - - -def process_targets(config_path="targets.yaml"): - try: - with open(config_path, "r", encoding="utf-8") as f: - config = yaml.safe_load(f) - except FileNotFoundError: - print(f"❌ Archivo '{config_path}' no encontrado.") - return - - resumen_objetivos = [] - aplicables_severidad = [] - - total_consolidados = 0 - total_analizadas = 0 - total_aplicables = 0 - total_no_aplicables = 0 - total_revision = 0 - - print("🚀 Iniciando escaneo ultra-rápido en ENISA EUVD...\n") - - for target in config.get("targets", []): - vendor = target.get("vendor", "") - product = target.get("product", "") - version = target.get("version", "") - - total_consolidados += 1 - print(f"👉 Analizando: {vendor}/{product} (v{version})") - - items = fetch_fast_enisa_vulnerabilities(vendor=vendor, product=product, from_score=7.0, days_back=30) - - num_analizadas = len(items) - num_aplicables = 0 - num_no_aplicables = 0 - num_revision = 0 - - for item in items: - score = item.get("baseScore", 0.0) - score_ver = item.get("baseScoreVersion", "3.1") - euvd_id = item.get("id", "N/A") - - aliases = item.get("aliases", "") - aliases_list = aliases.strip().split("\n") if isinstance(aliases, str) else [] - cve_id = aliases_list[0] if aliases_list and aliases_list[0] else euvd_id - - desc = item.get("description", "Sin descripción disponible.") - desc_short = desc[:180].strip() + "..." if len(desc) > 180 else desc - - num_aplicables += 1 - aplicables_severidad.append({ - "Fuente": "ENISA", - "ID": euvd_id, - "CVE": cve_id, - "Producto": product, - "Versión": version, - "BaseScore": f"{score} {score_ver}".strip(), - "Regla versión": f"Afecta rama v{version.split('.')[0]}.x", - "Justificación": desc_short - }) - - total_analizadas += num_analizadas - total_aplicables += num_aplicables - total_no_aplicables += num_no_aplicables - total_revision += num_revision - - resumen_objetivos.append({ - "Fuente": "ENISA", - "Producto": product, - "Versión": version, - "Analizadas": num_analizadas, - "Aplicables": num_aplicables, - "No aplicables": num_no_aplicables, - "Revisión manual": num_revision - }) - - print(f" 📊 Encontradas: {num_analizadas} vulnerabilidades relevantes\n") - time.sleep(0.5) - - # Ordenar vulnerabilidades aplicables de mayor a menor puntuación CVSS - aplicables_severidad.sort(key=lambda x: float(x["BaseScore"].split()[0]) if x["BaseScore"].split()[0].replace('.', '', 1).isdigit() else 0.0, reverse=True) - - # 1. Guardar JSON - reporte_json = { - "generado_en": datetime.now(timezone.utc).isoformat(), - "fuentes": ["ENISA"], - "resumen_por_objetivo": resumen_objetivos, - "aplicables_por_severidad": aplicables_severidad - } - with open("reporte_vulnerabilidades.json", "w", encoding="utf-8") as f: - json.dump(reporte_json, f, indent=2, ensure_ascii=False) - - # 2. Generar Markdown - generate_markdown_report( - total_consolidados, total_analizadas, total_aplicables, - total_no_aplicables, total_revision, resumen_objetivos, - aplicables_severidad - ) - -if __name__ == "__main__": - process_targets()