Skip to content

Commit c19d8ec

Browse files
committed
refactorizations and preparations for SocketIO implementation
1 parent e818f4c commit c19d8ec

File tree

2 files changed

+82
-60
lines changed

2 files changed

+82
-60
lines changed

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

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,12 @@ module Electron {
8989
/**
9090
* A model for the Main and Renderer process in an Electron app.
9191
*/
92-
abstract class Process extends EventEmitter::EventEmitterRange::Range { }
92+
abstract class Process extends EventEmitter::EventEmitterRange::Range {
93+
/**
94+
* Type tracking on a process. The type tracking tracks through chainable methods.
95+
*/
96+
DataFlow::SourceNode ref() { result = EventEmitter::trackEventEmitter(this) }
97+
}
9398

9499
/**
95100
* An instance of the Main process of an Electron app.
@@ -134,7 +139,7 @@ module Electron {
134139
}
135140

136141
override predicate canReturnTo(EventEmitter::EventDispatch dispatch) {
137-
dispatch.getCalleeName() = "sendSync"
142+
dispatch.(IPCDispatch).getCalleeName() = "sendSync"
138143
}
139144
}
140145

@@ -157,20 +162,20 @@ module Electron {
157162
* The 0th parameter in the callback is a event generated by the IPC system,
158163
* therefore these arguments start at 1.
159164
*/
160-
override DataFlow::Node getDispatchedArgument(int i) {
165+
override DataFlow::Node getSentItem(int i) {
161166
i >= 1 and
162167
result = getArgument(i)
163168
}
164169

165170
/**
166-
* Holds if this dispatch can send an event to the given EventRegistration destination.
171+
* Gets a registration that this dispatch can send an event to.
167172
*/
168-
override predicate canSendTo(EventEmitter::EventRegistration destination) {
173+
override IPCSendRegistration getAReceiver() {
169174
this.getEmitter() instanceof RendererProcess and
170-
destination.getEmitter() instanceof MainProcess
175+
result.getEmitter() instanceof MainProcess
171176
or
172177
this.getEmitter() instanceof ProcessSender and
173-
destination.getEmitter() instanceof RendererProcess
178+
result.getEmitter() instanceof RendererProcess
174179
}
175180
}
176181
}

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

Lines changed: 70 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -19,53 +19,57 @@ module EventEmitter {
1919
result = "prependOnceListener"
2020
}
2121

22+
23+
private DataFlow::SourceNode trackEventEmitter(DataFlow::TypeTracker t, EventEmitterRange::Range emitter) {
24+
t.start() and result = emitter
25+
or
26+
exists(DataFlow::TypeTracker t2, DataFlow::SourceNode pred | pred = trackEventEmitter(t2, emitter) |
27+
result = pred.track(t2, t)
28+
or
29+
// invocation of a chainable method
30+
exists(DataFlow::MethodCallNode mcn |
31+
mcn = pred.getAMethodCall(EventEmitter::chainableMethod()) and
32+
// exclude getter versions
33+
exists(mcn.getAnArgument()) and
34+
result = mcn and
35+
t = t2.continue()
36+
)
37+
)
38+
}
39+
2240
/**
23-
* An instance of the NodeJS EventEmitter class.
24-
* Extend this class to mark something as being an instance of the EventEmitter class.
41+
* Type tracking of an EventEmitter. Types are tracked through the chainable methods in the NodeJS eventEmitter.
42+
*/
43+
DataFlow::SourceNode trackEventEmitter(EventEmitterRange::Range emitter) {
44+
result = trackEventEmitter(DataFlow::TypeTracker::end(), emitter)
45+
}
46+
47+
/**
48+
* An EventEmitter instance that implements the NodeJS EventEmitter API.
2549
*/
2650
final class EventEmitter extends DataFlow::Node {
2751
EventEmitterRange::Range range;
2852

2953
EventEmitter() { this = range }
54+
}
3055

56+
module EventEmitterRange {
3157
/**
32-
* Get a method name that returns `this` on this type of emitter.
58+
* An object that implements the EventEmitter API.
59+
* Extending this class does nothing, its mostly to indicate intent.
60+
* The magic only happens when extending EventRegistration::Range and EventDispatch::Range.
3361
*/
34-
string getAChainableMethod() { result = range.getAChainableMethod() }
62+
abstract class Range extends DataFlow::Node {}
3563

3664
/**
37-
* Get a reference through type-tracking to this EventEmitter.
38-
* The type-tracking tracks through chainable methods.
65+
* An NodeJS EventEmitter instance.
66+
* Events dispatched on this EventEmitter will be handled by event handlers registered on this EventEmitter.
67+
* (That is opposed to e.g. SocketIO, which implements the same interface, but where events cross object boundaries).
3968
*/
40-
DataFlow::SourceNode ref() { result = range.ref() }
41-
}
42-
43-
module EventEmitterRange {
44-
abstract class Range extends DataFlow::Node {
45-
string getAChainableMethod() { result = EventEmitter::chainableMethod() }
46-
47-
private DataFlow::SourceNode ref(DataFlow::TypeTracker t) {
48-
t.start() and result = this
49-
or
50-
exists(DataFlow::TypeTracker t2, DataFlow::SourceNode pred | pred = ref(t2) |
51-
result = pred.track(t2, t)
52-
or
53-
// invocation of a chainable method
54-
exists(DataFlow::MethodCallNode mcn |
55-
mcn = pred.getAMethodCall(this.getAChainableMethod()) and
56-
// exclude getter versions
57-
exists(mcn.getAnArgument()) and
58-
result = mcn and
59-
t = t2.continue()
60-
)
61-
)
62-
}
63-
64-
DataFlow::SourceNode ref() { result = ref(DataFlow::TypeTracker::end()) }
69+
abstract class NodeJSEventEmitter extends Range {
70+
DataFlow::SourceNode ref() { result = trackEventEmitter(this) }
6571
}
6672

67-
abstract class NodeJSEventEmitter extends Range {}
68-
6973
private class ImportedNodeJSEventEmitter extends NodeJSEventEmitter {
7074
ImportedNodeJSEventEmitter() {
7175
exists(DataFlow::SourceNode clazz |
@@ -79,7 +83,7 @@ module EventEmitter {
7983
}
8084

8185
/**
82-
* A registration of an event handler on a particular EventEmitter.
86+
* A registration of an event handler on an EventEmitter.
8387
*/
8488
final class EventRegistration extends DataFlow::Node {
8589
EventRegistration::Range range;
@@ -93,7 +97,7 @@ module EventEmitter {
9397
string getChannel() { result = range.getChannel() }
9498

9599
/** Gets the `i`th parameter in the event handler. */
96-
DataFlow::Node getEventHandlerParameter(int i) { result = range.getEventHandlerParameter(i) }
100+
DataFlow::Node getReceivedItem(int i) { result = range.getReceivedItem(i) }
97101

98102
/**
99103
* Gets a value that is returned by the event handler.
@@ -109,17 +113,23 @@ module EventEmitter {
109113
}
110114

111115
module EventRegistration {
112-
abstract class Range extends DataFlow::CallNode {
116+
/**
117+
* A registration of an event handler on an EventEmitter.
118+
* The default implementation assumes that `this` is a DataFlow::InvokeNode where the
119+
* first argument is a string describing which channel is registered, and the second
120+
* argument is the event handler callback.
121+
*/
122+
abstract class Range extends DataFlow::Node {
113123
EventEmitterRange::Range emitter;
114124

115125
final EventEmitter getEmitter() { result = emitter }
116126

117127
string getChannel() {
118-
this.getArgument(0).mayHaveStringValue(result)
128+
this.(DataFlow::InvokeNode).getArgument(0).mayHaveStringValue(result)
119129
}
120130

121-
DataFlow::Node getEventHandlerParameter(int i) {
122-
result = this.getABoundCallbackParameter(1, i)
131+
DataFlow::Node getReceivedItem(int i) {
132+
result = this.(DataFlow::InvokeNode).getABoundCallbackParameter(1, i)
123133
}
124134

125135
DataFlow::Node getAReturnedValue() { none() }
@@ -137,7 +147,7 @@ module EventEmitter {
137147
/**
138148
* A dispatch of an event on an EventEmitter.
139149
*/
140-
final class EventDispatch extends DataFlow::CallNode {
150+
final class EventDispatch extends DataFlow::Node {
141151
EventDispatch::Range range;
142152

143153
EventDispatch() { this = range }
@@ -149,31 +159,38 @@ module EventEmitter {
149159
string getChannel() { result = range.getChannel() }
150160

151161
/** Gets the `i`th argument that is send to the event handler. */
152-
DataFlow::Node getDispatchedArgument(int i) { result = range.getDispatchedArgument(i) }
162+
DataFlow::Node getSentItem(int i) { result = range.getSentItem(i) }
153163

154164
/**
155-
* Holds if this event dispatch can send an event to the given even registration.
165+
* Get an EventRegistration that this event dispatch can send an event to.
156166
* The default implementation is that the emitters of the dispatch and registration have to be equal.
167+
* Channels are by default ignored.
157168
*/
158-
predicate canSendTo(EventRegistration destination) { range.canSendTo(destination) }
169+
EventRegistration getAReceiver() { result = range.getAReceiver() }
159170
}
160171

161172
module EventDispatch {
162-
abstract class Range extends DataFlow::CallNode {
173+
/**
174+
* A dispatch of an event on an EventEmitter.
175+
* The default implementation assumes that the dispatch is a DataFlow::InvokeNode,
176+
* where the first argument is a string describing the channel, and the `i`+1 argument
177+
* is the `i`th item sent to the event handler.
178+
*/
179+
abstract class Range extends DataFlow::Node {
163180
EventEmitterRange::Range emitter;
164181

165182
final EventEmitter getEmitter() { result = emitter }
166183

167184
string getChannel() {
168-
this.getArgument(0).mayHaveStringValue(result)
185+
this.(DataFlow::InvokeNode).getArgument(0).mayHaveStringValue(result)
169186
}
170187

171-
DataFlow::Node getDispatchedArgument(int i) {
172-
result = this.getArgument(i + 1)
188+
DataFlow::Node getSentItem(int i) {
189+
result = this.(DataFlow::InvokeNode).getArgument(i + 1)
173190
}
174191

175-
predicate canSendTo(EventRegistration destination) {
176-
this.getEmitter() = destination.getEmitter()
192+
EventRegistration::Range getAReceiver() {
193+
this.getEmitter() = result.getEmitter()
177194
}
178195
}
179196

@@ -193,14 +210,14 @@ module EventEmitter {
193210

194211
EventEmitterTaintStep() {
195212
this = dispatch and
196-
dispatch.canSendTo(reg) and
197-
reg.getChannel() = dispatch.getChannel()
213+
reg = dispatch.getAReceiver() and
214+
not dispatch.getChannel() != reg.getChannel()
198215
}
199216

200217
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
201218
exists(int i | i >= 0 |
202-
pred = dispatch.getDispatchedArgument(i) and
203-
succ = reg.getEventHandlerParameter(i)
219+
pred = dispatch.getSentItem(i) and
220+
succ = reg.getReceivedItem(i)
204221
)
205222
or
206223
reg.canReturnTo(dispatch) and

0 commit comments

Comments
 (0)