Una vulnerabilidad XSS dormida durante 4,5 años en los logs de CI
El investigador polaco Rafał Cieślak publicó el 24 de septiembre de 2026 un detallado writeup sobre una vulnerabilidad crítica en ansi2html, la librería de Python que convierte códigos de escape ANSI a HTML y que el servicio de CI/CD builds.sr.ht —parte de SourceHut, la plataforma de código abierto creada por Drew DeVault— utiliza para renderizar los logs de construcción.
El fallo, clasificado por Cieślak como un XSS (Cross-Site Scripting) con potencial de toma completa de cuenta (account takeover), estuvo presente en producción durante casi 4,5 años sin ser detectado: fue introducido en upstream el 3 de septiembre de 2021, llegó a la instancia principal de sr.ht el 8 de febrero de 2022 y solo fue mitigado el 4 de agosto de 2026.
¿Qué hace exactamente ansi2html?
ansi2html convierte secuencias de escape ANSI — esos códigos que los terminales interpretan para mostrar colores, hipervínculos y formato — en HTML equivalente. Es una pieza común en paneles de CI: el log crudo de un build contiene \033[31m (rojo), \033]8;;URL\007 (hipervínculo OSC 8) y similares; la librería los traduce a <span> y <a> para que el navegador los pinte.
👥 ¿Quieres ir más allá de la noticia?
En nuestra comunidad discutimos las tendencias, compartimos oportunidades y nos ayudamos entre emprendedores. Sin humo, solo acción.
👥 Unirme a la comunidadEl problema es que la implementación, escrita sin un transducer con estados formales, parseaba de forma laxa los atributos HTML de los enlaces. Como demostró Cieślak, era posible romper la frontera del atributo href con un payload como:
\033]8;;https://example.com/"/autofocus/tabindex="1"/onfocus="alert`xss`\007Texto\033]8;;\007
El navegador lo interpretaba como un <a> legítimo con href, autofocus, tabindex=1 y un handler onfocus que ejecutaba JavaScript sin que el usuario hiciera clic en nada. La misma técnica funcionaba con un href="javascript:alert(...)«` directo.
Por qué importa para los founders hispanohablantes
Si tu equipo usa SourceHut —o cualquier CI basado en web que renderice logs con esta u otra librería de coloreado de ANSI— el incidente es relevante por tres motivos:
- Vector de ataque «gratis». No necesitas cuenta en la plataforma: basta con enviar un patch a una lista de correo pública con CI integrado, o conseguir que un recurso remoto controlado por ti se imprima en el log. Cieślak estima que la barrera de entrada es prácticamente nula (vector CVSS:
AT:N). - Wormable. El propio artículo señala el atributo
AU:Y(Automatable: Yes): un admin que abre un log malicioso puede, sin querer, ejecutar payloads que se reenvían a otros usuarios. Esto convierte un único punto de exposición en una cadena. - Acceso a deploy keys con alcance sobre toda la instancia. En el caso concreto de SourceHut, el autor indica que builds.sr.ht tiene acceso a las deploy keys de sr.ht misma, lo que multiplica el blast radius. Para una startup que aloja su CI en una instancia self-hosted, el equivalente sería: las claves SSH de despliegue de tus repos productivos quedan expuestas al primer build log malicioso que alguien con permisos visualize.
La cronología: por qué tardó tanto
La línea de tiempo incluida en el writeup explica el porqué:
- 2021-09-03 — se introduce el bug en upstream ansi2html.
- 2022-02-08 — una versión afectada se empaqueta para Alpine y entra en producción.
- 2022-07-10 — el paquete vulnerable llega a Arch Linux.
- 2026-07-31 — Cieślak empieza a trabajar con SourceHut.
- 2026-08-04 — Drew DeVault confirma la vulnerabilidad y parchea builds.sr.ht.
- 2026-09-02 — se publica ansi2html 1.9.4 en PyPI con el arreglo en upstream.
- 2026-09-05 — Arch Linux actualiza a la versión corregida.
- 2026-09-23 — publicación del writeup.
El autor calcula que builds.sr.ht permaneció vulnerable durante casi 4,5 años, incluso con auditorías periódicas, porque cada bump de versión de la librería requería revalidar la lógica de escape.
Qué hicieron bien (y qué se podría mejorar)
Cieślak destaca el proceso de divulgación responsable como ejemplo a seguir: contactó primero a la lista de seguridad interna ~sircmpwn/[email protected], Drew DeVault parcheó en tres días la parte crítica (auto-sanitización del HTML generado por ansi2html) y luego coordinaron con upstream —el repo pycontribs/ansi2html— para liberar 1.9.4.
El autor también critica tres áreas donde la defensa en profundidad habría acortado la exposición:
- Content-Security-Policy. El header CSP de la página de logs permitía
unsafe-inlinepara scripts, necesario por el scroll con JS. Quitar esa directiva habría limitado el impacto de cualquier XSS, aunque no es trivial sin refactorizar el cliente de scroll. - Sanitización por defecto. La solución de Drew (sanitizar agresivamente la salida de ansi2html) elimina los colores del log — un trade-off entre usabilidad y seguridad que Cieślak considera excesivo.
- Reescritura de ansi2html como autómata con estados formales. El parser actual es demasiado permisivo; una conversión estricta atributo-por-atributo habría evitado la clase entera de bugs.
Indicadores de compromiso
Para quienes auto-hospedan builds.sr.ht o pipelines similares, Cieślak recomienda buscar en los logs crudos las secuencias maliciosas:
grep $'\033]8;[^\007\033]*"' ruta/a/logs/build.log
Cualquier coincidencia con un " dentro de un enlace OSC 8 es un indicador de intento de explotación.
Qué significa esto para tu startup
Si tu equipo depende de CI/CD para construir y desplegar software, este caso —publicado en una plataforma tan querida del mundo open source como SourceHut— es un recordatorio útil:
- Audita las dependencias que tocan HTML en logs y reportes. Colorizadores de ANSI, renderers de Markdown, librerías de resaltado de sintaxis: cualquier conversor entrada→HTML es una superficie de XSS si la entrada no es confiable. La regla práctica es: si el log de un build puede contener texto controlado por un usuario externo (un autor de parche, un PR de un contributor, una URL remota), la salida debe pasar por un sanitizador HTML.
- Aplica Content-Security-Policy estricta en páginas de logs.
script-src 'self'en lugar deunsafe-inlinelimita drásticamente lo que un XSS puede hacer. Si necesitas scroll con JS, mueve el manejo a un módulo separado o usa anclas URL en lugar deunsafe-inline. - Dos acciones concretas esta semana:
- Si usas ansi2html en cualquier pipeline, fija la versión a
>=1.9.4. Si tu distro aún no tiene el paquete arreglado (en el momento del writeup Alpine no estaba confirmado), instala desde PyPI. - Pasa un grep por tus logs de CI buscando la secuencia OSC 8 malformada; cualquier coincidencia es evidencia de un intento previo de explotación.
Fuentes
👥 ¿Quieres ir más allá de la noticia?
En nuestra comunidad discutimos las tendencias, compartimos oportunidades y nos ayudamos entre emprendedores. Sin humo, solo acción.
👥 Unirme a la comunidad













