From 626dadb556890aac4ed31adef8c60037c32e0719 Mon Sep 17 00:00:00 2001 From: CaIon Date: Sat, 27 Jun 2026 17:44:52 +0800 Subject: [PATCH] fix(web): secure rich content rendering --- web/default/src/components/html-content.tsx | 13 +++++++++++-- web/default/src/components/rich-content.tsx | 6 ++++-- web/default/src/features/about/index.tsx | 7 +++++-- web/default/src/features/home/index.tsx | 13 ++++++++++--- web/default/src/features/legal/legal-document.tsx | 7 +++++-- 5 files changed, 35 insertions(+), 11 deletions(-) diff --git a/web/default/src/components/html-content.tsx b/web/default/src/components/html-content.tsx index 74b8162a..c4e9d9cd 100644 --- a/web/default/src/components/html-content.tsx +++ b/web/default/src/components/html-content.tsx @@ -16,6 +16,9 @@ along with this program. If not, see . For commercial licensing, please contact support@quantumnous.com */ +import DOMPurify from 'dompurify' +import { useMemo } from 'react' + import { cn } from '@/lib/utils' interface HtmlContentProps { @@ -24,10 +27,16 @@ interface HtmlContentProps { } export function HtmlContent(props: HtmlContentProps) { + const html = useMemo(() => DOMPurify.sanitize(props.content), [props.content]) + return (
) } diff --git a/web/default/src/components/rich-content.tsx b/web/default/src/components/rich-content.tsx index 27299ece..6b3e2962 100644 --- a/web/default/src/components/rich-content.tsx +++ b/web/default/src/components/rich-content.tsx @@ -16,18 +16,20 @@ along with this program. If not, see . For commercial licensing, please contact support@quantumnous.com */ -import { isLikelyHtml } from '@/lib/content-format' import { HtmlContent } from '@/components/html-content' import { Markdown } from '@/components/ui/markdown' +type RichContentMode = 'markdown' | 'html' + interface RichContentProps { content: string + mode?: RichContentMode breaks?: boolean className?: string } export function RichContent(props: RichContentProps) { - if (isLikelyHtml(props.content)) { + if (props.mode === 'html') { return } diff --git a/web/default/src/features/about/index.tsx b/web/default/src/features/about/index.tsx index 6898a03b..ac19df2a 100644 --- a/web/default/src/features/about/index.tsx +++ b/web/default/src/features/about/index.tsx @@ -19,10 +19,12 @@ For commercial licensing, please contact support@quantumnous.com import { useQuery } from '@tanstack/react-query' import { Construction } from 'lucide-react' import { useTranslation } from 'react-i18next' + +import { PublicLayout } from '@/components/layout' import { RichContent } from '@/components/rich-content' import { Skeleton } from '@/components/ui/skeleton' -import { PublicLayout } from '@/components/layout' -import { isHttpUrl } from '@/lib/content-format' +import { isHttpUrl, isLikelyHtml } from '@/lib/content-format' + import { getAboutContent } from './api' function EmptyAboutState() { @@ -159,6 +161,7 @@ export function About() {
diff --git a/web/default/src/features/home/index.tsx b/web/default/src/features/home/index.tsx index 103864d1..fefdd67f 100644 --- a/web/default/src/features/home/index.tsx +++ b/web/default/src/features/home/index.tsx @@ -17,10 +17,13 @@ along with this program. If not, see . For commercial licensing, please contact support@quantumnous.com */ import { useTranslation } from 'react-i18next' -import { useAuthStore } from '@/stores/auth-store' -import { RichContent } from '@/components/rich-content' + import { PublicLayout } from '@/components/layout' import { Footer } from '@/components/layout/components/footer' +import { RichContent } from '@/components/rich-content' +import { isLikelyHtml } from '@/lib/content-format' +import { useAuthStore } from '@/stores/auth-store' + import { CTA, Features, Hero, HowItWorks, Stats } from './components' import { useHomePageContent } from './hooks' @@ -57,7 +60,11 @@ export function Home() { return (
- +
) diff --git a/web/default/src/features/legal/legal-document.tsx b/web/default/src/features/legal/legal-document.tsx index 4409089a..d60d8612 100644 --- a/web/default/src/features/legal/legal-document.tsx +++ b/web/default/src/features/legal/legal-document.tsx @@ -19,12 +19,14 @@ For commercial licensing, please contact support@quantumnous.com import { useQuery } from '@tanstack/react-query' import { FileWarning } from 'lucide-react' import { useTranslation } from 'react-i18next' + +import { PublicLayout } from '@/components/layout' import { RichContent } from '@/components/rich-content' import { Button } from '@/components/ui/button' import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' import { Skeleton } from '@/components/ui/skeleton' -import { PublicLayout } from '@/components/layout' -import { isHttpUrl } from '@/lib/content-format' +import { isHttpUrl, isLikelyHtml } from '@/lib/content-format' + import type { LegalDocumentResponse } from './types' type LegalDocumentProps = { @@ -127,6 +129,7 @@ export function LegalDocument({