From 44907eb35849d37670d4f6723707b7e56838cc79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anatol=20Karalko=C5=AD?= Date: Thu, 13 Jun 2024 14:04:49 +0200 Subject: [PATCH] Revert "feat: chat with insecure output handling vulnerability (#341)" This reverts commit 80f0387c98b2922c7f9ea97fec87efaf4f2f24cf. --- .env | 6 - README.md | 4 - client/public/assets/css/style.css | 91 +---- client/src/api/ApiUrl.ts | 3 +- client/src/api/httpClient.ts | 11 - client/src/interfaces/ChatMessage.ts | 4 - client/src/pages/chat/Chat.tsx | 18 - client/src/pages/chat/ChatWidget.tsx | 122 ------ client/src/pages/main/Header/Nav.tsx | 5 - client/src/router/RoutePath.ts | 3 +- client/src/router/Routes.tsx | 11 - package-lock.json | 555 +++++++++++++++++++++------ package.json | 2 +- src/app.module.ts | 3 +- src/chat/api/ChatMessage.ts | 15 - src/chat/chat.controller.api.desc.ts | 1 - src/chat/chat.controller.ts | 38 -- src/chat/chat.module.ts | 12 - src/chat/chat.service.ts | 61 --- src/main.ts | 2 - 20 files changed, 438 insertions(+), 529 deletions(-) delete mode 100644 client/src/interfaces/ChatMessage.ts delete mode 100644 client/src/pages/chat/Chat.tsx delete mode 100644 client/src/pages/chat/ChatWidget.tsx delete mode 100644 src/chat/api/ChatMessage.ts delete mode 100644 src/chat/chat.controller.api.desc.ts delete mode 100644 src/chat/chat.controller.ts delete mode 100644 src/chat/chat.module.ts delete mode 100644 src/chat/chat.service.ts diff --git a/.env b/.env index 80f8888b..a19c280a 100644 --- a/.env +++ b/.env @@ -26,9 +26,3 @@ KEYCLOAK_PUBLIC_CLIENT_SECRET=4bfb5df6-4647-46dd-bad1-c8b8ffd7caf4 BRIGHT_TOKEN= BRIGHT_CLUSTER=app.neuralegion.com SEC_TESTER_TARGET=http://localhost:8090 - -CHAT_API_URL=https://api-inference.huggingface.co/v1/chat/completions -CHAT_API_MODEL=meta-llama/Meta-Llama-3-8B-Instruct -CHAT_API_MAX_TOKENS=1000 -CHAT_API_TOKEN= - diff --git a/README.md b/README.md index c46bfcf8..0ea25200 100644 --- a/README.md +++ b/README.md @@ -201,7 +201,3 @@ Additionally, the endpoint PUT /api/users/one/{email}/photo accepts SVG images, To exploit the EP you can dispatch a request as such `/api/email/sendSupportEmail?name=Bob&to=username%40email.com%0aCc:%20bob@domain.com&subject=Help%20Request&content=I%20would%20like%20to%20request%20help%20regarding`. This will lead to the sending of a mail to both `username@email.com` and `bob@domain.com` (as the Cc). Note: This EP is also vulnerable to `Server side prototype pollution`, as mentioned in this README. - -* **Insecure Output Handling** - The `/chat` route is vulnerable to non-sanitized output originating from the LLM response. - Issue a `POST /api/chat` request with body payload like `[{"content": "Provide a minimal html markup for img tag with invalid source and onerror attribute with alert", "role": "user"}]`. - The response will include raw HTML code. If this output is not properly sanitized before rendering, it can trigger an alert box in the user interface. diff --git a/client/public/assets/css/style.css b/client/public/assets/css/style.css index 4a849954..ff50d4b2 100644 --- a/client/public/assets/css/style.css +++ b/client/public/assets/css/style.css @@ -577,7 +577,7 @@ section { margin-left: 20%; margin-right: 20%; max-width: -webkit-fill-available; - margin-top: 15px; + margin-top: 15px } .section-title h2 { @@ -1789,91 +1789,4 @@ section { .warning-text { color: #ff0000; -} - -/*-------------------------------------------------------------- -# Chat ---------------------------------------------------------------*/ -.chat .container { - max-width: 960px; -} - -.chat .messages { - display: flex; - flex-direction: column; - border: 1px solid #ccc; - margin-bottom: 10px; - padding: 10px; - border-radius: 4px; - overflow-y: auto; - min-height: 50vh; - max-height: calc(100vh - 280px); -} - -.chat .message { - margin-top: 10px; - padding: 4px 8px; - border-radius: 8px; - color: white; - white-space: pre-wrap; - max-width: 80%; -} - -.chat .message-role-user { - align-self: end; - background-color: #0297a4; -} - -.chat .message-role-assistant { - align-self: start; - background-color: #4272d7; -} - -.chat .message-role-assistant.message-error { - background-color: #ff5828; -} - -.chat .input-area { - display: flex; -} - -.chat .input-area textarea { - width: 100%; - min-height: 50px; - border-radius: 4px 0 0 4px; -} - -.chat .input-area button { - border-radius: 0 4px 4px 0; -} - -.chat .message-loading .animated-dots span { - animation-name: blink; - animation-duration: 1.4s; - animation-iteration-count: infinite; - animation-fill-mode: both; -} - -.chat .message-loading .animated-dots span:nth-child(1) { - animation-delay: 0s; -} - -.chat .message-loading .animated-dots span:nth-child(2) { - animation-delay: 0.2s; -} - -.chat .message-loading .animated-dots span:nth-child(3) { - animation-delay: 0.4s; -} - -@keyframes blink { - 0% { - opacity: 0; - } - 50% { - opacity: 1; - } - 100% { - opacity: 0; - } -} +} \ No newline at end of file diff --git a/client/src/api/ApiUrl.ts b/client/src/api/ApiUrl.ts index e94c7191..36bee57a 100644 --- a/client/src/api/ApiUrl.ts +++ b/client/src/api/ApiUrl.ts @@ -12,6 +12,5 @@ export enum ApiUrl { File = '/api/file', NestedJson = '/api/nestedJson', Partners = '/api/partners', - Email = '/api/email', - Chat = '/api/chat' + Email = '/api/email' } diff --git a/client/src/api/httpClient.ts b/client/src/api/httpClient.ts index 49a341a4..aaed3e1f 100644 --- a/client/src/api/httpClient.ts +++ b/client/src/api/httpClient.ts @@ -8,7 +8,6 @@ import { } from '../interfaces/User'; import { Product } from '../interfaces/Product'; import { OidcClient } from '../interfaces/Auth'; -import { ChatMessage } from '../interfaces/ChatMessage'; import { ApiUrl } from './ApiUrl'; import { makeApiRequest } from './makeApiRequest'; @@ -332,13 +331,3 @@ export function sendSupportEmailRequest( method: 'get' }); } - -export function queryChat(messages: ChatMessage[]): Promise { - return makeApiRequest({ - url: `${ApiUrl.Chat}/query`, - method: 'post', - data: messages - }).then((res) => { - return typeof res === 'string' ? res : ''; - }); -} diff --git a/client/src/interfaces/ChatMessage.ts b/client/src/interfaces/ChatMessage.ts deleted file mode 100644 index e108acf2..00000000 --- a/client/src/interfaces/ChatMessage.ts +++ /dev/null @@ -1,4 +0,0 @@ -export interface ChatMessage { - readonly role: 'user' | 'assistant' | 'system'; - readonly content: string; -} diff --git a/client/src/pages/chat/Chat.tsx b/client/src/pages/chat/Chat.tsx deleted file mode 100644 index aa380abf..00000000 --- a/client/src/pages/chat/Chat.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import React, { FC } from 'react'; -import Header from '../main/Header/Header'; -import ChatWidget from './ChatWidget'; - -export const Chat: FC = () => { - return ( - <> -
-
-
- -
-
- - ); -}; - -export default Chat; diff --git a/client/src/pages/chat/ChatWidget.tsx b/client/src/pages/chat/ChatWidget.tsx deleted file mode 100644 index 4f45f429..00000000 --- a/client/src/pages/chat/ChatWidget.tsx +++ /dev/null @@ -1,122 +0,0 @@ -import React, { - ChangeEvent, - FC, - KeyboardEvent, - useEffect, - useRef, - useState -} from 'react'; -import { queryChat } from '../../api/httpClient'; -import { ChatMessage } from '../../interfaces/ChatMessage'; - -const UnsafeComponent: FC<{ html: string }> = ({ html }) => { - return
; -}; - -export const ChatWidget: FC = () => { - const [chatMessages, setChatMessages] = useState([]); - const [userInput, setUserInput] = useState(''); - const [loading, setLoading] = useState(false); - const messagesRef = useRef(null); - const inputRef = useRef(null); - - useEffect(() => { - inputRef.current?.focus(); - }, []); - - useEffect(() => { - messagesRef.current?.lastElementChild?.scrollIntoView({ - behavior: 'smooth' - }); - }, [chatMessages]); - - const sendMessage = async () => { - if (!userInput.trim()) { - return; - } - - const userMessage: ChatMessage = { role: 'user', content: userInput }; - const messages = [...chatMessages, userMessage]; - setChatMessages(messages); - setLoading(true); - - try { - const answer = await queryChat(messages); - - const serverMessage: ChatMessage = { - role: 'assistant', - content: answer - }; - setChatMessages((messages) => [...messages, serverMessage]); - } catch (error) { - setChatMessages((messages) => [ - ...messages, - { - role: 'assistant', - content: '' - } - ]); - } finally { - setLoading(false); - } - }; - - const handleInputChange = (event: ChangeEvent) => { - setUserInput(event.target.value); - }; - - const handleKeyDown = async (event: KeyboardEvent) => { - if (event.key === 'Enter') { - event.preventDefault(); - setUserInput(''); - await sendMessage(); - } - }; - - return ( -
-
- {chatMessages.map((msg, index) => ( -
- {msg.role === 'user' ? ( - msg.content - ) : msg.content ? ( - - ) : ( - 'Chat API Error' - )} -
- ))} - {loading && ( -
- Typing - - . - . - . - -
- )} -
-
-