feat: add DOMPurify for sanitizing HTML content in CodeBlock component

pull/5091/head
Steven 1 month ago
parent f4e23727bb
commit 383553d3c8

@ -32,6 +32,7 @@
"cmdk": "^1.1.1", "cmdk": "^1.1.1",
"copy-to-clipboard": "^3.3.3", "copy-to-clipboard": "^3.3.3",
"dayjs": "^1.11.13", "dayjs": "^1.11.13",
"dompurify": "^3.2.6",
"fuse.js": "^7.1.0", "fuse.js": "^7.1.0",
"highlight.js": "^11.11.1", "highlight.js": "^11.11.1",
"i18next": "^25.4.2", "i18next": "^25.4.2",

@ -80,6 +80,9 @@ importers:
dayjs: dayjs:
specifier: ^1.11.13 specifier: ^1.11.13
version: 1.11.13 version: 1.11.13
dompurify:
specifier: ^3.2.6
version: 3.2.6
fuse.js: fuse.js:
specifier: ^7.1.0 specifier: ^7.1.0
version: 7.1.0 version: 7.1.0

@ -1,4 +1,5 @@
import copy from "copy-to-clipboard"; import copy from "copy-to-clipboard";
import DOMPurify from "dompurify";
import hljs from "highlight.js"; import hljs from "highlight.js";
import { CopyIcon } from "lucide-react"; import { CopyIcon } from "lucide-react";
import { useEffect, useMemo } from "react"; import { useEffect, useMemo } from "react";
@ -22,12 +23,63 @@ const CodeBlock: React.FC<Props> = ({ language, content }: Props) => {
const formatedLanguage = useMemo(() => (language || "").toLowerCase() || "text", [language]); const formatedLanguage = useMemo(() => (language || "").toLowerCase() || "text", [language]);
// Users can set Markdown code blocks as `__html` to render HTML directly. // Users can set Markdown code blocks as `__html` to render HTML directly.
// Content is sanitized to prevent XSS attacks while preserving safe HTML.
if (formatedLanguage === SpecialLanguage.HTML) { if (formatedLanguage === SpecialLanguage.HTML) {
const sanitizedHTML = DOMPurify.sanitize(content, {
// Allow common safe HTML tags and attributes
ALLOWED_TAGS: [
"div",
"span",
"p",
"br",
"strong",
"b",
"em",
"i",
"u",
"s",
"strike",
"h1",
"h2",
"h3",
"h4",
"h5",
"h6",
"blockquote",
"code",
"pre",
"ul",
"ol",
"li",
"dl",
"dt",
"dd",
"table",
"thead",
"tbody",
"tr",
"th",
"td",
"a",
"img",
"figure",
"figcaption",
"hr",
"small",
"sup",
"sub",
],
ALLOWED_ATTR: "href title alt src width height class id style target rel colspan rowspan".split(" "),
// Forbid dangerous attributes and tags
FORBID_ATTR: "onerror onload onclick onmouseover onfocus onblur onchange".split(" "),
FORBID_TAGS: "script iframe object embed form input button".split(" "),
});
return ( return (
<div <div
className="w-full overflow-auto my-2!" className="w-full overflow-auto my-2!"
dangerouslySetInnerHTML={{ dangerouslySetInnerHTML={{
__html: content, __html: sanitizedHTML,
}} }}
/> />
); );

Loading…
Cancel
Save