Feature #174: marked + DOMPurify markdown #175

Merged
crueber merged 1 commit from feat/issue-174 into main 2026-09-06 16:46:33 +00:00
Owner

Implements #174 (D-WEB-7, explicit user amendment).

  • New web/src/lib/render-md.js (marked GFM + pinned DOMPurify allowlists); five call sites switched; markdown.js + sanitize.js deleted (pre-1.0 rule).
  • Tests rewritten (marked layer in node, DOMPurify layer in real Chromium); docs (D-WEB-7, 12_web_ui.md Decisions) same commit.
  • Bundle delta +22.2 KB gzip (within ~25 KB budget); runtime budget exactly the four packages, both new deps zero-dependency.
  • Verification: node --test 363/363 green; make web clean; real-Chromium 14/14 assertions + zero console errors; / and /setup load with zero console errors.
  • Conscious mappings: breaks:true (preserve
    contract), class=language-* (nothing consumed data-lang), renderBody throws without DOM.
Implements #174 (D-WEB-7, explicit user amendment). - New web/src/lib/render-md.js (marked GFM + pinned DOMPurify allowlists); five call sites switched; markdown.js + sanitize.js deleted (pre-1.0 rule). - Tests rewritten (marked layer in node, DOMPurify layer in real Chromium); docs (D-WEB-7, 12_web_ui.md Decisions) same commit. - Bundle delta +22.2 KB gzip (within ~25 KB budget); runtime budget exactly the four packages, both new deps zero-dependency. - Verification: node --test 363/363 green; make web clean; real-Chromium 14/14 assertions + zero console errors; / and /setup load with zero console errors. - Conscious mappings: breaks:true (preserve <br> contract), class=language-* (nothing consumed data-lang), renderBody throws without DOM.
Replace hand-rolled markdown-lite (web/src/lib/markdown.js) and allowlist
sanitizer (web/src/lib/sanitize.js) with marked@18.0.11 (MIT) +
dompurify@3.4.15 (MPL-2.0-or-Apache-2.0), both zero-dependency, behind one
wrapper module web/src/lib/render-md.js (marked GFM + pinned DOMPurify
ALLOWED_TAGS/ALLOWED_ATTR incl. del/s/input/checked/disabled/type/class,
FORBID_CONTENTS for the old drop-content set). Five call sites
(Blob, Tree, Release, IssueNew, ThreadTimeline) render
innerHTML={renderBody(...)}; old modules deleted (pre-1.0 rule).

Conscious mappings: breaks:true preserves the <br> continuation contract;
fenced code keeps marked's class="language-*" (nothing consumed data-lang);
renderBody throws without a DOM (fail closed), marked layer stays
Node-importable.

Bundle delta +22.2 KB gzip on the shipped vite bundle (422.71 kB /
123.52 kB gzip vs 356.55 kB / 101.34 kB pre-change; budget <= ~25 KB).
Tests: node --test 363/363 green; make web clean; real-Chromium pass 14/14
(task/table/strike/autolink, script+content and javascript:/data: drops,
disabled checkboxes, dark+light identical) with zero console errors; / and
/setup load in Chromium with zero console errors.
Sign in to join this conversation.
No description provided.