Skip to content

Commit 87bbbd6

Browse files
committed
changes based on review feedback
1 parent af8b36b commit 87bbbd6

File tree

3 files changed

+104
-55
lines changed

3 files changed

+104
-55
lines changed

javascript/ql/src/semmle/javascript/frameworks/Electron.qll

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ module Electron {
7373
/**
7474
* A reference to the `webContents` property of a browser object.
7575
*/
76-
class WebContents extends DataFlow::SourceNode, EventEmitter::NodeJSEventEmitter {
76+
class WebContents extends DataFlow::SourceNode, NodeJSLib::NodeJSEventEmitter {
7777
WebContents() { this.(DataFlow::PropRead).accesses(any(BrowserObject bo), "webContents") }
7878
}
7979

@@ -128,7 +128,7 @@ module Electron {
128128
* Does mostly the same as an EventEmitter event handler,
129129
* except that values can be returned through the `event.returnValue` property.
130130
*/
131-
class IPCSendRegistration extends EventRegistration::Range,
131+
class IPCSendRegistration extends EventRegistration::DefaultEventRegistration,
132132
DataFlow::MethodCallNode {
133133
override Process emitter;
134134

@@ -148,7 +148,7 @@ module Electron {
148148
* An IPC event is sent from the renderer to the main process.
149149
* And a value can be returned through the `returnValue` property of the event (first parameter in the callback).
150150
*/
151-
class IPCDispatch extends EventDispatch::Range, DataFlow::InvokeNode {
151+
class IPCDispatch extends EventDispatch::DefaultEventDispatch, DataFlow::InvokeNode {
152152
override Process emitter;
153153

154154
IPCDispatch() {

javascript/ql/src/semmle/javascript/frameworks/EventEmitter.qll

Lines changed: 56 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,11 @@ module EventEmitter {
2222
/**
2323
* Gets a node that refers to an EventEmitter object.
2424
*/
25-
DataFlow::SourceNode trackEventEmitter(EventEmitter::Range emitter) {
25+
DataFlow::SourceNode trackEventEmitter(EventEmitter emitter) {
2626
result = trackEventEmitter(DataFlow::TypeTracker::end(), emitter)
2727
}
2828

29-
private DataFlow::SourceNode trackEventEmitter(
30-
DataFlow::TypeTracker t, EventEmitter::Range emitter
31-
) {
29+
private DataFlow::SourceNode trackEventEmitter(DataFlow::TypeTracker t, EventEmitter emitter) {
3230
t.start() and result = emitter
3331
or
3432
exists(DataFlow::TypeTracker t2, DataFlow::SourceNode pred |
@@ -38,7 +36,7 @@ module EventEmitter {
3836
or
3937
// invocation of a chainable method
4038
exists(DataFlow::MethodCallNode mcn |
41-
mcn = pred.getAMethodCall(EventEmitter::chainableMethod()) and
39+
mcn = pred.getAMethodCall(chainableMethod()) and
4240
// exclude getter versions
4341
exists(mcn.getAnArgument()) and
4442
result = mcn and
@@ -50,36 +48,24 @@ module EventEmitter {
5048
/**
5149
* An object that implements the EventEmitter API.
5250
* Extending this class does nothing, its mostly to indicate intent.
53-
* The magic only happens when extending EventRegistration::Range and EventDispatch::Range.
51+
*
52+
* The classes EventRegistration::Range and EventDispatch::Range must be extended to get a working EventEmitter model.
53+
* An EventRegistration models a method call that registers some event handler on an EventEmitter.
54+
* And EventDispatch models that some event is dispatched on an EventEmitter.
55+
*
56+
* Both the EventRegistration and EventDispatch have a field `emitter`,
57+
* which is the EventEmitter that events are registered on / dispatched to respectively.
58+
*
59+
* Here is a simple JavaScript example with the NodeJS EventEmitter:
60+
* var e = new EventEmitter(); // <- EventEmitter
61+
* e.on("name", (data) => {...}); // <- EventRegistration
62+
* e.emit("name", "foo"); // <- EventDispatch
5463
*/
5564
abstract class Range extends DataFlow::Node { }
56-
57-
/**
58-
* An NodeJS EventEmitter instance.
59-
* Events dispatched on this EventEmitter will be handled by event handlers registered on this EventEmitter.
60-
* (That is opposed to e.g. SocketIO, which implements the same interface, but where events cross object boundaries).
61-
*/
62-
abstract class NodeJSEventEmitter extends Range {
63-
/**
64-
* Get a Node that refers to a NodeJS EventEmitter instance.
65-
*/
66-
DataFlow::SourceNode ref() { result = trackEventEmitter(this) }
67-
}
68-
69-
private class ImportedNodeJSEventEmitter extends NodeJSEventEmitter {
70-
ImportedNodeJSEventEmitter() {
71-
exists(DataFlow::SourceNode clazz |
72-
clazz = DataFlow::moduleImport("events") or
73-
clazz = DataFlow::moduleMember("events", "EventEmitter")
74-
|
75-
this = clazz.getAnInstantiation()
76-
)
77-
}
78-
}
7965
}
8066

8167
/**
82-
* An EventEmitter instance that implements the NodeJS EventEmitter API.
68+
* An EventEmitter instance that implements the EventEmitter API.
8369
* Extend EventEmitter::Range to mark something as being an EventEmitter.
8470
*/
8571
class EventEmitter extends DataFlow::Node {
@@ -112,7 +98,7 @@ class EventRegistration extends DataFlow::Node {
11298
DataFlow::Node getAReturnedValue() { result = range.getAReturnedValue() }
11399

114100
/**
115-
* Get a dispatch that this event handler can return a value to.
101+
* Get a dispatch that this event handler can return a value to.
116102
* The default implementation is that there exists no such dispatch.
117103
*/
118104
EventDispatch getAReturnDispatch() { result = range.getAReturnDispatch() }
@@ -121,30 +107,39 @@ class EventRegistration extends DataFlow::Node {
121107
module EventRegistration {
122108
/**
123109
* A registration of an event handler on an EventEmitter.
124-
* The default implementation assumes that `this` is a DataFlow::InvokeNode where the
125-
* first argument is a string describing which channel is registered, and the second
126-
* argument is the event handler callback.
127110
*/
128111
abstract class Range extends DataFlow::Node {
129112
EventEmitter::Range emitter;
130113

131114
final EventEmitter getEmitter() { result = emitter }
132115

133-
string getChannel() { this.(DataFlow::InvokeNode).getArgument(0).mayHaveStringValue(result) }
116+
abstract string getChannel();
134117

135-
DataFlow::Node getReceivedItem(int i) {
136-
result = this.(DataFlow::InvokeNode).getABoundCallbackParameter(1, i)
137-
}
118+
abstract DataFlow::Node getReceivedItem(int i);
138119

139-
DataFlow::Node getAReturnedValue() { none() }
120+
abstract DataFlow::Node getAReturnedValue();
140121

141-
EventDispatch::Range getAReturnDispatch() { none() }
122+
abstract EventDispatch::Range getAReturnDispatch();
142123
}
143124

144-
private class NodeJSEventRegistration extends Range, DataFlow::MethodCallNode {
145-
override EventEmitter::NodeJSEventEmitter emitter;
125+
/**
126+
* A default implementation of an EventRegistration.
127+
* The default implementation assumes that `this` is a DataFlow::InvokeNode where the
128+
* first argument is a string describing which channel is registered, and the second
129+
* argument is the event handler callback.
130+
*/
131+
abstract class DefaultEventRegistration extends Range {
132+
override string getChannel() {
133+
this.(DataFlow::InvokeNode).getArgument(0).mayHaveStringValue(result)
134+
}
135+
136+
override DataFlow::Node getReceivedItem(int i) {
137+
result = this.(DataFlow::InvokeNode).getABoundCallbackParameter(1, i)
138+
}
146139

147-
NodeJSEventRegistration() { this = emitter.ref().getAMethodCall(EventEmitter::on()) }
140+
override DataFlow::Node getAReturnedValue() { none() }
141+
142+
override EventDispatch::Range getAReturnDispatch() { none() }
148143
}
149144
}
150145

@@ -176,26 +171,35 @@ class EventDispatch extends DataFlow::Node {
176171
module EventDispatch {
177172
/**
178173
* A dispatch of an event on an EventEmitter.
179-
* The default implementation assumes that the dispatch is a DataFlow::InvokeNode,
180-
* where the first argument is a string describing the channel, and the `i`+1 argument
181-
* is the `i`th item sent to the event handler.
182174
*/
183175
abstract class Range extends DataFlow::Node {
184176
EventEmitter::Range emitter;
185177

186178
final EventEmitter getEmitter() { result = emitter }
187179

188-
string getChannel() { this.(DataFlow::InvokeNode).getArgument(0).mayHaveStringValue(result) }
180+
abstract string getChannel();
189181

190-
DataFlow::Node getSentItem(int i) { result = this.(DataFlow::InvokeNode).getArgument(i + 1) }
182+
abstract DataFlow::Node getSentItem(int i);
191183

192-
EventRegistration::Range getAReceiver() { this.getEmitter() = result.getEmitter() }
184+
abstract EventRegistration::Range getAReceiver();
193185
}
194186

195-
private class NodeJSEventDispatch extends Range, DataFlow::MethodCallNode {
196-
override EventEmitter::NodeJSEventEmitter emitter;
187+
/**
188+
* A default implementation of an EventDispatch.
189+
* The default implementation assumes that the dispatch is a DataFlow::InvokeNode,
190+
* where the first argument is a string describing the channel, and the `i`+1 argument
191+
* is the `i`th item sent to the event handler.
192+
*/
193+
abstract class DefaultEventDispatch extends Range {
194+
override string getChannel() {
195+
this.(DataFlow::InvokeNode).getArgument(0).mayHaveStringValue(result)
196+
}
197+
198+
override DataFlow::Node getSentItem(int i) {
199+
result = this.(DataFlow::InvokeNode).getArgument(i + 1)
200+
}
197201

198-
NodeJSEventDispatch() { this = emitter.ref().getAMethodCall("emit") }
202+
override EventRegistration::Range getAReceiver() { this.getEmitter() = result.getEmitter() }
199203
}
200204
}
201205

javascript/ql/src/semmle/javascript/frameworks/NodeJSLib.qll

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -880,4 +880,49 @@ module NodeJSLib {
880880

881881
override string getSourceType() { result = "NodeJSClientRequest error event" }
882882
}
883+
884+
885+
/**
886+
* An NodeJS EventEmitter instance.
887+
* Events dispatched on this EventEmitter will be handled by event handlers registered on this EventEmitter.
888+
* (That is opposed to e.g. SocketIO, which implements the same interface, but where events cross object boundaries).
889+
*/
890+
abstract class NodeJSEventEmitter extends EventEmitter::Range {
891+
/**
892+
* Get a Node that refers to a NodeJS EventEmitter instance.
893+
*/
894+
DataFlow::SourceNode ref() { result = EventEmitter::trackEventEmitter(this) }
895+
}
896+
897+
/**
898+
* An instance of an EventEmitter that is imported through the 'events' module.
899+
*/
900+
private class ImportedNodeJSEventEmitter extends NodeJSEventEmitter {
901+
ImportedNodeJSEventEmitter() {
902+
exists(DataFlow::SourceNode clazz |
903+
clazz = DataFlow::moduleImport("events") or
904+
clazz = DataFlow::moduleMember("events", "EventEmitter")
905+
|
906+
this = clazz.getAnInstantiation()
907+
)
908+
}
909+
}
910+
911+
/**
912+
* A registration of an event handler on a NodeJS EventEmitter instance.
913+
*/
914+
private class NodeJSEventRegistration extends EventRegistration::DefaultEventRegistration, DataFlow::MethodCallNode {
915+
override NodeJSEventEmitter emitter;
916+
917+
NodeJSEventRegistration() { this = emitter.ref().getAMethodCall(EventEmitter::on()) }
918+
}
919+
920+
/**
921+
* A dispatch of an event on a NodeJS EventEmitter instance.
922+
*/
923+
private class NodeJSEventDispatch extends EventDispatch::DefaultEventDispatch, DataFlow::MethodCallNode {
924+
override NodeJSEventEmitter emitter;
925+
926+
NodeJSEventDispatch() { this = emitter.ref().getAMethodCall("emit") }
927+
}
883928
}

0 commit comments

Comments
 (0)