Dependabot te dice que es vulnerable… ¿pero es explotable de verdad?

¡Hola developer 👋🏻!

Cuando Dependabot te abre una pull request de seguridad, lo primero que sabes es esto: una versión concreta de una dependencia está dentro de un rango vulnerable.

Perfecto 👍
Pero… eso no responde a la pregunta importante: ¿esto es realmente explotable en mi código?

Porque una cosa es que exista una advisory…
y otra muy distinta es que tu aplicación esté usando justo ese patrón vulnerable y además se cumplan las condiciones para explotarlo.

Con esa idea en mente he jugado con una prueba de concepto en la que he estado trabajando en el repositorio dependabot-security-analyzer. La idea en este caso es usar un GitHub Agentic Workflow para analizar una PR de Dependabot de forma más profunda: identificar la dependencia afectada, consultar la advisory completa, revisar el repositorio y decidir si el caso merece prioridad alta o si la exposición no está realmente demostrada. En este artículo te muestro cómo lo estoy comprobando gracias a estos nuevos flujos.

Si todavía no has visto qué son los GitHub Agentic Workflows, aquí tienes un vídeo donde te lo explico en 30 minutos:

El problema no es detectar vulnerabilidades (eso ya lo hacemos bien)

Herramientas como Dependabot hacen muy bien la parte de detectar versiones afectadas y proponer actualizaciones. Pero entre “la dependencia es vulnerable” y “mi aplicación es explotable por esa vulnerabilidad” hay bastante distancia. A veces tu código ni siquiera usa la parte afectada. Otras veces sí la usa, pero sin cumplir las condiciones reales del exploit. Y en otros casos sí se da toda la cadena y entonces esa PR merece atención inmediata.

La intención de esta PoC no es discutir si hay que actualizar o no. Las dependencias vulnerables conviene corregirlas igualmente. La cuestión aquí es otra: aportar más contexto para priorizar mejor, reducir ruido y distinguir entre presencia de una advisory y exposición real en el código.

Cómo funciona el workflow

El flujo está definido en lenguaje natural dentro del propio workflow y se activa cuando un usuario de confianza comenta /dependabot-analyze en una pull request. El comando tiene que ser el primer token del comentario. A partir de ahí, el agente solo sigue adelante si el evento está realmente ligado a una PR y si esa PR ha sido abierta por dependabot[bot].

Ese filtro es importante porque el objetivo no es analizar cualquier cambio, sino específicamente pull requests automáticas de seguridad. Si el comentario no está en una PR o la PR no es de Dependabot, el flujo se detiene y deja una explicación breve.

El frontmatter del workflow deja bastante claro cómo está configurado este ejemplo. Aquí se define el trigger, los permisos, las herramientas disponibles, la red permitida y también el engine y el modelo que quiero usar:

---
name: 🛡️ VulnScope PR Guard 🔎
on:
  slash_command:
    name: dependabot-analyze
    events: [pull_request_comment]
  reaction: eyes
permissions:
  contents: read
  pull-requests: read
  security-events: read
tools:
  github:
    toolsets: [context, repos, pull_requests, security_advisories]
network:
  allowed:
    - defaults
engine:
  id: copilot
  model: gpt-4.1
safe-outputs:
  add-labels:
    allowed: ["🔴 vulnerability:in-use", "🟢 vulnerability:not-in-use", "🤔 vulnerability:unclear", "🚨 priority:high", "🕓 priority:low"]
    max: 2
  add-comment:
    max: 1
---

En este ejemplo he usado engine: copilot y el modelo gpt-4.1 por una razón bastante práctica: no consume premium requests y quería comprobar si era capaz de resolver bien un caso como este. La parte interesante es que esto no está cerrado. El workflow se puede adaptar para usar otros modelos e incluso otros agentes de IA si hiciera falta, dependiendo del nivel de razonamiento, del coste o del tipo de repositorio que quieras analizar.

También es importante tener en cuenta que este tipo de flujos no siempre se comportan igual con todos los modelos. A veces el prompt necesita pequeños ajustes en cómo formula las instrucciones, cómo restringe la salida o cómo pide justificar el veredicto. Yo lo voy iterando y afinando con el tiempo, y no me extrañaría que la versión óptima del prompt para un modelo no fuese exactamente la mejor para otro.

También es posible que en lugar de un slash command se lanzara siempre que ocurre una PR ejecutada por Dependabot, pero en este caso quería ser yo quien lo controlara. También por supuesto se puede «complicar más» y que cada vez que se haga push del código si todavía hay alertas de Dependabot pendientes que se ordene la re-ejecución de estos flujos para ver si en una siguiente iteración no solo no se ha arreglado la versión de la dependencia sino que encima ahora se está haciendo uso de la funcionalidad que la hace vulnerable 😅

Una vez validado el contexto, el workflow toma la propia pull request como fuente inicial para identificar qué paquete se actualiza, desde qué versión y hacia cuál. Si en el PR aparecen identificadores como un CVE o un GHSA, los usa como punto de partida; si no aparecen, intenta inferir la advisory a partir del paquete y del rango de versiones.

La parte clave: ir a la advisory completa

Aquí está una de las diferencias importantes respecto a una automatización más superficial. El flujo no se conforma con el resumen del PR de Dependabot. Va a buscar los detalles completos en GitHub Security Advisories para entender qué patrón concreto es vulnerable, qué versiones están afectadas, qué CWE aplica y, sobre todo, qué condiciones hacen falta para que el problema sea explotable.

