StackPractices
intermediate Por Mathias Paulenko

Plantilla de Reporte de Vulnerabilidades en Dependencias

Una plantilla para documentar vulnerabilidades individuales en dependencias, con explotabilidad, impacto, plan de remediación y aceptación de riesgo.

Descripción General

La salida cruda de un escáner no toma decisiones. Cuando Snyk o Trivy vuelcan doscientos hallazgos en un JSON, alguien todavía tiene que responder las preguntas que importan: ¿la función vulnerable se ejecuta en nuestro código? ¿Qué servicio está expuesto? ¿Quién se encarga de la corrección y para cuándo?

Una Plantilla de Reporte de Vulnerabilidades en Dependencias convierte un hallazgo en un documento listo para decidir. Combina los datos del CVE (severidad, versiones afectadas, versión corregida) con el contexto de ejecución de tu proyecto: alcanzabilidad, exposición a internet y controles compensatorios. Así, un desarrollador puede empezar la remediación y un responsable de seguridad puede defender la prioridad sin volver a correr el escaneo.

Esta plantilla está pensada para un hallazgo por reporte: un CVE en un proyecto. Si necesitas una vista consolidada de un escaneo de infraestructura sobre muchos hosts, imágenes y repositorios, usa la Plantilla de Reporte de Escaneo de Vulnerabilidades. Si solo necesitas una decisión rápida durante el triage, la Plantilla de Triage de Vulnerabilidades en Dependencias es más liviana: úsala primero y vuelve aquí para dejar el registro documentado.

Cuándo Usarla

  • Después de que un escaneo SCA programado o disparado por un PR genere un hallazgo que requiere revisión humana.
  • Cuando sale un CVE de alto perfil y cada equipo debe responder “¿nos afecta y dónde?” con fecha límite.
  • Antes de una release gate o una auditoría de seguridad, para demostrar que los hallazgos están trackeados y no enterrados en el dashboard del escáner.
  • Cuando un cliente, proveedor o auditor pide evidencia de cómo se gestionó un hallazgo concreto.
  • Para alimentar un programa de gestión de vulnerabilidades con responsables y fechas de entrega.

Cuándo no usarla: para reportes agregados de muchos hallazgos y activos; ese es el trabajo del reporte de escaneo. Y si el componente vulnerable no es una dependencia de terceros sino tu propio código, encaja mejor una plantilla de incidente o de hallazgo SAST. Una página por hallazgo es la unidad que mantiene la propiedad clara.

Prerrequisitos

  • Un escaneo SCA completado con Snyk, OWASP Dependency-Check, Trivy, Grype o GitHub Advanced Security. Conserva la salida JSON: es la fuente de verdad del reporte.
  • Acceso al repositorio afectado y a su manifiesto de dependencias (package-lock.json, requirements.txt, pom.xml, go.sum).
  • Una base de puntuación. CVSS es el punto de partida; la extenderás con señales de explotación en el paso de priorización de más abajo.
  • Un SLA de remediación acordado con ingeniería, para que “Crítico” signifique la misma fecha límite para todos los lectores.
  • Un SBOM si la dependencia se distribuye en más de un servicio: te dice en todos los lugares donde vive el paquete, no solo donde miró el escáner.

La Plantilla

Copia las tablas en un ticket, un ADR o un archivo Markdown junto al servicio que describe. Completa cada fila: un reporte con “TBD” en la columna de responsable es un hallazgo que envejecerá sin parchear.

1. Resumen del Hallazgo

CampoDescripciónEjemplo
ID del reporteIdentificador únicoVULN-2026-0042
Fecha de descubrimientoCuándo se identificó el hallazgo2026-06-27
Reportado porPersona o herramienta que lo encontróBot de Snyk / equipo de seguridad
ProyectoRepositorio o servicio afectadopayment-service
EntornoDónde corre la dependenciaProducción, CI, desarrollo

