Write down what the sanitiser was accused of, and cleared of

#142 suspected that parsing a message's HTML fetched the things it named -
remote stylesheets and fonts, seen as blocked requests while switching
conversations. It does not. A body carrying a stylesheet link, an @font-face,
an https image, a CSS url() and a script src was parsed through this exact
path with the network watched: nothing was requested, and no policy was
violated. The document really is inert.

So the comment saying so now says it was measured, and points at where the
real reach is: `safeImageSrc` passes any https image straight through to a
rendered <img>, which is what makes inline images and custom emoji work and
also what lets a sender learn their message was read. That is #145, and a
decision rather than a fix.

The blocked requests that started this remain unattributed. They were real -
captured twice - but they are not the sanitiser's, they are not in any stored
message, and a full reload plus a walk through every account produces none.

Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
2026-09-09 18:30:49 -04:00
co-authored by Claude Opus 5
parent 76c7c452c8
commit adc83b8a0b
+10
View File
@@ -121,6 +121,16 @@ interface Handlers {
export function RichText({ html, onRevealSpoiler, onOpenChannel, onOpenLink }: RichTextProps): JSX.Element {
// DOMParser builds an inert document: no scripts run, no images load, no
// network requests happen during parsing.
//
// Measured rather than assumed, because it was once doubted (#142): a body
// carrying `<link rel=stylesheet>`, `<style>@font-face`, `<img src=https:>`,
// a CSS `background: url()` and a `<script src>` was parsed here with the
// network watched, and not one of the five was fetched. The parse is safe;
// what a message can reach for, it reaches for when something *renders*.
//
// Which is a live surface rather than a theoretical one: `safeImageSrc`
// below passes any https image through, so a formatted body can name a URL
// its author controls and learn that the message was read - see #145.
const doc = new DOMParser().parseFromString(`<body>${html}</body>`, 'text/html')
return <>{walk(doc.body, { onRevealSpoiler, onOpenChannel, onOpenLink }, 0)}</>
}