Arusekk was setting up his own SourceHut instance when duplicated CSS in a build log’s page source annoyed him enough to go read the renderer. That led to ansi2html, the library builds.sr.ht uses to turn ANSI escape codes into HTML, and to ANSI hyperlink targets that were written into href attributes with no escaping. The result is a stored XSS in a page that anyone on the internet can read. It is CVE-2026-92973.

The mechanics:

  • ansi2html renders OSC 8 terminal hyperlinks, putting the link target straight into an href attribute
  • A crafted escape sequence closes the quote and injects new attributes: ESC]8;;https://example.com/"/autofocus/tabindex="1"/onfocus="alert`xss`"
  • A second variant accepts a javascript: URL as the link target
  • Anyone can get bytes into a public build log — mail a patch to a public mailing list with CI enabled, or control any remote resource that a build prints. No account required, paid or otherwise

Why the impact is not a popup:

  • The build log page carries the CSRF token and a form that resubmits builds, so the payload acts with the viewer’s session
  • An admin viewer means admin rights; at the far end sits every deploy key the instance holds, including sr.ht’s own on the flagship
  • CVSS 4.0 marks it automatable and wormable — one visitor reading one log can be made to spread it
  • Users can hide build jobs but not delete them, so there is no cleanup path for the payload

The timeline is the actual lesson:

  • Bug introduced upstream on 2021-09-03; the affected version was packaged for SourceHut on 2022-02-08 and for Arch Linux on 2022-07-10
  • builds.sr.ht was exposed for almost exactly four and a half years
  • Reported 2026-08-01, mitigated in builds.sr.ht on 2026-08-04 by sanitizing the library’s output, fixed upstream in ansi2html 1.9.4 on 2026-09-02
  • Vulnerable versions: ansi2html >= 1.7.0, < 1.9.4 and builds.sr.ht >= 0.40.0, < 0.105.1
  • Indicators of compromise: grep raw build logs for ESC]8;;https://example.com/"/… or ESC]8;;javascript:…

The writeup’s sharpest line is that auditing would not have saved SourceHut either, because the flaw lived in a dependency and the audit would have to be redone on every version bump. His proposed defenses follow from that: a Content-Security-Policy without 'unsafe-inline', sanitization at the boundary, and restructuring the converter as a state machine instead of ad-hoc string handling.

Two details worth keeping. The first upstream release after the report did not include the fix, and when the fix did land the sanitizing was aggressive enough that build logs lost their colors. The second is upstream maintenance reality: ansi2html had no activity for over a year before this work, and the repo was suspended at one point.

A converter that turns untrusted bytes into a DOM is part of your attack surface whether or not its maintainers think of it that way.