CVE-2024-29027: Insufficient Information (NVD-CWE-noinfo)
value: Parse Server is an open source backend that can be deployed to any infrastructure that can run Node.js. Prior to versions 6.5.5 and 7.0.0-alpha.29, calling an invalid Parse Server Cloud Function name or Cloud Job name crashes the server and may allow for code injection, internal store manipulation or remote code execution. The patch in versions 6.5.5 and 7.0.0-alpha.29 added string sanitation for Cloud Function name and Cloud Job name. As a workaround, sanitize the Cloud Function name and Cloud Job name before it reaches Parse Server.
CWE description: There is insufficient information about the issue to classify it; details are unkown or unspecified.
Published: 2024-03-19T19:15Z
Repository: https://github.com/parse-community/parse-server
Language: JavaScript
Files changed: spec/ParseHooks.spec.js, src/triggers.js
Commit date: 2024-03-19 17:42:00 +0100
Commit message: fix: Server crashes on invalid Cloud Function or Cloud Job name; fixes security vulnerability [GHSA-6hh7-46r2-vf29](https://github.com/parse-community/parse-server/security/advisories/GHSA-6hh7-46r2-vf29) (#9024)


Diff: diff --git a/spec/ParseHooks.spec.js b/spec/ParseHooks.spec.js
index 16a2e17be3..8d7c653fa2 100644
--- a/spec/ParseHooks.spec.js
+++ b/spec/ParseHooks.spec.js
@@ -692,5 +692,38 @@ describe('triggers', () => {
       context
     );
     expect(req.context).toBeUndefined();
   });
 });
+
+describe('sanitizing names', () => {
+  const invalidNames = [
+    `test'%3bdeclare%20@q%20varchar(99)%3bset%20@q%3d'%5c%5cxxxxxxxxxxxxxxx.yyyyy'%2b'fy.com%5cxus'%3b%20exec%20master.dbo.xp_dirtree%20@q%3b--%20`,
+    `test.function.name`,
+  ];
+
+  it('should not crash server and return error on invalid Cloud Function name', async () => {
+    for (const invalidName of invalidNames) {
+      let error;
+      try {
+        await Parse.Cloud.run(invalidName);
+      } catch (err) {
+        error = err;
+      }
+      expect(error).toBeDefined();
+      expect(error.message).toMatch(/Invalid function/);
+    }
+  });
+
+  it('should not crash server and return error on invalid Cloud Job name', async () => {
+    for (const invalidName of invalidNames) {
+      let error;
+      try {
+        await Parse.Cloud.startJob(invalidName);
+      } catch (err) {
+        error = err;
+      }
+      expect(error).toBeDefined();
+      expect(error.message).toMatch(/Invalid job/);
+    }
+  });
+});
diff --git a/src/triggers.js b/src/triggers.js
index bfebde2e83..34ace35b4b 100644
--- a/src/triggers.js
+++ b/src/triggers.js
@@ -84,19 +84,25 @@ const Category = {
   Jobs: 'Jobs',
   Triggers: 'Triggers',
 };
 
 function getStore(category, name, applicationId) {
+  const invalidNameRegex = /['"`]/;
+  if (invalidNameRegex.test(name)) {
+    // Prevent a malicious user from injecting properties into the store
+    return {};
+  }
+
   const path = name.split('.');
   path.splice(-1); // remove last component
   applicationId = applicationId || Parse.applicationId;
   _triggerStore[applicationId] = _triggerStore[applicationId] || baseStore();
   let store = _triggerStore[applicationId][category];
   for (const component of path) {
     store = store[component];
     if (!store) {
-      return undefined;
+      return {};
     }
   }
   return store;
 }
 

Vulnerable code:       return undefined;
Fixed code: 
describe('sanitizing names', () => {
  const invalidNames = [
    `test'%3bdeclare%20@q%20varchar(99)%3bset%20@q%3d'%5c%5cxxxxxxxxxxxxxxx.yyyyy'%2b'fy.com%5cxus'%3b%20exec%20master.dbo.xp_dirtree%20@q%3b--%20`,
    `test.function.name`,
  ];

  it('should not crash server and return error on invalid Cloud Function name', async () => {
    for (const invalidName of invalidNames) {
      let error;
      try {
        await Parse.Cloud.run(invalidName);
      } catch (err) {
        error = err;
      }
      expect(error).toBeDefined();
      expect(error.message).toMatch(/Invalid function/);
    }
  });

  it('should not crash server and return error on invalid Cloud Job name', async () => {
    for (const invalidName of invalidNames) {
      let error;
      try {
        await Parse.Cloud.startJob(invalidName);
      } catch (err) {
        error = err;
      }
      expect(error).toBeDefined();
      expect(error.message).toMatch(/Invalid job/);
    }
  });
});
  const invalidNameRegex = /['"`]/;
  if (invalidNameRegex.test(name)) {
    // Prevent a malicious user from injecting properties into the store
    return {};
  }

      return {};