2. Detalles de la Vulnerabilidad

CampoDescripciónEjemplo
ID del CVEIdentificador Common Vulnerabilities and ExposuresCVE-2021-44228
SeveridadPuntuación base CVSS y vectorCrítica (10.0, CVSS:3.1/AV:N/AC:L/…)
Puntuación EPSSProbabilidad de explotación predicha0.97 (percentil 97)
¿Está en CISA KEV?Explotación confirmada en la prácticaSí — agregado el 2021-12-10
Paquete afectadoLibrería y ecosistemalog4j-core (Maven)
Versión instaladaVersión actual en el proyecto2.14.0
Versión corregidaPrimera versión con el parche2.17.1
Vector de ataqueCómo se explota la vulnerabilidadRCE mediante mensajes de log controlados por el atacante
Disponibilidad de exploit¿Hay un exploit público conocido?PoC pública en menos de 24 h desde la publicación

3. Evaluación de Impacto

PreguntaRespuesta
¿La función vulnerable es alcanzable?Sí, a través del logging de peticiones
¿La dependencia está expuesta a internet?Sí, servicio de borde
¿La dependencia procesa entrada no confiable?Sí, contenido provisto por usuarios
¿Hay un control compensatorio activo?El WAF bloquea patrones de exploit conocidos
Impacto estimado en el negocioInterrupción del flujo de pagos

4. Plan de Remediación

PasoResponsableFecha límiteEstado
Actualizar la dependencia a 2.17.1Equipo de backend2026-07-01Sin empezar
Correr pruebas de regresión en stagingEquipo de QA2026-07-02Sin empezar
Desplegar a producciónDevOps2026-07-03Sin empezar
Verificar la corrección con un re-escaneoEquipo de seguridad2026-07-05Sin empezar

5. Aceptación del Riesgo

CampoValor
¿Se puede aceptar el riesgo?No
JustificaciónRCE expuesto a internet con exploit público
Responsable del riesgoGerente de ingeniería
Fecha de aprobaciónN/A
Fecha de revisiónN/A

Ejemplo Completo

Las tablas siguientes muestran un reporte terminado para un escenario realista: un escaneo SCA encuentra CVE-2021-44228 (Log4Shell) en log4j-core 2.14.0 dentro de un servicio de pagos. Fíjate en que cada fila responde una pregunta que haría quien decide: el reporte se lee como un argumento para actuar ya, no como salida de escáner.

1. Resumen del Hallazgo

CampoValor
ID del reporteVULN-2026-0042
Fecha de descubrimiento2026-06-27
Reportado porEscaneo semanal de Snyk, confirmado por el equipo de seguridad
Proyectopayment-service (repo: org/payment-service)
EntornoProducción (expuesto a internet), staging

2. Detalles de la Vulnerabilidad

CampoValor
ID del CVECVE-2021-44228
SeveridadCrítica — CVSS 10.0 (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H)
Puntuación EPSS~0.97, percentil superior
¿Está en CISA KEV?Sí — explotación activa confirmada
Paquete afectadoorg.apache.logging.log4j:log4j-core (Maven)
Versión instalada2.14.0 (dependencia directa)
Versión corregida2.17.1
Vector de ataqueLookup JNDI disparado por cadenas controladas por el atacante en mensajes de log: ejecución remota de código sin autenticación
Disponibilidad de exploitCódigo de exploit público y ampliamente disponible; escaneo masivo observado

3. Evaluación de Impacto

