CVE-2024-3025 (published 2024-04-10T17:15Z)

mintplex-labs/anything-llm is vulnerable to path traversal attacks due to insufficient validation of user-supplied input in the logo filename functionality. Attackers can exploit this vulnerability by manipulating the logo filename to reference files outside of the restricted directory. This can lead to unauthorized reading or deletion of files by utilizing the `/api/system/upload-logo` and `/api/system/logo` endpoints. The issue stems from the lack of filtering or validation on the logo filename, allowing attackers to target sensitive files such as the application's database.


Weakness information:
cwe_id: CWE-23
cwe_name: Relative Path Traversal
description: The product uses external input to construct a pathname that should be within a restricted directory, but it does not properly neutralize sequences such as ".." that can resolve to a location that is outside of that directory.
extended_description: This allows attackers to traverse the file system to access files or directories that are outside of the restricted directory.
         

Fix information:
repo_url: https://github.com/mintplex-labs/anything-llm
commit_details: repo_url: https://github.com/mintplex-labs/anything-llm
author: timothycarambat
author_date: 2024-03-26 15:25:46-07:00
msg: norm path logo
repository: repo_url: https://github.com/mintplex-labs/anything-llm
repo_name: Mintplex-Labs/anything-llm
description: The all-in-one Desktop & Docker AI application with full RAG and AI Agent capabilities.
repo_language: JavaScript
owner: mintplex-labs
file_changes: filename: logo.js
change_type: ModificationType.MODIFY
diff: @@ -3,6 +3,7 @@ const fs = require("fs");
 const { getType } = require("mime");
 const { v4 } = require("uuid");
 const { SystemSettings } = require("../../models/systemSettings");
+const { normalizePath } = require(".");
 const LOGO_FILENAME = "anything-llm.png";
 
 function validFilename(newFilename = "") {
@@ -21,7 +22,7 @@ async function determineLogoFilepath(defaultFilename = LOGO_FILENAME) {
   const defaultFilepath = path.join(basePath, defaultFilename);
 
   if (currentLogoFilename && validFilename(currentLogoFilename)) {
-    customLogoPath = path.join(basePath, currentLogoFilename);
+    customLogoPath = path.join(basePath, normalizePath(currentLogoFilename));
     return fs.existsSync(customLogoPath) ? customLogoPath : defaultFilepath;
   }
 
@@ -52,11 +53,11 @@ async function renameLogoFile(originalFilename = null) {
   const extname = path.extname(originalFilename) || ".png";
   const newFilename = `${v4()}${extname}`;
   const originalFilepath = process.env.STORAGE_DIR
-    ? path.join(process.env.STORAGE_DIR, "assets", originalFilename)
-    : path.join(__dirname, `../../storage/assets/${originalFilename}`);
+    ? path.join(process.env.STORAGE_DIR, "assets", normalizePath(originalFilename))
+    : path.join(__dirname, `../../storage/assets`, normalizePath(originalFilename));
   const outputFilepath = process.env.STORAGE_DIR
-    ? path.join(process.env.STORAGE_DIR, "assets", newFilename)
-    : path.join(__dirname, `../../storage/assets/${newFilename}`);
+    ? path.join(process.env.STORAGE_DIR, "assets", normalizePath(newFilename))
+    : path.join(__dirname, `../../storage/assets`, normalizePath(newFilename));
 
   fs.renameSync(originalFilepath, outputFilepath);
   return newFilename;
@@ -65,8 +66,8 @@ async function renameLogoFile(originalFilename = null) {
 async function removeCustomLogo(logoFilename = LOGO_FILENAME) {
   if (!logoFilename || !validFilename(logoFilename)) return false;
   const logoPath = process.env.STORAGE_DIR
-    ? path.join(process.env.STORAGE_DIR, `assets/${logoFilename}`)
-    : path.join(__dirname, `../../storage/assets/${logoFilename}`);
+    ? path.join(process.env.STORAGE_DIR, `assets`, normalizePath(logoFilename))
+    : path.join(__dirname, `../../storage/assets`, normalizePath(logoFilename));
   if (fs.existsSync(logoPath)) fs.unlinkSync(logoPath);
   return true;
 }

programming_language: JavaScript