@@ -5,6 +5,63 @@ private import codeql.ruby.ApiGraphs
5
5
private import codeql.ruby.dataflow.internal.DataFlowDispatch
6
6
private import codeql.ruby.dataflow.internal.DataFlowImplCommon
7
7
8
+ /**
9
+ * The `Kernel` module is included by the `Object` class, so its methods are available
10
+ * in every Ruby object. In addition, its module methods can be called by
11
+ * providing a specific receiver as in `Kernel.exit`.
12
+ */
13
+ class KernelMethodCall extends MethodCall {
14
+ KernelMethodCall ( ) {
15
+ this = API:: getTopLevelMember ( "Kernel" ) .getAMethodCall ( _) .asExpr ( ) .getExpr ( )
16
+ or
17
+ // we assume that if there's no obvious target for this method call
18
+ // and the method name matches a Kernel method, then it is a Kernel method call.
19
+ // TODO: ApiGraphs should ideally handle this case
20
+ not exists ( DataFlowCallable method , DataFlowCall call |
21
+ viableCallable ( call ) = method and call .getExpr ( ) = this
22
+ ) and
23
+ (
24
+ this .getReceiver ( ) instanceof Self and isPrivateKernelMethod ( this .getMethodName ( ) )
25
+ or
26
+ isPublicKernelMethod ( this .getMethodName ( ) )
27
+ )
28
+ }
29
+ }
30
+
31
+ /**
32
+ * Public methods in the `Kernel` module. These can be invoked on any object via the usual dot syntax.
33
+ * ```ruby
34
+ * arr = []
35
+ * arr.send("push", 5) # => [5]
36
+ * ```
37
+ */
38
+ private predicate isPublicKernelMethod ( string method ) {
39
+ method in [ "class" , "clone" , "frozen?" , "tap" , "then" , "yield_self" , "send" ]
40
+ }
41
+
42
+ /**
43
+ * Private methods in the `Kernel` module.
44
+ * These can be be invoked on `self`, on `Kernel`, or using a low-level primitive like `send` or `instance_eval`.
45
+ * ```ruby
46
+ * puts "hello world"
47
+ * Kernel.puts "hello world"
48
+ * 5.instance_eval { puts "hello world" }
49
+ * 5.send("puts", "hello world")
50
+ * ```
51
+ */
52
+ private predicate isPrivateKernelMethod ( string method ) {
53
+ method in [
54
+ "Array" , "Complex" , "Float" , "Hash" , "Integer" , "Rational" , "String" , "__callee__" , "__dir__" ,
55
+ "__method__" , "`" , "abort" , "at_exit" , "autoload" , "autoload?" , "binding" , "block_given?" ,
56
+ "callcc" , "caller" , "caller_locations" , "catch" , "chomp" , "chop" , "eval" , "exec" , "exit" ,
57
+ "exit!" , "fail" , "fork" , "format" , "gets" , "global_variables" , "gsub" , "iterator?" , "lambda" ,
58
+ "load" , "local_variables" , "loop" , "open" , "p" , "pp" , "print" , "printf" , "proc" , "putc" ,
59
+ "puts" , "raise" , "rand" , "readline" , "readlines" , "require" , "require_relative" , "select" ,
60
+ "set_trace_func" , "sleep" , "spawn" , "sprintf" , "srand" , "sub" , "syscall" , "system" , "test" ,
61
+ "throw" , "trace_var" , "trap" , "untrace_var" , "warn"
62
+ ]
63
+ }
64
+
8
65
/**
9
66
* A system command executed via subshell literal syntax.
10
67
* E.g.
@@ -73,21 +130,11 @@ class SubshellHeredocExecution extends SystemCommandExecution::Range {
73
130
* Ruby documentation: https://docs.ruby-lang.org/en/3.0.0/Kernel.html#method-i-system
74
131
*/
75
132
class KernelSystemCall extends SystemCommandExecution:: Range {
76
- MethodCall methodCall ;
133
+ KernelMethodCall methodCall ;
77
134
78
135
KernelSystemCall ( ) {
79
136
methodCall .getMethodName ( ) = "system" and
80
- this .asExpr ( ) .getExpr ( ) = methodCall and
81
- // `Kernel.system` can be reached via `Kernel.system` or just `system`
82
- // (if there's no other method by the same name in scope).
83
- (
84
- this = API:: getTopLevelMember ( "Kernel" ) .getAMethodCall ( "system" )
85
- or
86
- // we assume that if there's no obvious target for this method call, then it must refer to Kernel.system.
87
- not exists ( DataFlowCallable method , DataFlowCall call |
88
- viableCallable ( call ) = method and call .getExpr ( ) = methodCall
89
- )
90
- )
137
+ this .asExpr ( ) .getExpr ( ) = methodCall
91
138
}
92
139
93
140
override DataFlow:: Node getAnArgument ( ) { result .asExpr ( ) .getExpr ( ) = methodCall .getAnArgument ( ) }
@@ -104,22 +151,11 @@ class KernelSystemCall extends SystemCommandExecution::Range {
104
151
* Ruby documentation: https://docs.ruby-lang.org/en/3.0.0/Kernel.html#method-i-exec
105
152
*/
106
153
class KernelExecCall extends SystemCommandExecution:: Range {
107
- MethodCall methodCall ;
154
+ KernelMethodCall methodCall ;
108
155
109
156
KernelExecCall ( ) {
110
157
methodCall .getMethodName ( ) = "exec" and
111
- this .asExpr ( ) .getExpr ( ) = methodCall and
112
- // `Kernel.exec` can be reached via `Kernel.exec`, `Process.exec` or just `exec`
113
- // (if there's no other method by the same name in scope).
114
- (
115
- this = API:: getTopLevelMember ( [ "Kernel" , "Process" ] ) .getAMethodCall ( "exec" )
116
- or
117
- // we assume that if there's no obvious target for this method call, then
118
- // it must refer to Kernel.exec.
119
- not exists ( DataFlowCallable method , DataFlowCall call |
120
- viableCallable ( call ) = method and call .getExpr ( ) = methodCall
121
- )
122
- )
158
+ this .asExpr ( ) .getExpr ( ) = methodCall
123
159
}
124
160
125
161
override DataFlow:: Node getAnArgument ( ) { result .asExpr ( ) .getExpr ( ) = methodCall .getAnArgument ( ) }
@@ -141,20 +177,11 @@ class KernelExecCall extends SystemCommandExecution::Range {
141
177
* ```
142
178
*/
143
179
class KernelSpawnCall extends SystemCommandExecution:: Range {
144
- MethodCall methodCall ;
180
+ KernelMethodCall methodCall ;
145
181
146
182
KernelSpawnCall ( ) {
147
183
methodCall .getMethodName ( ) = "spawn" and
148
- this .asExpr ( ) .getExpr ( ) = methodCall and
149
- // `Kernel.spawn` can be reached via `Kernel.spawn`, `Process.spawn` or just `spawn`
150
- // (if there's no other method by the same name in scope).
151
- (
152
- this = API:: getTopLevelMember ( [ "Kernel" , "Process" ] ) .getAMethodCall ( "spawn" )
153
- or
154
- not exists ( DataFlowCallable method , DataFlowCall call |
155
- viableCallable ( call ) = method and call .getExpr ( ) = methodCall
156
- )
157
- )
184
+ this .asExpr ( ) .getExpr ( ) = methodCall
158
185
}
159
186
160
187
override DataFlow:: Node getAnArgument ( ) { result .asExpr ( ) .getExpr ( ) = methodCall .getAnArgument ( ) }
0 commit comments