PreguntaRespuesta
¿La función vulnerable es alcanzable?Sí — cada petición entrante se registra con cabeceras y campos del cuerpo
¿La dependencia está expuesta a internet?Sí — payment-service está detrás del API gateway público
¿La dependencia procesa entrada no confiable?Sí — User-Agent, IDs de petición y campos de formulario llegan a los logs
¿Hay un control compensatorio activo?Parcial — el WAF tiene reglas para patrones ${jndi: comunes, pero hay bypasses por ofuscación documentados
Impacto estimado en el negocioEjecución remota de código en la ruta de pagos; exposición de datos de tarjetas y brecha del alcance PCI

4. Plan de Remediación

PasoResponsableFecha límiteEstado
Actualizar log4j-core a 2.17.1 en pom.xmlEquipo de backend2026-06-28En curso
Agregar reglas de emergencia anti-JNDI al WAFEquipo de seguridad2026-06-27Hecho
Probar regresión del logging en stagingEquipo de QA2026-06-29Sin empezar
Desplegar a producción con excepción al change freezeDevOps2026-06-30Sin empezar
Re-escanear y verificar el árbol de dependenciasEquipo de seguridad2026-07-01Sin empezar

5. Aceptación del Riesgo

CampoValor
¿Se puede aceptar el riesgo?No
JustificaciónRCE listado en KEV sobre un servicio PCI expuesto a internet; no hay estado intermedio aceptable
Responsable del riesgoVP de Ingeniería
Fecha de aprobaciónN/A
Fecha de revisiónN/A — escalar a diario hasta cerrar

El ejemplo rellenado explicita lo implícito: la severidad por sí sola marcaría cientos de hallazgos, pero “en KEV + expuesto a internet + alcanzable vía logging de peticiones + sin autenticación” reduce la respuesta a “corregir hoy, con excepción al change freeze”.

Cómo Funciona

Las cinco secciones responden cinco preguntas distintas, y cada una justifica su lugar.

Resumen del Hallazgo ubica al reporte: quién lo encontró, dónde y cuándo. Cuando el mismo CVE reaparezca en otro servicio seis meses después, este bloque es lo que hace localizable el reporte anterior.

Detalles de la Vulnerabilidad es la parte objetiva. Todo aquí debería poder verificarse contra la salida del escáner, la NVD o el repositorio del paquete: severidad, EPSS, estado en KEV, versiones, disponibilidad de exploit. Si un campo no se puede verificar, dilo en vez de adivinar.

Evaluación de Impacto es donde el reporte gana su costo. Un CVSS 9.8 en una herramienta de build que nunca corre fuera de CI es un problema distinto de un CVSS 7.5 en un servicio de borde. Alcanzabilidad, exposición, entrada no confiable y controles compensatorios convierten una puntuación genérica en tu riesgo.

Plan de Remediación asigna el trabajo. Cada paso lleva responsable y fecha, y el plan termina con una verificación: un reporte que omite el re-escaneo se cierra a base de esperanza.

Aceptación del Riesgo registra la decisión de no parchear, cuando esa es la decisión correcta. Si la respuesta es “no”, la justificación explica por qué el riesgo no puede esperar; si es “sí”, un responsable con nombre y una fecha de revisión evitan que la aceptación se convierta en amnesia permanente.

flowchart diagram: Escaneo SCA encuentra CVE

Priorización de Hallazgos

CVSS mide qué tan grave es una vulnerabilidad en general; no dice qué tan grave es para ti. Dos señales más cubren la mayor parte de esa brecha:

  • EPSS (Exploit Prediction Scoring System, mantenido por FIRST.org) estima la probabilidad de que un CVE sea explotado en la práctica en los próximos 30 días. Un CVSS 9.8 con EPSS 0.01 es una conversación distinta de un CVSS 7.5 con EPSS 0.95.
  • CISA KEV (catálogo de Vulnerabilidades Conocidas Explotadas) lista CVEs con explotación confirmada en el mundo real. Los hallazgos listados en KEV sobre código alcanzable se saltan la cola por completo.
  • El análisis de alcanzabilidad en herramientas como Snyk o Endor Labs verifica si tu código realmente llama a la función vulnerable. Los hallazgos no alcanzables suelen poder documentarse y programarse en vez de atenderse de urgencia.

Combinarlas produce un esquema de prioridad simple y defendible. Los SLA de abajo son puntos de partida habituales: ajústalos a tu propia política de gestión de vulnerabilidades:

PrioridadSeñalesSLA de ejemplo
P0 — EmergenciaListada en KEV, o exploit público + expuesta a internet + alcanzableCorregir en 72 horas
P1 — UrgenteEPSS alto (>0.5) o ruta de explotación alcanzableCorregir en 7 días
P2 — EstándarCVSS alto/crítico, sin exploit conocidoCorregir en 30 días
P3 — ProgramadaSeveridad media, alcanzableCorregir en 90 días
P4 — BacklogNo alcanzable, solo dev, o controles compensatorios fuertesPróximo ciclo de mantenimiento

Registra la prioridad y su justificación en el reporte. Seis meses después, “¿por qué este CVE se corrigió en 48 horas mientras aquel esperó un trimestre?” debería poder responderse solo con el documento.

Variantes

  • Reporte ejecutivo: una página con conteos por severidad, tendencia temporal y los principales riesgos abiertos — para liderazgo que necesita postura, no listas de CVE.
  • Reporte de fallo en CI/CD: captura qué hallazgo bloqueó el pipeline, por qué saltó la gate y qué lo desbloquea. Vive junto al build fallido.
  • Reporte para mantenedor open source: reformateado para divulgación upstream (disparador reproducible, versiones afectadas, corrección sugerida) siguiendo las normas de divulgación coordinada.
  • Reporte de tendencia histórica: agrega hallazgos por trimestre para mostrar si la postura mejora y dónde reaparecen las mismas clases de errores.

Lo Que Funciona

  • Un reporte por hallazgo (o por par CVE/proyecto): los reportes fusionados pierden responsable, fecha límite y rastro de auditoría.
  • Documenta siempre la explotabilidad en tu contexto, no solo la puntuación CVSS cruda.
  • Asigna responsable y fecha límite antes de que el reporte salga del triage; un hallazgo sin dueño es un hallazgo sin corregir.
  • Enlaza el reporte con la salida del escaneo, la entrada del SBOM y el pull request de remediación: trazabilidad completa en ambas direcciones.
  • Re-escanea tras la remediación y registra el paso de verificación; cierra con evidencia, no con el merge.
  • Revisa los riesgos aceptados con cadencia fija (trimestral funciona): las condiciones cambian, y la decisión debería cambiar con ellas.
  • Usa el análisis de alcanzabilidad para recortar falsos positivos, y conserva la justificación cuando despriorizas un hallazgo. Los auditores aceptan “no alcanzable, documentado”; no aceptan “ignorado”.
  • Comenta en los PRs que introducen dependencias vulnerables: detectar una dependencia mala en revisión cuesta menos que cualquier reporte:
# .github/dependabot.yml
version: 2
updates:
  - package-ecosystem: "npm"
    directory: "/"
    schedule:
      interval: "weekly"
    allow:
      - dependency-type: "all"
    labels:
      - "security"
      - "dependencies"
    open-pull-requests-limit: 10

Errores Comunes

  • Remediar solo por CVSS. La puntuación es un insumo; la alcanzabilidad, EPSS, KEV y la exposición suelen pesar más.
  • Olvidar probar la regresión de la dependencia actualizada bajo carga real: los cambios de versión rompen comportamiento, no solo APIs.
  • Cerrar el reporte antes de que un re-escaneo confirme que la corrección llegó al artefacto construido.
  • Reportar solo dependencias directas. La mayoría de los hallazgos SCA viven en paquetes transitivos; el Runbook de Actualización de Dependencias cubre rutas seguras de actualización para esos casos.
  • Aceptar riesgo sin justificación escrita, responsable y fecha de revisión. La aceptación no documentada es indistinguible de la negligencia.
  • Ignorar las dependencias de desarrollo. Corren en CI —con tus secretos y claves de firma— así que escanéalas también:
# npm audit cubre todo el árbol, incluidas las devDependencies
npm audit
npm audit --omit=dev   # vista solo de producción
snyk test --dev        # incluye dependencias de desarrollo en Snyk
  • Pasar por alto los paquetes de sistema operativo en las imágenes de contenedor. Un escaneo de package-lock.json no dice nada sobre OpenSSL en tu imagen base: escanéala por separado:
trivy image --severity HIGH,CRITICAL myapp:latest
grype myapp:latest --fail-on high

Automatización del Reporte

Escaneo SCA automatizado en GitHub Actions

Este workflow escanea en cada PR que toca dependencias y con una programación semanal, y luego emite una tabla Markdown directa a un archivo de reporte. Fíjate en el continue-on-error: Snyk devuelve código de salida distinto de cero cuando encuentra vulnerabilidades, y sin él el paso del reporte nunca correría.

name: Dependency Security Scan
on:
  pull_request:
    paths:
      - "package.json"
      - "package-lock.json"
      - "requirements.txt"
      - "pom.xml"
  schedule:
    - cron: "0 6 * * 1"  # Escaneo semanal los lunes

jobs:
  sca-scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Run Snyk scan
        uses: snyk/actions/node@master  # fijar a un commit SHA en producción
        continue-on-error: true
        env:
          SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
        with:
          args: --severity-threshold=high --json-file-output=snyk-results.json --dev

      - name: Run Trivy filesystem scan
        run: |
          trivy fs --format json --output trivy-results.json .
          trivy fs --format table --exit-code 0 --severity HIGH,CRITICAL .

      - name: Generate vulnerability report
        run: |
          node -e "
            const fs = require('fs');
            const snyk = fs.existsSync('snyk-results.json')
              ? JSON.parse(fs.readFileSync('snyk-results.json'))
              : { vulnerabilities: [] };
            const trivy = fs.existsSync('trivy-results.json')
              ? JSON.parse(fs.readFileSync('trivy-results.json'))
              : { Results: [] };
            const findings = [
              ...snyk.vulnerabilities.map(v => ({
                source: 'snyk', cve: v.identifiers?.CVE?.[0] || 'N/A',
                package: v.name, severity: v.severity,
                fixedVersion: v.fixedIn?.[0] || 'N/A'
              })),
              ...(trivy.Results || []).flatMap(r => r.Vulnerabilities || []).map(v => ({
                source: 'trivy', cve: v.VulnerabilityID,
                package: v.PkgName, severity: v.Severity,
                fixedVersion: v.FixedVersion || 'N/A'
              }))
            ];
            console.log('| Source | CVE | Package | Severity | Fixed Version |');
            console.log('|--------|-----|---------|----------|---------------|');
            findings.forEach(f => {
              console.log('| ' + f.source + ' | ' + f.cve + ' | ' + f.package + ' | ' + f.severity + ' | ' + f.fixedVersion + ' |');
            });
          " > vulnerability-report.md

      - name: Upload report as artifact
        uses: actions/upload-artifact@v4
        with:
          name: vulnerability-report
          path: vulnerability-report.md

Dos detalles que vale la pena conservar: --json-file-output escribe resultados legibles por máquina en vez de capturar stdout (Snyk imprime texto de progreso ahí), y fixedIn es el campo que realmente trae la versión corregida en el JSON de Snyk; fixInfo no existe en el esquema de salida.

Generación de SBOM con CycloneDX

Genera un Software Bill of Materials para rastrear todas las dependencias, incluidas las transitivas:

#!/bin/bash
set -euo pipefail

# SBOM para un proyecto Node.js (requiere lockfile de npm)
npx @cyclonedx/cyclonedx-npm --output-format JSON --output-file sbom.json

# SBOM para un proyecto Python (cyclonedx-bom 4.x)
pip install cyclonedx-bom
cyclonedx-py requirements requirements.txt --of JSON -o sbom-python.json

# Enriquecer el SBOM con datos de vulnerabilidades vía Trivy
trivy sbom --format json --output sbom-enriched.json sbom.json

# Extraer los paquetes vulnerables del SBOM enriquecido
jq '.Results[] | select(.Class=="sbom") | .Vulnerabilities[] | {
  cve: .VulnerabilityID,
  package: .PkgName,
  severity: .Severity,
  fixed_version: .FixedVersion
}' sbom-enriched.json

