diff --git a/ruby/ql/lib/codeql/ruby/frameworks/ActiveStorage.qll b/ruby/ql/lib/codeql/ruby/frameworks/ActiveStorage.qll index 389ac9ee64d..4393f4c2bdb 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/ActiveStorage.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/ActiveStorage.qll @@ -168,7 +168,7 @@ module ActiveStorage { * A call on an ActiveStorage object that results in an image transformation. * Arguments to these calls may be executed as system commands. */ - private class ImageProcessingCall extends DataFlow::CallNode, SystemCommandExecution::Range { + private class ImageProcessingCall extends SystemCommandExecution::Range instanceof DataFlow::CallNode { ImageProcessingCall() { this.getReceiver() instanceof BlobInstance and this.getMethodName() = ["variant", "preview", "representation"] @@ -209,7 +209,7 @@ module ActiveStorage { this = API::getTopLevelMember("ActiveStorage").getAMethodCall("video_preview_arguments=") } - override DataFlow::Node getAnArgument() { result = this.getArgument(0) } + override DataFlow::Node getAnArgument() { result = super.getArgument(0) } } /** diff --git a/ruby/ql/lib/codeql/ruby/frameworks/PosixSpawn.qll b/ruby/ql/lib/codeql/ruby/frameworks/PosixSpawn.qll index 6c4d2ab1a47..92b6eb0fe71 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/PosixSpawn.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/PosixSpawn.qll @@ -20,7 +20,7 @@ module PosixSpawn { /** * A call to `POSIX::Spawn::Child.new` or `POSIX::Spawn::Child.build`. */ - class ChildCall extends SystemCommandExecution::Range, DataFlow::CallNode { + class ChildCall extends SystemCommandExecution::Range instanceof DataFlow::CallNode { ChildCall() { this = [ @@ -30,7 +30,7 @@ module PosixSpawn { } override DataFlow::Node getAnArgument() { - result = this.getArgument(_) and not result.asExpr() instanceof ExprNodes::PairCfgNode + result = super.getArgument(_) and not result.asExpr() instanceof ExprNodes::PairCfgNode } override predicate isShellInterpreted(DataFlow::Node arg) { none() } @@ -39,7 +39,7 @@ module PosixSpawn { /** * A call to `POSIX::Spawn.spawn` or a related method. */ - class SystemCall extends SystemCommandExecution::Range, DataFlow::CallNode { + class SystemCall extends SystemCommandExecution::Range instanceof DataFlow::CallNode { SystemCall() { this = posixSpawnModule() @@ -71,7 +71,7 @@ module PosixSpawn { } private predicate argument(DataFlow::Node arg) { - arg = this.getArgument(_) and + arg = super.getArgument(_) and not arg.asExpr() instanceof ExprNodes::HashLiteralCfgNode and not arg.asExpr() instanceof ExprNodes::ArrayLiteralCfgNode and not arg.asExpr() instanceof ExprNodes::PairCfgNode diff --git a/ruby/ql/lib/codeql/ruby/frameworks/Railties.qll b/ruby/ql/lib/codeql/ruby/frameworks/Railties.qll index fdcd3a0e1a8..509f72cafb9 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/Railties.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/Railties.qll @@ -27,12 +27,12 @@ module Railties { * A call to `Rails::Generators::Actions#execute_command`. * This method concatenates its first and second arguments and executes the result as a shell command. */ - private class ExecuteCommandCall extends SystemCommandExecution::Range, DataFlow::CallNode { + private class ExecuteCommandCall extends SystemCommandExecution::Range instanceof DataFlow::CallNode { ExecuteCommandCall() { this = generatorsActionsClass().getAnInstanceSelf().getAMethodCall("execute_command") } - override DataFlow::Node getAnArgument() { result = this.getArgument([0, 1]) } + override DataFlow::Node getAnArgument() { result = super.getArgument([0, 1]) } override predicate isShellInterpreted(DataFlow::Node arg) { arg = this.getAnArgument() } } @@ -40,7 +40,7 @@ module Railties { /** * A call to a method in `Rails::Generators::Actions` which delegates to `execute_command`. */ - private class ExecuteCommandWrapperCall extends SystemCommandExecution::Range, DataFlow::CallNode { + private class ExecuteCommandWrapperCall extends SystemCommandExecution::Range instanceof DataFlow::CallNode { ExecuteCommandWrapperCall() { this = generatorsActionsClass() @@ -48,7 +48,7 @@ module Railties { .getAMethodCall(["rake", "rails_command", "git"]) } - override DataFlow::Node getAnArgument() { result = this.getArgument(0) } + override DataFlow::Node getAnArgument() { result = super.getArgument(0) } override predicate isShellInterpreted(DataFlow::Node arg) { arg = this.getAnArgument() } } diff --git a/ruby/ql/lib/codeql/ruby/frameworks/core/IO.qll b/ruby/ql/lib/codeql/ruby/frameworks/core/IO.qll index 0828a6dcea7..e751e882ddb 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/core/IO.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/core/IO.qll @@ -114,7 +114,7 @@ module IO { * ``` * Ruby documentation: https://docs.ruby-lang.org/en/3.1/IO.html#method-c-popen */ - class POpenCall extends SystemCommandExecution::Range, DataFlow::CallNode { + class POpenCall extends SystemCommandExecution::Range instanceof DataFlow::CallNode { POpenCall() { this = API::getTopLevelMember("IO").getAMethodCall("popen") } override DataFlow::Node getAnArgument() { this.argument(result, _) } @@ -131,7 +131,7 @@ module IO { not n instanceof ExprNodes::ArrayLiteralCfgNode and ( // IO.popen({var: "a"}, "cmd", {some: :opt}) - arg = this.getArgument([0, 1]) and + arg = super.getArgument([0, 1]) and // We over-approximate by assuming a subshell if the argument isn't an array or "-". // This increases the sensitivity of the CommandInjection query at the risk of some FPs. if n.getConstantValue().getString() = "-" then shell = false else shell = true @@ -139,7 +139,7 @@ module IO { // IO.popen([{var: "b"}, "cmd", "arg1", "arg2", {some: :opt}]) // IO.popen({var: "a"}, ["cmd", "arg1", "arg2", {some: :opt}]) shell = false and - exists(ExprNodes::ArrayLiteralCfgNode arr | this.getArgument([0, 1]).asExpr() = arr | + exists(ExprNodes::ArrayLiteralCfgNode arr | super.getArgument([0, 1]).asExpr() = arr | n = arr.getAnArgument() or // IO.popen([{var: "b"}, ["cmd", "argv0"], "arg1", "arg2", {some: :opt}]) diff --git a/ruby/ql/lib/codeql/ruby/frameworks/stdlib/Open3.qll b/ruby/ql/lib/codeql/ruby/frameworks/stdlib/Open3.qll index 3272056b4f1..fb081d5113e 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/stdlib/Open3.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/stdlib/Open3.qll @@ -2,11 +2,13 @@ * Provides modeling for the `Open3` library. */ +private import ruby private import codeql.ruby.AST private import codeql.ruby.DataFlow private import codeql.ruby.ApiGraphs private import codeql.ruby.frameworks.Stdlib private import codeql.ruby.Concepts +private import codeql.ruby.frameworks.core.Kernel /** * Provides modeling for the `Open3` library. @@ -17,23 +19,19 @@ module Open3 { * These methods take the same argument forms as `Kernel.system`. * See `KernelSystemCall` for details. */ - class Open3Call extends SystemCommandExecution::Range { - MethodCall methodCall; - + class Open3Call extends SystemCommandExecution::Range instanceof DataFlow::CallNode { Open3Call() { - this.asExpr().getExpr() = methodCall and this = API::getTopLevelMember("Open3") .getAMethodCall(["popen3", "popen2", "popen2e", "capture3", "capture2", "capture2e"]) } - override DataFlow::Node getAnArgument() { - result.asExpr().getExpr() = methodCall.getAnArgument() - } + override DataFlow::Node getAnArgument() { result = super.getArgument(_) } override predicate isShellInterpreted(DataFlow::Node arg) { // These Open3 methods invoke a subshell if you provide a single string as argument - methodCall.getNumberOfArguments() = 1 and arg.asExpr().getExpr() = methodCall.getAnArgument() + super.getNumberOfArguments() = 1 and + arg = this.getAnArgument() } } @@ -47,11 +45,8 @@ module Open3 { * Open3.pipeline([{}, "cat", "foo.txt"], "tail") * Open3.pipeline([["cat", "cat"], "foo.txt"], "tail") */ - class Open3PipelineCall extends SystemCommandExecution::Range { - MethodCall methodCall; - + class Open3PipelineCall extends SystemCommandExecution::Range instanceof DataFlow::CallNode { Open3PipelineCall() { - this.asExpr().getExpr() = methodCall and this = API::getTopLevelMember("Open3") .getAMethodCall([ @@ -59,14 +54,12 @@ module Open3 { ]) } - override DataFlow::Node getAnArgument() { - result.asExpr().getExpr() = methodCall.getAnArgument() - } + override DataFlow::Node getAnArgument() { result = super.getArgument(_) } override predicate isShellInterpreted(DataFlow::Node arg) { // A command in the pipeline is executed in a subshell if it is given as a single string argument. arg.asExpr().getExpr() instanceof StringlikeLiteral and - arg.asExpr().getExpr() = methodCall.getAnArgument() + arg = this.getAnArgument() } } }