<!-- mobian-agent-page publisher="dailydev" canonical="https://daily.dev/posts/sourcehut-account-takeover-via-build-logs-xss-in-ansi2html-py--haj6tzlgi" -->

---
title: SourceHut account takeover via build logs (XSS in...
description: A wormable cross-site scripting vulnerability in the ansi2html.py library (used to render ANSI escape codes as HTML in build logs) allowed anyone able to...
canonical: https://daily.dev/posts/sourcehut-account-takeover-via-build-logs-xss-in-ansi2html-py--haj6tzlgi
twitter:card: summary_large_image
twitter:site: @dailydotdev
og:type: website
og:site_name: daily.dev
og:title: SourceHut account takeover via build logs (XSS in ansi2html.py) | daily.dev
og:description: A wormable cross-site scripting vulnerability in the ansi2html.py library (used to render ANSI escape codes as HTML in build logs) allowed anyone able to...
og:url: https://daily.dev/posts/sourcehut-account-takeover-via-build-logs-xss-in-ansi2html-py--haj6tzlgi
og:image: https://api.daily.dev/og/posts/HAJ6tzLGI.png
og:image:alt: SourceHut account takeover via build logs (XSS in ansi2html.py)
og:image:width: 1200
og:image:height: 630
og:locale: en
---

> ## Documentation Index
> Fetch the complete documentation index at: https://daily.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# SourceHut account takeover via build logs (XSS in ansi2html.py)

**[Lobsters](https://daily.dev/sources/lobsters)** · 13 min read · 0 upvotes · 0 comments

## Summary

A wormable cross-site scripting vulnerability in the ansi2html.py library (used to render ANSI escape codes as HTML in build logs) allowed anyone able to inject text into a builds.sr.ht job log to execute arbitrary JavaScript in the browser of anyone viewing that log, including admins. The root cause was improper sanitization of OSC 8 hyperlink escape sequences, letting an attacker craft an href with embedded HTML attributes like onfocus handlers. Because build logs can be populated without an account (e.g., via patches to public mailing lists with CI enabled), an attacker could get a malicious payload to execute in a victim's browser, steal the CSRF token, resubmit a forged build, and potentially gain access to sensitive deploy keys, including those for sr.ht itself on the flagship instance. The bug existed in ansi2html since 2021 and in builds.sr.ht since 2019 (four and a half years), affecting ansi2html versions >=1.7.0 and <1.9.4, and builds.sr.ht versions >=0.40.0 and <0.105.1. SourceHut mitigated the issue in builds.sr.ht in August, and ansi2html shipped a proper fix in version 1.9.4 in September, with the issue tracked as CVE-2026-92973.

## Full article

daily.dev links to this article rather than hosting it. Read it at the original source: <https://blog.arusekk.pl/posts/srht-account-takeover>

## Questions this post answers

### What is CVE-2026-92973 and how does it affect SourceHut's builds.sr.ht?

CVE-2026-92973 is a wormable cross-site scripting vulnerability caused by improper sanitization of OSC 8 hyperlink escape sequences in ansi2html, the library builds.sr.ht uses to render build logs as HTML. An attacker who can inject text into a build log, even without an account, can craft a malicious href containing attributes like onfocus with embedded JavaScript, which executes in the browser of anyone who views that log.

_Teams running CI pipelines that render ANSI logs to HTML can track fixes like this one on daily.dev._

### Which versions of ansi2html and builds.sr.ht are vulnerable to this XSS bug?

Vulnerable versions are ansi2html from 1.7.0 up to but not including 1.9.4, and builds.sr.ht from 0.40.0 up to but not including 0.105.1. The underlying bug was introduced into ansi2html upstream in September 2021 and reached the SourceHut flagship instance via an Alpine package in February 2022, remaining unpatched for roughly four and a half years until ansi2html 1.9.4 shipped the fix.

_Developers auditing dependency versions for known CVEs can keep tabs on fixes like this via daily.dev._

### How can I check if my build logs were exposed to the ansi2html OSC 8 XSS exploit?

Search raw build logs for the escape sequence pattern containing OSC 8 hyperlinks with a quote character embedded in the URL, such as an escape followed by ]8;;https://example.com/"/... or a javascript: URI, for example using a grep command like grep $'\33]8;[^\7\33]*"' against log files. Presence of such a pattern indicates a potential exploitation attempt.

_Anyone hardening CI log pipelines against injected escape sequences can find similar advisories on daily.dev._

## Community take

How the wider developer community reacted, aggregated from 2 discussions and 36 comments across hackernews, lobsters (as of 2026-09-25).

**TL;DR:** Commenters praise the technical write-up and the researcher's disclosure work, while debating fair compensation for the finding and digging into how OSC 8 hyperlink escape sequences keep causing terminal/XSS security issues.

**Sentiment:** 40% positive · 45% mixed · 15% skeptical

**The case for**

- The write-up and explanation of the vulnerability's mechanics is considered clear and well done.
- The disclosure approach (offering verbal credit, avoiding premature public exposure) is seen as responsible.
- One commenter appreciated the detailed breakdown of exactly which HTML attributes (onfocus, autofocus, tabindex) made the exploit work.

**The pushback**

- Some feel the researcher should have been compensated more concretely than verbal credit.
- OSC 8 hyperlink escape sequences are criticized as a recurring source of CVEs in terminal emulators and related tooling.
- Build logs are flagged as an inherently tricky attack surface since sanitizing arbitrary build output without breaking formatting is very hard.
- It's seen as alarming that the exploit could be triggered without an account, simply by sending a patch to a public mailing list with CI enabled.

**By community**

- hackernews (mixed): Discussion mixes praise for the researcher with debate over compensation, a tangent on OSC 8's history of causing security bugs, and a nod to the seriousness of the deploy-key exposure.
- lobsters (positive): Commenters focus on understanding the technical exploit details and compliment the write-up, with one questioning the wisdom of javascript: URL schemes still existing.

**Hottest debate:** Whether the researcher deserved more than verbal credit/recognition for finding such a serious vulnerability.

**Open questions**

- Could similar OSC 8-based exploits exist in other terminal emulators or ANSI-to-HTML converters?
- How can build log sanitization be made robust without breaking legitimate formatting?

**Highlights**

> The worse impact is that you have access to all the deploy keys, and on builds.sr.ht, there are deploy keys for sr.ht itself (probably not the case with other instances). This is the most unexpected bit. Circumstances made it so I got a new computer right around the time srht put out their anti-LLM policy. So I immediately started setting up a forgejo instance on my home network. With the latest waves of attacks on he AUR, I decided to keep that machine completely free from all AUR packages or helpers. So I also set up a package registry through the forgejo instance. With some work I have a workflow that takes a PKGBUILD and publishes an arch package that I can just `pacman - Syu` to install. Obviously I only put my own pkgbuilds and the few I have manually vetted there. Even after manual vetting I wanted to lock things down as much as possible on the CI. So. I went with rootless podman, and some patching of arch's build infra just to get it to be able to run `pkgctl build`. One of the main thing I looked out for was making sure no credentials were available anywhere during the jobs where the pkgbuilds (which can contain arbitrary commands) are being executed. I'm sure srht has to consider many more scenarios than I do. That will definitely make it harder to wall off credentials access. But it is still surprising that deploy keys are left laying around. BTW this is not an attack on srht. I stopped actively using it because of their new policy. I still have a paid subscription that I do not plan to cancel anytime soon. I still have my public projects where I don't use LLMs there. I think opinionated maintainers and projects (Linus/linux, Pottering/systemd and Drew come to mind) are a net good for open source. Opinionated people make better software because they care imo. And those opinions don't have to be restricted to purely technical matters. But anyway, it's a small team and there is a lot to cover, but they should get those leaking keys under control as much as possible.
> — [vhantz on hackernews · 2 comments](https://news.ycombinator.com/item?id=49840867)

> > So if you happen to be able to make ␛]8;;https://example.com/"/...␇ appear in the job logs —4 which you can, either without even having an account, by sending a patch to a public mailing list with continuous integration turned on The fact that this could be triggered so easily — just sending a malicious patch to a mailing list — is pretty insane.
> — [nsagent on hackernews](https://news.ycombinator.com/item?id=49840014)

> Nice article. I'm not that knowledgeable about HTML and JavaScript so it wasn't immediately obvious to me what the trigger was in the HTML especially since `<a>` elements require you to click on them to do something (or so I thought.) I looked up each attribute from the reproducer, in case it's helpful to others. * `onfocus` executes JavaScript when an element gains focus. * `autofocus` specifies an element should automatically gain focus (triggering the above). * `taxindex=1` causes the first TAB key press to focus on the element. I assume this is an additional measure in case autofocus does not pan out. https://www.w3schools.com/jsref/event_onfocus.asp https://www.w3schools.com/tags/att_autofocus.asp https://www.w3schools.com/TAGS/att_tabindex.asp
> — [connor on lobsters · 1 points, 1 comments](https://lobste.rs/s/ky1cr0/sourcehut_account_takeover_via_build#c_bfm1su)

> Oh my God, it's OSC 8 again. Because copy-pasting an URI from the terminal window is so 2003, and goodness gracious, having to look at an actual URL instead of an arbitrarily inaccurate description of it? That's, like, 1993. When I wrote my variant of ansi2html, I aggressively stripped out every C0 and C1, and all of the possible APC/DCS/OSC/PM sequences. Meanwhile, internal links between different parts of a man page still don't exist (unless you use GNU Info but seriously, I'd rather use lynx on a folder of HTML files instead).
> — [Joker\_vD on hackernews · 2 comments](https://news.ycombinator.com/item?id=49838050)

> A free-for-life "Professional hackers" package (https://sourcehut.org/pricing/) would be the minimum compensation in my eyes.
> — [spyc on hackernews](https://news.ycombinator.com/item?id=49842914)

**Source threads**

- [hackernews](https://news.ycombinator.com/item?id=49835996) · 48 points · 32 comments
- [lobsters](https://lobste.rs/s/ky1cr0/sourcehut_account_takeover_via_build) · 71 points · 4 comments

## Similar posts on daily.dev

- [HTB: CodeTwo](https://daily.dev/posts/htb-codetwo-klj991bhn) · 0xdf hacks stuff · 2 upvotes · 0 comments
- [GHSA-h7wh-q2qr-458c \(rails-html-sanitizer\): Possible XSS via shared scrubber state in concurrent rails-html-sanitizer calls](https://daily.dev/posts/ghsa-h7wh-q2qr-458c-rails-html-sanitizer-possible-xss-via-shared-scrubber-state-in-concurrent-rai-kp1cnlzqz) · RUBYLAND · 0 upvotes · 0 comments
- [XSS in yarr](https://daily.dev/posts/xss-in-yarr-ifx2swkk4) · Awesome Go · 0 upvotes · 0 comments
- [CVE-2026-49216: XSS in symfony/ux-autocomplete via unescaped AJAX response data \(Symfony Blog\)](https://daily.dev/posts/cve-2026-49216-xss-in-symfony-ux-autocomplete-via-unescaped-ajax-response-data-symfony-blog--be5s2bwyd) · Symfony · 0 upvotes · 0 comments

---

Tags: [#security](https://daily.dev/tags/security)

[View this post on daily.dev](https://daily.dev/posts/sourcehut-account-takeover-via-build-logs-xss-in-ansi2html-py--haj6tzlgi)

```json
{"@context":"https://schema.org","@graph":[{"@type":"Organization","@id":"https://daily.dev/#organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180},"sameAs":["https://twitter.com/dailydotdev","https://github.com/dailydotdev","https://www.linkedin.com/company/daily-dev-ltd"]},{"@type":"WebSite","@id":"https://daily.dev/#website","url":"https://daily.dev","name":"daily.dev","publisher":{"@id":"https://daily.dev/#organization"},"potentialAction":{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https://daily.dev/search?q={search_term_string}"},"query-input":"required name=search_term_string"}}]}
{"@context":"https://schema.org","@type":"TechArticle","headline":"SourceHut account takeover via build logs (XSS in ansi2html.py)","url":"https://daily.dev/posts/sourcehut-account-takeover-via-build-logs-xss-in-ansi2html-py--haj6tzlgi","mainEntityOfPage":{"@type":"WebPage","@id":"https://daily.dev/posts/sourcehut-account-takeover-via-build-logs-xss-in-ansi2html-py--haj6tzlgi"},"datePublished":"2026-09-24T23:04:39.349Z","dateModified":"2026-09-25T11:20:57.253Z","description":"A wormable cross-site scripting vulnerability in the ansi2html.py library (used to render ANSI escape codes as HTML in build logs) allowed anyone able to...","image":"https://media.daily.dev/image/upload/s--foaA6JGU--/f_auto/v1722860399/public/Placeholder%2004","thumbnailUrl":"https://media.daily.dev/image/upload/s--foaA6JGU--/f_auto/v1722860399/public/Placeholder%2004","isAccessibleForFree":true,"articleSection":"Lobsters","inLanguage":"en","publisher":{"@type":"Organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180}},"author":{"@type":"Organization","name":"Lobsters","logo":"https://media.daily.dev/image/upload/s--tl8v_Fku--/f_auto,t_logo/v1698841318/logos/lobste.jpg","url":"https://daily.dev/sources/lobsters"},"commentCount":0,"discussionUrl":"https://daily.dev/posts/sourcehut-account-takeover-via-build-logs-xss-in-ansi2html-py--haj6tzlgi","interactionStatistic":[{"@type":"InteractionCounter","interactionType":{"@type":"LikeAction"},"userInteractionCount":0},{"@type":"InteractionCounter","interactionType":{"@type":"CommentAction"},"userInteractionCount":0}],"keywords":"security","timeRequired":"PT13M"}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https://daily.dev"},{"@type":"ListItem","position":2,"name":"Lobsters","item":"https://daily.dev/sources/lobsters"},{"@type":"ListItem","position":3,"name":"SourceHut account takeover via build logs (XSS in ansi2html.py)"}]}
{"@context":"https://schema.org","@type":"FAQPage","@id":"https://daily.dev/posts/sourcehut-account-takeover-via-build-logs-xss-in-ansi2html-py--haj6tzlgi#faq","mainEntity":[{"@type":"Question","name":"What is CVE-2026-92973 and how does it affect SourceHut's builds.sr.ht?","acceptedAnswer":{"@type":"Answer","text":"CVE-2026-92973 is a wormable cross-site scripting vulnerability caused by improper sanitization of OSC 8 hyperlink escape sequences in ansi2html, the library builds.sr.ht uses to render build logs as HTML. An attacker who can inject text into a build log, even without an account, can craft a malicious href containing attributes like onfocus with embedded JavaScript, which executes in the browser of anyone who views that log. Teams running CI pipelines that render ANSI logs to HTML can track fixes like this one on daily.dev."}},{"@type":"Question","name":"Which versions of ansi2html and builds.sr.ht are vulnerable to this XSS bug?","acceptedAnswer":{"@type":"Answer","text":"Vulnerable versions are ansi2html from 1.7.0 up to but not including 1.9.4, and builds.sr.ht from 0.40.0 up to but not including 0.105.1. The underlying bug was introduced into ansi2html upstream in September 2021 and reached the SourceHut flagship instance via an Alpine package in February 2022, remaining unpatched for roughly four and a half years until ansi2html 1.9.4 shipped the fix. Developers auditing dependency versions for known CVEs can keep tabs on fixes like this via daily.dev."}},{"@type":"Question","name":"How can I check if my build logs were exposed to the ansi2html OSC 8 XSS exploit?","acceptedAnswer":{"@type":"Answer","text":"Search raw build logs for the escape sequence pattern containing OSC 8 hyperlinks with a quote character embedded in the URL, such as an escape followed by ]8;;https://example.com/\"/... or a javascript: URI, for example using a grep command like grep $'\\33]8;[^\\7\\33]*\"' against log files. Presence of such a pattern indicates a potential exploitation attempt. Anyone hardening CI log pipelines against injected escape sequences can find similar advisories on daily.dev."}}]}
```

