import sys
from pathlib import Path
import json
import pandas as pd
# Agregar el directorio scripts al path para importar el modulo generate_grype
# Este módulo contiene la clase GrypeAnalyzer que orquesta todo el análisis
project_root = next(p for p in [Path.cwd(), *Path.cwd().parents] if (p / 'pyproject.toml').exists())
scripts_path = project_root / "scripts"
if str(scripts_path) not in sys.path:
sys.path.insert(0, str(scripts_path))
print(f"Notebook working directory: {Path().cwd()}")
print(f"Project root: {project_root}")
print(f"Scripts path: {scripts_path}")
print(f"Importing from: generate_grype.py")Análisis de Vulnerabilidades con Grype
Preparación: Importar scripts/generate_grype.py
A continuación importaremos el módulo GrypeAnalyzer desde el archivo scripts/generate_grype.py.
# Importar la clase GrypeAnalyzer desde generate_grype.py
from generate_grype import GrypeAnalyzer
print("✓ Importado correctamente: GrypeAnalyzer desde scripts/generate_grype.py")Configuración
# Configurar rutas
repos_path = project_root / "data" / "repos"
output_path = project_root / "data" / "results"
# Crear analizador
analizador = GrypeAnalyzer(
repos_path=str(repos_path),
output_path=str(output_path)
)
# Configurar para ejecución normal (no dry-run)
analizador.dry_run = False
print(f"Repos path: {repos_path}")
print(f"Output path: {output_path}")
print(f"Dry run: {analizador.dry_run}")Ejecución: Análisis de Vulnerabilidades (usando scripts/generate_grype.py)
Ahora ejecutaremos el análisis de vulnerabilidades sobre todos los repositorios descubiertos. El script generate_grype.py manejará automáticamente la orquestación completa.
# Descubrir repositorios usando el método del GrypeAnalyzer
# Este método viene de scripts/generate_grype.py
repositorios = analizador.discover_repositories()
print(f"Repositorios encontrados: {len(repositorios)}")
for repo in repositorios:
print(f" - {repo}")print("Iniciando análisis de vulnerabilidades con scripts/generate_grype.py...")
analizador.run()
print("✓ Análisis completado")Inspección de Resultados
# Listar archivos de resultados generados
output_path = project_root / "data" / "results"
grype_files = sorted(output_path.glob("*-grype.json"))
grype_raw_files = sorted(output_path.glob("*-grype-raw.json"))
print(f"📁 Buscando en: {output_path}")
print(f"✓ Archivos de análisis Grype (normalizados): {len(grype_files)}")
for archivo in grype_files:
tamaño = archivo.stat().st_size / 1024 # KB
print(f" - {archivo.name} ({tamaño:.1f} KB)")
print(f"\n✓ Archivos de análisis Grype (raw): {len(grype_raw_files)}")
for archivo in grype_raw_files:
tamaño = archivo.stat().st_size / 1024 # KB
print(f" - {archivo.name} ({tamaño:.1f} KB)")# Cargar y mostrar resumen de UN análisis
results_dir = project_root / "data" / "results"
results_files = sorted(results_dir.glob("*-grype.json"))
print(f"📂 Buscando en: {results_dir}")
print(f"📋 Archivos encontrados: {len(results_files)}")
if results_files:
result_file = results_files[0]
print(f"📖 Leyendo: {result_file.name}")
with open(result_file, 'r', encoding='utf-8') as f:
data_content = json.load(f)
repo_name_clean = result_file.stem.replace('-grype', '')
total_found = data_content.get('total_vulnerabilities', 0)
print(f"\n📊 ANÁLISIS: {repo_name_clean}")
print(f"{'='*50}")
print(f"✓ Total de vulnerabilidades encontradas: {total_found}")
if total_found > 0:
severity_data = data_content.get('vulnerabilities_by_severity', {})
print(f"\n🔍 Vulnerabilidades por severidad:")
for severity_type, count in severity_data.items():
if count > 0:
emoji = "🔴" if severity_type == "critical" else "🟠" if severity_type == "high" else "🟡" if severity_type == "medium" else "⚪"
print(f" {emoji} {severity_type}: {count}")
else:
print("⚠️ No se encontraron vulnerabilidades")
else:
print("❌ No se encontraron archivos de análisis Grype")# Mostrar vulnerabilidades detalladas
output_path_det = project_root / "data" / "results"
grype_files_det = sorted(output_path_det.glob("*-grype.json"))
if grype_files_det:
archivo_det = grype_files_det[0]
with open(archivo_det, 'r', encoding='utf-8') as f:
analisis_det = json.load(f)
vulnerabilities_list = analisis_det.get('vulnerabilities', [])
print(f"📋 Leyendo: {archivo_det.name}")
print(f"📊 Total de vulnerabilidades en archivo: {len(vulnerabilities_list)}")
if vulnerabilities_list:
# Crear DataFrame con las vulnerabilidades
df_vulns = pd.DataFrame(vulnerabilities_list)
# Seleccionar y reordenar columnas
cols_mostrar = ['package_name', 'current_version', 'vuln_id', 'vuln_severity', 'fix_version']
cols_disponibles = [c for c in cols_mostrar if c in df_vulns.columns]
df_mostrar = df_vulns[cols_disponibles].head(20)
print(f"\n📋 Primeras 20 vulnerabilidades encontradas:")
print(f"{'='*120}")
for idx, row in df_mostrar.iterrows():
print(f"\n{idx+1}. [{row['vuln_severity'].upper()}] {row['vuln_id']}")
print(f" 📦 {row['package_name']} (actual: {row['current_version']})")
if pd.notna(row['fix_version']) and row['fix_version'] != 'N/A':
print(f" ✓ Fix disponible en: {row['fix_version']}")
if len(vulnerabilities_list) > 20:
print(f"\n... y {len(vulnerabilities_list)-20} vulnerabilidades más")
else:
print("⚠️ No hay detalles de vulnerabilidades en el archivo")Análisis Detallado de Vulnerabilidades
# Compilar estadísticas consolidadas
from collections import defaultdict
output_path_stats = project_root / "data" / "results"
grype_files_stats = sorted(output_path_stats.glob("*-grype.json"))
stats_consolidadas = {
'total_repositories': len(grype_files_stats),
'total_vulnerabilities': 0,
'vulnerabilities_by_repo': {},
'vulnerabilities_by_severity': defaultdict(int),
'top_packages': defaultdict(int)
}
for archivo in grype_files_stats:
with open(archivo, 'r', encoding='utf-8') as f:
data = json.load(f)
repo_name = archivo.stem.replace('-grype', '')
total = data.get('total_vulnerabilities', 0)
stats_consolidadas['total_vulnerabilities'] += total
stats_consolidadas['vulnerabilities_by_repo'][repo_name] = total
# Contar por severidad
for severity, count in data.get('vulnerabilities_by_severity', {}).items():
stats_consolidadas['vulnerabilities_by_severity'][severity] += count
# Contar top packages con vulnerabilidades
for vuln in data.get('vulnerabilities', []):
pkg = vuln.get('package', 'unknown')
stats_consolidadas['top_packages'][pkg] += 1
# Convertir defaultdicts a dicts para visualización
stats_consolidadas['vulnerabilities_by_severity'] = dict(stats_consolidadas['vulnerabilities_by_severity'])
stats_consolidadas['top_packages'] = dict(sorted(
stats_consolidadas['top_packages'].items(),
key=lambda x: x[1],
reverse=True
)[:10])
print("\n📊 ESTADÍSTICAS CONSOLIDADAS DE GRYPE")
print(f"{'='*60}")
print(f"\n📁 Repositorios analizados: {stats_consolidadas['total_repositories']}")
print(f"🔴 Total de vulnerabilidades: {stats_consolidadas['total_vulnerabilities']}")
print(f"\n📋 Vulnerabilidades por repositorio:")
for repo, count in stats_consolidadas['vulnerabilities_by_repo'].items():
print(f" • {repo}: {count}")
print(f"\n⚠️ Vulnerabilidades por severidad:")
for severity, count in sorted(stats_consolidadas['vulnerabilities_by_severity'].items()):
emoji = "🔴" if severity == "critical" else "🟠" if severity == "high" else "🟡" if severity == "medium" else "⚪"
print(f" {emoji} {severity}: {count}")
print(f"\n📦 Top 10 paquetes con vulnerabilidades:")
for pkg, count in list(stats_consolidadas['top_packages'].items())[:10]:
print(f" • {pkg}: {count}")Próximos Pasos
Los resultados del análisis Grype se han guardado en data/results/ con los patrones: - {repo-name}-grype-raw.json: Salida original de Grype (útil para debug) - {repo-name}-grype.json: Formato normalizado (para análisis y visualización)
Cómo usar estos resultados:
- Revisar vulnerabilidades críticas: Filtrar por
vuln_severity: "critical"para identificar problemas urgentes - Seguimiento: Integrar con sistemas de seguimiento (issues, PRs) para gestionar remediación
- Análisis de tendencias: Comparar resultados entre repositorios y períodos de tiempo
- Actualizar dependencias: Usar
fix_versionpara actualizar librerías vulnerables - Automatizar: Integrar estos análisis en CI/CD para detectar problemas continuamente
Para agregar nuevos repositorios:
- Edita
data/repos.jsony agrega nuevos repositorios - Ejecuta en la terminal:
uv run python scripts/add_submodules.py- Ejecuta el análisis nuevamente desde el notebook o la terminal:
uv run scripts/generate_grype.py