Skip to content

Commit 5d2ab8a

Browse files
authored
Merge pull request github#11191 from erik-krogh/arrJoin
RB: add join(" ") calls as a sink for rb/shell-command-constructed-from-input
2 parents 54958fd + 88de299 commit 5d2ab8a

File tree

3 files changed

+45
-1
lines changed

3 files changed

+45
-1
lines changed

ruby/ql/lib/codeql/ruby/security/UnsafeShellCommandConstructionCustomizations.qll

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@
44
* well as extension points for adding your own.
55
*/
66

7+
private import ruby
78
private import codeql.ruby.DataFlow
89
private import codeql.ruby.DataFlow2
910
private import codeql.ruby.ApiGraphs
1011
private import codeql.ruby.frameworks.core.Gem::Gem as Gem
11-
private import codeql.ruby.AST as Ast
1212
private import codeql.ruby.Concepts as Concepts
1313

1414
/**
@@ -81,6 +81,37 @@ module UnsafeShellCommandConstruction {
8181
override DataFlow::Node getStringConstruction() { result.asExpr().getExpr() = lit }
8282
}
8383

84+
/**
85+
* A string constructed using a `.join(" ")` call, where the resulting string ends up being executed as a shell command.
86+
*/
87+
class ArrayJoin extends Sink {
88+
Concepts::SystemCommandExecution s;
89+
DataFlow::CallNode call;
90+
91+
ArrayJoin() {
92+
call.getMethodName() = "join" and
93+
call.getNumberOfArguments() = 1 and
94+
call.getArgument(0).getConstantValue().getString() = " " and
95+
isUsedAsShellCommand(call, s) and
96+
(
97+
this = call.getReceiver() and
98+
not call.getReceiver().asExpr() instanceof Cfg::CfgNodes::ExprNodes::ArrayLiteralCfgNode
99+
or
100+
this.asExpr() =
101+
call.getReceiver()
102+
.asExpr()
103+
.(Cfg::CfgNodes::ExprNodes::ArrayLiteralCfgNode)
104+
.getAnArgument()
105+
)
106+
}
107+
108+
override string describe() { result = "array" }
109+
110+
override DataFlow::Node getCommandExecution() { result = s }
111+
112+
override DataFlow::Node getStringConstruction() { result = call }
113+
}
114+
84115
import codeql.ruby.security.TaintedFormatStringSpecific as TaintedFormat
85116

86117
/**

ruby/ql/test/query-tests/security/cwe-078/UnsafeShellCommandConstruction/UnsafeShellCommandConstruction.expected

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ edges
99
| impl/unsafeShell.rb:33:12:33:17 | target : | impl/unsafeShell.rb:34:19:34:27 | #{...} |
1010
| impl/unsafeShell.rb:37:10:37:10 | x : | impl/unsafeShell.rb:38:19:38:22 | #{...} |
1111
| impl/unsafeShell.rb:47:16:47:21 | target : | impl/unsafeShell.rb:48:19:48:27 | #{...} |
12+
| impl/unsafeShell.rb:51:17:51:17 | x : | impl/unsafeShell.rb:52:14:52:14 | x |
13+
| impl/unsafeShell.rb:51:17:51:17 | x : | impl/unsafeShell.rb:54:29:54:29 | x |
1214
nodes
1315
| impl/sub/notImported.rb:2:12:2:17 | target : | semmle.label | target : |
1416
| impl/sub/notImported.rb:3:19:3:27 | #{...} | semmle.label | #{...} |
@@ -30,6 +32,9 @@ nodes
3032
| impl/unsafeShell.rb:38:19:38:22 | #{...} | semmle.label | #{...} |
3133
| impl/unsafeShell.rb:47:16:47:21 | target : | semmle.label | target : |
3234
| impl/unsafeShell.rb:48:19:48:27 | #{...} | semmle.label | #{...} |
35+
| impl/unsafeShell.rb:51:17:51:17 | x : | semmle.label | x : |
36+
| impl/unsafeShell.rb:52:14:52:14 | x | semmle.label | x |
37+
| impl/unsafeShell.rb:54:29:54:29 | x | semmle.label | x |
3338
subpaths
3439
#select
3540
| impl/sub/notImported.rb:3:14:3:28 | "cat #{...}" | impl/sub/notImported.rb:2:12:2:17 | target : | impl/sub/notImported.rb:3:19:3:27 | #{...} | This string construction which depends on $@ is later used in a $@. | impl/sub/notImported.rb:2:12:2:17 | target | library input | impl/sub/notImported.rb:3:5:3:34 | call to popen | shell command |
@@ -42,3 +47,5 @@ subpaths
4247
| impl/unsafeShell.rb:34:14:34:28 | "cat #{...}" | impl/unsafeShell.rb:33:12:33:17 | target : | impl/unsafeShell.rb:34:19:34:27 | #{...} | This string construction which depends on $@ is later used in a $@. | impl/unsafeShell.rb:33:12:33:17 | target | library input | impl/unsafeShell.rb:34:5:34:34 | call to popen | shell command |
4348
| impl/unsafeShell.rb:38:14:38:23 | "cat #{...}" | impl/unsafeShell.rb:37:10:37:10 | x : | impl/unsafeShell.rb:38:19:38:22 | #{...} | This string construction which depends on $@ is later used in a $@. | impl/unsafeShell.rb:37:10:37:10 | x | library input | impl/unsafeShell.rb:38:5:38:29 | call to popen | shell command |
4449
| impl/unsafeShell.rb:48:14:48:28 | "cat #{...}" | impl/unsafeShell.rb:47:16:47:21 | target : | impl/unsafeShell.rb:48:19:48:27 | #{...} | This string construction which depends on $@ is later used in a $@. | impl/unsafeShell.rb:47:16:47:21 | target | library input | impl/unsafeShell.rb:48:5:48:34 | call to popen | shell command |
50+
| impl/unsafeShell.rb:52:14:52:24 | call to join | impl/unsafeShell.rb:51:17:51:17 | x : | impl/unsafeShell.rb:52:14:52:14 | x | This array which depends on $@ is later used in a $@. | impl/unsafeShell.rb:51:17:51:17 | x | library input | impl/unsafeShell.rb:52:5:52:30 | call to popen | shell command |
51+
| impl/unsafeShell.rb:54:14:54:40 | call to join | impl/unsafeShell.rb:51:17:51:17 | x : | impl/unsafeShell.rb:54:29:54:29 | x | This array which depends on $@ is later used in a $@. | impl/unsafeShell.rb:51:17:51:17 | x | library input | impl/unsafeShell.rb:54:5:54:46 | call to popen | shell command |

ruby/ql/test/query-tests/security/cwe-078/UnsafeShellCommandConstruction/impl/unsafeShell.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,4 +47,10 @@ def thisIsSafe()
4747
def self.foo(target)
4848
IO.popen("cat #{target}", "w") # NOT OK
4949
end
50+
51+
def arrayJoin(x)
52+
IO.popen(x.join(' '), "w") # NOT OK
53+
54+
IO.popen(["foo", "bar", x].join(' '), "w") # NOT OK
55+
end
5056
end

0 commit comments

Comments
 (0)