Skip to content

fix: toJSONwithObjects leaks ParseACL instances when afterFind hooks modify objects with directAccess - #10244

Open
yog27ray wants to merge 13 commits into
parse-community:alphafrom
yog27ray:fix/afterfind-acl-directaccess
Open

fix: toJSONwithObjects leaks ParseACL instances when afterFind hooks modify objects with directAccess#10244
yog27ray wants to merge 13 commits into
parse-community:alphafrom
yog27ray:fix/afterfind-acl-directaccess

Conversation

@yog27ray

@yog27ray yog27ray commented Mar 19, 2026

Copy link
Copy Markdown
Contributor

Issue

Closes #8473

When directAccess is enabled, toJSONwithObjects in src/triggers.js leaks live ParseACL instances into the response instead of plain JSON objects. This causes the client-side Parse SDK to throw:

TypeError: Tried to create an ACL with an invalid permission type.

The bug occurs when an afterFind hook modifies objects via .setACL() or .set(). Since ParseACL lacks _toFullJSON, the raw instance is assigned directly to the response JSON. With directAccess, the response bypasses HTTP serialization (JSON.stringify), so the ParseACL instance leaks through to ParseObject._finishFetch, which expects plain JSON.

Approach

In toJSONwithObjects, when a pending op value lacks _toFullJSON, check if it has a toJSON method and call it before assigning. This serializes SDK types like ParseACL and ParseGeoPoint to plain JSON — consistent with what object.toJSON() already does in the same function.

Change in src/triggers.js:198:

// Before
toJSON[key] = val;

// After
toJSON[key] = val && typeof val.toJSON === 'function' ? val.toJSON() : val;

Tasks

  • Add tests
  • Add changes to documentation (guides, repository pages, code comments)

Summary by CodeRabbit

  • Bug Fixes

    • Improved serialization of pending ACL objects into plain JSON while preserving read/write permissions and date fields.
    • Ensured ACL updates made during after-find processing remain available in returned results, including when direct access is enabled.
  • Tests

    • Added coverage for after-find hook behavior, ACL serialization, permission preservation, and direct-access scenarios.

…oks modify objects

When `directAccess` is enabled, `toJSONwithObjects` iterates pending ops and
calls `object.get(key)` for each dirty field. For ACL, this returns a live
`ParseACL` instance. Since `ParseACL` lacks `_toFullJSON`, the instance was
assigned directly to the response JSON. With `directAccess`, the response
bypasses HTTP serialization, so the raw `ParseACL` leaks to the client SDK.
The client's `ParseObject._finishFetch` then calls `new ParseACL(aclData)`
expecting plain JSON but receiving a `ParseACL` instance, throwing:
"TypeError: Tried to create an ACL with an invalid permission type."

The fix calls `val.toJSON()` on values that support it before assigning,
ensuring SDK types like `ParseACL` and `ParseGeoPoint` are serialized to
plain JSON — consistent with what `object.toJSON()` already does.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@parse-github-assistant

parse-github-assistant Bot commented Mar 19, 2026

Copy link
Copy Markdown

🚀 Thanks for opening this pull request! We appreciate your effort in improving the project. Please let us know once your pull request is ready for review.

Tip

  • Keep pull requests small. Large PRs will be rejected. Break complex features into smaller, incremental PRs.
  • Use Test Driven Development. Write failing tests before implementing functionality. Ensure tests pass.
  • Group code into logical blocks. Add a short comment before each block to explain its purpose.
  • We offer conceptual guidance. Coding is up to you. PRs must be merge-ready for human review.
  • Our review focuses on concept, not quality. PRs with code issues will be rejected. Use an AI agent.
  • Human review time is precious. Avoid review ping-pong. Inspect and test your AI-generated code.

Note

Please respond to review comments from AI agents just like you would to comments from a human reviewer. Let the reviewer resolve their own comments, unless they have reviewed and accepted your commit, or agreed with your explanation for why the feedback was incorrect.

Caution

Pull requests must be written using an AI agent with human supervision. Pull requests written entirely by a human will likely be rejected, because of lower code quality, higher review effort and the higher risk of introducing bugs. Please note that AI review comments on this pull request alone do not satisfy this requirement.

@parseplatformorg

parseplatformorg commented Mar 19, 2026

Copy link
Copy Markdown
Contributor

Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@coderabbitai

coderabbitai Bot commented Mar 19, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: d89bcc35-2165-48cd-a50d-45b6a8ed11fb

📥 Commits

Reviewing files that changed from the base of the PR and between f9148ab and 6bfb2d0.

📒 Files selected for processing (1)
  • src/triggers.js

Included review availability: Your plan provides up to 10 included reviews per hour; 9 remain after this review.


📝 Walkthrough

Walkthrough

toJSONwithObjects now serializes pending Parse.ACL instances to plain JSON. Tests cover ACL serialization in afterFind hooks and direct-access behavior.

Changes

ACL serialization

Layer / File(s) Summary
Serialize pending ACL values
src/triggers.js
toJSONwithObjects calls toJSON() for pending Parse.ACL values. Other pending values retain their existing behavior.
Validate trigger behavior
spec/CloudCode.spec.js
Tests verify plain ACL output, date serialization, and ACL preservation when an afterFind hook runs with direct access enabled.

Estimated code review effort: 2 (Simple) | ~10 minutes

Merge Risk: ⚪ Minimal · up to 6bfb2

The change serializes SDK values such as ParseACL before direct-access responses, addressing client errors caused by leaked live instances. No actionable merge-blocking risk remains at the current head, so the PR is merge-ready after normal checks and review.

Suggested reviewers: dblythy, mtrezza


Caution

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

  • Ignore

❌ Failed checks (1 error)

