Skip to content

Commit a693c6d

Browse files
committed
Ruby: sinks for code injection via calls to method
1 parent 9133a13 commit a693c6d

File tree

4 files changed

+31
-2
lines changed

4 files changed

+31
-2
lines changed

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

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,8 @@ module Kernel {
4444
*/
4545
private predicate isPublicKernelMethod(string method) {
4646
method in [
47-
"class", "clone", "frozen?", "tap", "then", "yield_self", "send", "public_send", "__send__"
47+
"class", "clone", "frozen?", "tap", "then", "yield_self", "send", "public_send", "__send__",
48+
"method", "public_method", "singleton_method"
4849
]
4950
}
5051

@@ -176,6 +177,24 @@ module Kernel {
176177
override predicate runsArbitraryCode() { none() }
177178
}
178179

180+
/**
181+
* A call to `method`, `public_method` or `singleton_method` which returns a method object.
182+
* To actually execute the method, the `call` method needs to be called on the object.
183+
* ```ruby
184+
* m = method("exit")
185+
* m.call()
186+
* ```
187+
*/
188+
class MethodCallCodeExecution extends CodeExecution::Range, KernelMethodCall {
189+
MethodCallCodeExecution() {
190+
this.getMethodName() = ["method", "public_method", "singleton_method"]
191+
}
192+
193+
override DataFlow::Node getCode() { result = this.getArgument(0) }
194+
195+
override predicate runsArbitraryCode() { none() }
196+
}
197+
179198
private class TapSummary extends SimpleSummarizedCallable {
180199
TapSummary() { this = "tap" }
181200

ruby/ql/test/library-tests/frameworks/core/Kernel.expected

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@ sendCallCodeExecutions
4242
| Eval.rb:7:1:7:19 | call to send | Eval.rb:7:8:7:13 | "push" |
4343
| Kernel.rb:2:1:2:22 | call to send | Kernel.rb:2:6:2:12 | "raise" |
4444
| Kernel.rb:5:1:5:19 | call to send | Kernel.rb:5:8:5:13 | "push" |
45+
methodCallCodeExecutions
46+
| Kernel.rb:92:1:92:14 | call to method | Kernel.rb:92:8:92:13 | "exit" |
47+
| Kernel.rb:93:1:93:21 | call to public_method | Kernel.rb:93:15:93:20 | "exit" |
48+
| Kernel.rb:94:1:94:23 | call to singleton_method | Kernel.rb:94:18:94:22 | "foo" |
4549
evalCallCodeExecutions
4650
| Eval.rb:3:1:3:43 | call to eval | Eval.rb:3:6:3:22 | "raise \\"error\\"" |
4751
| Kernel.rb:1:1:1:43 | call to eval | Kernel.rb:1:6:1:22 | "raise \\"error\\"" |

ruby/ql/test/library-tests/frameworks/core/Kernel.ql

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,6 @@ query predicate kernelSpawnCallExecutions(KernelSpawnCall c) { any() }
99

1010
query DataFlow::Node sendCallCodeExecutions(SendCallCodeExecution e) { result = e.getCode() }
1111

12+
query DataFlow::Node methodCallCodeExecutions(MethodCallCodeExecution e) { result = e.getCode() }
13+
1214
query DataFlow::Node evalCallCodeExecutions(EvalCallCodeExecution e) { result = e.getCode() }

ruby/ql/test/library-tests/frameworks/core/Kernel.rb

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,4 +87,8 @@ def run
8787
end
8888
end
8989

90-
UnknownModule.system("ls")
90+
UnknownModule.system("ls")
91+
92+
method("exit").call
93+
public_method("exit").call
94+
singleton_method("foo").call

0 commit comments

Comments
 (0)