Skip to content

Relation query with a null operand throws an uncaught TypeError (500) #10637

Description

@AdrianCurtin

New Issue Checklist

Filed publicly rather than through the security policy: this is an unhandled exception on malformed input, not a disclosure or an authentication problem. The stack trace stays in the server log, the client gets a generic 500, and the process survives, so it is a per-request failure rather than a crash.

Issue Description

reduceInRelation reads .objectId off each constraint operand without checking that the operand is an object. A null anywhere in a relation query constraint therefore raises an uncaught TypeError and the request fails with a generic 500.

Four call sites dereference unguarded (src/Controllers/DatabaseController.js, at the 9.10.1-alpha.6 tree):

if (constraintKey === 'objectId') {
  relatedIds = [query[key].objectId];                     // :1096
} else if (constraintKey == '$in') {
  relatedIds = query[key]['$in'].map(r => r.objectId);    // :1098
} else if (constraintKey == '$nin') {
  isNegation = true;
  relatedIds = query[key]['$nin'].map(r => r.objectId);   // :1101
} else if (constraintKey == '$ne') {
  isNegation = true;
  relatedIds = [query[key]['$ne'].objectId];              // :1104
}

A non-object operand that is not null, for example {"$in": [7]}, is harmless: (7).objectId is undefined, which contributes no id. Only null throws, because it cannot be boxed.

This is reachable with the application id alone. No master key, session token or client key is required.

Related: #4742 reported the same shape in the same function ($in given a non-array, so .map is not a function) and was closed in 2018. The operands here are still unvalidated.

Steps to reproduce

  1. Create a class Owner with a Relation field named friends, and save one object so the field is in the schema.
  2. Send a query whose relation constraint contains a null:
GET /parse/classes/Owner?where={"friends":{"$in":[null]}}
X-Parse-Application-Id: <appId>

Each of these reproduces it:

Constraint Result
{"friends":{"$in":[null]}} 500
{"friends":{"$nin":[null]}} 500
{"friends":{"$ne":null,"$in":[]}} 500
{"friends":{"$ne":null,"$nin":[]}} 500
{"friends":{"$ne":null}} 200, empty results

The last row does not throw because a bare falsy $ne fails the gate at :1084-1090, which requires one of $in, $ne, $nin to be truthy, so the extraction loop never runs. Adding any truthy sibling operator satisfies the gate and the falsy $ne is then dereferenced. An empty array is enough, since [] is truthy.

Actual Outcome

500 {"code":1,"message":"Internal server error."}

Expected Outcome

Either Parse.Error.INVALID_JSON, or the null treated the way every other operand without an objectId is already treated: it yields undefined and contributes no related id, so the constraint resolves to no owners. The second matches the existing behavior for {"$in":[7]} and for {"$in":[{"foo":1}]}, which both return an empty result rather than an error.

Environment

Server

  • Parse Server version: 9.10.1-alpha.6 (ca75b1fed69921f7843dc20b921d9b02dc352626)
  • Operating system: macOS 26.5.2
  • Local or remote host: local

Database

  • System (MongoDB or Postgres): MongoDB
  • Database version: 7.0.25
  • Local or remote host: local

Client

  • SDK (iOS, Android, JavaScript, PHP, Unity, etc): REST
  • SDK version: n/a

The Postgres adapter was not tested.

Logs

TypeError: Cannot read properties of null (reading 'objectId')
    at .../lib/Controllers/DatabaseController.js:946:45
    at Array.map (<anonymous>)
    at .../lib/Controllers/DatabaseController.js:934:43
    at Array.map (<anonymous>)
    at DatabaseController.reduceInRelation (.../lib/Controllers/DatabaseController.js:923:42)
    at .../lib/Controllers/DatabaseController.js:1215:238
    at async _UnsafeRestQuery.runFind (.../lib/RestQuery.js:785:19)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions