From 15f2c3d357cffb485230f93f0b9dd4ddcbeac5bd Mon Sep 17 00:00:00 2001 From: John Coffey Date: Tue, 1 Sep 2026 20:32:59 -0700 Subject: [PATCH] Read a Markdown file as the document it is A .md previewed as its own source, which is reading the punctuation rather than the notes. It now opens rendered, with Rendered | Source in the dialog footer for anyone who wants what the file actually says. Markdown only; a .txt has nothing to toggle between. Rendering is `marked`, sanitised by DOMPurify -- the one the app already carries for mail. Markdown is not a safe subset of anything: raw HTML passes through it by design, so a \n\n\n\n\n"); + expect(html).not.toContain(" { + const html = renderMarkdown("[click](javascript:alert(1))"); + expect(html).not.toContain("javascript:"); + }); + + it("shows an image as a link instead of fetching it", () => { + // A remote image in a file is a tracking pixel by another name; this app + // blocks those in mail and does not undo that here. + const html = renderMarkdown("![a diagram](https://tracker.example/px.png)"); + expect(html).not.toContain(" { + const html = renderMarkdown("![local](./diagram.png)"); + expect(html).not.toContain(" { + const html = renderMarkdown("[docs](https://docs.ihasmail.org)"); + expect(html).toContain('rel="noopener noreferrer"'); + expect(html).toContain('target="_blank"'); + }); +}); diff --git a/web/src/lib/markdown.ts b/web/src/lib/markdown.ts new file mode 100644 index 0000000..abec1da --- /dev/null +++ b/web/src/lib/markdown.ts @@ -0,0 +1,72 @@ +import DOMPurify from "dompurify"; +import { marked } from "marked"; + +/** + * Markdown, rendered for the file viewer. + * + * The source is somebody else's file -- uploaded, or shared into the account + * by another user -- so it is treated as hostile. Markdown is not a safe + * subset of anything: raw HTML passes straight through it by design, so + * `