-
Notifications
You must be signed in to change notification settings - Fork 30k
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
querystring: don't inherit from Object.prototype #6055
querystring: don't inherit from Object.prototype #6055
Conversation
@@ -5,6 +5,18 @@ | |||
const QueryString = exports; | |||
const Buffer = require('buffer').Buffer; | |||
|
|||
// This constructor is used to store parsed query string values. Instantiating | |||
// this is faster than explicitly calling `Object.create(null)` to get a | |||
// "clean" empty object |
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.
Could you add a note about the version of V8 that this applies to.
LGTM pending CI |
// "clean" empty object | ||
function ParsedQueryString() {} | ||
ParsedQueryString.prototype = Object.create(null, { | ||
constructor: { |
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.
what's with the constructor
if I might ask? why bothering?
Also, I think you still need to return an instanceof Object
to avoid breaking code using this module.
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.
to be more specific:
function ParsedQueryString() {}
ParsedQueryString.prototype = Object.create(null);
// before returning, if we don't want outer code to break
return Object.setPrototypeOf(obj, Object.prototype);
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.
constructor
is removed now.
IMHO I think more people are going to be using typeof foo === 'object'
rather than foo instanceof Object
. Perhaps @ChALkeR can give us some insight about this?
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.
It's not about instanceof
per-se, it's about .hasOwnProperty
and other
methods from Object.prototype
that most devs give, wrongly, for granted
with objects. This won't affect my code but it will break every code with
for/in
loops. I personally don't care
On Apr 5, 2016 1:59 PM, "Brian White" [email protected] wrote:
In lib/querystring.js
#6055 (comment):@@ -5,6 +5,18 @@
const QueryString = exports;
const Buffer = require('buffer').Buffer;+// This constructor is used to store parsed query string values. Instantiating
+// this is faster than explicitly callingObject.create(null)
to get a
+// "clean" empty object
+function ParsedQueryString() {}
+ParsedQueryString.prototype = Object.create(null, {
- constructor: {
constructor is removed now.
IMHO I think more people are going to be using typeof foo === 'object'
rather than foo instanceof Object. Perhaps @ChALkeR
https://github.com/ChALkeR can give us some insight about this?—
You are receiving this because you were mentioned.
Reply to this email directly or view it on GitHub
https://github.com/nodejs/node/pull/6055/files/ef5f84a85820794a572ba0deb7e15a8b1622b9f4#r58532723
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.
I'm a bit concerned about not setting the object prototype also. We need to get some insight into just how significant of a change that is. Either way, this is definitely semver-major because of that change.
ab2ca7c
to
31d52c8
Compare
LGTM but I'd like to get more insight into what impact not setting the prototype on return would have. |
It'd definitely be a semver-major thing. I just ran some benchmarks with and without setting the prototype on return and there is definitely a noticeable performance regression when setting the prototype on return in almost all of the benchmark cases. |
Yep, I was consistently seeing the same thing. I'm happy with the change, just want to make sure we know what'll break |
Also for completeness here are benchmark results I am getting as this PR currently stands (no setting prototype on return) with the number of iterations bumped from querystring/querystring-parse.js type=noencode n=10000000: ./node: 689650 ./node-pre-qs: 597360 ...... 15.45% querystring/querystring-parse.js type=multicharsep n=10000000: ./node: 633240 ./node-pre-qs: 534580 .. 18.46% querystring/querystring-parse.js type=encodemany n=10000000: ./node: 367970 ./node-pre-qs: 301350 .... 22.11% querystring/querystring-parse.js type=encodelast n=10000000: ./node: 563560 ./node-pre-qs: 424940 .... 32.62% querystring/querystring-parse.js type=multivalue n=10000000: ./node: 540460 ./node-pre-qs: 502680 ..... 7.52% querystring/querystring-parse.js type=multivaluemany n=10000000: ./node: 243350 ./node-pre-qs: 232420 . 4.70% querystring/querystring-parse.js type=manypairs n=10000000: ./node: 189290 ./node-pre-qs: 151430 ..... 25.00% |
Interesting. If this works out well, we should look into doing a similar thing for events, per #728. Does this break any sort of enumeration i.e. Another thought, what if we were able to expose ourselves something like |
@Fishrock123 RE: EventEmitter, I already had that in mind, was going to test it later today. |
No, it doesn't affect any
I don't know why // forcing `new Dictionary` initialization
class Dictionary {}
delete Object.setPrototypeOf(
Dictionary.prototype,
null
).constructor; |
Related: #6044 |
(same as #6092 (comment)) Hmmm, can this be back-ported? Theoretically, this could be considered a security issue for unvalidated input. |
This LGTM if CI and CITGM look good. I'm currently -1 on backporting until we can get a better sense about what breaks with this change. |
CI one last time: https://ci.nodejs.org/job/node-test-pull-request/2304/ |
LGTM but we should also get a CITGM run on it. /cc @thealphanerd |
I ran citgm 11 days ago, but we can do it again I suppose. |
oh! ha! missed that comment :-) |
CI is green |
This PR appears to introduce an unfortunate side effect in |
This commit safely allows querystring keys that are named the same as properties that are ordinarily inherited from Object.prototype such as __proto__. Additionally, this commit provides a bit of a speed improvement (~25% in the querystring-parse 'manypairs' benchmark) when there are many unique keys. Fixes: nodejs#5642 PR-URL: nodejs#6055 Reviewed-By: Colin Ihrig <[email protected]> Reviewed-By: James M Snell <[email protected]>
This commit safely allows querystring keys that are named the same as properties that are ordinarily inherited from Object.prototype such as __proto__. Additionally, this commit provides a bit of a speed improvement (~25% in the querystring-parse 'manypairs' benchmark) when there are many unique keys. Fixes: #5642 PR-URL: #6055 Reviewed-By: Colin Ihrig <[email protected]> Reviewed-By: James M Snell <[email protected]>
The default behaviour was changed with node version 6. https://github.com/nodejs/node/blob/master/doc/changelogs/CHANGELOG_V6.md nodejs/node#6055
Don't use Object.prototpye.hasOwnProperty on object returned by querystring.parse Since it does not inherit from Object.prototype since nodejs/node#6055 (node v6.0.0+)
Previously, we assumed that whatever object the user passed to the object assertion helpers had its prototype set to Object and would thus have things like a hasOwnProperty property. However, it turns out that this is not a valid assumption. This isn't limited to just corner cases; Node 6.x changed the behavior of its `querystring` module (which is also used by its `url` module) to explicitly set the prototype of querystring objects to `null` in order to prevent key collisions if someone sent a URL query string parameter with the same key as a property on the prototype. See nodejs/node#6055 for where this change was made. Therefore, we use Object.prototype.hasOwnProperty, which is guaranteed to always exist, and call it on the object the user passed in. We do the same for propertyIsEnumerable as applicable.
After V8 5.6, using Object.create(null) directly is now faster than using a constructor for map-like objects. PR-URL: #11930 Refs: emberjs/ember.js#15001 Refs: https://crrev.com/532c16eca071df3ec8eed394dcebb932ef584ee6 Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: Colin Ihrig <[email protected]> Reviewed-By: Luigi Pinca <[email protected]> Reviewed-By: James M Snell <[email protected]>
Did this solve a security issue, or just about avoiding name conflicts and supporting more property names? |
Pull Request check-list
make -j8 test
(UNIX) orvcbuild test nosign
(Windows) pass withthis change (including linting)?
test (or a benchmark) included?
Affected core subsystem(s)
Description of change
This commit safely allows querystring keys that are named the same as
properties that are ordinarily inherited from
Object.prototype
suchas proto. Additionally, this commit provides a bit of a speed
improvement (~25% in the querystring-parse 'manypairs' benchmark)
when there are many unique keys.
Fixes: #5642