From 42af4cd890cef9a9c37b191368789f5383797cfe Mon Sep 17 00:00:00 2001 From: Max Burri Date: Wed, 8 Jul 2026 13:47:21 +0000 Subject: [PATCH 1/4] chore: harden markdown renderer sanitization --- app/components/markdown.tsx | 43 +++++++++++++++++++++++++++++++++---- 1 file changed, 39 insertions(+), 4 deletions(-) diff --git a/app/components/markdown.tsx b/app/components/markdown.tsx index c99a39682..f69e4a954 100644 --- a/app/components/markdown.tsx +++ b/app/components/markdown.tsx @@ -1,13 +1,46 @@ import clsx from "clsx"; import { ComponentProps } from "react"; import ReactMarkdown from "react-markdown"; -import rehypeRaw from "rehype-raw"; -import rehypeSanitize from "rehype-sanitize"; +import rehypeSanitize, { defaultSchema } from "rehype-sanitize"; import remarkGfm from "remark-gfm"; import classes from "@/components/markdown.module.css"; import { palette } from "@/themes/palette"; +const sanitizeSchema = { + ...defaultSchema, + tagNames: [ + "h1", + "h2", + "h3", + "h4", + "h5", + "h6", + "p", + "strong", + "em", + "ins", + "del", + "s", + "ul", + "ol", + "li", + "blockquote", + "code", + "pre", + "br", + "hr", + "a", + ], + attributes: { + ...defaultSchema.attributes, + a: ["href", "title", "target"], + "*": (defaultSchema.attributes?.["*"] ?? []).filter( + (attr) => !(typeof attr === "string" && attr.startsWith("on")) + ), + }, +}; + const components: ComponentProps["components"] = { h1: ({ children, className, ...props }) => (

@@ -47,6 +80,7 @@ const components: ComponentProps["components"] = { a: ({ children, className, style, ...props }) => ( ); @@ -165,6 +199,7 @@ const componentsInheritFonts: ComponentProps< @@ -180,7 +215,7 @@ export const MarkdownInheritFonts = ( ); From 9e949c27e2ef8935837fd1e1ad8afd2279f40e37 Mon Sep 17 00:00:00 2001 From: Max Burri Date: Wed, 8 Jul 2026 14:05:31 +0000 Subject: [PATCH 2/4] doc: add changelog entry --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e62e13b8..8fc60a5a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,9 +11,9 @@ You can also check the ## Unreleased - - Fixes - Correctly set CSP headers for index pages + - Harden markdown renderer sanitization - Maintenance - Add dev-container support From 5396a7fe919de1bbfbae55a1788789ef29a1a3b9 Mon Sep 17 00:00:00 2001 From: Max Burri Date: Wed, 8 Jul 2026 14:11:32 +0000 Subject: [PATCH 3/4] chore: remove unused dependency --- app/package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/app/package.json b/app/package.json index 2aa8f0394..cf046c6d1 100644 --- a/app/package.json +++ b/app/package.json @@ -153,7 +153,6 @@ "react-transition-group": "^4.4.5", "react-virtualized-auto-sizer": "^1.0.25", "react-window": "^1.8.10", - "rehype-raw": "^7.0.0", "rehype-sanitize": "^6.0.0", "remark-gfm": "^4.0.0", "simple-statistics": "^7.6.0", From a43c275652cce67fef3d39a591efc209c625e83a Mon Sep 17 00:00:00 2001 From: Max Burri Date: Wed, 8 Jul 2026 14:22:54 +0000 Subject: [PATCH 4/4] chore: fixes after review --- app/components/markdown.tsx | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/app/components/markdown.tsx b/app/components/markdown.tsx index f69e4a954..7dca5c1fe 100644 --- a/app/components/markdown.tsx +++ b/app/components/markdown.tsx @@ -34,10 +34,8 @@ const sanitizeSchema = { ], attributes: { ...defaultSchema.attributes, - a: ["href", "title", "target"], - "*": (defaultSchema.attributes?.["*"] ?? []).filter( - (attr) => !(typeof attr === "string" && attr.startsWith("on")) - ), + a: ["href", "title"], + "*": defaultSchema.attributes?.["*"] ?? [], }, }; @@ -79,11 +77,11 @@ const components: ComponentProps["components"] = { ), a: ({ children, className, style, ...props }) => ( {children} @@ -115,7 +113,11 @@ export const InlineMarkdown = ({ p: ({ children }) => <>{children}, strong: ({ children }) => {children}, em: ({ children }) => {children}, - a: ({ children, href }) => {children}, + a: ({ children, href }) => { + const safeHref = + href && /^(https?|mailto|ircs?):/i.test(href) ? href : undefined; + return {children}; + }, h1: () => null, h2: () => null, h3: () => null, @@ -197,11 +199,11 @@ const componentsInheritFonts: ComponentProps< ), a: ({ children, className, style, ...props }) => ( {children}