El SBOM enriquecido responde la pregunta que un escaneo de un solo repo no puede: ¿en cuáles de mis servicios se distribuye este paquete? Para un ejemplo completo de escáner, mira Python: Escaneo de Vulnerabilidades con pip-audit.

Encontrar la cadena transitiva

npm audit te dice qué paquete es vulnerable pero no cómo llegó. npm ls <paquete> imprime la ruta de dependencias: ejecútalo por cada paquete vulnerable para ver qué dependencia directa lo arrastra:

#!/bin/bash
set -euo pipefail

npm audit --json > audit-output.json 2>/dev/null || true

echo "=== Paquetes vulnerables y sus rutas de dependencia ==="
jq -r '.vulnerabilities | keys[]' audit-output.json | while read -r pkg; do
  echo "── $pkg"
  npm ls "$pkg" 2>/dev/null | head -15 || echo "   no resuelto en el árbol"
done

Cuando el paquete vulnerable es transitivo y la dependencia directa no ha publicado corrección, overrides (npm ≥8.3) o resolutions (Yarn) fuerza una versión segura. Fija el override, documéntalo en el reporte y programa un recordatorio para quitarlo cuando el paquete upstream se ponga al día.

Ver También

Preguntas frecuentes

¿Cómo elijo qué vulnerabilidades corregir primero?

