-
Notifications
You must be signed in to change notification settings - Fork 29.6k
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
tools: add eslint rule for __proto__: null
in object
#48646
base: main
Are you sure you want to change the base?
tools: add eslint rule for __proto__: null
in object
#48646
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.
should it only run for the lib folder?
Yes, I think so.
let hasProto = false; | ||
|
||
for (const property of properties) { | ||
if (property.key && property.key.name === '__proto__') { |
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.
Can we verify that the value is null
?
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.
yes, and I'm gonna make it fixable
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.
there are cases where we want to have __proto__
to not be null
, see https://github.com/nodejs/node/blob/757c10414776b6f035ced453599527a841f47d6e/tools/eslint-rules/prefer-proto.js
Could you please run the CITGM, I'm 90% sure it can break the ecosystem after adding to all the codebase after that if there are a lot of conflicts I will revert the added proto to the code base and rerun it... |
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'd be awesome if the autofix could handle the multiline piece properly too
@ljharb agree but before I fix this I want to run the CITGM as I'm afraid it gonna break the eco, can you please run it for me? |
I'm not able to, unfortunately. |
let optsWithoutSignal = options; | ||
if (optsWithoutSignal.signal) { | ||
optsWithoutSignal = ObjectAssign({}, options); | ||
optsWithoutSignal = ObjectAssign({ __proto__: null }, options); | ||
delete optsWithoutSignal.signal; | ||
} |
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.
let optsWithoutSignal = options; | |
if (optsWithoutSignal.signal) { | |
optsWithoutSignal = ObjectAssign({}, options); | |
optsWithoutSignal = ObjectAssign({ __proto__: null }, options); | |
delete optsWithoutSignal.signal; | |
} | |
const { signal, ...optsWithoutSignal } = options; |
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 want to avoid code changes that are not related to this 😄
You would need to split this PR in chunks small enough to be reviewable, I think the diff is too big to be reviewed in one go, and you are going to run in an endless stream of conflicts. |
Yeah, I hate to say it, but this is going to have to be done in smaller pieces. Here's one option:
There are other valid approaches too. But an all-at-once approach like this is unlikely to get reviewed much, I'm sorry to say. |
(Also: Thank you for doing this and wanting to make the Node.js runtime more secure!) |
Would they be semver-major if the object isn't being passed out to user code? |
CIGTM run: https://ci.nodejs.org/view/Node.js-citgm/job/citgm-smoker/3177/ (it did not start yet but I queued it) |
It's unlikely to give us much insight, we are not going to land this PR in this state anyway, no matter the result of this run we will still want it split up in several PRs, and each PR will probably need a CITGM run 🤷♂️ |
Before I continue with this I wanna know what I'm dealing with... |
I cancelled the run so it doesn't go in the way of the pending release (scheduled for tomorrow).
Well I'm afraid you are not going to get anything useful out of it; if the results show lots of regressions, that doesn't mean that we won't introduce the lint rule, just that we need to investigate where they are coming from, and that's going to be much easier on smaller PRs. Even if a CITGM show no regressions on this PR, I would argue this is a waste of time and resources because we probably want to run it again on the PRs that are actually going to land. |
@Trott what if we gonna:
This way I won't need to keep rebasing, and I could work on this without much of a conflict (Not sure if should have autofix enabled as it's can be dangerous) WDYT? |
Before I go through this journey, where should we NOT add the |
Our lint jobs treat warnings as errors. Changing that is not impossible but others might oppose the change. |
I don't understand why not run CITGM, it will show a lot of regressions, @rluvaton can then go ahead and compare them to the release CITGM and then get an effort estimation of how much extra breakage there is for the whole thing. It's entirely reasonable from his PoV to want to know roughly how much work there is here and whether he wants to invest time into this. I don't understand the reluctance to run CITGM here? (if it's just the release can you start one after the release?) |
I think in general this is only needed when we are accessing properties that may not have been defined in the object e.g. checking if it's undefined or falsy, when the object can come from user land or from internal code that does not define these properties. For completely internal objects where the creators always define the keys, a null prototype puts the object in dictionary mode and the lookup would be slower than it could've been. https://v8.dev/blog/fast-properties |
1a91514
to
68b033b
Compare
a5f145c
to
aafd615
Compare
Can we run the CITGM after the release, please? |
c54494c
to
9093d9a
Compare
I'm having second thoughts on adding this globally, when I start extracting this into modules, the http was rejected due to performance implications. Also we said we don't want to add it for objects returning to userland which there are quite a few so adding eslint ignore comment will just make noise. WDYT? |
Having the rules would be useful for when we introduce new modules, where we probably want to use null-prototype objects. For example, the test runner code is the perfect place where we would benefit from this; maybe it's the only place where we end up using it, but it's still worth it imo :) |
Honestly I'm personally very skeptical of I also would strongly prefer if we had a build step to do this instead of it being in the codebase so contributors could write "regular" code and it would become primordials in core "under the hood". Even with a build step it would need escape analysis to be useful. |
does having it as a build step meaning without the eslint rule and other that transform on build? if so, it's super complicated because not all places are equal - userland and some other places |
That's theoretically impossible in JavaScript. It might be possible in some stricter variant of TypeScript. It was discussed by the TSC and the TSC voted against using a build step, and for using primordials as long as perf were not impacted. |
@benjamingr the security risk of NOT using proto null is that anyone can modify Object.prototype and potentially get privileged access to node internals. |
Right, but by doing this in a non verifiable way we are creating a false sense of "node guards against this" when in fact we don't, creating a false sense of security. If you've given someone else the chance to run code in your Node.js server you pretty much have to assume they have access to anything. |
It’s not black or white - every place it’s guarded against is an improvement, even while there remains places that it’s not. |
instead of each review someone need to remind that you need to add
__proto__: null
when creating an object, I created this eslint rulebefore continue let's agree on the name so I won't need to rename after adding ignore comments
this does not add
__proto__: null
tomodule.exports
as it break the ecosystem.