Check name Status Explanation Resolution
Title check ❌ Error The title uses the allowed fix: prefix and describes the bug, but the first word after the prefix starts with lowercase toJSONwithObjects. Capitalize the first word after the prefix, for example: fix: Prevent toJSONwithObjects ACL leaks with directAccess.
✅ Passed checks (6 passed)
Check name Status Explanation
Description check ✅ Passed The description includes the issue, approach, and tasks with tests and documentation marked complete, but omits the standard security and license section.
Linked Issues check ✅ Passed The change serializes pending Parse.ACL values to plain JSON and adds directAccess regression tests, addressing the malformed ACL data reported in [#8473].
Out of Scope Changes check ✅ Passed The changes are limited to ACL serialization in toJSONwithObjects and focused regression tests, with no unrelated code changes.
Docstring Coverage ✅ Passed Docstring check was indeterminate for this PR — some files could not be analyzed in time. Not blocking.
Security Check ✅ Passed The PR diff changes only ACL serialization and tests; Parse.ACL.toJSON() replaces a live instance without altering authorization, protected-field filtering, dependencies, or prototype-sensitive d...
Engage In Review Feedback ✅ Passed The PR includes a follow-up commit that narrows serialization to Parse.ACL and adds a Date-preservation test, directly implementing the reviewer’s reported regression feedback.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

coderabbitai[bot]
coderabbitai Bot previously approved these changes Mar 19, 2026
@codecov

codecov Bot commented Mar 19, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 93.78%. Comparing base (64d58ff) to head (6bfb2d0).
⚠️ Report is 1 commits behind head on alpha.

Additional details and impacted files
@@           Coverage Diff           @@
##            alpha   #10244   +/-   ##
=======================================
  Coverage   93.78%   93.78%           
=======================================
  Files         192      192           
  Lines       16832    16832           
  Branches      248      248           
=======================================
  Hits        15786    15786           
  Misses       1025     1025           
  Partials       21       21           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@yog27ray

Copy link
Copy Markdown
Contributor Author

@mtrezza can you re-run the "MongoDB 8, ReplicaSet" looks like a flaky test case. Also "Docker Build" action failing on this PR only?

@mtrezza

mtrezza commented Mar 20, 2026

Copy link
Copy Markdown
Member

@yog27ray Is this a security vulnerability?

@yog27ray

yog27ray commented Mar 21, 2026

Copy link
Copy Markdown
Contributor Author

@mtrezza This isn't a security vulnerability — it's a bug that occurs when the directAccess flag is enabled. I previously reported a related issue here: #8473. We've since upgraded to parse-server 9.5.1, but the same bug is still present in this version as well.

@yog27ray

Copy link
Copy Markdown
Contributor Author

@mtrezza is there any change required in this PR?

@yog27ray

Copy link
Copy Markdown
Contributor Author

@mtrezza can someone review this?

@yog27ray

yog27ray commented Apr 2, 2026

Copy link
Copy Markdown
Contributor Author

@mtrezza any concern with this PR ?

@mtrezza

mtrezza commented Apr 3, 2026

Copy link
Copy Markdown
Member

@yog27ray it's in review

@mtrezza

mtrezza commented Apr 15, 2026

Copy link
Copy Markdown
Member

The ACL fix is correct, but calling val.toJSON() unconditionally silently breaks Date fields modified in afterFind under directAccess.

The client SDK's decode passes raw Date instances through unchanged:

if (value === null || typeof value !== 'object' || value instanceof Date) {
  return value;
}
  • Before this PR: raw Date leaks → client returns it as a Dateobj.get('date') works.
  • After this PR: Date.prototype.toJSON() returns an ISO string → decode passes the string through → obj.get('date') returns a string, and .getTime() throws.

Unlike ACL (which is already a hard TypeError), Date under directAccess currently works, so this is a real regression.

A fix could be to narrow the conditional to ACL:

if (val instanceof Parse.ACL) {
  toJSON[key] = val.toJSON();
} else {
  toJSON[key] = val;
}

The root cause is that the pending-ops loop overrides the properly-encoded output of object.toJSON() with raw values from object.get(key). This also breaks Date fields modified in afterFind on the HTTP path (client sees an ISO string instead of {__type: "Date", iso: "..."}). Long-term fix could be using Parse's encode function so both directAccess and HTTP produce canonical wire format. Non-breaking for SDK consumers since the decoder round-trips {__type: ...}.

The broader issue is that if directAccess currently returns a date string, and we cannot even switch to a Parse-style date object, because even though Parse SDKs would be able to parse it correctly, it would be a breaking change for direct REST consumers.

Approach HTTP REST consumers SDK consumers Fixes ACL bug?
Current PR (val.toJSON()) ISO string → ISO string (no change) Datestring (broken)
Narrow to Parse.ACL no change no change
Use encode ISO string → {__type: "Date"} (breaking change) no change

… Date instances

Calling val.toJSON() on all objects with a toJSON method converts Date
instances to ISO strings, breaking directAccess consumers. Narrowing
to Parse.ACL fixes the original bug without regressing Date fields.
@yog27ray

Copy link
Copy Markdown
Contributor Author

@mtrezza requested changes done

@yog27ray

Copy link
Copy Markdown
Contributor Author

@mtrezza any further changes required.

@yog27ray

Copy link
Copy Markdown
Contributor Author

@mtrezza any update on this

@yog27ray

yog27ray commented Jun 3, 2026

Copy link
Copy Markdown
Contributor Author

@mtrezza is there any changes required?

@yog27ray

Copy link
Copy Markdown
Contributor Author

@mtrezza any thing that i can help with to make this PR merge.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Error "Tried to create an ACL with an invalid permission type."

4 participants