Haz triage con cuatro señales: si la función vulnerable es alcanzable en tu código, si hay exploit conocido (CISA KEV, PoC pública, EPSS alto), si el servicio afectado mira a internet y cuál es la severidad CVSS. Una puntuación media en un servicio público suele pesar más que una crítica en un batch interno.

¿Qué pasa si no existe una versión corregida?

Documenta un control compensatorio —una regla de WAF, aislamiento de red, validación de entrada— con responsable y fecha de revisión. Si el riesgo residual sigue siendo inaceptable, reemplaza la dependencia o desactiva la función vulnerable. Nunca dejes el campo vacío: "sin parche todavía" también es una decisión documentada.

¿Quién debería recibir este reporte?

El equipo de ingeniería dueño de la corrección, seguridad para supervisión y seguimiento del SLA, product owners cuando la remediación desplaza trabajo del roadmap, y compliance cuando el reporte alimenta una auditoría o un cuestionario de cliente.

¿Cómo manejo una dependencia vulnerable que no puedo actualizar?

Para un paquete transitivo bloqueado por una dependencia upstream, fuerza una versión segura con overrides de npm, resolutions de Yarn o constraints de pip, y documenta el motivo. Para una dependencia directa fijada por un framework viejo, evalúa control compensatorio versus migración planificada, y pon ambas opciones con fechas en el plan de remediación.

¿Cuál es la diferencia entre SCA y SAST?

SCA escanea dependencias de terceros buscando vulnerabilidades conocidas (CVE). SAST analiza tu propio código fuente buscando fallos como inyección o deserialización insegura. Cubren capas distintas: necesitas ambas, porque SCA no encontrará el bug que escribiste y SAST no encontrará el CVE que importaste.

¿Cómo se relaciona este reporte con el SBOM?

El SBOM es el inventario: cada paquete, cada versión, cada servicio que lo distribuye. Este reporte es la decisión: un hallazgo, su contexto, su plan. Usa el SBOM para encontrar todos los lugares donde aterriza un CVE nuevo, y luego abre un reporte por proyecto afectado.