-
-
Notifications
You must be signed in to change notification settings - Fork 735
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Fix] parse
: Fix parsing when the global Object prototype is frozen
#473
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We'd need some tests to cover this.
One challenge, though, is that this would fail in an env where Object.create isn't available (if Object.prototype is frozen, violating the spec, then Object.create surely might be deleted), which hasn't been the case by default before.
However, the suggestion below would address that. Either way, we'd need tests to cover it.
Whoops, I wrote |
Note that |
TIL, thanks for pointing this out! Apparently the correct way to enable strict mode in the Node.js REPL is to use the |
64d8894
to
dce27b1
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Refactored a bit to use mock-property
and leave it configurable, since that isn't relevant to the test
parse
: Fix parsing when the global Object prototype is frozen
Codecov ReportPatch coverage:
Additional details and impacted files@@ Coverage Diff @@
## main #473 +/- ##
==========================================
- Coverage 99.79% 99.00% -0.80%
==========================================
Files 8 8
Lines 1487 1501 +14
Branches 176 177 +1
==========================================
+ Hits 1484 1486 +2
- Misses 3 15 +12
☔ View full report in Codecov by Sentry. |
node < 6 has a different error message; i fixed that locally with a regex, but node <= 0.8 actually doesn't throw when Object.prototype is frozen, so I'll make a package to detect that, and update this, and then land it. |
dce27b1
to
f5872dd
Compare
Thank you! |
f5872dd
to
7895b94
Compare
Background
Click to expand
Note: examples use the Node.js REPL with strict mode:
NODE_REPL_MODE=strict node
.Inheritance and Shadowing
Objects in JavaScript inherit properties from their prototype chain. For example, the "toString" property can be accessed on all objects, but it doesn't actually exist on each object, it exists on the global Object prototype:
Under normal circumstances, you can assign a property to an object using the
=
operator, and any property of the same name in the object's prototype chain will not be modified, but will be "shadowed" by the new property:Prototype Pollution
From Snyk:
There are a few different ways to mitigate Prototype Pollution, and one way to do it across the board is to freeze the global "root" objects and their prototypes (Object, Function, Array, etc.)
From MDN:
This means that any attempt to change the Object prototype will fail. If using strict mode, it will throw an error; otherwise, it will be silently ignored.
If the Object prototype becomes frozen, all of its properties are no longer writable or configurable:
This also prevents shadowing properties with assignment. If an object doesn't already have a property defined (such as "toString"), and it inherits a non-writable property of that name from its prototype chain, any attempt to assign the property on that object will fail:
This behavior is described in the ECMAScript 2016 specification:
The Problem
The
allowPrototypes
option is designed to strip any query parameters if they would shadow/overwrite properties on the global Object prototype (for example, "toString"). This logic is in theparseKeys()
function:qs/lib/parse.js
Lines 172 to 177 in 9dca37f
However, before calling
parseKeys()
, the parse module first callsparseValues()
to create a temporary object:qs/lib/parse.js
Lines 246 to 256 in 9dca37f
Unfortunately, when you have frozen the global Object prototype, the
parseValues()
function will throw a TypeError when inherited properties are present. Example:This means that projects cannot use this package if they have frozen the global Object prototype.
The Solution
Since the
parseValues()
function returns a temporary object, the simple solution here is to create a "plain object" that does not have a prototype. This doesn't require any other logic changes, because the if theallowPrototypes
option is set to false, theparseKeys()
function will subsequently strip out any problematic properties.With this change, this package will be compatible with the "frozen prototype" approach of mitigating Prototype Pollution 🎉
Now you can use the same example as above, and it works as expected: