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: special EventEmitter keys leak information about other rules #9328
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.
Left a few notes about sinon usage.
tests/lib/linter.js
Outdated
|
||
linter.defineRule("checker", () => ({ Program: spy })); | ||
linter.verify("foo", { rules: { checker: "error" } }); | ||
assert(spy.calledOnce); |
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 think this would be more readable by adding assert.notOk(spy.firstCall.thisValue)
so all of the assertions are in one place. But I won't insist on it.
tests/lib/linter.js
Outdated
|
||
linter.defineRule("checker", () => ({ newListener: spy })); | ||
linter.verify("foo", { rules: { checker: "error", "no-undef": "error" } }); | ||
assert(!spy.calledOnce); |
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.
This should be assert(!spy.called)
or assert(spy.notCalled)
or similar. Using calledOnce means a call twice would erroneously pass.
2a01331
to
2a874d2
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.
LGTM, just left a few suggestions for improving the inline documentation. Sorry for the delay.
lib/linter.js
Outdated
* 2. It calls listener functions without any `this` value. (`EventEmitter` calls listeners with a | ||
* `this` value of the emitter instance, which would give listeners access to other listeners.) | ||
* 3. Events can be emitted with at most 3 arguments. | ||
* @returns {{on: Function, emit: Function, eventNames: Function}} An emitter |
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'd love to see this document in a JSDoc typedef, but this is nowhere near a blocker.
lib/linter.js
Outdated
* another module throws an error or registers a listener. | ||
* 2. It calls listener functions without any `this` value. (`EventEmitter` calls listeners with a | ||
* `this` value of the emitter instance, which would give listeners access to other listeners.) | ||
* 3. Events can be emitted with at most 3 arguments. |
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'd love to see an example of 3-argument usage documented somewhere, maybe here, if possible. Not a blocker.
lib/linter.js
Outdated
@@ -50,6 +49,40 @@ const MAX_AUTOFIX_PASSES = 10; | |||
//------------------------------------------------------------------------------ | |||
|
|||
/** | |||
* Creates an object which can listen for and emit events. |
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 this be moved out into it's own file? Since the behavior doesn't have direct connection to Linter, I'd rather see a separate file, even if it's small.
`Linter` uses Node's `EventEmitter` API to register listeners for rules. However, the `EventEmitter` API has a few problems for this use case: * `EventEmitter` has three "special" events (`newListener`, `removeListener`, and `error`) which are called when something happens with another listener. This is undesirable because `Linter` allows rules to register listeners for arbitrary string events, and we don't want rule listeners to be able to detect each other. * `EventEmitter` calls listeners with a `this` value of the event emitter itself. This is undesirable because this would allow rules to modify or tamper with listeners registered by other rules. This commit fixes the problem by updating `Linter` to use a custom event-emitting object with a similar API, rather than `EventEmitter` itself.
862dee5
to
9d82dd1
Compare
9d82dd1
to
0c82e73
Compare
LGTM |
What is the purpose of this pull request? (put an "X" next to item)
[x] Bug fix
Tell us about your environment
What parser (default, Babel-ESLint, etc.) are you using?
N/A
Please show your full configuration:
N/A
What did you do? Please include the actual source code causing the issue.
What did you expect to happen?
I expected the
newListener
method to never get called, because there are no nodes with typenewListener
.What actually happened? Please include the actual, raw output from ESLint.
The
newListener
method got called because a listener was added fromno-undef
, and the string"newListener"
has special behavior when added to an event emitter.What changes did you make? (Give an overview)
Linter
uses Node'sEventEmitter
API to register listeners for rules. However, theEventEmitter
API has a few problems for this use case:EventEmitter
has three "special" events (newListener
,removeListener
, anderror
) which are called when something happens with another listener. This is undesirable becauseLinter
allows rules to register listeners for arbitrary string events, and we don't want rule listeners to be able to detect each other.EventEmitter
calls listeners with athis
value of the event emitter itself. This is undesirable because this would allow rules to modify or tamper with listeners registered by other rules.This commit fixes the problem by updating
Linter
to use a custom event-emitting object with a similar API, rather thanEventEmitter
itself.Is there anything you'd like reviewers to focus on?
Nothing in particular