Eso cambia bastante el tipo de análisis que se puede hacer. No es lo mismo detectar que una librería está instalada que saber que la advisory solo afecta a una función concreta, a cierto modo de invocación o a un caso donde el input del usuario llega sin validación a un punto determinado. El workflow intenta aterrizar precisamente eso.

Si la advisory pública no da suficiente detalle técnico, el planteamiento del flujo contempla ampliar contexto con referencias adicionales y fuentes públicas más detalladas. La idea es que el veredicto no salga de una lectura superficial del PR, sino de entender bien el patrón vulnerable que describe la advisory.

El prompt también forma parte del diseño

Otro punto que me parece muy interesante es que el comportamiento del workflow no está definido solo por el frontmatter. El prompt en sí también es parte central del diseño, porque es donde se obliga al agente a seguir un orden concreto, a no confundir riesgos adyacentes con la advisory analizada y a justificar por qué marca un caso como realmente explotable o no.

Este es un fragmento representativo del prompt actual:

## Trigger
This workflow runs when a trusted user comments `/dependabot-analyze` on a pull request.
The command must be the first token in the comment.
If the command was not used on a pull request, stop immediately and do nothing.
You must then check if the target PR was created by Dependabot (author is `dependabot[bot]`).
If it was NOT created by Dependabot, stop here and leave a short comment explaining that this command only supports Dependabot PRs.
## Your tasks
### 3. Get complete vulnerability details from GitHub Security Advisories
- Extract from the advisory:
  - Vulnerable version range
  - Vulnerable code patterns
  - CWE classification
  - Exploitation conditions
  - References
### 4. Analyze the codebase
- Distinguish clearly between these three questions:
  - Is the vulnerable method/pattern present?
  - Are the advisory's exploitation preconditions actually satisfied in this codebase?
  - Is there a different security issue nearby that is real but outside the scope of this advisory?
### 6. Add labels to the PR
- `🔴 vulnerability:in-use` + `🚨 priority:high`
- `🟢 vulnerability:not-in-use` + `🕓 priority:low`
- `🤔 vulnerability:unclear` when the vulnerable pattern cannot be determined

Me gusta enseñar esta parte porque aquí se ve muy bien que el valor no está solo en “usar IA”, sino en cómo se define el criterio. El prompt no le pide al agente que mire si una librería sale en un fichero, sino que determine si el patrón vulnerable existe de verdad, si las precondiciones del exploit están demostradas y si hay que separar ese análisis de otros problemas de seguridad que puedan aparecer cerca.

Y aquí vuelve a entrar el tema del modelo. Este prompt seguramente seguirá cambiando. Yo lo voy mejorando a medida que pruebo más escenarios y, muy probablemente, algunas decisiones de redacción, granularidad o formato de salida tendrán que ser un poco distintas según el modelo o el agente que quiera usar detrás.

Pero lo que si que es realmente importante aquí es: esta ayuda adicional es más que cero.

Qué devuelve en la PR de Dependabot

El resultado del análisis se deja directamente en la propia PR. El workflow comprueba primero si existen las labels que necesita y, si no están, las crea. Luego aplica una combinación de etiquetas según el veredicto:

  • 🔴 vulnerability:in-use y 🚨 priority:high si el patrón vulnerable está en uso y las precondiciones del exploit están demostradas o muy respaldadas por el código.
  • 🟢 vulnerability:not-in-use y 🕓 priority:low si el patrón vulnerable no aparece, el uso es seguro o la explotabilidad no está demostrada.
  • 🤔 vulnerability:unclear si después de consultar advisories y fuentes públicas no hay suficiente detalle técnico para identificar el patrón vulnerable con confianza.

Algo como esto:

Además deja un comentario estructurado con el resumen de la advisory, severidad, CWE, patrón vulnerable, condiciones de explotación, referencias y una tabla por archivos donde distingue entre uso del patrón, precondiciones cumplidas, riesgos adyacentes y veredicto final.

Ese detalle me parece importante porque convierte la PR en algo bastante más accionable. No es solo una actualización propuesta por Dependabot, sino una revisión con criterio explícito sobre si el riesgo está realmente activado en el código del repositorio.

Lo que me parece interesante de esta PoC

Lo que más me interesa aquí no es solo automatizar pasos, sino introducir mejor criterio de priorización en un problema muy ruidoso. En muchos repositorios acaban acumulándose alertas, PRs automáticas y decisiones rápidas basadas únicamente en la existencia de una advisory. Tener un flujo que intente responder si el patrón vulnerable está de verdad en uso me parece bastante más útil.

También me gusta porque obliga al análisis a no mezclar hallazgos distintos. Puede haber una advisory que no aplique realmente a tu código y, al mismo tiempo, puede existir otro problema de seguridad cerca de ese mismo flujo. El workflow intenta dejar claro cuándo está hablando de la advisory concreta de Dependabot y cuándo se trata de un riesgo adyacente distinto.

Una forma más útil de revisar PRs de seguridad

En definitiva, esta prueba de concepto busca responder una pregunta que Dependabot, y otras herramientas, no resuelven a día de hoy del todo: no solo si una dependencia está afectada por una advisory, sino si tu repositorio está usando realmente el patrón que convierte esa advisory en un problema práctico.

Para mí ese es el punto interesante de combinar GitHub Agentic Workflows con análisis de seguridad de dependencias: usar la automatización no solo para abrir PRs, sino para devolver contexto que ayude a decidir mejor qué corregir primero y por qué.

Si quieres explorar otros escenarios en los que GitHub Agentic Workflows pueden tener sentido aquí te dejo otro vídeo donde te muestro dónde lo estoy usando yo:

¡Nos vemos 👋🏻!

Deja un comentario

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.