@@ -165,17 +165,19 @@ class KernelSpawnCall extends SystemCommandExecution::Range {
165
165
}
166
166
}
167
167
168
+ /**
169
+ * A system command executed via one of the `Open3` methods.
170
+ * These methods take the same argument forms as `Kernel.system`.
171
+ * See `KernelSystemCall` for details.
172
+ */
168
173
class Open3Call extends SystemCommandExecution:: Range {
169
174
MethodCall methodCall ;
170
175
171
176
Open3Call ( ) {
172
177
this .asExpr ( ) .getExpr ( ) = methodCall and
173
178
this =
174
179
API:: getTopLevelMember ( "Open3" )
175
- .getAMethodCall ( [
176
- "popen3" , "popen2" , "popen2e" , "capture3" , "capture2" , "capture2e" , "pipeline_rw" ,
177
- "pipeline_r" , "pipeline_w" , "pipeline_start" , "pipeline"
178
- ] )
180
+ .getAMethodCall ( [ "popen3" , "popen2" , "popen2e" , "capture3" , "capture2" , "capture2e" ] )
179
181
}
180
182
181
183
override DataFlow:: Node getAnArgument ( ) { result .asExpr ( ) .getExpr ( ) = methodCall .getAnArgument ( ) }
@@ -185,3 +187,32 @@ class Open3Call extends SystemCommandExecution::Range {
185
187
methodCall .getNumberOfArguments ( ) = 1 and arg .asExpr ( ) .getExpr ( ) = methodCall .getAnArgument ( )
186
188
}
187
189
}
190
+
191
+ /**
192
+ * A pipeline of system commands constructed via one of the `Open3` methods.
193
+ * These methods accept a variable argument list of commands.
194
+ * Commands can be in any form supported by `Kernel.system`. See `KernelSystemCall` for details.
195
+ * ```ruby
196
+ * Open3.pipeline("cat foo.txt", "tail")
197
+ * Open3.pipeline(["cat", "foo.txt"], "tail")
198
+ * Open3.pipeline([{}, "cat", "foo.txt"], "tail")
199
+ * Open3.pipeline([["cat", "cat"], "foo.txt"], "tail")
200
+ */
201
+ class Open3PipelineCall extends SystemCommandExecution:: Range {
202
+ MethodCall methodCall ;
203
+
204
+ Open3PipelineCall ( ) {
205
+ this .asExpr ( ) .getExpr ( ) = methodCall and
206
+ this =
207
+ API:: getTopLevelMember ( "Open3" )
208
+ .getAMethodCall ( [ "pipeline_rw" , "pipeline_r" , "pipeline_w" , "pipeline_start" , "pipeline" ] )
209
+ }
210
+
211
+ override DataFlow:: Node getAnArgument ( ) { result .asExpr ( ) .getExpr ( ) = methodCall .getAnArgument ( ) }
212
+
213
+ override predicate isShellInterpreted ( DataFlow:: Node arg ) {
214
+ // A command in the pipeline is executed in a subshell if it is given as a single string argument.
215
+ arg .asExpr ( ) .getExpr ( ) instanceof StringlikeLiteral and
216
+ arg .asExpr ( ) .getExpr ( ) = methodCall .getAnArgument ( )
217
+ }
218
+ }
0 commit comments