parse-community / parse-community/parse-server

Malformed ACL on signup answers 500, or a 400 that still persists the user

Open
#10,638 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
JavaScript
Stars
21.4k
Forks
4.8k
Avg merge
7h 45m
Merged PRs (30d)
11

Description

New Issue Checklist
Issue Description

RestWrite.prototype.transformUser assigns the owner entry onto whatever the client sent as ACL:

var ACL = this.data.ACL;
if (!ACL) { ACL = {}; if (!this.config.enforcePrivateUsers) { ACL['*'] = { read: true, write: false }; } }
ACL[this.data.objectId] = { read: true, write: true };
this.data.ACL = ACL;

ACL is never checked for being an object, so when a client signs up with a primitive ACL the
assignment throws a TypeError out of runDatabaseOperation.

Two things follow, and the second is the one I would prioritise:

  1. POST /users with "ACL": "nonsense", 123 or true answers a bare 500.
  2. The status depends on whether the body carries an email, and on the branch that answers 500
    the user row is written before the throw.
    With an email present the request is validated
    before the database write and answers 400 {"code":-1,"error":"ACL must be a Parse ACL."} with
    nothing persisted. Without one, the row is inserted and the throw happens afterwards, so the
    client is told the request failed while a _User exists and its username and email have been
    consumed by the unique indexes. A retry with that username then answers 202 USERNAME_TAKEN.

Both are reachable unauthenticated, since signup is unauthenticated.

The same unchecked assignment is why "ACL": {"__op": "Increment", "amount": 1} answers
400 ... "ACL must be a Parse ACL." rather than a schema error: an operation envelope is an object,
so the assignment succeeds and the value is rejected later by Parse.ACL construction.

Steps to reproduce

Server started with defaults, no email in the body:

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

Then confirm what was persisted, 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":"probe1"}'

Repeat both with "email":"probe1@example.com" added to the signup body to see the other branch,
and with "ACL":{"__op":"Increment","amount":1} to see the operation case.

Actual Outcome
body response row written username consumed
"ACL":"nonsense", no email 500 {"code":1,"message":"Internal server error."} no no
"ACL":"nonsense", with email 400 {"code":-1,"error":"ACL must be a Parse ACL."} no no
"ACL":{"__op":"Increment","amount":1}, no email 400 {"code":-1,"error":"ACL must be a Parse ACL."} yes yes
"ACL":{"__op":"Increment","amount":1}, with email 400 {"code":-1,"error":"ACL must be a Parse ACL."} no no

The server log for the 500 case carries the underlying error:

TypeError: Cannot create property '2S2lZDgQ3C' on string 'nonsense'
    at RestWrite.runDatabaseOperation (.../lib/RestWrite.js:1396:31)
Expected Outcome

A malformed ACL should be rejected with a single, consistent client error before any write, so
that:

  • the status does not depend on whether an unrelated field such as email is present,
  • a rejected signup never leaves a _User row behind or consumes a username,
  • and no request answers 500 for a body the server has already decided is invalid.

400 {"code":-1,"error":"ACL must be a Parse ACL."} is presumably the intended answer, since that
is what the validated path already produces.

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
TypeError: Cannot create property '2S2lZDgQ3C' on string 'nonsense'
    at RestWrite.runDatabaseOperation (.../lib/RestWrite.js:1396:31)
    at process.processTicksAndRejections (node:internal/process/task_queues:105:5)

Found while building a reimplementation and comparing behaviour against a server built at
ca75b1fe; every row in the table above was measured rather than read off the source.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

The signup path reaches RestWrite.prototype.transformUser in lib/RestWrite.js, with the reported failure at line 1396; start there and compare validation with the database-write path. Reproduce the listed curl cases, then verify that malformed ACL values consistently return the intended 400 response without persisting a user or consuming its username.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript, mongodb, node.js
Domain
api, backend, databases, security
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
62/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.