CVE-2023-5833 (published 2023-10-30T13:15Z)

Improper Access Control in GitHub repository mintplex-labs/anything-llm prior to 0.1.0.

CVSS v3 base score: 8.8, HIGH (CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H)

Weakness information:
cwe_id: CWE-284
cwe_name: Improper Access Control
description: The product does not restrict or incorrectly restricts access to a resource from an unauthorized actor.
extended_description: 
            Access control involves the use of several protection mechanisms such as:
            
               Authentication (proving the identity of an actor)
               Authorization (ensuring that a given actor can access a resource), and
               Accountability (tracking of activities that were performed)
            
            When any mechanism is not applied or otherwise fails, attackers can compromise the security of the product by gaining privileges, reading sensitive information, executing commands, evading detection, etc.
            There are two distinct behaviors that can introduce access control weaknesses:
               
                  Specification: incorrect privileges, permissions, ownership, etc. are explicitly specified for either the user or the resource (for example, setting a password file to be world-writable, or giving administrator capabilities to a guest user). This action could be performed by the program or the administrator.
                  Enforcement: the mechanism contains errors that prevent it from properly enforcing the specified access control requirements (e.g., allowing the user to specify their own privileges, or allowing a syntactically-incorrect ACL to produce insecure settings). This problem occurs within the program itself, in that it does not actually enforce the intended security policy that the administrator specifies.
               
         
		 

Fix information:
repo_url: https://github.com/mintplex-labs/anything-llm
commit_details: repo_url: https://github.com/mintplex-labs/anything-llm
author: Timothy Carambat
author_date: 2023-09-29 19:44:40+02:00
msg: Prevent updates of specific keys via API (#256)

prevent non-admin from updating settings
prevent password updates in multi-user mdoe
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: system.js
change_type: ModificationType.MODIFY
diff: @@ -239,6 +239,16 @@ function systemEndpoints(app) {
     async (request, response) => {
       try {
         const body = reqBody(request);
+
+        // Only admins can update the ENV settings.
+        if (multiUserMode(response)) {
+          const user = await userFromSession(request, response);
+          if (!user || user?.role !== "admin") {
+            response.sendStatus(401).end();
+            return;
+          }
+        }
+
         const { newValues, error } = updateENV(body);
         if (process.env.NODE_ENV === "production") await dumpENV();
         response.status(200).json({ newValues, error });
@@ -254,11 +264,21 @@ function systemEndpoints(app) {
     [validatedRequest],
     async (request, response) => {
       try {
+        // Cannot update password in multi - user mode.
+        if (multiUserMode(response)) {
+          response.sendStatus(401).end();
+          return;
+        }
+
         const { usePassword, newPassword } = reqBody(request);
-        const { error } = updateENV({
-          AuthToken: usePassword ? newPassword : "",
-          JWTSecret: usePassword ? v4() : "",
-        });
+        const { error } = updateENV(
+          {
+            AuthToken: usePassword ? newPassword : "",
+            JWTSecret: usePassword ? v4() : "",
+          },
+          true
+        );
+        if (process.env.NODE_ENV === "production") await dumpENV();
         response.status(200).json({ success: !error, error });
       } catch (e) {
         console.log(e.message, e);
@@ -293,8 +313,15 @@ function systemEndpoints(app) {
           limit_user_messages: false,
           message_limit: 25,
         });
-        process.env.AUTH_TOKEN = null;
-        process.env.JWT_SECRET = process.env.JWT_SECRET ?? v4(); // Make sure JWT_SECRET is set for JWT issuance.
+
+        updateENV(
+          {
+            AuthToken: null,
+            JWTSecret: process.env.JWT_SECRET ?? v4(),
+          },
+          true
+        );
+        if (process.env.NODE_ENV === "production") await dumpENV();
         await Telemetry.sendTelemetry("enabled_multi_user_mode");
         response.status(200).json({ success: !!user, error });
       } catch (e) {

programming_language: JavaScript
filename: updateENV.js
change_type: ModificationType.MODIFY
diff: @@ -72,11 +72,11 @@ const KEY_MAPPING = {
   // System Settings
   AuthToken: {
     envKey: "AUTH_TOKEN",
-    checks: [],
+    checks: [requiresForceMode],
   },
   JWTSecret: {
     envKey: "JWT_SECRET",
-    checks: [],
+    checks: [requiresForceMode],
   },
   // Not supported yet.
   // 'StorageDir': 'STORAGE_DIR',
@@ -143,11 +143,15 @@ function validAzureURL(input = "") {
   }
 }
 
+function requiresForceMode(_, forceModeEnabled = false) {
+  return forceModeEnabled === true ? null : "Cannot set this setting.";
+}
+
 // This will force update .env variables which for any which reason were not able to be parsed or
 // read from an ENV file as this seems to be a complicating step for many so allowing people to write
 // to the process will at least alleviate that issue. It does not perform comprehensive validity checks or sanity checks
 // and is simply for debugging when the .env not found issue many come across.
-function updateENV(newENVs = {}) {
+function updateENV(newENVs = {}, force = false) {
   let error = "";
   const validKeys = Object.keys(KEY_MAPPING);
   const ENV_KEYS = Object.keys(newENVs).filter(
@@ -159,7 +163,7 @@ function updateENV(newENVs = {}) {
     const { envKey, checks } = KEY_MAPPING[key];
     const value = newENVs[key];
     const errors = checks
-      .map((validityCheck) => validityCheck(value))
+      .map((validityCheck) => validityCheck(value, force))
       .filter((err) => typeof err === "string");
 
     if (errors.length > 0) {

programming_language: JavaScript
filename: migrateFromSqlite.js
change_type: ModificationType.MODIFY
diff: @@ -253,7 +253,7 @@ async function migrateTable(tableName, migrateRowFunc) {
   // Check table exists
   const { count } = await db.get(
     `SELECT COUNT(*) as count FROM sqlite_master WHERE name='${tableName}'`
-  )
+  );
   if (count === 0) {
     console.log(
       `${tableName} does not exist in legacy DB - nothing to migrate - skipping.`

programming_language: TypeScript