VYPR
Medium severity4.3NVD Advisory· Published Mar 31, 2026· Updated Apr 2, 2026

CVE-2026-34595

CVE-2026-34595

Description

Parse Server is an open source backend that can be deployed to any infrastructure that can run Node.js. Prior to versions 8.6.70 and 9.7.0-alpha.18, an authenticated user with find class-level permission can bypass the protectedFields class-level permission setting on LiveQuery subscriptions. By sending a subscription with a $or, $and, or $nor operator value as a plain object with numeric keys and a length property (an "array-like" object) instead of an array, the protected-field guard is bypassed. The subscription event firing acts as a binary oracle, allowing the attacker to infer whether a protected field matches a given test value. This issue has been patched in versions 8.6.70 and 9.7.0-alpha.18.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
parse-servernpm
>= 9.0.0, < 9.7.0-alpha.169.7.0-alpha.16
parse-servernpm
< 8.6.708.6.70

Affected products

16
  • cpe:2.3:a:parseplatform:parse-server:9.7.0:alpha10:*:*:*:node.js:*:*+ 15 more
    • cpe:2.3:a:parseplatform:parse-server:9.7.0:alpha10:*:*:*:node.js:*:*
    • cpe:2.3:a:parseplatform:parse-server:9.7.0:alpha11:*:*:*:node.js:*:*
    • cpe:2.3:a:parseplatform:parse-server:9.7.0:alpha12:*:*:*:node.js:*:*
    • cpe:2.3:a:parseplatform:parse-server:9.7.0:alpha13:*:*:*:node.js:*:*
    • cpe:2.3:a:parseplatform:parse-server:9.7.0:alpha14:*:*:*:node.js:*:*
    • cpe:2.3:a:parseplatform:parse-server:9.7.0:alpha15:*:*:*:node.js:*:*
    • cpe:2.3:a:parseplatform:parse-server:9.7.0:alpha1:*:*:*:node.js:*:*
    • cpe:2.3:a:parseplatform:parse-server:9.7.0:alpha2:*:*:*:node.js:*:*
    • cpe:2.3:a:parseplatform:parse-server:9.7.0:alpha3:*:*:*:node.js:*:*
    • cpe:2.3:a:parseplatform:parse-server:9.7.0:alpha4:*:*:*:node.js:*:*
    • cpe:2.3:a:parseplatform:parse-server:9.7.0:alpha5:*:*:*:node.js:*:*
    • cpe:2.3:a:parseplatform:parse-server:9.7.0:alpha6:*:*:*:node.js:*:*
    • cpe:2.3:a:parseplatform:parse-server:9.7.0:alpha7:*:*:*:node.js:*:*
    • cpe:2.3:a:parseplatform:parse-server:9.7.0:alpha8:*:*:*:node.js:*:*
    • cpe:2.3:a:parseplatform:parse-server:9.7.0:alpha9:*:*:*:node.js:*:*
    • cpe:2.3:a:parseplatform:parse-server:*:*:*:*:*:node.js:*:*range: <8.6.70

Patches

2
ffad0ec6b971

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)) (#10351)

4 files changed · +3589 3449
  • spec/vulnerabilities.spec.js+3557 3437 modified
  • src/LiveQuery/ParseLiveQueryServer.ts+16 12 modified
    @@ -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));
                   }
    
  • src/LiveQuery/QueryTools.js+9 0 modified
    @@ -206,6 +206,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;
    @@ -214,6 +217,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;
    @@ -222,6 +228,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;
    
  • src/RestQuery.js+7 0 modified
    @@ -904,6 +904,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));
           }
    
f63fd1a3fe0a

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)

4 files changed · +3867 3727
  • spec/vulnerabilities.spec.js+3835 3715 modified
  • src/LiveQuery/ParseLiveQueryServer.ts+16 12 modified
    @@ -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));
                   }
    
  • src/LiveQuery/QueryTools.js+9 0 modified
    @@ -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;
    
  • src/RestQuery.js+7 0 modified
    @@ -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));
           }
    

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

News mentions

0

No linked articles in our index yet.