CVE-2024-39309 (published 2024-07-01T22:15Z)

Parse Server is an open source backend that can be deployed to any infrastructure that can run Node.js. A vulnerability in versions prior to 6.5.7 and 7.1.0 allows SQL injection when Parse Server is configured to use the PostgreSQL database. The algorithm to detect SQL injection has been improved in versions 6.5.7 and 7.1.0. No known workarounds are available.


Weakness information:
cwe_id: NVD-CWE-noinfo
cwe_name: Insufficient Information
description: There is insufficient information about the issue to classify it; details are unkown or unspecified.
extended_description: Insufficient Information

Fix information:
repo_url: https://github.com/parse-community/parse-server
commit_details: repo_url: https://github.com/parse-community/parse-server
author: Manuel
author_date: 2024-06-30 03:34:08+02:00
msg: fix: SQL injection when using Parse Server with PostgreSQL; fixes security vulnerability [GHSA-c2hr-cqg6-8j6r](https://github.com/parse-community/parse-server/security/advisories/GHSA-c2hr-cqg6-8j6r) (#9167)
repository: repo_url: https://github.com/parse-community/parse-server
repo_name: parse-community/parse-server
description: Parse Server for Node.js / Express
repo_language: JavaScript
owner: parse-community
file_changes: filename: package.json
change_type: ModificationType.MODIFY
diff: @@ -125,6 +125,7 @@
     "test:mongodb:5.3.2": "npm run test:mongodb --dbversion=5.3.2",
     "test:mongodb:6.0.2": "npm run test:mongodb --dbversion=6.0.2",
     "test:mongodb:7.0.1": "npm run test:mongodb --dbversion=7.0.1",
+    "test:postgres:testonly": "cross-env PARSE_SERVER_TEST_DB=postgres PARSE_SERVER_TEST_DATABASE_URI=postgres://postgres:password@localhost:5432/parse_server_postgres_adapter_test_database npm run testonly",
     "pretest": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=5.3.2} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} mongodb-runner start -t ${MONGODB_TOPOLOGY} --version ${MONGODB_VERSION} -- --port 27017",
     "testonly": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=5.3.2} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} TESTING=1 jasmine",
     "test": "npm run testonly",

programming_language: JavaScript
filename: PostgresStorageAdapter.js
change_type: ModificationType.MODIFY
diff: @@ -2614,16 +2614,16 @@ function isAnyValueRegexStartsWith(values) {
   });
 }
 
-function createLiteralRegex(remaining) {
+function createLiteralRegex(remaining: string) {
   return remaining
     .split('')
     .map(c => {
-      const regex = RegExp('[0-9 ]|\\p{L}', 'u'); // Support all unicode letter chars
+      const regex = RegExp('[0-9 ]|\\p{L}', 'u'); // Support all Unicode letter chars
       if (c.match(regex) !== null) {
-        // don't escape alphanumeric characters
+        // Don't escape alphanumeric characters
         return c;
       }
-      // escape everything else (single quotes with single quotes, everything else with a backslash)
+      // Escape everything else (single quotes with single quotes, everything else with a backslash)
       return c === `'` ? `''` : `\\${c}`;
     })
     .join('');
@@ -2633,14 +2633,14 @@ function literalizeRegexPart(s: string) {
   const matcher1 = /\\Q((?!\\E).*)\\E$/;
   const result1: any = s.match(matcher1);
   if (result1 && result1.length > 1 && result1.index > -1) {
-    // process regex that has a beginning and an end specified for the literal text
+    // Process Regex that has a beginning and an end specified for the literal text
     const prefix = s.substring(0, result1.index);
     const remaining = result1[1];
 
     return literalizeRegexPart(prefix) + createLiteralRegex(remaining);
   }
 
-  // process regex that has a beginning specified for the literal text
+  // Process Regex that has a beginning specified for the literal text
   const matcher2 = /\\Q((?!\\E).*)$/;
   const result2: any = s.match(matcher2);
   if (result2 && result2.length > 1 && result2.index > -1) {
@@ -2650,14 +2650,18 @@ function literalizeRegexPart(s: string) {
     return literalizeRegexPart(prefix) + createLiteralRegex(remaining);
   }
 
-  // remove all instances of \Q and \E from the remaining text & escape single quotes
+  // Remove problematic chars from remaining text
   return s
+    // Remove all instances of \Q and \E
     .replace(/([^\\])(\\E)/, '$1')
     .replace(/([^\\])(\\Q)/, '$1')
     .replace(/^\\E/, '')
     .replace(/^\\Q/, '')
-    .replace(/([^'])'/g, `$1''`)
-    .replace(/^'([^'])/, `''$1`);
+    // Ensure even number of single quote sequences by adding an extra single quote if needed;
+    // this ensures that every single quote is escaped
+    .replace(/'+/g, match => {
+      return match.length % 2 === 0 ? match : match + "'";
+    });
 }
 
 var GeoPointCoder = {

programming_language: TypeScript