mirror of
https://github.com/github/codeql.git
synced 2026-04-27 17:55:19 +02:00
Ruby: Model the posix-spawn gem
This gem exists primarily to provide methods that spawn subprocesses. We model these as SystemCommandExecutions.
This commit is contained in:
@@ -15,3 +15,4 @@ private import codeql.ruby.frameworks.Files
|
||||
private import codeql.ruby.frameworks.HttpClients
|
||||
private import codeql.ruby.frameworks.XmlParsing
|
||||
private import codeql.ruby.frameworks.ActionDispatch
|
||||
private import codeql.ruby.frameworks.PosixSpawn
|
||||
|
||||
81
ruby/ql/lib/codeql/ruby/frameworks/PosixSpawn.qll
Normal file
81
ruby/ql/lib/codeql/ruby/frameworks/PosixSpawn.qll
Normal file
@@ -0,0 +1,81 @@
|
||||
/**
|
||||
* Provides modeling for the `posix-spawn` gem.
|
||||
* Version: 0.3.15
|
||||
*/
|
||||
|
||||
private import codeql.ruby.Concepts
|
||||
private import codeql.ruby.ApiGraphs
|
||||
private import codeql.ruby.DataFlow
|
||||
private import codeql.ruby.controlflow.CfgNodes
|
||||
|
||||
/**
|
||||
* Provides modeling for the `posix-spawn` gem.
|
||||
* Version: 0.3.15
|
||||
*/
|
||||
module PosixSpawn {
|
||||
private API::Node posixSpawnModule() {
|
||||
result = API::getTopLevelMember("POSIX").getMember("Spawn")
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to `POSIX::Spawn::Child.new` or `POSIX::Spawn::Child.build`.
|
||||
*/
|
||||
class ChildCall extends SystemCommandExecution::Range, DataFlow::CallNode {
|
||||
ChildCall() {
|
||||
this =
|
||||
[
|
||||
posixSpawnModule().getMember("Child").getAMethodCall("build"),
|
||||
posixSpawnModule().getMember("Child").getAnInstantiation()
|
||||
]
|
||||
}
|
||||
|
||||
override DataFlow::Node getAnArgument() {
|
||||
result = this.getArgument(_) and not result.asExpr() instanceof ExprNodes::PairCfgNode
|
||||
}
|
||||
|
||||
override predicate isShellInterpreted(DataFlow::Node arg) { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to `POSIX::Spawn.spawn` or a related method.
|
||||
*/
|
||||
class SystemCall extends SystemCommandExecution::Range, DataFlow::CallNode {
|
||||
SystemCall() {
|
||||
exists(API::Node spawn | spawn = API::getTopLevelMember("POSIX").getMember("Spawn") |
|
||||
this =
|
||||
posixSpawnModule()
|
||||
.getAMethodCall(["spawn", "fspawn", "popen4", "pspawn", "system", "_pspawn", "`"])
|
||||
)
|
||||
}
|
||||
|
||||
override DataFlow::Node getAnArgument() { this.argument(result, _) }
|
||||
|
||||
// From the docs:
|
||||
// When only command is given and includes a space character, the command
|
||||
// text is executed by the system shell interpreter.
|
||||
// This means the following signatures are shell interpreted:
|
||||
//
|
||||
// spawn(cmd)
|
||||
// spawn(cmd, opts)
|
||||
// spawn(env, cmd)
|
||||
// spawn(env, cmd, opts)
|
||||
//
|
||||
// env and opts will be hashes. We over-approximate by assuming the argument
|
||||
// is shell interpreted unless there is another argument with a string
|
||||
// constant value.
|
||||
override predicate isShellInterpreted(DataFlow::Node arg) {
|
||||
exists(int n, int m, DataFlow::Node otherArg |
|
||||
this.argument(arg, n) and
|
||||
this.argument(otherArg, m) and
|
||||
otherArg.asExpr().getConstantValue().isString(_)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate argument(DataFlow::Node arg, int n) {
|
||||
arg = this.getArgument(n) and
|
||||
not arg.asExpr() instanceof ExprNodes::HashLiteralCfgNode and
|
||||
not arg.asExpr() instanceof ExprNodes::ArrayLiteralCfgNode and
|
||||
not arg.asExpr() instanceof ExprNodes::PairCfgNode
|
||||
}
|
||||
}
|
||||
}
|
||||
26
ruby/ql/test/library-tests/frameworks/PosixSpawn.expected
Normal file
26
ruby/ql/test/library-tests/frameworks/PosixSpawn.expected
Normal file
@@ -0,0 +1,26 @@
|
||||
systemCalls
|
||||
| PosixSpawn.rb:1:1:1:32 | call to popen4 | PosixSpawn.rb:1:22:1:25 | "ls" |
|
||||
| PosixSpawn.rb:1:1:1:32 | call to popen4 | PosixSpawn.rb:1:28:1:31 | "-l" |
|
||||
| PosixSpawn.rb:2:1:2:31 | call to popen4 | PosixSpawn.rb:2:21:2:24 | "ls" |
|
||||
| PosixSpawn.rb:2:1:2:31 | call to popen4 | PosixSpawn.rb:2:27:2:30 | "-l" |
|
||||
| PosixSpawn.rb:7:1:7:40 | call to spawn | PosixSpawn.rb:7:20:7:39 | * ... |
|
||||
| PosixSpawn.rb:8:1:8:30 | call to spawn | PosixSpawn.rb:8:21:8:29 | "sleep 5" |
|
||||
| PosixSpawn.rb:13:1:13:60 | call to system | PosixSpawn.rb:13:21:13:25 | "foo" |
|
||||
| PosixSpawn.rb:13:1:13:60 | call to system | PosixSpawn.rb:13:28:13:32 | "bar" |
|
||||
| PosixSpawn.rb:13:1:13:60 | call to system | PosixSpawn.rb:13:35:13:44 | "--a-flag" |
|
||||
| PosixSpawn.rb:13:1:13:60 | call to system | PosixSpawn.rb:13:47:13:52 | call to before |
|
||||
| PosixSpawn.rb:13:1:13:60 | call to system | PosixSpawn.rb:13:55:13:59 | call to after |
|
||||
| PosixSpawn.rb:15:1:15:28 | call to fspawn | PosixSpawn.rb:15:21:15:27 | call to command |
|
||||
| PosixSpawn.rb:16:1:16:28 | call to pspawn | PosixSpawn.rb:16:21:16:27 | call to command |
|
||||
| PosixSpawn.rb:17:1:17:28 | call to popen4 | PosixSpawn.rb:17:21:17:27 | call to command |
|
||||
| PosixSpawn.rb:19:1:19:28 | call to ` | PosixSpawn.rb:19:16:19:20 | "foo" |
|
||||
| PosixSpawn.rb:19:1:19:28 | call to ` | PosixSpawn.rb:19:23:19:27 | "bar" |
|
||||
childCalls
|
||||
| PosixSpawn.rb:4:1:4:77 | call to new | PosixSpawn.rb:4:25:4:39 | call to [] |
|
||||
| PosixSpawn.rb:4:1:4:77 | call to new | PosixSpawn.rb:4:42:4:51 | ... + ... |
|
||||
| PosixSpawn.rb:4:1:4:77 | call to new | PosixSpawn.rb:4:54:4:58 | * ... |
|
||||
| PosixSpawn.rb:5:1:5:80 | call to new | PosixSpawn.rb:5:25:5:32 | * ... |
|
||||
| PosixSpawn.rb:10:1:10:35 | call to new | PosixSpawn.rb:10:25:10:28 | "ls" |
|
||||
| PosixSpawn.rb:10:1:10:35 | call to new | PosixSpawn.rb:10:31:10:34 | "-l" |
|
||||
| PosixSpawn.rb:11:1:11:38 | call to build | PosixSpawn.rb:11:27:11:32 | "echo" |
|
||||
| PosixSpawn.rb:11:1:11:38 | call to build | PosixSpawn.rb:11:35:11:37 | call to msg |
|
||||
11
ruby/ql/test/library-tests/frameworks/PosixSpawn.ql
Normal file
11
ruby/ql/test/library-tests/frameworks/PosixSpawn.ql
Normal file
@@ -0,0 +1,11 @@
|
||||
import ruby
|
||||
import codeql.ruby.frameworks.PosixSpawn
|
||||
import codeql.ruby.DataFlow
|
||||
|
||||
query predicate systemCalls(PosixSpawn::SystemCall call, DataFlow::Node arg) {
|
||||
arg = call.getAnArgument()
|
||||
}
|
||||
|
||||
query predicate childCalls(PosixSpawn::ChildCall call, DataFlow::Node arg) {
|
||||
arg = call.getAnArgument()
|
||||
}
|
||||
19
ruby/ql/test/library-tests/frameworks/PosixSpawn.rb
Normal file
19
ruby/ql/test/library-tests/frameworks/PosixSpawn.rb
Normal file
@@ -0,0 +1,19 @@
|
||||
POSIX::Spawn::popen4("ls", "-l")
|
||||
POSIX::Spawn.popen4("ls", "-l")
|
||||
|
||||
POSIX::Spawn::Child.new({'ENV' => @var}, "foo/"+cmd, *argv, :chdir=>root_dir)
|
||||
POSIX::Spawn::Child.new(*command, input: options[:stdin].to_s, timeout: timeout)
|
||||
|
||||
POSIX::Spawn.spawn(*(argv+[{:in => f}]))
|
||||
POSIX::Spawn::spawn('sleep 5')
|
||||
|
||||
POSIX::Spawn::Child.new("ls", "-l")
|
||||
POSIX::Spawn::Child.build("echo", msg)
|
||||
|
||||
POSIX::Spawn.system("foo", "bar", "--a-flag", before, after)
|
||||
|
||||
POSIX::Spawn.fspawn(command)
|
||||
POSIX::Spawn.pspawn(command)
|
||||
POSIX::Spawn.popen4(command)
|
||||
|
||||
POSIX::Spawn.`("foo", "bar")
|
||||
Reference in New Issue
Block a user