Repository navigation
Malformed ACL on signup answers 500, or a 400 that still persists the user #10638
Description
New Issue Checklist
- Report security issues confidentially.
- Any contribution is under this license.
- Before posting search existing issues.
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:
POST /userswith"ACL": "nonsense",123ortrueanswers a bare 500.- 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 anemailpresent the request is validated
before the database write and answers400 {"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_Userexists and its username and email have been
consumed by the unique indexes. A retry with that username then answers202 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":"[email protected]" 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
emailis present, - a rejected signup never leaves a
_Userrow 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(commitca75b1fe) - 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.
Activity
parse-github-assistant commentedon Aug 19, 2026
🚀 Thanks for opening this issue!
ℹ️ You can help us to fix this issue faster by opening a pull request with a failing test. See our Contribution Guide for how to make a pull request, or read our New Contributor's Guide if this is your first time contributing.