Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions src/components/Chat/Root/Conversation/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import CopyAfterIcon from '@/public/icon/copyAfterIcon';
import CopyBeforeIcon from '@/public/icon/copyBeforeIcon';
import { useLingui } from '@lingui/react/macro';
import { marked } from 'marked';
import DOMPurify from 'dompurify';
import { safeSanitize } from '@/utils/sanitize';
import { CreatePageBtn } from '@/components/Chat/CreatePageBtn';

export function Conversation({ onLeaveBottom }: { onLeaveBottom: (isLeave: boolean) => void }) {
Expand Down Expand Up @@ -150,7 +150,7 @@ function NoticeMessage({ id, name, children }: { id: string; name: string; child
return (
<Root id={id}>
<Name name={name}></Name>
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(children) }}></div>
<div dangerouslySetInnerHTML={{ __html: safeSanitize(children) }}></div>
</Root>
);
}
Expand Down Expand Up @@ -186,7 +186,7 @@ function SimilarityStartMessage({
return (
<Root id={id}>
<Name name={name} justifyContent="start"></Name>
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(children ?? '') }}></div>
<div dangerouslySetInnerHTML={{ __html: safeSanitize(children ?? '') }}></div>
</Root>
);
}
Expand Down Expand Up @@ -251,7 +251,7 @@ function LLMResponseMessage({
if (!content) return '';
// marked로 마크다운을 HTML로 변환하고 DOMPurify로 안전하게 정화
const rawHtml = marked(content, markedOptions) as string;
return DOMPurify.sanitize(rawHtml);
return safeSanitize(rawHtml);
};

return (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { useMemo } from 'react';
import s from './EditorLoadingErrorFallback.module.css';
import DOMPurify from 'dompurify';
import { safeSanitize } from '@/utils/sanitize';
import { useLingui } from '@lingui/react/macro';

interface EditorLoadingErrorFallbackProps {
Expand All @@ -11,7 +11,7 @@ export function EditorLoadingErrorFallback({ html }: EditorLoadingErrorFallbackP
const { t } = useLingui();

const sanitizedHtml = useMemo(() => {
return DOMPurify.sanitize(html);
return safeSanitize(html);
}, [html]);

return (
Expand Down
6 changes: 3 additions & 3 deletions src/components/common/ConfirmDialog/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { useEffect, useRef, useState } from 'react';
import { useAtom } from 'jotai';
import { useLingui } from '@lingui/react/macro';
import DOMPurify from 'dompurify';
import { safeSanitize } from '@/utils/sanitize';
import { confirmState, closeConfirmState } from '@/lib/jotai'; // 경로는 프로젝트 설정에 맞게 조정하세요.
import Dialog from '@mui/material/Dialog';
import DialogActions from '@mui/material/DialogActions';
Expand Down Expand Up @@ -64,7 +64,7 @@ export default function ConfirmDialog() {

// 내용물의 실제 너비를 계산하기 위해 텍스트를 복제하여 임시로 측정
const tempSpan = document.createElement('span');
tempSpan.innerHTML = DOMPurify.sanitize(confirm.message);
tempSpan.innerHTML = safeSanitize(confirm.message);
tempSpan.style.position = 'absolute';
tempSpan.style.visibility = 'hidden';
tempSpan.style.whiteSpace = 'nowrap';
Expand Down Expand Up @@ -138,7 +138,7 @@ export default function ConfirmDialog() {
ref={contentRef}
className={`${s.content} ${isCentered ? s.contentCentered : ''} ${isContentVisible ? s.visible : s.hidden}`}
dangerouslySetInnerHTML={{
__html: DOMPurify.sanitize(confirm.message),
__html: safeSanitize(confirm.message),
}}
></div>
{confirm.customContent && (
Expand Down
4 changes: 2 additions & 2 deletions src/components/common/ContentListMessage/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useAtom } from 'jotai';
import { contentListMessageState } from '@/lib/jotai';
import DOMPurify from 'dompurify';
import { safeSanitize } from '@/utils/sanitize';
import { useTheme } from '@mui/material/styles';

export default function ContentListMessage() {
Expand Down Expand Up @@ -42,7 +42,7 @@ export default function ContentListMessage() {
>
<div
dangerouslySetInnerHTML={{
__html: DOMPurify.sanitize(contentListMessage),
__html: safeSanitize(contentListMessage),
}}
></div>
</div>
Expand Down
4 changes: 2 additions & 2 deletions src/components/layout/bottom/agreement/docDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import DialogActions from '@mui/material/DialogActions';
import DialogContent from '@mui/material/DialogContent';
import DialogTitle from '@mui/material/DialogTitle';
import { useLingui } from '@lingui/react/macro';
import DOMPurify from 'dompurify';
import { safeSanitize } from '@/utils/sanitize';
import s from './docDialog.module.css';

export function DocsDialog({
Expand All @@ -24,7 +24,7 @@ export function DocsDialog({
<Dialog open={open} onClose={onClose} className={s.root}>
<DialogTitle>{title}</DialogTitle>
<DialogContent
dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(body) }}
dangerouslySetInnerHTML={{ __html: safeSanitize(body) }}
></DialogContent>
<DialogActions>
<Button variant="contained" onClick={onClose} color="secondary">
Expand Down
18 changes: 18 additions & 0 deletions src/utils/sanitize.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import DOMPurify from 'dompurify';

export const safeSanitize = (content: string, options?: any): string => {
if (typeof window !== 'undefined') {
if (DOMPurify.sanitize) {
return DOMPurify.sanitize(content, options) as unknown as string;
}
// Handle case where DOMPurify is a factory function (e.g. in some environments)
if (typeof DOMPurify === 'function') {
const factory = DOMPurify as any;
const sanitizer = factory(window);
if (sanitizer && typeof sanitizer.sanitize === 'function') {
return sanitizer.sanitize(content, options) as unknown as string;
}
}
}
return '';
};
Loading