@@ -37,12 +37,15 @@ class PipeCall extends DataFlow::MethodCallNode {
37
37
* Gets a reference to a value that is known to not be a Node.js stream.
38
38
* This is used to exclude pipe calls on non-stream objects from analysis.
39
39
*/
40
- DataFlow:: Node getNonNodeJsStreamType ( ) {
40
+ private DataFlow:: Node getNonNodeJsStreamType ( ) {
41
41
result = getNonStreamApi ( ) .getAValueReachableFromSource ( )
42
42
}
43
43
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 ( ) {
46
49
exists ( string moduleName |
47
50
moduleName
48
51
.regexpMatch ( [
@@ -65,12 +68,12 @@ API::Node getNonStreamApi() {
65
68
* Gets the method names used to register event handlers on Node.js streams.
66
69
* These methods are used to attach handlers for events like `error`.
67
70
*/
68
- string getEventHandlerMethodName ( ) { result = [ "on" , "once" , "addListener" ] }
71
+ private string getEventHandlerMethodName ( ) { result = [ "on" , "once" , "addListener" ] }
69
72
70
73
/**
71
74
* Gets the method names that are chainable on Node.js streams.
72
75
*/
73
- string getChainableStreamMethodName ( ) {
76
+ private string getChainableStreamMethodName ( ) {
74
77
result =
75
78
[
76
79
"setEncoding" , "pause" , "resume" , "unpipe" , "destroy" , "cork" , "uncork" , "setDefaultEncoding" ,
@@ -81,14 +84,14 @@ string getChainableStreamMethodName() {
81
84
/**
82
85
* Gets the method names that are not chainable on Node.js streams.
83
86
*/
84
- string getNonchainableStreamMethodName ( ) {
87
+ private string getNonchainableStreamMethodName ( ) {
85
88
result = [ "read" , "write" , "end" , "pipe" , "unshift" , "push" , "isPaused" , "wrap" , "emit" ]
86
89
}
87
90
88
91
/**
89
92
* Gets the property names commonly found on Node.js streams.
90
93
*/
91
- string getStreamPropertyName ( ) {
94
+ private string getStreamPropertyName ( ) {
92
95
result =
93
96
[
94
97
"readable" , "writable" , "destroyed" , "closed" , "readableHighWaterMark" , "readableLength" ,
@@ -103,7 +106,7 @@ string getStreamPropertyName() {
103
106
/**
104
107
* Gets all method names commonly found on Node.js streams.
105
108
*/
106
- string getStreamMethodName ( ) {
109
+ private string getStreamMethodName ( ) {
107
110
result = [ getChainableStreamMethodName ( ) , getNonchainableStreamMethodName ( ) ]
108
111
}
109
112
@@ -123,7 +126,7 @@ class ErrorHandlerRegistration extends DataFlow::MethodCallNode {
123
126
* Connects destination streams to their corresponding pipe call nodes.
124
127
* Connects streams to their chainable methods.
125
128
*/
126
- predicate streamFlowStep ( DataFlow:: Node streamNode , DataFlow:: Node relatedNode ) {
129
+ private predicate streamFlowStep ( DataFlow:: Node streamNode , DataFlow:: Node relatedNode ) {
127
130
exists ( PipeCall pipe |
128
131
streamNode = pipe .getDestinationStream ( ) and
129
132
relatedNode = pipe
@@ -139,42 +142,42 @@ predicate streamFlowStep(DataFlow::Node streamNode, DataFlow::Node relatedNode)
139
142
/**
140
143
* Tracks the result of a pipe call as it flows through the program.
141
144
*/
142
- private DataFlow:: SourceNode pipeResultTracker ( DataFlow:: TypeTracker t , PipeCall pipe ) {
145
+ private DataFlow:: SourceNode destinationStreamRef ( DataFlow:: TypeTracker t , PipeCall pipe ) {
143
146
t .start ( ) and result = pipe .getALocalSource ( )
144
147
or
145
148
exists ( DataFlow:: SourceNode prev |
146
- prev = pipeResultTracker ( t .continue ( ) , pipe ) and
149
+ prev = destinationStreamRef ( t .continue ( ) , pipe ) and
147
150
streamFlowStep ( result .getALocalUse ( ) , prev )
148
151
)
149
152
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 ) )
151
154
}
152
155
153
156
/**
154
157
* Gets a reference to the result of a pipe call.
155
158
*/
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 )
158
161
}
159
162
160
163
/**
161
164
* Holds if the pipe call result is used to call a non-stream method.
162
165
* Since pipe() returns the destination stream, this finds cases where
163
166
* the destination stream is used with methods not typical of streams.
164
167
*/
165
- predicate isPipeFollowedByNonStreamMethod ( PipeCall pipeCall ) {
168
+ private predicate isPipeFollowedByNonStreamMethod ( PipeCall pipeCall ) {
166
169
exists ( DataFlow:: MethodCallNode call |
167
- call = pipeResultRef ( pipeCall ) .getAMethodCall ( ) and
170
+ call = destinationStreamRef ( pipeCall ) .getAMethodCall ( ) and
168
171
not call .getMethodName ( ) = getStreamMethodName ( )
169
172
)
170
173
}
171
174
172
175
/**
173
176
* Holds if the pipe call result is used to access a property that is not typical of streams.
174
177
*/
175
- predicate isPipeFollowedByNonStreamProperty ( PipeCall pipeCall ) {
178
+ private predicate isPipeFollowedByNonStreamProperty ( PipeCall pipeCall ) {
176
179
exists ( DataFlow:: PropRef propRef |
177
- propRef = pipeResultRef ( pipeCall ) .getAPropertyRead ( ) and
180
+ propRef = destinationStreamRef ( pipeCall ) .getAPropertyRead ( ) and
178
181
not propRef .getPropertyName ( ) = [ getStreamPropertyName ( ) , getStreamMethodName ( ) ]
179
182
)
180
183
}
@@ -183,7 +186,7 @@ predicate isPipeFollowedByNonStreamProperty(PipeCall pipeCall) {
183
186
* Holds if the pipe call result is used in a non-stream-like way,
184
187
* either by calling non-stream methods or accessing non-stream properties.
185
188
*/
186
- predicate isPipeFollowedByNonStreamAccess ( PipeCall pipeCall ) {
189
+ private predicate isPipeFollowedByNonStreamAccess ( PipeCall pipeCall ) {
187
190
isPipeFollowedByNonStreamMethod ( pipeCall ) or
188
191
isPipeFollowedByNonStreamProperty ( pipeCall )
189
192
}
@@ -192,65 +195,66 @@ predicate isPipeFollowedByNonStreamAccess(PipeCall pipeCall) {
192
195
* Gets a reference to a stream that may be the source of the given pipe call.
193
196
* Uses type back-tracking to trace stream references in the data flow.
194
197
*/
195
- private DataFlow:: SourceNode streamRef ( DataFlow:: TypeBackTracker t , PipeCall pipeCall ) {
198
+ private DataFlow:: SourceNode sourceStreamRef ( DataFlow:: TypeBackTracker t , PipeCall pipeCall ) {
196
199
t .start ( ) and
197
200
result = pipeCall .getSourceStream ( ) .getALocalSource ( )
198
201
or
199
202
exists ( DataFlow:: SourceNode prev |
200
- prev = streamRef ( t .continue ( ) , pipeCall ) and
203
+ prev = sourceStreamRef ( t .continue ( ) , pipeCall ) and
201
204
streamFlowStep ( result .getALocalUse ( ) , prev )
202
205
)
203
206
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 ) )
205
208
}
206
209
207
210
/**
208
211
* Gets a reference to a stream that may be the source of the given pipe call.
209
212
*/
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 )
212
215
}
213
216
214
217
/**
215
218
* Holds if the source stream of the given pipe call has an `error` handler registered.
216
219
*/
217
- predicate hasErrorHandlerRegistered ( PipeCall pipeCall ) {
220
+ private predicate hasErrorHandlerRegistered ( PipeCall pipeCall ) {
218
221
exists ( ErrorHandlerRegistration handler |
219
- handler = streamRef ( pipeCall ) .getAMethodCall ( getEventHandlerMethodName ( ) )
222
+ handler = sourceStreamRef ( pipeCall ) .getAMethodCall ( getEventHandlerMethodName ( ) )
220
223
)
221
224
or
222
225
hasPlumber ( pipeCall )
223
226
}
224
227
225
228
/**
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.
227
231
*/
228
- predicate hasPlumber ( PipeCall pipeCall ) {
232
+ private predicate hasPlumber ( PipeCall pipeCall ) {
229
233
pipeCall .getDestinationStream ( ) .getALocalSource ( ) = API:: moduleImport ( "gulp-plumber" ) .getACall ( )
230
234
or
231
- streamRef + ( pipeCall ) = API:: moduleImport ( "gulp-plumber" ) .getACall ( )
235
+ sourceStreamRef + ( pipeCall ) = API:: moduleImport ( "gulp-plumber" ) .getACall ( )
232
236
}
233
237
234
238
/**
235
239
* Holds if the source or destination of the given pipe call is identified as a non-Node.js stream.
236
240
*/
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 ( )
240
244
}
241
245
242
246
/**
243
247
* Holds if the source stream of the given pipe call is used in a non-stream-like way.
244
248
*/
245
249
private predicate hasNonStreamSourceLikeUsage ( PipeCall pipeCall ) {
246
250
exists ( DataFlow:: MethodCallNode call , string name |
247
- call .getReceiver ( ) .getALocalSource ( ) = streamRef ( pipeCall ) and
251
+ call .getReceiver ( ) .getALocalSource ( ) = sourceStreamRef ( pipeCall ) and
248
252
name = call .getMethodName ( ) and
249
253
not name = getStreamMethodName ( )
250
254
)
251
255
or
252
256
exists ( DataFlow:: PropRef propRef , string propName |
253
- propRef .getBase ( ) .getALocalSource ( ) = streamRef ( pipeCall ) and
257
+ propRef .getBase ( ) .getALocalSource ( ) = sourceStreamRef ( pipeCall ) and
254
258
propName = propRef .getPropertyName ( ) and
255
259
not propName = [ getStreamPropertyName ( ) , getStreamMethodName ( ) ]
256
260
)
@@ -259,9 +263,9 @@ private predicate hasNonStreamSourceLikeUsage(PipeCall pipeCall) {
259
263
/**
260
264
* Holds if the pipe call destination stream has an error handler registered.
261
265
*/
262
- predicate hasErrorHandlerDownstream ( PipeCall pipeCall ) {
266
+ private predicate hasErrorHandlerDownstream ( PipeCall pipeCall ) {
263
267
exists ( ErrorHandlerRegistration handler |
264
- handler .getReceiver ( ) .getALocalSource ( ) = pipeResultRef ( pipeCall )
268
+ handler .getReceiver ( ) .getALocalSource ( ) = destinationStreamRef ( pipeCall )
265
269
)
266
270
}
267
271
0 commit comments