# Análisis de Vulnerabilidades con Grype


<!-- WARNING: THIS FILE WAS AUTOGENERATED! DO NOT EDIT! -->

## Preparación: Importar `scripts/generate_grype.py`

A continuación importaremos el módulo `GrypeAnalyzer` desde el archivo
`scripts/generate_grype.py`.

``` python
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 = Path().cwd().parent.parent
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")
```

    Notebook working directory: /workspaces/id_2026/nbs/vuln
    Project root: /workspaces/id_2026
    Scripts path: /workspaces/id_2026/scripts
    Importing from: generate_grype.py

``` python
# Importar la clase GrypeAnalyzer desde generate_grype.py
from generate_grype import GrypeAnalyzer

print("✓ Importado correctamente: GrypeAnalyzer desde scripts/generate_grype.py")
```

    ✓ Importado correctamente: GrypeAnalyzer desde scripts/generate_grype.py

## Configuración

``` python
# 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}")
```

    Repos path: /workspaces/id_2026/data/repos
    Output path: /workspaces/id_2026/data/results
    Dry run: False

## 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.

``` python
# 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}")
```

    Repositorios encontrados: 3
      - data/repos/claude-code-action
      - data/repos/genai-code-review
      - data/repos/opencode

``` python
print("Iniciando análisis de vulnerabilidades con scripts/generate_grype.py...")
analizador.run()
print("✓ Análisis completado")
```

    INFO | === Grype Environment Diagnostics ===
    INFO | Grype CLI: Application:         grype
    Version:             0.111.0
    BuildDate:           2026-04-09T13:14:56Z
    GitCommit:           1f19355a7ee2d7e2bd58da6255bdeb618eb0c0d1
    GitDescription:      v0.111.0
    Platform:            linux/amd64
    GoVersion:           go1.25.9
    Compiler:            gc
    Syft Version:        v1.42.4
    Supported DB Schema: 6

    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

``` python
# 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)")
```

    📁 Buscando en: /workspaces/id_2026/data/results
    ✓ Archivos de análisis Grype (normalizados): 3
      - claude-code-action-grype.json (2.1 KB)
      - genai-code-review-grype.json (1.3 KB)
      - opencode-grype.json (3.4 KB)

    ✓ Archivos de análisis Grype (raw): 3
      - claude-code-action-grype-raw.json (22.4 KB)
      - genai-code-review-grype-raw.json (15.7 KB)
      - opencode-grype-raw.json (30.8 KB)

``` python
# 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")
```

    📂 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

``` python
# 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")
```

    📋 Leyendo: claude-code-action-grype.json
    📊 Total de vulnerabilidades en archivo: 5

    📋 Primeras 20 vulnerabilidades encontradas:
    ========================================================================================================================

    1. [LOW] GHSA-v9p9-hfj2-hcw8
       📦 undici (actual: 5.29.0)

    2. [LOW] GHSA-vrm6-8vpv-qv8q
       📦 undici (actual: 5.29.0)

    3. [LOW] GHSA-g9mf-h72j-4rw9
       📦 undici (actual: 5.29.0)

    4. [LOW] GHSA-2mjp-6q6p-2qxm
       📦 undici (actual: 5.29.0)

    5. [LOW] GHSA-4992-7rv2-5pvq
       📦 undici (actual: 5.29.0)

## Análisis Detallado de Vulnerabilidades

``` python
# 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}")
```


    📊 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:

1.  **Revisar vulnerabilidades críticas**: Filtrar por
    `vuln_severity: "critical"` para identificar problemas urgentes
2.  **Seguimiento**: Integrar con sistemas de seguimiento (issues, PRs)
    para gestionar remediación
3.  **Análisis de tendencias**: Comparar resultados entre repositorios y
    períodos de tiempo
4.  **Actualizar dependencias**: Usar `fix_version` para actualizar
    librerías vulnerables
5.  **Automatizar**: Integrar estos análisis en CI/CD para detectar
    problemas continuamente

#### Para agregar nuevos repositorios:

1.  **Edita `data/repos.json`** y agrega nuevos repositorios
2.  **Ejecuta en la terminal**:

``` bash
uv run scripts/add_submodules.py
```

3.  **Ejecuta el análisis** nuevamente desde el notebook o la terminal:

``` bash
uv run scripts/generate_grype.py
```
