diff options
| author | Owen Jacobson <owen@grimoire.ca> | 2025-04-21 22:00:09 -0400 |
|---|---|---|
| committer | Owen Jacobson <owen@grimoire.ca> | 2025-04-21 23:09:46 -0400 |
| commit | ef87bb0719579d55a692992e1843f20e57f209d6 (patch) | |
| tree | 9c6124461e4be0aec88af17ae83e77021f8c2cb9 /ui/lib/markdown.js | |
| parent | 1ef57107b1c355ef896327f0714344277df7ae18 (diff) | |
Add the following attributes to all markdown-generated links:
* `target="_blank"`: when Pilcrow is running in a browser, clicking a link should not replace Pilcrow with the target of the link. Pilcrow is "app-like" enough that opening links in a new tab _by default_, without user intervention, is likely more appropriate.
* `rel="noreferrer"`, which (A) stops most UAs from setting a referrer header when following those links, and (B) also implies `noopener`, preventing the link target from using `window.opener` from reaching back into Pilcrow's DOM.
I briefly experimented with DOMPurify's `RETURN_DOM_FRAGMENT` mode, which would have made the tests somewhat easier to write, but I wasn't able to find a good way to integrate the returned `DocumentFragment` objects with Svelte components, so HTML-as-strings it is. Sigh.
Diffstat (limited to 'ui/lib/markdown.js')
| -rw-r--r-- | ui/lib/markdown.js | 22 |
1 files changed, 21 insertions, 1 deletions
diff --git a/ui/lib/markdown.js b/ui/lib/markdown.js index 2e73309..c4f2803 100644 --- a/ui/lib/markdown.js +++ b/ui/lib/markdown.js @@ -1,6 +1,26 @@ import { marked } from 'marked'; import DOMPurify from 'dompurify'; +const extension = { + useNewRenderer: true, + renderer: { + link({ title, href, tokens }) { + const titleAttr = title ? ` title="${title}"` : ``; + const text = this.parser.parseInline(tokens); + return `<a + target="_blank" + rel="noreferrer" + ${titleAttr} + href="${href}">${text}</a>`; + } + } +}; + +marked.use(extension); + export function render(body) { - return DOMPurify.sanitize(marked.parse(body, { breaks: true })); + const rendered = marked.parse(body, { breaks: true }); + return DOMPurify.sanitize(rendered, { + ADD_ATTR: ['target'] + }); } |
