diff --git a/frontend/package-lock.json b/frontend/package-lock.json index e99283135..f5f85de6b 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -15,6 +15,7 @@ "axios": "^1.6.7", "clsx": "^2.1.0", "date-fns": "^4.1.0", + "dompurify": "^3.4.5", "lucide-react": "^0.314.0", "marked": "^18.0.3", "react": "^18.2.0", @@ -1638,6 +1639,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "license": "MIT", + "optional": true + }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "6.21.0", "dev": true, @@ -2410,6 +2418,15 @@ "url": "https://github.com/sponsors/kossnocorp" } }, + "node_modules/dompurify": { + "version": "3.4.5", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.5.tgz", + "integrity": "sha512-OrwIBKsdNSVEeubdJ1HBv/wNENRM9ytAVCv7YXt//A3vPdVMNuACRqK9mXCGCBW2ln7BT/A4X0jXHo2Gu89miA==", + "license": "(MPL-2.0 OR Apache-2.0)", + "optionalDependencies": { + "@types/trusted-types": "^2.0.7" + } + }, "node_modules/debug": { "version": "4.4.3", "dev": true, diff --git a/frontend/package.json b/frontend/package.json index 399bfb431..c41d9f62c 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -17,6 +17,7 @@ "axios": "^1.6.7", "clsx": "^2.1.0", "date-fns": "^4.1.0", + "dompurify": "^3.4.5", "lucide-react": "^0.314.0", "marked": "^18.0.3", "react": "^18.2.0", diff --git a/frontend/src/components/DocumentEditor.tsx b/frontend/src/components/DocumentEditor.tsx index 330e76b73..a0b03214a 100644 --- a/frontend/src/components/DocumentEditor.tsx +++ b/frontend/src/components/DocumentEditor.tsx @@ -1,7 +1,8 @@ -import { useState, useEffect, useCallback } from 'react' +import { useState, useEffect, useCallback, useMemo } from 'react' import { Save, Eye, EyeOff } from 'lucide-react' import CodeMirror from '@uiw/react-codemirror' import { markdown } from '@codemirror/lang-markdown' +import DOMPurify from 'dompurify' import { marked } from 'marked' import api from '../services/api' @@ -22,6 +23,10 @@ export default function DocumentEditor({ const [showPreview, setShowPreview] = useState(false) const [isSaving, setIsSaving] = useState(false) const [saveTimeout, setSaveTimeout] = useState | null>(null) + const sanitizedPreview = useMemo(() => { + const renderedMarkdown = marked.parse(content, { async: false }) as string + return DOMPurify.sanitize(renderedMarkdown) + }, [content]) const handleSave = useCallback(async () => { setIsSaving(true) @@ -91,7 +96,7 @@ export default function DocumentEditor({
{showPreview ? (
-
+
) : (
@@ -107,4 +112,4 @@ export default function DocumentEditor({
) -} \ No newline at end of file +}