CVE-2024-39309
Description
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.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
parse-servernpm | < 6.5.7 | 6.5.7 |
parse-servernpm | >= 7.0.0, < 7.1.0 | 7.1.0 |
Patches
2f332d5457760fix: 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) (#9168)
2 files changed · +14 −9
package.json+1 −0 modified@@ -124,6 +124,7 @@ "test:mongodb:4.4.13": "npm run test:mongodb --dbversion=4.4.13", "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: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", "posttest:mongodb": "mongodb-runner stop", "pretest": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=5.3.2} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} mongodb-runner start", "testonly": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=5.3.2} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} TESTING=1 jasmine",
src/Adapters/Storage/Postgres/PostgresStorageAdapter.js+13 −9 modified@@ -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 = {
2edf1e4c0363fix: 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)
2 files changed · +14 −9
package.json+1 −0 modified@@ -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",
src/Adapters/Storage/Postgres/PostgresStorageAdapter.js+13 −9 modified@@ -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 = {
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
7- github.com/advisories/GHSA-c2hr-cqg6-8j6rghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2024-39309ghsaADVISORY
- github.com/parse-community/parse-server/commit/2edf1e4c0363af01e97a7fbc97694f851b7d1ff3nvdWEB
- github.com/parse-community/parse-server/commit/f332d54577608c5ad927255e06d8c694e2e0ff5bnvdWEB
- github.com/parse-community/parse-server/pull/9167nvdWEB
- github.com/parse-community/parse-server/pull/9168nvdWEB
- github.com/parse-community/parse-server/security/advisories/GHSA-c2hr-cqg6-8j6rnvdWEB
News mentions
0No linked articles in our index yet.