Skip to content

Commit fb23a2e

Browse files
committed
Add SubshellHeredocExecution
This is a form of command execution: result = <<`EOF` echo foo bar #{baz} EOF
1 parent 799ef4e commit fb23a2e

File tree

6 files changed

+33
-0
lines changed

6 files changed

+33
-0
lines changed

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

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,25 @@ class SubshellLiteralExecution extends SystemCommandExecution::Range {
2626
override predicate isShellInterpreted(DataFlow::Node arg) { arg = getAnArgument() }
2727
}
2828

29+
/**
30+
* A system command executed via shell heredoc syntax.
31+
* E.g.
32+
* ```ruby
33+
* <<`EOF`
34+
* cat foo.text
35+
* EOF
36+
* ```
37+
*/
38+
class SubshellHeredocExecution extends SystemCommandExecution::Range {
39+
HereDoc heredoc;
40+
41+
SubshellHeredocExecution() { this.asExpr().getExpr() = heredoc and heredoc.isSubShell() }
42+
43+
override DataFlow::Node getAnArgument() { result.asExpr().getExpr() = heredoc.getComponent(_) }
44+
45+
override predicate isShellInterpreted(DataFlow::Node arg) { arg = getAnArgument() }
46+
}
47+
2948
/**
3049
* A system command executed via the `Kernel.system` method.
3150
* `Kernel.system` accepts three argument forms:

ql/test/library-tests/frameworks/CommandExecution.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,3 +63,7 @@
6363
Open3.pipeline_w("echo foo")
6464
Open3.pipeline_start("echo foo")
6565
Open3.pipeline("echo foo")
66+
67+
<<`EOF`
68+
echo foo
69+
EOF

ql/test/library-tests/frameworks/StandardLibrary.expected

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ subshellLiteralExecutions
44
| CommandExecution.rb:3:1:3:12 | `echo foo` |
55
| CommandExecution.rb:4:1:4:12 | `echo foo` |
66
| CommandExecution.rb:5:1:5:12 | `echo foo` |
7+
subshellHeredocExecutions
8+
| CommandExecution.rb:67:1:67:7 | <<`EOF` |
79
kernelSystemCallExecutions
810
| CommandExecution.rb:7:1:7:18 | call to system |
911
| CommandExecution.rb:8:1:8:21 | call to system |

ql/test/library-tests/frameworks/StandardLibrary.ql

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import codeql.ruby.frameworks.StandardLibrary
22

33
query predicate subshellLiteralExecutions(SubshellLiteralExecution e) { any() }
44

5+
query predicate subshellHeredocExecutions(SubshellHeredocExecution e) { any() }
6+
57
query predicate kernelSystemCallExecutions(KernelSystemCall c) { any() }
68

79
query predicate kernelExecCallExecutions(KernelExecCall c) { any() }

ql/test/query-tests/security/cwe-078/CommandInjection.expected

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,17 @@ edges
33
| CommandInjection.rb:5:15:5:20 | call to params : | CommandInjection.rb:7:16:7:18 | cmd |
44
| CommandInjection.rb:5:15:5:20 | call to params : | CommandInjection.rb:8:14:8:16 | cmd |
55
| CommandInjection.rb:5:15:5:20 | call to params : | CommandInjection.rb:9:17:9:22 | #{...} |
6+
| CommandInjection.rb:5:15:5:20 | call to params : | CommandInjection.rb:11:9:11:14 | #{...} |
67
nodes
78
| CommandInjection.rb:5:15:5:20 | call to params : | semmle.label | call to params : |
89
| CommandInjection.rb:6:10:6:15 | #{...} | semmle.label | #{...} |
910
| CommandInjection.rb:7:16:7:18 | cmd | semmle.label | cmd |
1011
| CommandInjection.rb:8:14:8:16 | cmd | semmle.label | cmd |
1112
| CommandInjection.rb:9:17:9:22 | #{...} | semmle.label | #{...} |
13+
| CommandInjection.rb:11:9:11:14 | #{...} | semmle.label | #{...} |
1214
#select
1315
| CommandInjection.rb:6:10:6:15 | #{...} | CommandInjection.rb:5:15:5:20 | call to params : | CommandInjection.rb:6:10:6:15 | #{...} | This command depends on $@. | CommandInjection.rb:5:15:5:20 | call to params | a user-provided value |
1416
| CommandInjection.rb:7:16:7:18 | cmd | CommandInjection.rb:5:15:5:20 | call to params : | CommandInjection.rb:7:16:7:18 | cmd | This command depends on $@. | CommandInjection.rb:5:15:5:20 | call to params | a user-provided value |
1517
| CommandInjection.rb:8:14:8:16 | cmd | CommandInjection.rb:5:15:5:20 | call to params : | CommandInjection.rb:8:14:8:16 | cmd | This command depends on $@. | CommandInjection.rb:5:15:5:20 | call to params | a user-provided value |
1618
| CommandInjection.rb:9:17:9:22 | #{...} | CommandInjection.rb:5:15:5:20 | call to params : | CommandInjection.rb:9:17:9:22 | #{...} | This command depends on $@. | CommandInjection.rb:5:15:5:20 | call to params | a user-provided value |
19+
| CommandInjection.rb:11:9:11:14 | #{...} | CommandInjection.rb:5:15:5:20 | call to params : | CommandInjection.rb:11:9:11:14 | #{...} | This command depends on $@. | CommandInjection.rb:5:15:5:20 | call to params | a user-provided value |

ql/test/query-tests/security/cwe-078/CommandInjection.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ def create
77
system(cmd)
88
exec(cmd)
99
%x(echo #{cmd})
10+
result = <<`EOF`
11+
#{cmd}
12+
EOF
1013

1114
safe_cmd = Shellwords.escape(cmd)
1215
`echo #{safe_cmd}`

0 commit comments

Comments
 (0)