File tree Expand file tree Collapse file tree 8 files changed +55
-64
lines changed
python/ql/src/experimental/semmle/python Expand file tree Collapse file tree 8 files changed +55
-64
lines changed Original file line number Diff line number Diff line change @@ -15,12 +15,23 @@ private import semmle.python.dataflow.new.TaintTracking
1515private import experimental.semmle.python.Frameworks
1616private import semmle.python.Concepts
1717
18- /**
19- * A data-flow node that responsible for a command that can be executed on a secondary remote system,
20- *
21- * Extend this class to model new APIs.
22- */
23- abstract class SecondaryCommandInjection extends DataFlow:: Node { }
18+ /** Provides classes for modeling remote server command execution related APIs. */
19+ module RemoteCommandExecution {
20+ /**
21+ * A data-flow node that executes an operating system command,
22+ * on a remote server likely by SSH connections.
23+ *
24+ * Extend this class to model new APIs. If you want to refine existing API models,
25+ * extend `SystemCommandExecution` instead.
26+ */
27+ abstract class Range extends DataFlow:: Node {
28+ /** Gets the argument that specifies the command to be executed. */
29+ abstract DataFlow:: Node getCommand ( ) ;
30+
31+ /** Holds if a shell interprets `arg`. */
32+ predicate isShellInterpreted ( DataFlow:: Node arg ) { none ( ) }
33+ }
34+ }
2435
2536/** Provides classes for modeling copying file related APIs. */
2637module CopyFile {
Original file line number Diff line number Diff line change @@ -22,16 +22,9 @@ private module Asyncssh {
2222 /**
2323 * A `run` method responsible for executing commands on remote secondary servers.
2424 */
25- class AsyncsshRun extends SecondaryCommandInjection {
26- AsyncsshRun ( ) {
27- this =
28- asyncssh ( )
29- .getMember ( "connect" )
30- .getReturn ( )
31- .getMember ( "run" )
32- .getACall ( )
33- .getParameter ( 0 , "command" )
34- .asSink ( )
35- }
25+ class AsyncsshRun extends RemoteCommandExecution:: Range , API:: CallNode {
26+ AsyncsshRun ( ) { this = asyncssh ( ) .getMember ( "connect" ) .getReturn ( ) .getMember ( "run" ) .getACall ( ) }
27+
28+ override DataFlow:: Node getCommand ( ) { result = this .getParameter ( 0 , "command" ) .asSink ( ) }
3629 }
3730}
Original file line number Diff line number Diff line change @@ -29,21 +29,27 @@ private module Netmiko {
2929 /**
3030 * The `send_*` methods responsible for executing commands on remote secondary servers.
3131 */
32- class NetmikoSendCommand extends SecondaryCommandInjection {
32+ class NetmikoSendCommand extends RemoteCommandExecution:: Range , API:: CallNode {
33+ boolean isMultiline ;
34+
3335 NetmikoSendCommand ( ) {
3436 this =
3537 netmikoConnectHandler ( )
3638 .getMember ( [ "send_command" , "send_command_expect" , "send_command_timing" ] )
37- .getACall ( )
38- .getParameter ( 0 , "command_string" )
39- .asSink ( )
39+ .getACall ( ) and
40+ isMultiline = false
4041 or
4142 this =
42- netmikoConnectHandler ( )
43- .getMember ( [ "send_multiline" , "send_multiline_timing" ] )
44- .getACall ( )
45- .getParameter ( 0 , "commands" )
46- .asSink ( )
43+ netmikoConnectHandler ( ) .getMember ( [ "send_multiline" , "send_multiline_timing" ] ) .getACall ( ) and
44+ isMultiline = true
45+ }
46+
47+ override DataFlow:: Node getCommand ( ) {
48+ result = this .getParameter ( 0 , "command_string" ) .asSink ( ) and
49+ isMultiline = false
50+ or
51+ result = this .getParameter ( 0 , "commands" ) .asSink ( ) and
52+ isMultiline = true
4753 }
4854 }
4955}
Original file line number Diff line number Diff line change @@ -27,10 +27,9 @@ private module Paramiko {
2727 /**
2828 * The `exec_command` of `paramiko.SSHClient` class execute command on ssh target server
2929 */
30- class ParamikoExecCommand extends SecondaryCommandInjection {
31- ParamikoExecCommand ( ) {
32- this =
33- paramikoClient ( ) .getMember ( "exec_command" ) .getACall ( ) .getParameter ( 0 , "command" ) .asSink ( )
34- }
30+ class ParamikoExecCommand extends RemoteCommandExecution:: Range , API:: CallNode {
31+ ParamikoExecCommand ( ) { this = paramikoClient ( ) .getMember ( "exec_command" ) .getACall ( ) }
32+
33+ override DataFlow:: Node getCommand ( ) { result = this .getParameter ( 0 , "command" ) .asSink ( ) }
3534 }
3635}
Original file line number Diff line number Diff line change @@ -17,7 +17,7 @@ private module Pexpect {
1717 * The calls to `pexpect.pxssh.pxssh` functions that execute commands
1818 * See https://pexpect.readthedocs.io/en/stable/api/pxssh.html
1919 */
20- class PexpectCommandExec extends SecondaryCommandInjection {
20+ class PexpectCommandExec extends RemoteCommandExecution :: Range , API :: CallNode {
2121 PexpectCommandExec ( ) {
2222 this =
2323 API:: moduleImport ( "pexpect" )
@@ -26,8 +26,8 @@ private module Pexpect {
2626 .getReturn ( )
2727 .getMember ( [ "send" , "sendline" ] )
2828 .getACall ( )
29- .getParameter ( 0 , "s" )
30- .asSink ( )
3129 }
30+
31+ override DataFlow:: Node getCommand ( ) { result = this .getParameter ( 0 , "s" ) .asSink ( ) }
3232 }
3333}
Original file line number Diff line number Diff line change @@ -32,7 +32,7 @@ private module Scrapli {
3232 /**
3333 * A `send_command` method responsible for executing commands on remote secondary servers.
3434 */
35- class ScrapliSendCommand extends SecondaryCommandInjection {
35+ class ScrapliSendCommand extends RemoteCommandExecution :: Range , API :: CallNode {
3636 ScrapliSendCommand ( ) {
3737 this =
3838 scrapliCore ( )
@@ -44,26 +44,13 @@ private module Scrapli {
4444 .getReturn ( )
4545 .getMember ( "send_command" )
4646 .getACall ( )
47- .getParameter ( 0 , "command" )
48- .asSink ( )
4947 or
50- this =
51- scrapli ( )
52- .getMember ( "Scrapli" )
53- .getReturn ( )
54- .getMember ( "send_command" )
55- .getACall ( )
56- .getParameter ( 0 , "command" )
57- .asSink ( )
48+ this = scrapli ( ) .getMember ( "Scrapli" ) .getReturn ( ) .getMember ( "send_command" ) .getACall ( )
5849 or
5950 this =
60- scrapliDriver ( )
61- .getMember ( "GenericDriver" )
62- .getReturn ( )
63- .getMember ( "send_command" )
64- .getACall ( )
65- .getParameter ( 0 , "command" )
66- .asSink ( )
51+ scrapliDriver ( ) .getMember ( "GenericDriver" ) .getReturn ( ) .getMember ( "send_command" ) .getACall ( )
6752 }
53+
54+ override DataFlow:: Node getCommand ( ) { result = this .getParameter ( 0 , "command" ) .asSink ( ) }
6855 }
6956}
Original file line number Diff line number Diff line change @@ -29,16 +29,11 @@ private module Ssh2 {
2929 /**
3030 * An `execute` method responsible for executing commands on remote secondary servers.
3131 */
32- class Ssh2Execute extends SecondaryCommandInjection {
32+ class Ssh2Execute extends RemoteCommandExecution :: Range , API :: CallNode {
3333 Ssh2Execute ( ) {
34- this =
35- ssh2Session ( )
36- .getMember ( "open_session" )
37- .getReturn ( )
38- .getMember ( "execute" )
39- .getACall ( )
40- .getParameter ( 0 , "command" )
41- .asSink ( )
34+ this = ssh2Session ( ) .getMember ( "open_session" ) .getReturn ( ) .getMember ( "execute" ) .getACall ( )
4235 }
36+
37+ override DataFlow:: Node getCommand ( ) { result = this .getParameter ( 0 , "command" ) .asSink ( ) }
4338 }
4439}
Original file line number Diff line number Diff line change @@ -19,7 +19,7 @@ private module Twisted {
1919 /**
2020 * The `newConnection` and `existingConnection` functions of `twisted.conch.endpoints.SSHCommandClientEndpoint` class execute command on ssh target server
2121 */
22- class ParamikoExecCommand extends SecondaryCommandInjection {
22+ class ParamikoExecCommand extends RemoteCommandExecution :: Range , API :: CallNode {
2323 ParamikoExecCommand ( ) {
2424 this =
2525 API:: moduleImport ( "twisted" )
@@ -28,8 +28,8 @@ private module Twisted {
2828 .getMember ( "SSHCommandClientEndpoint" )
2929 .getMember ( [ "newConnection" , "existingConnection" ] )
3030 .getACall ( )
31- .getParameter ( 1 , "command" )
32- .asSink ( )
3331 }
32+
33+ override DataFlow:: Node getCommand ( ) { result = this .getParameter ( 1 , "command" ) .asSink ( ) }
3434 }
3535}
You can’t perform that action at this time.
0 commit comments