Skip to content

Commit 2e2b9a9

Browse files
committed
Make predicates private and clarify stream reference naming.
1 parent f8f5d8f commit 2e2b9a9

File tree

1 file changed

+40
-36
lines changed

1 file changed

+40
-36
lines changed

javascript/ql/src/Quality/UnhandledStreamPipe.ql

Lines changed: 40 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,15 @@ class PipeCall extends DataFlow::MethodCallNode {
3737
* Gets a reference to a value that is known to not be a Node.js stream.
3838
* This is used to exclude pipe calls on non-stream objects from analysis.
3939
*/
40-
DataFlow::Node getNonNodeJsStreamType() {
40+
private DataFlow::Node getNonNodeJsStreamType() {
4141
result = getNonStreamApi().getAValueReachableFromSource()
4242
}
4343

44-
//highland, arktype execa
45-
API::Node getNonStreamApi() {
44+
/**
45+
* Gets API nodes from modules that are known to not provide Node.js streams.
46+
* This includes reactive programming libraries, frontend frameworks, and other non-stream APIs.
47+
*/
48+
private API::Node getNonStreamApi() {
4649
exists(string moduleName |
4750
moduleName
4851
.regexpMatch([
@@ -65,12 +68,12 @@ API::Node getNonStreamApi() {
6568
* Gets the method names used to register event handlers on Node.js streams.
6669
* These methods are used to attach handlers for events like `error`.
6770
*/
68-
string getEventHandlerMethodName() { result = ["on", "once", "addListener"] }
71+
private string getEventHandlerMethodName() { result = ["on", "once", "addListener"] }
6972

7073
/**
7174
* Gets the method names that are chainable on Node.js streams.
7275
*/
73-
string getChainableStreamMethodName() {
76+
private string getChainableStreamMethodName() {
7477
result =
7578
[
7679
"setEncoding", "pause", "resume", "unpipe", "destroy", "cork", "uncork", "setDefaultEncoding",
@@ -81,14 +84,14 @@ string getChainableStreamMethodName() {
8184
/**
8285
* Gets the method names that are not chainable on Node.js streams.
8386
*/
84-
string getNonchainableStreamMethodName() {
87+
private string getNonchainableStreamMethodName() {
8588
result = ["read", "write", "end", "pipe", "unshift", "push", "isPaused", "wrap", "emit"]
8689
}
8790

8891
/**
8992
* Gets the property names commonly found on Node.js streams.
9093
*/
91-
string getStreamPropertyName() {
94+
private string getStreamPropertyName() {
9295
result =
9396
[
9497
"readable", "writable", "destroyed", "closed", "readableHighWaterMark", "readableLength",
@@ -103,7 +106,7 @@ string getStreamPropertyName() {
103106
/**
104107
* Gets all method names commonly found on Node.js streams.
105108
*/
106-
string getStreamMethodName() {
109+
private string getStreamMethodName() {
107110
result = [getChainableStreamMethodName(), getNonchainableStreamMethodName()]
108111
}
109112

@@ -123,7 +126,7 @@ class ErrorHandlerRegistration extends DataFlow::MethodCallNode {
123126
* Connects destination streams to their corresponding pipe call nodes.
124127
* Connects streams to their chainable methods.
125128
*/
126-
predicate streamFlowStep(DataFlow::Node streamNode, DataFlow::Node relatedNode) {
129+
private predicate streamFlowStep(DataFlow::Node streamNode, DataFlow::Node relatedNode) {
127130
exists(PipeCall pipe |
128131
streamNode = pipe.getDestinationStream() and
129132
relatedNode = pipe
@@ -139,42 +142,42 @@ predicate streamFlowStep(DataFlow::Node streamNode, DataFlow::Node relatedNode)
139142
/**
140143
* Tracks the result of a pipe call as it flows through the program.
141144
*/
142-
private DataFlow::SourceNode pipeResultTracker(DataFlow::TypeTracker t, PipeCall pipe) {
145+
private DataFlow::SourceNode destinationStreamRef(DataFlow::TypeTracker t, PipeCall pipe) {
143146
t.start() and result = pipe.getALocalSource()
144147
or
145148
exists(DataFlow::SourceNode prev |
146-
prev = pipeResultTracker(t.continue(), pipe) and
149+
prev = destinationStreamRef(t.continue(), pipe) and
147150
streamFlowStep(result.getALocalUse(), prev)
148151
)
149152
or
150-
exists(DataFlow::TypeTracker t2 | result = pipeResultTracker(t2, pipe).track(t2, t))
153+
exists(DataFlow::TypeTracker t2 | result = destinationStreamRef(t2, pipe).track(t2, t))
151154
}
152155

153156
/**
154157
* Gets a reference to the result of a pipe call.
155158
*/
156-
private DataFlow::SourceNode pipeResultRef(PipeCall pipe) {
157-
result = pipeResultTracker(DataFlow::TypeTracker::end(), pipe)
159+
private DataFlow::SourceNode destinationStreamRef(PipeCall pipe) {
160+
result = destinationStreamRef(DataFlow::TypeTracker::end(), pipe)
158161
}
159162

160163
/**
161164
* Holds if the pipe call result is used to call a non-stream method.
162165
* Since pipe() returns the destination stream, this finds cases where
163166
* the destination stream is used with methods not typical of streams.
164167
*/
165-
predicate isPipeFollowedByNonStreamMethod(PipeCall pipeCall) {
168+
private predicate isPipeFollowedByNonStreamMethod(PipeCall pipeCall) {
166169
exists(DataFlow::MethodCallNode call |
167-
call = pipeResultRef(pipeCall).getAMethodCall() and
170+
call = destinationStreamRef(pipeCall).getAMethodCall() and
168171
not call.getMethodName() = getStreamMethodName()
169172
)
170173
}
171174

172175
/**
173176
* Holds if the pipe call result is used to access a property that is not typical of streams.
174177
*/
175-
predicate isPipeFollowedByNonStreamProperty(PipeCall pipeCall) {
178+
private predicate isPipeFollowedByNonStreamProperty(PipeCall pipeCall) {
176179
exists(DataFlow::PropRef propRef |
177-
propRef = pipeResultRef(pipeCall).getAPropertyRead() and
180+
propRef = destinationStreamRef(pipeCall).getAPropertyRead() and
178181
not propRef.getPropertyName() = [getStreamPropertyName(), getStreamMethodName()]
179182
)
180183
}
@@ -183,7 +186,7 @@ predicate isPipeFollowedByNonStreamProperty(PipeCall pipeCall) {
183186
* Holds if the pipe call result is used in a non-stream-like way,
184187
* either by calling non-stream methods or accessing non-stream properties.
185188
*/
186-
predicate isPipeFollowedByNonStreamAccess(PipeCall pipeCall) {
189+
private predicate isPipeFollowedByNonStreamAccess(PipeCall pipeCall) {
187190
isPipeFollowedByNonStreamMethod(pipeCall) or
188191
isPipeFollowedByNonStreamProperty(pipeCall)
189192
}
@@ -192,65 +195,66 @@ predicate isPipeFollowedByNonStreamAccess(PipeCall pipeCall) {
192195
* Gets a reference to a stream that may be the source of the given pipe call.
193196
* Uses type back-tracking to trace stream references in the data flow.
194197
*/
195-
private DataFlow::SourceNode streamRef(DataFlow::TypeBackTracker t, PipeCall pipeCall) {
198+
private DataFlow::SourceNode sourceStreamRef(DataFlow::TypeBackTracker t, PipeCall pipeCall) {
196199
t.start() and
197200
result = pipeCall.getSourceStream().getALocalSource()
198201
or
199202
exists(DataFlow::SourceNode prev |
200-
prev = streamRef(t.continue(), pipeCall) and
203+
prev = sourceStreamRef(t.continue(), pipeCall) and
201204
streamFlowStep(result.getALocalUse(), prev)
202205
)
203206
or
204-
exists(DataFlow::TypeBackTracker t2 | result = streamRef(t2, pipeCall).backtrack(t2, t))
207+
exists(DataFlow::TypeBackTracker t2 | result = sourceStreamRef(t2, pipeCall).backtrack(t2, t))
205208
}
206209

207210
/**
208211
* Gets a reference to a stream that may be the source of the given pipe call.
209212
*/
210-
private DataFlow::SourceNode streamRef(PipeCall pipeCall) {
211-
result = streamRef(DataFlow::TypeBackTracker::end(), pipeCall)
213+
private DataFlow::SourceNode sourceStreamRef(PipeCall pipeCall) {
214+
result = sourceStreamRef(DataFlow::TypeBackTracker::end(), pipeCall)
212215
}
213216

214217
/**
215218
* Holds if the source stream of the given pipe call has an `error` handler registered.
216219
*/
217-
predicate hasErrorHandlerRegistered(PipeCall pipeCall) {
220+
private predicate hasErrorHandlerRegistered(PipeCall pipeCall) {
218221
exists(ErrorHandlerRegistration handler |
219-
handler = streamRef(pipeCall).getAMethodCall(getEventHandlerMethodName())
222+
handler = sourceStreamRef(pipeCall).getAMethodCall(getEventHandlerMethodName())
220223
)
221224
or
222225
hasPlumber(pipeCall)
223226
}
224227

225228
/**
226-
* Holds if one of the arguments of the pipe call is a `gulp-plumber` monkey patch.
229+
* Holds if the pipe call uses `gulp-plumber`, which automatically handles stream errors.
230+
* Gulp-plumber is a Node.js module that prevents pipe breaking caused by errors from gulp plugins.
227231
*/
228-
predicate hasPlumber(PipeCall pipeCall) {
232+
private predicate hasPlumber(PipeCall pipeCall) {
229233
pipeCall.getDestinationStream().getALocalSource() = API::moduleImport("gulp-plumber").getACall()
230234
or
231-
streamRef+(pipeCall) = API::moduleImport("gulp-plumber").getACall()
235+
sourceStreamRef+(pipeCall) = API::moduleImport("gulp-plumber").getACall()
232236
}
233237

234238
/**
235239
* Holds if the source or destination of the given pipe call is identified as a non-Node.js stream.
236240
*/
237-
predicate hasNonNodeJsStreamSource(PipeCall pipeCall) {
238-
streamRef(pipeCall) = getNonNodeJsStreamType() or
239-
pipeResultRef(pipeCall) = getNonNodeJsStreamType()
241+
private predicate hasNonNodeJsStreamSource(PipeCall pipeCall) {
242+
sourceStreamRef(pipeCall) = getNonNodeJsStreamType() or
243+
destinationStreamRef(pipeCall) = getNonNodeJsStreamType()
240244
}
241245

242246
/**
243247
* Holds if the source stream of the given pipe call is used in a non-stream-like way.
244248
*/
245249
private predicate hasNonStreamSourceLikeUsage(PipeCall pipeCall) {
246250
exists(DataFlow::MethodCallNode call, string name |
247-
call.getReceiver().getALocalSource() = streamRef(pipeCall) and
251+
call.getReceiver().getALocalSource() = sourceStreamRef(pipeCall) and
248252
name = call.getMethodName() and
249253
not name = getStreamMethodName()
250254
)
251255
or
252256
exists(DataFlow::PropRef propRef, string propName |
253-
propRef.getBase().getALocalSource() = streamRef(pipeCall) and
257+
propRef.getBase().getALocalSource() = sourceStreamRef(pipeCall) and
254258
propName = propRef.getPropertyName() and
255259
not propName = [getStreamPropertyName(), getStreamMethodName()]
256260
)
@@ -259,9 +263,9 @@ private predicate hasNonStreamSourceLikeUsage(PipeCall pipeCall) {
259263
/**
260264
* Holds if the pipe call destination stream has an error handler registered.
261265
*/
262-
predicate hasErrorHandlerDownstream(PipeCall pipeCall) {
266+
private predicate hasErrorHandlerDownstream(PipeCall pipeCall) {
263267
exists(ErrorHandlerRegistration handler |
264-
handler.getReceiver().getALocalSource() = pipeResultRef(pipeCall)
268+
handler.getReceiver().getALocalSource() = destinationStreamRef(pipeCall)
265269
)
266270
}
267271

0 commit comments

Comments
 (0)