@@ -5,6 +5,63 @@ private import codeql.ruby.ApiGraphs
55private import codeql.ruby.dataflow.internal.DataFlowDispatch
66private import codeql.ruby.dataflow.internal.DataFlowImplCommon
77
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+
865/**
966 * A system command executed via subshell literal syntax.
1067 * E.g.
@@ -73,21 +130,11 @@ class SubshellHeredocExecution extends SystemCommandExecution::Range {
73130 * Ruby documentation: https://docs.ruby-lang.org/en/3.0.0/Kernel.html#method-i-system
74131 */
75132class KernelSystemCall extends SystemCommandExecution:: Range {
76- MethodCall methodCall ;
133+ KernelMethodCall methodCall ;
77134
78135 KernelSystemCall ( ) {
79136 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
91138 }
92139
93140 override DataFlow:: Node getAnArgument ( ) { result .asExpr ( ) .getExpr ( ) = methodCall .getAnArgument ( ) }
@@ -104,22 +151,11 @@ class KernelSystemCall extends SystemCommandExecution::Range {
104151 * Ruby documentation: https://docs.ruby-lang.org/en/3.0.0/Kernel.html#method-i-exec
105152 */
106153class KernelExecCall extends SystemCommandExecution:: Range {
107- MethodCall methodCall ;
154+ KernelMethodCall methodCall ;
108155
109156 KernelExecCall ( ) {
110157 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
123159 }
124160
125161 override DataFlow:: Node getAnArgument ( ) { result .asExpr ( ) .getExpr ( ) = methodCall .getAnArgument ( ) }
@@ -141,20 +177,11 @@ class KernelExecCall extends SystemCommandExecution::Range {
141177 * ```
142178 */
143179class KernelSpawnCall extends SystemCommandExecution:: Range {
144- MethodCall methodCall ;
180+ KernelMethodCall methodCall ;
145181
146182 KernelSpawnCall ( ) {
147183 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
158185 }
159186
160187 override DataFlow:: Node getAnArgument ( ) { result .asExpr ( ) .getExpr ( ) = methodCall .getAnArgument ( ) }
0 commit comments