fix: ParseServerRESTController passes context by reference causing cross-request mutation#10291
fix: ParseServerRESTController passes context by reference causing cross-request mutation#10291yog27ray wants to merge 5 commits intoparse-community:alphafrom
Conversation
…on leak across requests Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
I will reformat the title to use the proper commit message syntax. |
|
🚀 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
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. |
✅ Snyk checks have passed. No issues have been found so far.
💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse. |
📝 WalkthroughWalkthroughDeep-clone the incoming REST request Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant REST as ParseServerRESTController
participant Router
participant Hook as CloudHook
Client->>REST: POST /... with options.context (sharedObj)
REST->>REST: try structuredClone(sharedObj) -> requestContext
alt clone succeeds
REST->>Router: tryRouteRequest({ ..., context: requestContext })
Router->>Hook: invoke beforeSave(req.context)
Hook-->>Router: returns (may mutate req.context)
Router-->>REST: response
REST-->>Client: 200 OK (original sharedObj unchanged)
else clone fails
REST-->>Client: reject Promise with DataCloneError
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes 🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## alpha #10291 +/- ##
=======================================
Coverage 92.52% 92.52%
=======================================
Files 192 192
Lines 16568 16572 +4
Branches 231 231
=======================================
+ Hits 15330 15334 +4
Misses 1216 1216
Partials 22 22 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/ParseServerRESTController.js`:
- Line 123: The structuredClone(options.context || {}) call can throw
synchronously; wrap that call in a try/catch inside the surrounding promise
callback and propagate errors to the outer rejection path instead of allowing a
sync throw to escape. Locate the structuredClone usage (referenced as
structuredClone and options.context) and change it to perform the clone inside a
try { const ctx = structuredClone(options.context || {}); use ctx } catch (err)
{ return reject(err); } (or call the appropriate error callback/next) so any
clone errors are explicitly rejected/handled.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 16bf7b4a-3327-4532-8474-f32a6b334672
📒 Files selected for processing (2)
spec/ParseServerRESTController.spec.jssrc/ParseServerRESTController.js
…t values Moves structuredClone() before the async chain and wraps it in try/catch so non-cloneable values properly reject the promise instead of causing an unhandled rejection. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
@mtrezza is there any further changes required in this PR? |
|
@mtrezza can someone review this? |
|
@mtrezza can you review this PR. |
There was a problem hiding this comment.
Good fix — the placement before the async chain with try/catch is correct, and the tests cover sequential isolation, concurrency, and the error case.
1. Wrap the DataCloneError
If someone passes a function in context, they'll get a raw DOMException named DataCloneError with no indication it came from context cloning. This should be a Parse.Error with a message like "Context contains non-cloneable values" so it's actionable without digging into internals. Should fix before merging.
2. Not quite HTTP-equivalent
The description says this matches JSON serialization behavior, but structuredClone preserves Date/Map/Set (JSON doesn't) and throws on functions (JSON silently drops them). It's actually stricter and higher-fidelity than HTTP — which is fine, but worth noting so nobody expects exact parity. Minor nit.
Issue
When
directAccessis enabled,ParseServerRESTControllerbypasses the HTTP layer and routes requests internally. Unlike the HTTP path — where request context is serialized to JSON and deserialized on the server side (naturally creating a fresh copy) — the direct access path passesoptions.contextby reference.This means the same context object instance is shared across all requests within the same session. When a
beforeSaveorafterSavehook mutatesreq.context(which is a common and documented pattern for passing data between hooks), those mutations leak back into the caller's original context object and into any subsequent requests that reuse the same context.Reproduction
directAccess: truein Parse Server configconst ctx = { counter: 0 }{ context: ctx }beforeSavehook, mutatereq.context(e.g.,req.context.counter++)ctxobject is mutated; subsequent requests see the mutated stateImpact
directAccessa non-transparent optimizationApproach
Use
structuredClone()to deep copyoptions.contextbefore attaching it to the internal request object inParseServerRESTController. This ensures each request gets its own isolated context — matching the behavior of the HTTP code path where JSON serialization/deserialization naturally creates independent copies.Change
In
src/ParseServerRESTController.js, line 123:Tasks
Summary by CodeRabbit
Bug Fixes
Tests