From 17cd7adcb8115ac5ea921b5391cb90899607cbcf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C5=ABdolfs=20O=C5=A1i=C5=86=C5=A1?= Date: Fri, 22 May 2026 12:01:51 +0200 Subject: [PATCH] Harden rendered markdown links --- src/components/Markdown.svelte | 1 + src/lib/markdown.ts | 15 ++++++++++----- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/components/Markdown.svelte b/src/components/Markdown.svelte index 0d7c11b74..67bcd8766 100644 --- a/src/components/Markdown.svelte +++ b/src/components/Markdown.svelte @@ -129,6 +129,7 @@ const url = new URL(e.href); if (url.origin !== window.origin) { e.target = "_blank"; + e.rel = "noopener noreferrer"; } } catch (e) { console.warn("Not able to parse url", e); diff --git a/src/lib/markdown.ts b/src/lib/markdown.ts index c4d0bf951..430af0b56 100644 --- a/src/lib/markdown.ts +++ b/src/lib/markdown.ts @@ -2,6 +2,7 @@ import type { Config } from "dompurify"; import type { MarkedExtension, Tokens } from "marked"; import type { Route } from "@app/lib/router"; +import escape from "lodash/escape"; import footnoteMarkedExtension from "marked-footnote"; import katexMarkedExtension from "marked-katex-extension"; import linkifyMarkedExtension from "marked-linkify-it"; @@ -29,6 +30,7 @@ export const sanitizeConfig: Config = { "href", "id", "name", + "rel", "src", "target", "text", @@ -106,10 +108,8 @@ export class Renderer extends BaseRenderer { const text = this.parser.parseInline(tokens); if (href.startsWith("#")) { // By lowercasing we avoid casing mismatches, between headings and links. - return `${text}`; - } - - if (this.#route.resource === "repo.source" && !isUrl(href)) { + href = href.toLowerCase(); + } else if (this.#route.resource === "repo.source" && !isUrl(href)) { href = routeToPath({ ...this.#route, path: canonicalize(href, this.#route.path || "README.md"), @@ -117,7 +117,12 @@ export class Renderer extends BaseRenderer { }); } - return `${text}`; + const attrs = [`href="${escape(href)}"`]; + if (title) { + attrs.push(`title="${escape(title)}"`); + } + + return `${text}`; } }