@@ -1060,7 +1060,11 @@ private module StdlibPrivate {
10601060 private class OsSystemCall extends SystemCommandExecution:: Range , DataFlow:: CallCfgNode {
10611061 OsSystemCall ( ) { this = os ( ) .getMember ( "system" ) .getACall ( ) }
10621062
1063- override DataFlow:: Node getCommand ( ) { result = this .getArg ( 0 ) }
1063+ override DataFlow:: Node getCommand ( ) {
1064+ result in [ this .getArg ( 0 ) , this .getArgByName ( "command" ) ]
1065+ }
1066+
1067+ override predicate isShellInterpreted ( DataFlow:: Node arg ) { arg = this .getCommand ( ) }
10641068 }
10651069
10661070 /**
@@ -1071,7 +1075,7 @@ private module StdlibPrivate {
10711075 * Although deprecated since version 2.6, they still work in 2.7.
10721076 * See https://docs.python.org/2.7/library/os.html#os.popen2
10731077 */
1074- private class OsPopenCall extends SystemCommandExecution:: Range , DataFlow :: CallCfgNode {
1078+ private class OsPopenCall extends SystemCommandExecution:: Range , API :: CallNode {
10751079 string name ;
10761080
10771081 OsPopenCall ( ) {
@@ -1085,6 +1089,8 @@ private module StdlibPrivate {
10851089 not name = "popen" and
10861090 result = this .getArgByName ( "cmd" )
10871091 }
1092+
1093+ override predicate isShellInterpreted ( DataFlow:: Node arg ) { arg = this .getCommand ( ) }
10881094 }
10891095
10901096 /**
@@ -1104,6 +1110,10 @@ private module StdlibPrivate {
11041110 override DataFlow:: Node getCommand ( ) { result = this .getArg ( 0 ) }
11051111
11061112 override DataFlow:: Node getAPathArgument ( ) { result = this .getCommand ( ) }
1113+
1114+ override predicate isShellInterpreted ( DataFlow:: Node arg ) {
1115+ none ( ) // this is a safe API.
1116+ }
11071117 }
11081118
11091119 /**
@@ -1131,6 +1141,10 @@ private module StdlibPrivate {
11311141 }
11321142
11331143 override DataFlow:: Node getAPathArgument ( ) { result = this .getCommand ( ) }
1144+
1145+ override predicate isShellInterpreted ( DataFlow:: Node arg ) {
1146+ none ( ) // this is a safe API.
1147+ }
11341148 }
11351149
11361150 /**
@@ -1145,6 +1159,10 @@ private module StdlibPrivate {
11451159 override DataFlow:: Node getCommand ( ) { result in [ this .getArg ( 0 ) , this .getArgByName ( "path" ) ] }
11461160
11471161 override DataFlow:: Node getAPathArgument ( ) { result = this .getCommand ( ) }
1162+
1163+ override predicate isShellInterpreted ( DataFlow:: Node arg ) {
1164+ none ( ) // this is a safe API.
1165+ }
11481166 }
11491167
11501168 /** An additional taint step for calls to `os.path.join` */
@@ -1170,7 +1188,7 @@ private module StdlibPrivate {
11701188 * See https://docs.python.org/3.8/library/subprocess.html#subprocess.Popen
11711189 * ref: https://docs.python.org/3/library/subprocess.html#legacy-shell-invocation-functions
11721190 */
1173- private class SubprocessPopenCall extends SystemCommandExecution:: Range , DataFlow :: CallCfgNode {
1191+ private class SubprocessPopenCall extends SystemCommandExecution:: Range , API :: CallNode {
11741192 SubprocessPopenCall ( ) {
11751193 exists ( string name |
11761194 name in [
@@ -1180,43 +1198,33 @@ private module StdlibPrivate {
11801198 )
11811199 }
11821200
1183- /** Gets the ControlFlowNode for the `args` argument, if any. */
1184- private DataFlow :: Node get_args_arg ( ) { result in [ this .getArg ( 0 ) , this . getArgByName ( "args" ) ] }
1201+ /** Gets the API-node for the `args` argument, if any. */
1202+ private API :: Node get_args_arg ( ) { result = this .getParameter ( 0 , "args" ) }
11851203
1186- /** Gets the ControlFlowNode for the `shell` argument, if any. */
1187- private DataFlow:: Node get_shell_arg ( ) {
1188- result in [ this .getArg ( 8 ) , this .getArgByName ( "shell" ) ]
1189- }
1204+ /** Gets the API-node for the `shell` argument, if any. */
1205+ private API:: Node get_shell_arg ( ) { result = this .getParameter ( 8 , "shell" ) }
11901206
11911207 private boolean get_shell_arg_value ( ) {
11921208 not exists ( this .get_shell_arg ( ) ) and
11931209 result = false
11941210 or
1195- exists ( DataFlow:: Node shell_arg | shell_arg = this .get_shell_arg ( ) |
1196- result = shell_arg .asCfgNode ( ) .getNode ( ) .( ImmutableLiteral ) .booleanValue ( )
1197- or
1198- // TODO: Track the "shell" argument to determine possible values
1199- not shell_arg .asCfgNode ( ) .getNode ( ) instanceof ImmutableLiteral and
1200- (
1201- result = true
1202- or
1203- result = false
1204- )
1205- )
1211+ result =
1212+ this .get_shell_arg ( ) .getAValueReachingSink ( ) .asExpr ( ) .( ImmutableLiteral ) .booleanValue ( )
1213+ or
1214+ not this .get_shell_arg ( ) .getAValueReachingSink ( ) .asExpr ( ) instanceof ImmutableLiteral and
1215+ result = false // defaults to `False`
12061216 }
12071217
1208- /** Gets the ControlFlowNode for the `executable` argument, if any. */
1209- private DataFlow:: Node get_executable_arg ( ) {
1210- result in [ this .getArg ( 2 ) , this .getArgByName ( "executable" ) ]
1211- }
1218+ /** Gets the API-node for the `executable` argument, if any. */
1219+ private API:: Node get_executable_arg ( ) { result = this .getParameter ( 2 , "executable" ) }
12121220
12131221 override DataFlow:: Node getCommand ( ) {
12141222 // TODO: Track arguments ("args" and "shell")
12151223 // TODO: Handle using `args=["sh", "-c", <user-input>]`
1216- result = this .get_executable_arg ( )
1224+ result = this .get_executable_arg ( ) . asSink ( )
12171225 or
12181226 exists ( DataFlow:: Node arg_args , boolean shell |
1219- arg_args = this .get_args_arg ( ) and
1227+ arg_args = this .get_args_arg ( ) . asSink ( ) and
12201228 shell = this .get_shell_arg_value ( )
12211229 |
12221230 // When "executable" argument is set, and "shell" argument is `False`, the
@@ -1242,6 +1250,11 @@ private module StdlibPrivate {
12421250 )
12431251 )
12441252 }
1253+
1254+ override predicate isShellInterpreted ( DataFlow:: Node arg ) {
1255+ arg = [ this .get_executable_arg ( ) , this .get_args_arg ( ) ] .asSink ( ) and
1256+ this .get_shell_arg_value ( ) = true
1257+ }
12451258 }
12461259
12471260 // ---------------------------------------------------------------------------
@@ -1389,6 +1402,8 @@ private module StdlibPrivate {
13891402 }
13901403
13911404 override DataFlow:: Node getCommand ( ) { result in [ this .getArg ( 0 ) , this .getArgByName ( "cmd" ) ] }
1405+
1406+ override predicate isShellInterpreted ( DataFlow:: Node arg ) { arg = this .getCommand ( ) }
13921407 }
13931408
13941409 // ---------------------------------------------------------------------------
@@ -1405,6 +1420,8 @@ private module StdlibPrivate {
14051420 PlatformPopenCall ( ) { this = platform ( ) .getMember ( "popen" ) .getACall ( ) }
14061421
14071422 override DataFlow:: Node getCommand ( ) { result in [ this .getArg ( 0 ) , this .getArgByName ( "cmd" ) ] }
1423+
1424+ override predicate isShellInterpreted ( DataFlow:: Node arg ) { arg = this .getCommand ( ) }
14081425 }
14091426
14101427 // ---------------------------------------------------------------------------
0 commit comments