Skip to content

Commit e971d72

Browse files
committed
add getACommandArgument predicate to the SystemCommandExecution concept
1 parent 5a0cce2 commit e971d72

File tree

5 files changed

+57
-0
lines changed

5 files changed

+57
-0
lines changed

ruby/ql/lib/codeql/ruby/Concepts.qll

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -707,6 +707,9 @@ deprecated module HTTP = Http;
707707
* for instance by spawning a new process.
708708
*/
709709
class SystemCommandExecution extends DataFlow::Node instanceof SystemCommandExecution::Range {
710+
/** Gets an argument to this execution that specifies the command. */
711+
DataFlow::Node getACommandArgument() { result = super.getACommandArgument() }
712+
710713
/** Holds if a shell interprets `arg`. */
711714
predicate isShellInterpreted(DataFlow::Node arg) { super.isShellInterpreted(arg) }
712715

@@ -729,6 +732,9 @@ module SystemCommandExecution {
729732

730733
/** Holds if a shell interprets `arg`. */
731734
predicate isShellInterpreted(DataFlow::Node arg) { none() }
735+
736+
/** Gets an argument to this execution that specifies the command. */
737+
DataFlow::Node getACommandArgument() { none() }
732738
}
733739
}
734740

ruby/ql/lib/codeql/ruby/frameworks/PosixSpawn.qll

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ module PosixSpawn {
3333
result = super.getArgument(_) and not result.asExpr() instanceof ExprNodes::PairCfgNode
3434
}
3535

36+
override DataFlow::Node getACommandArgument() { result = super.getArgument(0) }
37+
3638
override predicate isShellInterpreted(DataFlow::Node arg) { none() }
3739
}
3840

@@ -48,6 +50,10 @@ module PosixSpawn {
4850

4951
override DataFlow::Node getAnArgument() { this.argument(result) }
5052

53+
override DataFlow::Node getACommandArgument() {
54+
result = super.getArgument(0) and this.argument(result)
55+
}
56+
5157
// From the docs:
5258
// When only command is given and includes a space character, the command
5359
// text is executed by the system shell interpreter.

ruby/ql/lib/codeql/ruby/frameworks/core/IO.qll

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ private import codeql.ruby.Concepts
77
private import codeql.ruby.DataFlow
88
private import codeql.ruby.controlflow.CfgNodes
99
private import codeql.ruby.frameworks.Files as Files
10+
private import codeql.ruby.frameworks.core.Kernel
1011
private import internal.IOOrFile
1112

1213
/** Provides modeling for the `IO` class. */
@@ -121,6 +122,10 @@ module IO {
121122

122123
override predicate isShellInterpreted(DataFlow::Node arg) { this.argument(arg, true) }
123124

125+
override DataFlow::Node getACommandArgument() {
126+
result = Kernel::getACommandArgumentFromShellCall(this)
127+
}
128+
124129
/**
125130
* Holds if `arg` is an argument to this call. `shell` is true if the argument is passed to a subshell.
126131
*/

ruby/ql/lib/codeql/ruby/frameworks/core/Kernel.qll

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,25 @@ module Kernel {
3131
}
3232
}
3333

34+
/**
35+
* Gets an argument to `call` that specifies the command to execute,
36+
* assuming that `call` has the same API as `Kernel.system`.
37+
*/
38+
bindingset[call]
39+
DataFlow::Node getACommandArgumentFromShellCall(DataFlow::CallNode call) {
40+
(
41+
// Kernel.system invokes a subprocess if you provide a command and one or more arguments
42+
call.getNumberOfArguments() > 1 and
43+
result = call.getArgument([0, 1])
44+
or
45+
// Kernel.system invokes a subprocess if you provide an array containing the command name and argv[0]
46+
call.getNumberOfArguments() > 1 and
47+
result.asExpr() =
48+
call.getArgument([0, 1]).asExpr().(CfgNodes::ExprNodes::ArrayLiteralCfgNode).getArgument(0)
49+
) and
50+
not result.asExpr() instanceof CfgNodes::ExprNodes::HashLiteralCfgNode // not the environment hash
51+
}
52+
3453
/**
3554
* Public methods in the `Kernel` module. These can be invoked on any object via the usual dot syntax.
3655
* ```ruby
@@ -101,6 +120,10 @@ module Kernel {
101120
// Kernel.system invokes a subshell if you provide a single string as argument
102121
this.getNumberOfArguments() = 1 and arg = this.getAnArgument()
103122
}
123+
124+
override DataFlow::Node getACommandArgument() {
125+
result = getACommandArgumentFromShellCall(this)
126+
}
104127
}
105128

106129
/**
@@ -117,6 +140,10 @@ module Kernel {
117140
// Kernel.exec invokes a subshell if you provide a single string as argument
118141
this.getNumberOfArguments() = 1 and arg = this.getAnArgument()
119142
}
143+
144+
override DataFlow::Node getACommandArgument() {
145+
result = getACommandArgumentFromShellCall(this)
146+
}
120147
}
121148

122149
/**
@@ -138,6 +165,10 @@ module Kernel {
138165
// Kernel.spawn invokes a subshell if you provide a single string as argument
139166
this.getNumberOfArguments() = 1 and arg = this.getAnArgument()
140167
}
168+
169+
override DataFlow::Node getACommandArgument() {
170+
result = getACommandArgumentFromShellCall(this)
171+
}
141172
}
142173

143174
/**

ruby/ql/lib/codeql/ruby/frameworks/stdlib/Open3.qll

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
private import ruby
66
private import codeql.ruby.ApiGraphs
77
private import codeql.ruby.Concepts
8+
private import codeql.ruby.frameworks.core.Kernel
89

910
/**
1011
* Provides modeling for the `Open3` library.
@@ -29,6 +30,10 @@ module Open3 {
2930
super.getNumberOfArguments() = 1 and
3031
arg = this.getAnArgument()
3132
}
33+
34+
override DataFlow::Node getACommandArgument() {
35+
result = Kernel::getACommandArgumentFromShellCall(this)
36+
}
3237
}
3338

3439
/**
@@ -57,5 +62,9 @@ module Open3 {
5762
arg.asExpr().getExpr() instanceof Ast::StringlikeLiteral and
5863
arg = this.getAnArgument()
5964
}
65+
66+
override DataFlow::Node getACommandArgument() {
67+
result = Kernel::getACommandArgumentFromShellCall(this)
68+
}
6069
}
6170
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy