Introducción
En noviembre de 2024, el proyecto SpotBugs sufrió un compromiso que marcó el inicio de una ola de incidentes en la supply chain. Un pull request malicioso desde un fork sin revisar ejecutó un workflow con el trigger pull_request_target, un mecanismo diseñado para etiquetar PRs de colaboradores externos pero que, en la práctica, otorga acceso completo a secretos y tokens de escritura. Cuatro meses después, ese mismo token de mantenimiento se usó para comprometer tj-actions/changed-files, lo que derivó en la filtración de credenciales en 23.000 repositorios. La ironía: GitHub documentó este riesgo desde 2021, pero el trigger sigue activo sin salvaguardas más allá de un párrafo en la documentación.
El problema no es puntual. En abril de 2026, elementary-data publicó un wheel malicioso en PyPI solo 10 minutos después de que un comentario en un PR disparara un workflow con issue_comment y $ sin sanitizar. Los equipos de DevOps revisan logs, escanean dependencias y parchean CVEs, pero rara vez miran bajo el capó de GitHub Actions: un sistema donde conveniencia y riesgo avanzan de la mano, y cuyos defaults —pensados para CI privada en empresas— hoy corren código no auditado en forks anónimos y PRs de desconocidos.
Qué ocurrió
Los incidentes recientes comparten un patrón: un feature de GitHub Actions funcionó exactamente como está documentado, pero el diseño permite escalar riesgos globales. Estos son los casos más relevantes, ordenados cronológicamente:
1. SpotBugs (noviembre 2024): El pull request que robó un PAT
- Trigger:
pull_request_target(diseñado para etiquetar PRs de forks). - Vector: Un PR malicioso desde un fork ejecutó un workflow que:
checkout del commit del PR con un token de escritura (GITHUB_TOKEN con scope repo).– Ejecutó un script que llamó a gh auth login con las credenciales del maintainer.
- Resultado: El PAT robado tenía acceso a reviewdog, y ese mismo actor usó el token meses después para comprometer tj-actions.
> Nota: GitHub advirtió sobre esta combinación desde 2021, pero pull_request_target sigue sin restricciones por defecto.
2. Ultralytics (diciembre 2024): Cache poisoning en PyPI
- Trigger:
pull_request_target+ caché compartida. - Vector:
– Cuando el workflow legítimo de liberación restauró la caché, ejecutó un payload que inyectó un minero de criptomonedas en dos versiones publicadas en PyPI.
- Detalle técnico: La caché se comparte entre ramas y forks, por lo que un ataque en un PR de fork afecta al repositorio principal.
3. tj-actions (marzo 2025): Tags mutables y supply chain masiva
- Trigger:
pull_request_target+ tags force-pushados. - Vector:
v1 de reviewdog/action-setup a un commit malicioso.– tj-actions/eslint-changed-files usaba reviewdog@v1 (tag mutable), por lo que heredó el código malicioso.
– 23.000 repositorios que dependían de changed-files@v1 ejecutaron un memory scraper que volcó secretos del runner en logs públicos.
- Impacto: Coinbase reportó fugas de credenciales, y CISA emitió una advertencia.
4. nx (agosto 2025): Titulares en PRs como código
- Trigger:
pull_request_target+ interpolación de$en scripts. - Vector:
$(cat /etc/passwd) se expandió en un step de shell, ejecutando código arbitrario con un token de publicación de npm.– El atacante usó credenciales de asistentes de IA para enumerar repositorios privados y filtrar 5.000 repos temporalmente públicos.
- Detalle técnico: GitHub Actions expande
$en scripts antes de pasarlos al shell, sin sanitización.
5. Trivy (febrero-marzo 2026): Dos compromisos en tres semanas
- Trigger:
pull_request_target+ tags force-pushados. - Vector:
– En marzo, usó los tokens robados para force-push 76 de 77 tags históricos (ej. @0.x.y), por lo que incluso usuarios con pines fijos ejecutaron un credential stealer.
- Impacto: Los tags force-pushados hacían que usuarios con
uses: aquasecurity/[email protected]ejecutaran código malicioso.
6. elementary-data (abril 2026): Comentarios en PRs como vectores de ataque
- Trigger:
issue_comment+$sin sanitizar. - Vector:
echo y ejecutó un stager que:– Obtuvo un GITHUB_TOKEN con scope write (default en repositorios creados antes de febrero 2023).
– Hizo git commit con autor github-actions[bot].
– Disparó el workflow de liberación, publicando un wheel malicioso en PyPI y una imagen en GHCR en 10 minutos.
- Detalle clave: No hubo PR aceptado ni intervención humana.
Impacto para DevOps / Infraestructura / Cloud / Seguridad
| Área | Riesgo concreto | Datos cuantificables |
|---|---|---|
| **Supply Chain** | Paquetes maliciosos en ecosistemas como PyPI, npm, Docker Hub. | 2 versiones de Ultralytics en PyPI (minero). 23K repos afectados por tj-actions. |
| **Secrets Leak** | Filtración de tokens de mantenimiento, credenciales de cloud, claves API. | 5K repositorios privados expuestos en nx. |
| **Ejecución remota** | Código arbitrario en runners de GitHub Actions (ej. *memory scrapers*). | Trivy force-pushó 76 tags históricos. |
| **Cache Poisoning** | Caches compartidas entre forks y repositorios, con payloads persistentes. | Ultralytics: caché restaurada en liberación. |
| **Token Scope** |
