Skip to content

allowCustomObjectId accepts an operation as objectId and creates an unreachable user #10639

Description

@AdrianCurtin

New Issue Checklist

Issue Description

With allowCustomObjectId: true, a create whose objectId is an operation envelope such as
{"__op":"Delete"} is accepted, and the row that results is unusable in three separate ways.

getType returns nothing for an operation envelope, so validateObject hits
if (!expected) { continue; } and never compares the value against the String type of the
objectId column. The value then reaches the database layer, where the driver generates its own
_id.

The outcome is a _User that:

  • is stored under a Mongo-generated _id such as 6a862fa06e17011e3f5a9c96, which is not a Parse
    objectId and is not what the client was told,
  • has _rperm and _wperm of ["[object Object]"], because the owner ACL is keyed by stringifying
    the same operation object, so the row is readable and writable by nobody,
  • and is echoed back to the client with "objectId": {"__op":"Delete"}, along with a working
    session token for a user that session can never read.

A non-operation value behaves correctly and is refused with 111 INCORRECT_TYPE, so this is
specific to values whose type cannot be inferred.

Steps to reproduce

Start a server with allowCustomObjectId: true, then:

curl -s -X POST http://127.0.0.1:1337/parse/users \
  -H 'X-Parse-Application-Id: myAppId' -H 'Content-Type: application/json' \
  -d '{"objectId":{"__op":"Delete"},"username":"probe2","password":"pw"}'

Then look at the stored row, with the master key:

curl -s -G http://127.0.0.1:1337/parse/classes/_User \
  -H 'X-Parse-Application-Id: myAppId' -H 'X-Parse-Master-Key: myMasterKey' \
  --data-urlencode 'where={"username":"probe2"}'

For contrast, "objectId": 123 in the same body is refused as expected.

Actual Outcome

201 {"objectId":{"__op":"Delete"},"createdAt":"...","sessionToken":"r:..."}

and in MongoDB:

_id:     "6a862fa06e17011e3f5a9c96"
_rperm:  ["[object Object]"]
_wperm:  ["[object Object]"]

An anonymous GET /classes/_User does not return the row, and neither does a query by another
authenticated user, so the account exists and is unreachable by everyone except the master key.

With "objectId": 123 instead: 400 {"code":111,...} and no row, which is the expected behaviour.

Expected Outcome

An objectId whose type cannot be inferred should be refused rather than skipped. Either the same
111 INCORRECT_TYPE that every other non-string value produces, or an explicit rejection of
operation envelopes in that position.

At minimum the response should not report an objectId that differs from the stored _id, since a
client cannot address the object it just created.

Environment

Server

  • Parse Server version: 9.10.1-alpha.6 (commit ca75b1fe)
  • 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): none, raw REST over HTTP
  • SDK version: n/a

Logs

No error is logged; the request succeeds.

Found while building a reimplementation and comparing behaviour against a server built at
ca75b1fe. The stored _id and permission columns above were read directly out of MongoDB rather
than inferred.

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