🐛 fix(chatMessage): sanitize HTML content to prevent potential XSS attacks

✨ feat(chatMessage): improve rendering of multi-line messages
The `convert` variable is now imported before being used to convert ANSI to HTML. Additionally, the `DOMPurify` library is imported and used to sanitize the HTML content before rendering it, preventing potential XSS attacks. The rendering of multi-line messages has been improved by splitting the message by newline characters and rendering each line separately with a line break.
This commit is contained in:
Gabriel Luiz Freitas Almeida 2023-06-29 10:02:28 -03:00
commit 3faf5f5b96
2 changed files with 14 additions and 14 deletions

View file

@ -2,9 +2,9 @@ import { useState } from "react";
import { ChatMessageType } from "../../../types/chat"; import { ChatMessageType } from "../../../types/chat";
import { nodeColors } from "../../../utils"; import { nodeColors } from "../../../utils";
import Convert from "ansi-to-html"; import Convert from "ansi-to-html";
const convert = new Convert({ newline: true });
import { MessageCircle } from "lucide-react"; import { MessageCircle } from "lucide-react";
import DOMPurify from "dompurify";
const convert = new Convert({ newline: true });
export default function ChatMessage({ chat }: { chat: ChatMessageType }) { export default function ChatMessage({ chat }: { chat: ChatMessageType }) {
const [hidden, setHidden] = useState(true); const [hidden, setHidden] = useState(true);
return ( return (
@ -23,13 +23,14 @@ export default function ChatMessage({ chat }: { chat: ChatMessageType }) {
<MessageCircle className="w-5 h-5 animate-bounce" /> <MessageCircle className="w-5 h-5 animate-bounce" />
</div> </div>
)} )}
{chat.thought && chat.thought !== "" && !hidden && ( {chat.thought && chat.thought !== "" && !hidden && (
<div <div
onClick={() => setHidden((prev) => !prev)} onClick={() => setHidden((prev) => !prev)}
style={{ backgroundColor: nodeColors["thought"] }} style={{ backgroundColor: nodeColors["thought"] }}
className="text-start inline-block w-full pb-3 pt-3 px-5 cursor-pointer" className="text-start inline-block w-full pb-3 pt-3 px-5 cursor-pointer"
dangerouslySetInnerHTML={{ dangerouslySetInnerHTML={{
__html: convert.toHtml(chat.thought), __html: DOMPurify.sanitize(convert.toHtml(chat.thought)),
}} }}
></div> ></div>
)} )}

View file

@ -11,7 +11,7 @@ import remarkMath from "remark-math";
import { CodeBlock } from "./codeBlock"; import { CodeBlock } from "./codeBlock";
import Convert from "ansi-to-html"; import Convert from "ansi-to-html";
import { User2, MessageCircle } from "lucide-react"; import { User2, MessageCircle } from "lucide-react";
import DOMPurify from "dompurify";
export default function ChatMessage({ export default function ChatMessage({
chat, chat,
lockChat, lockChat,
@ -78,10 +78,9 @@ export default function ChatMessage({
{chat.thought && chat.thought !== "" && !hidden && ( {chat.thought && chat.thought !== "" && !hidden && (
<div <div
onClick={() => setHidden((prev) => !prev)} onClick={() => setHidden((prev) => !prev)}
className=" text-start inline-block rounded-md text-gray-600 dark:text-gray-200 h-full border border-gray-300 dark:border-gray-500 className="text-start inline-block rounded-md text-gray-600 dark:text-gray-200 h-full border border-gray-300 dark:border-gray-500 bg-muted dark:bg-gray-800 w-[95%] pb-3 pt-3 px-2 ml-3 cursor-pointer scrollbar-hide overflow-scroll"
bg-muted dark:bg-gray-800 w-[95%] pb-3 pt-3 px-2 ml-3 cursor-pointer scrollbar-hide overflow-scroll"
dangerouslySetInnerHTML={{ dangerouslySetInnerHTML={{
__html: convert.toHtml(chat.thought), __html: DOMPurify.sanitize(convert.toHtml(chat.thought)),
}} }}
></div> ></div>
)} )}
@ -152,12 +151,12 @@ export default function ChatMessage({
) : ( ) : (
<div className="w-full flex items-center"> <div className="w-full flex items-center">
<div className="text-start inline-block px-3 text-gray-600 dark:text-white"> <div className="text-start inline-block px-3 text-gray-600 dark:text-white">
<span {message.split("\n").map((line, index) => (
className="text-gray-600 dark:text-gray-200" <span key={index}>
dangerouslySetInnerHTML={{ {line}
__html: message.replace(/\n/g, "<br>"), <br />
}} </span>
></span> ))}
</div> </div>
</div> </div>
)} )}