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) (#9168)
This commit is contained in:
@@ -124,6 +124,7 @@
|
|||||||
"test:mongodb:4.4.13": "npm run test:mongodb --dbversion=4.4.13",
|
"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: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: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",
|
"posttest:mongodb": "mongodb-runner stop",
|
||||||
"pretest": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=5.3.2} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} mongodb-runner start",
|
"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",
|
"testonly": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=5.3.2} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} TESTING=1 jasmine",
|
||||||
|
|||||||
@@ -2614,16 +2614,16 @@ function isAnyValueRegexStartsWith(values) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function createLiteralRegex(remaining) {
|
function createLiteralRegex(remaining: string) {
|
||||||
return remaining
|
return remaining
|
||||||
.split('')
|
.split('')
|
||||||
.map(c => {
|
.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) {
|
if (c.match(regex) !== null) {
|
||||||
// don't escape alphanumeric characters
|
// Don't escape alphanumeric characters
|
||||||
return c;
|
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}`;
|
return c === `'` ? `''` : `\\${c}`;
|
||||||
})
|
})
|
||||||
.join('');
|
.join('');
|
||||||
@@ -2633,14 +2633,14 @@ function literalizeRegexPart(s: string) {
|
|||||||
const matcher1 = /\\Q((?!\\E).*)\\E$/;
|
const matcher1 = /\\Q((?!\\E).*)\\E$/;
|
||||||
const result1: any = s.match(matcher1);
|
const result1: any = s.match(matcher1);
|
||||||
if (result1 && result1.length > 1 && result1.index > -1) {
|
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 prefix = s.substring(0, result1.index);
|
||||||
const remaining = result1[1];
|
const remaining = result1[1];
|
||||||
|
|
||||||
return literalizeRegexPart(prefix) + createLiteralRegex(remaining);
|
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 matcher2 = /\\Q((?!\\E).*)$/;
|
||||||
const result2: any = s.match(matcher2);
|
const result2: any = s.match(matcher2);
|
||||||
if (result2 && result2.length > 1 && result2.index > -1) {
|
if (result2 && result2.length > 1 && result2.index > -1) {
|
||||||
@@ -2650,14 +2650,18 @@ function literalizeRegexPart(s: string) {
|
|||||||
return literalizeRegexPart(prefix) + createLiteralRegex(remaining);
|
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
|
return s
|
||||||
|
// Remove all instances of \Q and \E
|
||||||
.replace(/([^\\])(\\E)/, '$1')
|
.replace(/([^\\])(\\E)/, '$1')
|
||||||
.replace(/([^\\])(\\Q)/, '$1')
|
.replace(/([^\\])(\\Q)/, '$1')
|
||||||
.replace(/^\\E/, '')
|
.replace(/^\\E/, '')
|
||||||
.replace(/^\\Q/, '')
|
.replace(/^\\Q/, '')
|
||||||
.replace(/([^'])'/g, `$1''`)
|
// Ensure even number of single quote sequences by adding an extra single quote if needed;
|
||||||
.replace(/^'([^'])/, `''$1`);
|
// this ensures that every single quote is escaped
|
||||||
|
.replace(/'+/g, match => {
|
||||||
|
return match.length % 2 === 0 ? match : match + "'";
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
var GeoPointCoder = {
|
var GeoPointCoder = {
|
||||||
|
|||||||
Reference in New Issue
Block a user