From 7ac9a2ff2d458bab0378f8e8e8397ea86176eab6 Mon Sep 17 00:00:00 2001
From: yyg-max <175597134+yyg-max@users.noreply.github.com>
Date: Mon, 3 Aug 2026 10:11:15 +0800
Subject: [PATCH 1/2] fix(markdown): prevent stored XSS in image error fallback
---
.../common/markdown/ContentRender.tsx | 96 +++++++++++--------
1 file changed, 55 insertions(+), 41 deletions(-)
diff --git a/frontend/components/common/markdown/ContentRender.tsx b/frontend/components/common/markdown/ContentRender.tsx
index 0c6be11..75a8864 100644
--- a/frontend/components/common/markdown/ContentRender.tsx
+++ b/frontend/components/common/markdown/ContentRender.tsx
@@ -3,7 +3,7 @@
import ReactMarkdown from 'react-markdown';
import remarkGfm from 'remark-gfm';
import {copyToClipboard} from '@/lib/utils';
-import {ReactNode} from 'react';
+import {ComponentPropsWithoutRef, ReactNode, useState} from 'react';
import type {Components} from 'react-markdown';
/**
@@ -33,6 +33,57 @@ const getCodeContent = (node: ReactNode): string => {
return '';
};
+/**
+ * Markdown 图片组件
+ */
+const MarkdownImage = ({src, alt, title}: ComponentPropsWithoutRef<'img'>) => {
+ const [hasError, setHasError] = useState(false);
+ const fallbackText = alt || (typeof src === 'string' ? src : '');
+
+ return (
+ <>
+ {hasError ? (
+
+
+
图片加载失败
+
+ {fallbackText}
+
+
+ ) : (
+ <>
+ {/**
+ * 此处使用原生 img 元素而非 Next.js Image 组件。
+ * Markdown 图片通常来自外部源,使用 Next.js Image 会增加域名白名单配置复杂性。
+ */}
+ {/* eslint-disable-next-line @next/next/no-img-element */}
+
setHasError(true)}
+ />
+ >
+ )}
+ {(alt || title) && (
+
+ {title || alt}
+
+ )}
+ >
+ );
+};
+
/**
* Markdown 组件配置映射
*/
@@ -254,44 +305,7 @@ const markdownComponents: Components = {
hr: () => (
),
- img: ({src, alt, title}) => {
- return (
- <>
- {/*
- * 注意:此组件中使用原生 img 元素而非 Next.js Image 组件。
- * Markdown 内容中的图片 URL 通常来自外部源,使用 Next.js Image 组件会导致跨域问题和域名白名单配置复杂性。
- */}
- {/* eslint-disable-next-line @next/next/no-img-element */}
-
{
- const target = e.target as HTMLImageElement;
- const errorDiv = document.createElement('div');
- errorDiv.className = 'bg-muted border border-border rounded-lg p-4 text-center text-muted-foreground my-6';
- errorDiv.innerHTML = `
-
- 图片加载失败
- ${alt || src}
- `;
- target.parentNode?.replaceChild(errorDiv, target);
- }}
- />
- {(alt || title) && (
-
- {title || alt}
-
- )}
- >
- );
- },
+ img: MarkdownImage,
table: ({children}) => (
@@ -301,12 +315,12 @@ const markdownComponents: Components = {
),
thead: ({children}) => (
- {children}
+ {children}
),
tbody: ({children}) => (
- {children}
+ {children}
),
tr: ({children}) => (
From 2a76e8b6e04809aab1dc04501bd24f83aa932676 Mon Sep 17 00:00:00 2001
From: yyg-max <175597134+yyg-max@users.noreply.github.com>
Date: Mon, 3 Aug 2026 10:15:05 +0800
Subject: [PATCH 2/2] fix(markdown): prevent stored XSS in image error fallback
---
frontend/components/common/markdown/ContentRender.tsx | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/frontend/components/common/markdown/ContentRender.tsx b/frontend/components/common/markdown/ContentRender.tsx
index 75a8864..a5d3993 100644
--- a/frontend/components/common/markdown/ContentRender.tsx
+++ b/frontend/components/common/markdown/ContentRender.tsx
@@ -315,12 +315,12 @@ const markdownComponents: Components = {
),
thead: ({children}) => (
- {children}
+ {children}
),
tbody: ({children}) => (
- {children}
+ {children}
),
tr: ({children}) => (