Skip to content

fix(frontend): render raw HTML in package READMEs (sanitized)#602

Merged
Kamirus merged 4 commits into
mainfrom
fix/render-raw-html-in-readme
Jun 30, 2026
Merged

fix(frontend): render raw HTML in package READMEs (sanitized)#602
Kamirus merged 4 commits into
mainfrom
fix/render-raw-html-in-readme

Conversation

@ggreif

@ggreif ggreif commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Problem

Package README pages show raw HTML tags as literal visible text. For example, mops.one/googlemail-client@0.1.3 renders a <details>/<summary> collapsible block as the plain strings <details> and <summary> rather than a working disclosure widget. GitHub renders the same README correctly.

Root cause

frontend/logic/markdown-to-html.ts configures markdown-it with html: false, which escapes all raw HTML in README source. The inline comment even read // Enable HTML tags in source — the opposite of what false does. GitHub, by contrast, permits a sanitized allowlist of tags (including <details>/<summary>), which is why authors write them.

Fix

Enable html: true and sanitize the rendered output with DOMPurify — the standard, GitHub-equivalent approach:

  • Allows a safe tag allowlist (incl. <details>/<summary>, and keeps class so starry-night syntax highlighting is preserved).
  • Strips <script>, inline event handlers, and javascript: URLs — so enabling raw HTML does not open an XSS hole, which matters since READMEs are arbitrary third-party author content.

Two incidental cleanups in the same file:

  • Corrected the misleading comment to match behavior.
  • Removed a dead duplicate mdit.render(markdown) call whose result was discarded (the source was parsed twice per render).

Impact / retroactivity

README → HTML conversion happens client-side at view time (PackageReadme.svelte calls markdownToHtml(readme, …) on each render), from the stored Markdown source. So this fix repairs every already-published package page on the next load — no republish, version bump, or re-upload required.

Validation

  • svelte-check --threshold error0 errors (pre-existing warnings unchanged).
  • DOMPurify v3 ships its own types; pinned exact (3.4.11) to match the repo's dependency convention. Lockfile gains only the single dependency.

🤖 Generated with Claude Code

markdown-it was configured with `html: false`, which escapes all raw
HTML in README source. Tags like <details>/<summary> rendered as
literal visible text on package pages (e.g. mops.one/googlemail-client)
instead of as elements, even though GitHub renders them via its
sanitized allowlist.

Enable `html: true` and sanitize the rendered output with DOMPurify
(GitHub-equivalent: allow a safe tag allowlist incl. <details>/<summary>,
strip <script>/event-handlers/javascript: URLs). README rendering is
client-side at view time, so this fixes every already-published package
page on next load — no republish needed.

Also:
- the inline comment claimed "Enable HTML tags in source" while the
  value was `false`; corrected to match behavior.
- removed a dead duplicate `mdit.render(markdown)` whose result was
  discarded (parsed twice per render).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@ggreif ggreif self-assigned this Jun 30, 2026
@github-actions

github-actions Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Cursor AI review

👍 APPROVE — looks safe to merge

Category Assessment Details
Summary Enables raw HTML in package README/release-note markdown via html: true, sanitizes with DOMPurify (allowlist incl. <details>/<summary>), removes a dead duplicate render, and adds DOMPurify to AsciiDoc docs output.
Code Quality Standard fix pattern; removes wasted double mdit.render(); focused dependency add.
Consistency Aligns with GitHub-style README rendering; {@html} paths now consistently sanitized.
Security Net improvement over base (escaped HTML / unsanitized AsciiDoc); allowlist + DOMPurify URI handling addresses third-party README XSS.
Tests No new tests, but this path had none at base; change is straightforward sanitization wiring.
Maintainability Clear comments; pinned dompurify@3.4.11; small, reviewable diff across 4 files.

Verdict

Decision: APPROVE
Risk: Low
Reason: The change correctly fixes escaped raw HTML in READMEs with a standard markdown-it + DOMPurify allowlist approach, and the incidental PackageDocs sanitization hardens an existing {@html} path without regressions. No correctness or security defects identified relative to base.


Generated for commit 9efd2d5

@ggreif ggreif requested a review from Kamirus June 30, 2026 05:38
ggreif and others added 2 commits June 30, 2026 11:28
Package authors could embed malicious HTML/JS in AsciiDoc documentation.
Apply DOMPurify.sanitize() to doc.convert() output, consistent with how
markdown-to-html.ts already sanitizes markdown. Adds @types/dompurify dev
dependency for TypeScript support.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@Kamirus

Kamirus commented Jun 30, 2026

Copy link
Copy Markdown
Collaborator

Added a follow-up commit: sanitize Asciidoctor output in PackageDocs to prevent XSS.

  • PackageDocs.svelte:140 — wraps doc.convert() with DOMPurify.sanitize() to block script/event-handler injection from AsciiDoc docs.
  • Adds @types/dompurify as a dev dependency.

Also added another commit: restrict DOMPurify allowlist to safe Markdown output tags — the bare DOMPurify.sanitize() call in markdown-to-html.ts used DOMPurify's broad defaults which allow <form>, <input>, <button>, and <style>. Switched to an explicit ALLOWED_TAGS/ALLOWED_ATTR allowlist scoped to Markdown output elements (matching GitHub's sanitizer posture).

Default DOMPurify config permits <form>, <input>, <button>, and <style>,
allowing README authors to embed phishing forms or page-breaking CSS.
Restrict to an explicit structural allowlist matching GitHub's sanitizer
posture.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@Kamirus Kamirus merged commit c5fa77b into main Jun 30, 2026
26 checks passed
@Kamirus Kamirus deleted the fix/render-raw-html-in-readme branch June 30, 2026 09:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants