Guard deepMerge against prototype pollution#10109
Closed
gunjanjaswal wants to merge 2 commits into
Closed
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The basics
The details
Resolves
Fixes #10048
Proposed Changes
deepMergewalkssourcewithfor...inand never skips the prototype-pollution keys, so a source object with an own enumerable__proto__(which is exactly whatJSON.parseproduces) gets written ontoObject.prototypeduring the recursive merge. This adds a small guard that skips__proto__,constructor, andprototypebefore merging each key.Reason for Changes
Blockly.utils.object.deepMergeis a public, package-root-reachable API. If a host app passes any user-influenced data through it,deepMerge({}, JSON.parse('{"__proto__":{"x":"y"}}'))setsObject.prototype.x, which then leaks onto every plain object in the process (CWE-1321). The guard is minimal and leaves behavior unchanged for all normal keys.Test Coverage
Added a case to the existing
deepMergemocha suite inutils_test.js: it merges aJSON.parse('{"__proto__": {"blocklyPolluted": "yes"}}')payload into{}and assertsObject.prototype.blocklyPollutedstaysundefined. It fails before the guard and passes after. I don't have the Blockly build toolchain set up locally, so I leaned on the added test plus CI rather than a local full run.Documentation
None needed, behavior is unchanged for normal keys.
Additional Information
On #10048 a maintainer mentioned you might prefer to drop
deepMergein favor of the spread operator. Happy to do that instead if you'd rather remove the function than harden it; this guard is the minimal fix in the meantime.