diff --git a/src/components/shared/MarkdownRenderer.test.ts b/src/components/shared/MarkdownRenderer.test.ts
index 3c91c970..8aeedb27 100644
--- a/src/components/shared/MarkdownRenderer.test.ts
+++ b/src/components/shared/MarkdownRenderer.test.ts
@@ -1,5 +1,6 @@
import { describe, it, expect } from 'vitest';
-import { markdownToHtml, clusterMarkdownLines } from './MarkdownRenderer';
+import { markdownToHtml } from './MarkdownRenderer';
+import DOMPurify from 'dompurify';
// Tests cover the pure markdownToHtml function (no DOM/React needed).
@@ -194,6 +195,84 @@ describe('markdownToHtml', () => {
expect(html).toContain('
A | ');
expect(html).toContain('1 | ');
});
+
+ // ── DOMPurify sanitization ───────────────────────────────────────────────────
+
+ it('sanitizes malicious HTML with DOMPurify using component config', () => {
+ const md = '
';
+ const raw = markdownToHtml(md);
+ const sanitized = DOMPurify.sanitize(raw, {
+ ALLOWED_TAGS: [
+ 'h1',
+ 'h2',
+ 'h3',
+ 'h4',
+ 'h5',
+ 'h6',
+ 'p',
+ 'br',
+ 'strong',
+ 'em',
+ 'del',
+ 'code',
+ 'pre',
+ 'ul',
+ 'ol',
+ 'li',
+ 'blockquote',
+ 'hr',
+ 'a',
+ 'img',
+ 'table',
+ 'thead',
+ 'tbody',
+ 'tr',
+ 'th',
+ 'td',
+ 'input',
+ ],
+ ALLOWED_ATTR: ['href', 'src', 'alt', 'class', 'target', 'rel', 'type', 'checked', 'disabled'],
+ });
+ expect(sanitized).not.toContain('onerror');
+ });
+
+ it('sanitizes script tags with DOMPurify using component config', () => {
+ const md = '';
+ const raw = markdownToHtml(md);
+ const sanitized = DOMPurify.sanitize(raw, {
+ ALLOWED_TAGS: [
+ 'h1',
+ 'h2',
+ 'h3',
+ 'h4',
+ 'h5',
+ 'h6',
+ 'p',
+ 'br',
+ 'strong',
+ 'em',
+ 'del',
+ 'code',
+ 'pre',
+ 'ul',
+ 'ol',
+ 'li',
+ 'blockquote',
+ 'hr',
+ 'a',
+ 'img',
+ 'table',
+ 'thead',
+ 'tbody',
+ 'tr',
+ 'th',
+ 'td',
+ 'input',
+ ],
+ ALLOWED_ATTR: ['href', 'src', 'alt', 'class', 'target', 'rel', 'type', 'checked', 'disabled'],
+ });
+ expect(sanitized).not.toContain('