fix: LiveQuery protected-field guard bypass via array-like logical operator value ([GHSA-mmg8-87c5-jrc2](https://github.com/parse-community/parse-server/security/advisories/GHSA-mmg8-87c5-jrc2)) (#10350)

This commit is contained in:
Manuel
2026-03-29 19:36:52 +01:00
committed by GitHub
parent f897d83e2e
commit f63fd1a3fe
4 changed files with 4214 additions and 4074 deletions
+4182 -4062
View File
File diff suppressed because it is too large Load Diff
+16 -12
View File
@@ -555,6 +555,16 @@ class ParseLiveQueryServer {
if (typeof where !== 'object' || where === null) {
return;
}
for (const op of ['$or', '$and', '$nor']) {
if (where[op] !== undefined && !Array.isArray(where[op])) {
throw new Parse.Error(Parse.Error.INVALID_QUERY, `${op} must be an array`);
}
if (Array.isArray(where[op])) {
where[op].forEach((subQuery: any) => {
this._validateQueryConstraints(subQuery);
});
}
}
for (const key of Object.keys(where)) {
const constraint = where[key];
if (typeof constraint === 'object' && constraint !== null) {
@@ -582,18 +592,6 @@ class ParseLiveQueryServer {
);
}
}
for (const op of ['$or', '$and', '$nor']) {
if (Array.isArray(constraint[op])) {
constraint[op].forEach((subQuery: any) => {
this._validateQueryConstraints(subQuery);
});
}
}
if (Array.isArray(where[key])) {
where[key].forEach((subQuery: any) => {
this._validateQueryConstraints(subQuery);
});
}
}
}
}
@@ -1048,6 +1046,9 @@ class ParseLiveQueryServer {
return;
}
for (const op of ['$or', '$and', '$nor']) {
if (where[op] !== undefined && !Array.isArray(where[op])) {
throw new Parse.Error(Parse.Error.INVALID_QUERY, `${op} must be an array`);
}
if (Array.isArray(where[op])) {
for (const subQuery of where[op]) {
checkDepth(subQuery, depth + 1);
@@ -1111,6 +1112,9 @@ class ParseLiveQueryServer {
}
}
for (const op of ['$or', '$and', '$nor']) {
if (where[op] !== undefined && !Array.isArray(where[op])) {
throw new Parse.Error(Parse.Error.INVALID_QUERY, `${op} must be an array`);
}
if (Array.isArray(where[op])) {
where[op].forEach((subQuery: any) => checkWhere(subQuery));
}
+9
View File
@@ -213,6 +213,9 @@ function matchesKeyConstraints(object, key, constraints) {
}
var i;
if (key === '$or') {
if (!Array.isArray(constraints)) {
return false;
}
for (i = 0; i < constraints.length; i++) {
if (matchesQuery(object, constraints[i])) {
return true;
@@ -221,6 +224,9 @@ function matchesKeyConstraints(object, key, constraints) {
return false;
}
if (key === '$and') {
if (!Array.isArray(constraints)) {
return false;
}
for (i = 0; i < constraints.length; i++) {
if (!matchesQuery(object, constraints[i])) {
return false;
@@ -229,6 +235,9 @@ function matchesKeyConstraints(object, key, constraints) {
return true;
}
if (key === '$nor') {
if (!Array.isArray(constraints)) {
return false;
}
for (i = 0; i < constraints.length; i++) {
if (matchesQuery(object, constraints[i])) {
return false;
+7
View File
@@ -924,6 +924,13 @@ _UnsafeRestQuery.prototype.denyProtectedFields = async function () {
}
}
for (const op of ['$or', '$and', '$nor']) {
if (where[op] !== undefined && !Array.isArray(where[op])) {
throw createSanitizedError(
Parse.Error.INVALID_QUERY,
`${op} must be an array`,
this.config
);
}
if (Array.isArray(where[op])) {
where[op].forEach(subQuery => checkWhere(subQuery));
}