CVE-2024-3570: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting') (CWE-79)
value: A stored Cross-Site Scripting (XSS) vulnerability exists in the chat functionality of the mintplex-labs/anything-llm repository, allowing attackers to execute arbitrary JavaScript in the context of a user's session. By manipulating the ChatBot responses, an attacker can inject malicious scripts to perform actions on behalf of the user, such as creating a new admin account or changing the user's password, leading to a complete takeover of the AnythingLLM application. The vulnerability stems from the improper sanitization of user and ChatBot input, specifically through the use of `dangerouslySetInnerHTML`. Successful exploitation requires convincing an admin to add a malicious LocalAI ChatBot to their AnythingLLM instance.
CWE description: The product does not neutralize or incorrectly neutralizes user-controllable input before it is placed in output that is used as a web page that is served to other users.
Published: 2024-04-10T17:15Z
Repository: https://github.com/mintplex-labs/anything-llm
Language: JSON
Files changed: frontend/package.json, frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/HistoricalMessage/index.jsx, frontend/yarn.lock
Commit date: 2024-01-11 09:57:59 -0800
Commit message: Patch minor XSS opportunity where user can self-XSS themselves.  (#574)

Patch minor XSS opportunity where user can self-XSS themselvess. There is not real vuln here as any instance is not public facing
Diff: diff --git a/frontend/package.json b/frontend/package.json
index ff2698953..86e552ab7 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -14,10 +14,11 @@
     "@esbuild-plugins/node-globals-polyfill": "^0.1.1",
     "@metamask/jazzicon": "^2.0.0",
     "@microsoft/fetch-event-source": "^2.0.1",
     "@phosphor-icons/react": "^2.0.13",
     "buffer": "^6.0.3",
+    "dompurify": "^3.0.8",
     "he": "^1.2.0",
     "highlight.js": "^11.9.0",
     "lodash.debounce": "^4.0.8",
     "markdown-it": "^13.0.1",
     "pluralize": "^8.0.0",
diff --git a/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/HistoricalMessage/index.jsx b/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/HistoricalMessage/index.jsx
index f0605a372..4637b1cd7 100644
--- a/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/HistoricalMessage/index.jsx
+++ b/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/HistoricalMessage/index.jsx
@@ -4,10 +4,12 @@ import Jazzicon from "../../../../UserIcon";
 import renderMarkdown from "@/utils/chat/markdown";
 import { userFromStorage } from "@/utils/request";
 import Citations from "../Citation";
 import { AI_BACKGROUND_COLOR, USER_BACKGROUND_COLOR } from "@/utils/constants";
 import { v4 } from "uuid";
+import createDOMPurify from "dompurify";
+const DOMPurify = createDOMPurify(window);
 
 const HistoricalMessage = forwardRef(
   (
     { uuid = v4(), message, role, workspace, sources = [], error = false },
     ref
@@ -43,11 +45,13 @@ const HistoricalMessage = forwardRef(
                 respond to message.
               </span>
             ) : (
               <span
                 className={`whitespace-pre-line text-white font-normal text-sm md:text-sm flex flex-col gap-y-1 mt-2`}
-                dangerouslySetInnerHTML={{ __html: renderMarkdown(message) }}
+                dangerouslySetInnerHTML={{
+                  __html: DOMPurify.sanitize(renderMarkdown(message)),
+                }}
               />
             )}
           </div>
           {role === "assistant" && <Citations sources={sources} />}
         </div>
diff --git a/frontend/yarn.lock b/frontend/yarn.lock
index e7b223df9..c9181f15f 100644
--- a/frontend/yarn.lock
+++ b/frontend/yarn.lock
@@ -1019,10 +1019,15 @@ doctrine@^3.0.0:
   resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961"
   integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==
   dependencies:
     esutils "^2.0.2"
 
+dompurify@^3.0.8:
+  version "3.0.8"
+  resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-3.0.8.tgz#e0021ab1b09184bc8af7e35c7dd9063f43a8a437"
+  integrity sha512-b7uwreMYL2eZhrSCRC4ahLTeZcPZxSmYfmcQGXGkXiZSNW1X85v+SDM5KsWcpivIiUBH47Ji7NtyUdpLeF5JZQ==
+
 electron-to-chromium@^1.4.535:
   version "1.4.576"
   resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.576.tgz#0c6940fdc0d60f7e34bd742b29d8fa847c9294d1"
   integrity sha512-yXsZyXJfAqzWk1WKryr0Wl0MN2D47xodPvEEwlVePBnhU5E7raevLQR+E6b9JAD3GfL/7MbAL9ZtWQQPcLx7wA==
 

Vulnerable code:                 dangerouslySetInnerHTML={{ __html: renderMarkdown(message) }}
Fixed code:     "dompurify": "^3.0.8",
import createDOMPurify from "dompurify";
const DOMPurify = createDOMPurify(window);
                dangerouslySetInnerHTML={{
                  __html: DOMPurify.sanitize(renderMarkdown(message)),
                }}
dompurify@^3.0.8:
  version "3.0.8"
  resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-3.0.8.tgz#e0021ab1b09184bc8af7e35c7dd9063f43a8a437"
  integrity sha512-b7uwreMYL2eZhrSCRC4ahLTeZcPZxSmYfmcQGXGkXiZSNW1X85v+SDM5KsWcpivIiUBH47Ji7NtyUdpLeF5JZQ==