
Nadie audita la salida, todos auditan el código
El otro día apareció un writeup que me dejó pensando: un investigador que va por Arusekk encontró un XSS en SourceHut, la forja de software open source de Drew DeVault, que estuvo abierto cuatro años y medio. Ya está parcheado, tiene su CVE asignado (CVE-2026-92973) y la corrección salió el 2 de septiembre en ansi2html 1.9.4. Pero la forma del bug es una clase gratis de seguridad, y la quiero compartir.
El contexto: SourceHut tiene un servicio de CI, builds.sr.ht, que muestra los logs de cada build en el navegador. Para que esos logs se vean con colores, usa una librería de Python llamada ansi2html, que convierte los códigos de escape ANSI de la terminal a HTML. El investigador, mientras montaba su propia instancia, se fijó que el CSS de la página tenía clases duplicadas a lo loco y entró a revisar el código de esa conversión. Ahí encontró la joya: además de los colores, la librería interpreta los hipervínculos OSC 8 —esa secuencia que usan las terminales modernas para meter links dentro del texto— y no escapaba nada al generar el HTML.
El resultado era un <a> con atributos inyectados: autofocus, tabindex y un onfocus con el JavaScript que a uno se le ocurriera. Y acá viene lo importante: para que el payload apareciera en un log no necesitabas ni cuenta. Bastaba mandar un parche a una lista de correo pública con el CI activado, o controlar cualquier recurso remoto que el build imprimiera en el log, algo tan simple como que tu script hiciera curl a tu propio servidor. Cualquiera que abriera ese log ejecutaba el JavaScript en el dominio de SourceHut, con el token CSRF de la página a un querySelector de distancia y acceso a llaves de despliegue. Si la víctima era un admin, probablemente te convertías en admin.
Lo que me queda del cuento
Yo corro caleta de cron y agentes que escupen salida de herramientas a logs y dashboards, en mi infraestructura propia y en la de clientes. Y confieso que trato la salida de mis propios scripts como si fuera confiable. Este caso me recuerda que toda salida es entrada: un log no es texto inofensivo, es datos que un atacante puede controlar, y el momento en que lo renderizas en HTML se convierte en superficie de ataque.
Segunda lección: una conversión de formato es un parser, y los parsers son donde viven los bugs. ansi2html es una librería vieja y respetada, estaba en el famoso cómic del xkcd 2347 de las dependencias críticas, y aun así tenía un bug grave. Que la uses no te salva: el que renderiza es el responsable, y por eso SourceHut terminó saneando la salida en builds.sr.ht por su cuenta, porque auditar la librería upstream no alcanza si no lo repites en cada actualización.
Tercera: un CSP sin unsafe-inline habría cortado el peor escenario. La página del log ni siquiera puede prescindir del inline script porque lo usa para el scroll, o sea que el problema es de diseño, no de configuración. A la próxima vez que montes un panel que pinta salida de terminal, pregúntate qué pasa si el log no lo escribiste tú. En mi pega, donde trabajo con sistemas críticos, esa pregunta es la diferencia entre una anécdota y un parte.
El writeup completo, con la cadena de explotación y la línea de tiempo de la divulgación, es lectura obligada si administras CI o pipelines de agentes.
Fuente: SourceHut account takeover via build logs (XSS in ansi2html.py), CVE-2026-92973, del blog de Arusekk.
Fuente de inspiración: SourceHut account takeover via build logs (XSS in ansi2html.py) | CVE-2026-92973