瀏覽代碼

fix: xss in render svg (#16433)

Joel 1 月之前
父節點
當前提交
e61415223b
共有 1 個文件被更改,包括 19 次插入10 次删除
  1. 19 10
      web/app/components/base/markdown.tsx

+ 19 - 10
web/app/components/base/markdown.tsx

@@ -21,7 +21,6 @@ import ImageGallery from '@/app/components/base/image-gallery'
 import { useChatContext } from '@/app/components/base/chat/chat/context'
 import VideoGallery from '@/app/components/base/video-gallery'
 import AudioGallery from '@/app/components/base/audio-gallery'
-import SVGRenderer from '@/app/components/base/svg-gallery'
 import MarkdownButton from '@/app/components/base/markdown-blocks/button'
 import MarkdownForm from '@/app/components/base/markdown-blocks/form'
 import ThinkBlock from '@/app/components/base/markdown-blocks/think-block'
@@ -137,13 +136,14 @@ const CodeBlock: any = memo(({ inline, className, children, ...props }: any) =>
         </div>
       )
     }
-    else if (language === 'svg' && isSVG) {
-      return (
-        <ErrorBoundary>
-          <SVGRenderer content={content} />
-        </ErrorBoundary>
-      )
-    }
+    // Attention: SVGRenderer has xss vulnerability
+    // else if (language === 'svg' && isSVG) {
+    //   return (
+    //     <ErrorBoundary>
+    //       <SVGRenderer content={content} />
+    //     </ErrorBoundary>
+    //   )
+    // }
     else {
       return (
         <SyntaxHighlighter
@@ -175,7 +175,7 @@ const CodeBlock: any = memo(({ inline, className, children, ...props }: any) =>
         <div className='flex items-center gap-1'>
           {(['mermaid', 'svg']).includes(language!) && <SVGBtn isSVG={isSVG} setIsSVG={setIsSVG} />}
           <ActionButton>
-            <CopyIcon content={String(children).replace(/\n$/, '')}/>
+            <CopyIcon content={String(children).replace(/\n$/, '')} />
           </ActionButton>
         </div>
       </div>
@@ -240,11 +240,20 @@ const Link = ({ node, ...props }: any) => {
   }
 }
 
+function escapeSVGTags(htmlString: string): string {
+  return htmlString.replace(/(<svg[\s\S]*?>)([\s\S]*?)(<\/svg>)/gi, (match: string, openTag: string, innerContent: string, closeTag: string): string => {
+    return openTag.replace(/</g, '&lt;').replace(/>/g, '&gt;')
+      + innerContent.replace(/</g, '&lt;').replace(/>/g, '&gt;')
+      + closeTag.replace(/</g, '&lt;').replace(/>/g, '&gt;')
+  })
+}
+
 export function Markdown(props: { content: string; className?: string; customDisallowedElements?: string[] }) {
   const latexContent = flow([
     preprocessThinkTag,
     preprocessLaTeX,
-  ])(props.content)
+  ])(escapeSVGTags(props.content))
+
   return (
     <div className={cn('markdown-body', '!text-text-primary', props.className)}>
       <ReactMarkdown