A continuación importaremos el módulo GrypeAnalyzer desde el archivo scripts/generate_grype.py.
import sysfrom pathlib import Pathimport jsonimport 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álisisproject_root = Path().cwd().parent.parentscripts_path = project_root /"scripts"ifstr(scripts_path) notin 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")
# Importar la clase GrypeAnalyzer desde generate_grype.pyfrom generate_grype import GrypeAnalyzerprint("✓ Importado correctamente: GrypeAnalyzer desde scripts/generate_grype.py")
✓ Importado correctamente: GrypeAnalyzer desde scripts/generate_grype.py
Configuración
# Configurar rutasrepos_path = project_root /"data"/"repos"output_path = project_root /"data"/"results"# Crear analizadoranalizador = GrypeAnalyzer( repos_path=str(repos_path), output_path=str(output_path))# Configurar para ejecución normal (no dry-run)analizador.dry_run =Falseprint(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.pyrepositorios = analizador.discover_repositories()print(f"Repositorios encontrados: {len(repositorios)}")for repo in repositorios:print(f" - {repo}")
Iniciando análisis de vulnerabilidades con scripts/generate_grype.py...
INFO | Grype DB status: Path: /home/vscode/.cache/grype/db/6/vulnerability.db
Schema: v6.1.4
Built: 2026-04-11T06:33:07Z
From: https://grype.anchore.io/databases/v6/vulnerability-db_v6.1.4_2026-04-11T00:33:16Z_1775889187.tar.zst?checksum=sha256%3Adc346c1a349a2a05bb697264987bca2607deb8ce3795bd3c07d8bc43996be21b
Status: valid
INFO | === Environment Check Complete ===
INFO | [1/3] Scanning data/repos/claude-code-action with Grype...
INFO | Manifests found in claude-code-action: package-lock.json, package.json
INFO | Running Grype on claude-code-action...
INFO | Parsing Grype output...
INFO | Found 5 matches in Grype output
INFO | Parsed 5 vulnerabilities total
INFO | Raw Grype output saved to data/results/claude-code-action-grype-raw.json
INFO | Normalized analysis saved to data/results/claude-code-action-grype.json
INFO | [2/3] Scanning data/repos/genai-code-review with Grype...
INFO | Manifests found in genai-code-review: requirements.txt
INFO | Running Grype on genai-code-review...
INFO | Parsing Grype output...
INFO | Found 3 matches in Grype output
INFO | Parsed 3 vulnerabilities total
INFO | Raw Grype output saved to data/results/genai-code-review-grype-raw.json
INFO | Normalized analysis saved to data/results/genai-code-review-grype.json
INFO | [3/3] Scanning data/repos/opencode with Grype...
INFO | Manifests found in opencode: Cargo.lock, Cargo.toml, package.json
INFO | Running Grype on opencode...
INFO | Parsing Grype output...
INFO | Found 9 matches in Grype output
INFO | Parsed 9 vulnerabilities total
INFO | Raw Grype output saved to data/results/opencode-grype-raw.json
INFO | Normalized analysis saved to data/results/opencode-grype.json
INFO | Summary | total_repos=3 | repos_scanned=3 | files_generated=6 | skipped=0 | errors=0
✓ Análisis completado
Inspección de Resultados
# Listar archivos de resultados generadosoutput_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# KBprint(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# KBprint(f" - {archivo.name} ({tamaño:.1f} KB)")
# Cargar y mostrar resumen de UN análisisresults_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}")withopen(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")
📂 Buscando en: /workspaces/id_2026/data/results
📋 Archivos encontrados: 3
📖 Leyendo: claude-code-action-grype.json
📊 ANÁLISIS: claude-code-action
==================================================
✓ Total de vulnerabilidades encontradas: 5
🔍 Vulnerabilidades por severidad:
⚪ low: 5
# Mostrar vulnerabilidades detalladasoutput_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]withopen(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']}")iflen(vulnerabilities_list) >20:print(f"\n... y {len(vulnerabilities_list)-20} vulnerabilidades más")else:print("⚠️ No hay detalles de vulnerabilidades en el archivo")
# Compilar estadísticas consolidadasfrom collections import defaultdictoutput_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:withopen(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 severidadfor severity, count in data.get('vulnerabilities_by_severity', {}).items(): stats_consolidadas['vulnerabilities_by_severity'][severity] += count# Contar top packages con vulnerabilidadesfor vuln in data.get('vulnerabilities', []): pkg = vuln.get('package', 'unknown') stats_consolidadas['top_packages'][pkg] +=1# Convertir defaultdicts a dicts para visualizaciónstats_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 insorted(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 inlist(stats_consolidadas['top_packages'].items())[:10]:print(f" • {pkg}: {count}")
📊 ESTADÍSTICAS CONSOLIDADAS DE GRYPE
============================================================
📁 Repositorios analizados: 3
🔴 Total de vulnerabilidades: 17
📋 Vulnerabilidades por repositorio:
• claude-code-action: 5
• genai-code-review: 3
• opencode: 9
⚠️ Vulnerabilidades por severidad:
🔴 critical: 0
🟠 high: 0
⚪ low: 17
🟡 medium: 0
📦 Top 10 paquetes con vulnerabilidades:
• unknown: 17
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_version para actualizar librerías vulnerables
Automatizar: Integrar estos análisis en CI/CD para detectar problemas continuamente
Para agregar nuevos repositorios:
Edita data/repos.json y agrega nuevos repositorios
Ejecuta en la terminal:
uv run scripts/add_submodules.py
Ejecuta el análisis nuevamente desde el notebook o la terminal: