Skip to content

Commit 2edf1e4

Browse files
authored
fix: SQL injection when using Parse Server with PostgreSQL; fixes security vulnerability [GHSA-c2hr-cqg6-8j6r](GHSA-c2hr-cqg6-8j6r) (#9167)
1 parent ae72cf0 commit 2edf1e4

File tree

2 files changed

+14
-9
lines changed

2 files changed

+14
-9
lines changed

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@
125125
"test:mongodb:5.3.2": "npm run test:mongodb --dbversion=5.3.2",
126126
"test:mongodb:6.0.2": "npm run test:mongodb --dbversion=6.0.2",
127127
"test:mongodb:7.0.1": "npm run test:mongodb --dbversion=7.0.1",
128+
"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",
128129
"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",
129130
"testonly": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=5.3.2} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} TESTING=1 jasmine",
130131
"test": "npm run testonly",

src/Adapters/Storage/Postgres/PostgresStorageAdapter.js

+13-9
Original file line numberDiff line numberDiff line change
@@ -2614,16 +2614,16 @@ function isAnyValueRegexStartsWith(values) {
26142614
});
26152615
}
26162616
2617-
function createLiteralRegex(remaining) {
2617+
function createLiteralRegex(remaining: string) {
26182618
return remaining
26192619
.split('')
26202620
.map(c => {
2621-
const regex = RegExp('[0-9 ]|\\p{L}', 'u'); // Support all unicode letter chars
2621+
const regex = RegExp('[0-9 ]|\\p{L}', 'u'); // Support all Unicode letter chars
26222622
if (c.match(regex) !== null) {
2623-
// don't escape alphanumeric characters
2623+
// Don't escape alphanumeric characters
26242624
return c;
26252625
}
2626-
// escape everything else (single quotes with single quotes, everything else with a backslash)
2626+
// Escape everything else (single quotes with single quotes, everything else with a backslash)
26272627
return c === `'` ? `''` : `\\${c}`;
26282628
})
26292629
.join('');
@@ -2633,14 +2633,14 @@ function literalizeRegexPart(s: string) {
26332633
const matcher1 = /\\Q((?!\\E).*)\\E$/;
26342634
const result1: any = s.match(matcher1);
26352635
if (result1 && result1.length > 1 && result1.index > -1) {
2636-
// process regex that has a beginning and an end specified for the literal text
2636+
// Process Regex that has a beginning and an end specified for the literal text
26372637
const prefix = s.substring(0, result1.index);
26382638
const remaining = result1[1];
26392639

26402640
return literalizeRegexPart(prefix) + createLiteralRegex(remaining);
26412641
}
26422642

2643-
// process regex that has a beginning specified for the literal text
2643+
// Process Regex that has a beginning specified for the literal text
26442644
const matcher2 = /\\Q((?!\\E).*)$/;
26452645
const result2: any = s.match(matcher2);
26462646
if (result2 && result2.length > 1 && result2.index > -1) {
@@ -2650,14 +2650,18 @@ function literalizeRegexPart(s: string) {
26502650
return literalizeRegexPart(prefix) + createLiteralRegex(remaining);
26512651
}
26522652

2653-
// remove all instances of \Q and \E from the remaining text & escape single quotes
2653+
// Remove problematic chars from remaining text
26542654
return s
2655+
// Remove all instances of \Q and \E
26552656
.replace(/([^\\])(\\E)/, '$1')
26562657
.replace(/([^\\])(\\Q)/, '$1')
26572658
.replace(/^\\E/, '')
26582659
.replace(/^\\Q/, '')
2659-
.replace(/([^'])'/g, `$1''`)
2660-
.replace(/^'([^'])/, `''$1`);
2660+
// Ensure even number of single quote sequences by adding an extra single quote if needed;
2661+
// this ensures that every single quote is escaped
2662+
.replace(/'+/g, match => {
2663+
return match.length % 2 === 0 ? match : match + "'";
2664+
});
26612665
}
26622666

26632667
var GeoPointCoder = {

0 commit comments

Comments
 (0)