mirror of
https://github.com/github/codeql.git
synced 2026-05-01 19:55:15 +02:00
Python: Model os.exec* os.spawn* and os.posix_spawn*
I also had to exclude the inline expectation tests from files outside the test repo.
This commit is contained in:
@@ -10,7 +10,7 @@ private import experimental.semmle.python.Concepts
|
||||
|
||||
private module Stdlib {
|
||||
/** Gets a reference to the `os` module. */
|
||||
DataFlow::Node os(DataFlow::TypeTracker t) {
|
||||
private DataFlow::Node os(DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
result = DataFlow::importModule("os")
|
||||
or
|
||||
@@ -20,34 +20,38 @@ private module Stdlib {
|
||||
/** Gets a reference to the `os` module. */
|
||||
DataFlow::Node os() { result = os(DataFlow::TypeTracker::end()) }
|
||||
|
||||
module os {
|
||||
/** Gets a reference to the `os.system` function. */
|
||||
DataFlow::Node system(DataFlow::TypeTracker t) {
|
||||
/**
|
||||
* Gets a reference to the attribute `attr_name` of the `os` module.
|
||||
* WARNING: Only holds for a few predefined attributes.
|
||||
*
|
||||
* For example, using `attr_name = "system"` will get all uses of `os.system`.
|
||||
*/
|
||||
private DataFlow::Node os_attr(string attr_name, DataFlow::TypeTracker t) {
|
||||
attr_name in ["system", "popen",
|
||||
// exec
|
||||
"execl", "execle", "execlp", "execlpe", "execv", "execve", "execvp", "execvpe",
|
||||
// spawn
|
||||
"spawnl", "spawnle", "spawnlp", "spawnlpe", "spawnv", "spawnve", "spawnvp", "spawnvpe",
|
||||
"posix_spawn", "posix_spawnp"] and
|
||||
(
|
||||
t.start() and
|
||||
result = DataFlow::importMember("os", "system")
|
||||
result = DataFlow::importMember("os", attr_name)
|
||||
or
|
||||
t.startInAttr("system") and
|
||||
t.startInAttr(attr_name) and
|
||||
result = os()
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = os::system(t2).track(t2, t))
|
||||
}
|
||||
exists(DataFlow::TypeTracker t2 | result = os_attr(attr_name, t2).track(t2, t))
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets a reference to the `os.system` function. */
|
||||
DataFlow::Node system() { result = os::system(DataFlow::TypeTracker::end()) }
|
||||
|
||||
/** Gets a reference to the `os.popen` function. */
|
||||
DataFlow::Node popen(DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
result = DataFlow::importMember("os", "popen")
|
||||
or
|
||||
t.startInAttr("popen") and
|
||||
result = os()
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = os::popen(t2).track(t2, t))
|
||||
}
|
||||
|
||||
/** Gets a reference to the `os.popen` function. */
|
||||
DataFlow::Node popen() { result = os::popen(DataFlow::TypeTracker::end()) }
|
||||
/**
|
||||
* Gets a reference to the attribute `attr_name` of the `os` module.
|
||||
* WARNING: Only holds for a few predefined attributes.
|
||||
*
|
||||
* For example, using `"system"` will get all uses of `os.system`.
|
||||
*/
|
||||
private DataFlow::Node os_attr(string attr_name) {
|
||||
result = os_attr(attr_name, DataFlow::TypeTracker::end())
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -55,7 +59,7 @@ private module Stdlib {
|
||||
* See https://docs.python.org/3/library/os.html#os.system
|
||||
*/
|
||||
private class OsSystemCall extends SystemCommandExecution::Range {
|
||||
OsSystemCall() { this.asCfgNode().(CallNode).getFunction() = os::system().asCfgNode() }
|
||||
OsSystemCall() { this.asCfgNode().(CallNode).getFunction() = os_attr("system").asCfgNode() }
|
||||
|
||||
override DataFlow::Node getCommand() {
|
||||
result.asCfgNode() = this.asCfgNode().(CallNode).getArg(0)
|
||||
@@ -67,7 +71,54 @@ private module Stdlib {
|
||||
* See https://docs.python.org/3/library/os.html#os.popen
|
||||
*/
|
||||
private class OsPopenCall extends SystemCommandExecution::Range {
|
||||
OsPopenCall() { this.asCfgNode().(CallNode).getFunction() = os::popen().asCfgNode() }
|
||||
OsPopenCall() { this.asCfgNode().(CallNode).getFunction() = os_attr("popen").asCfgNode() }
|
||||
|
||||
override DataFlow::Node getCommand() {
|
||||
result.asCfgNode() = this.asCfgNode().(CallNode).getArg(0)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to any of the `os.exec*` functions
|
||||
* See https://docs.python.org/3.8/library/os.html#os.execl
|
||||
*/
|
||||
private class OsExecCall extends SystemCommandExecution::Range {
|
||||
OsExecCall() {
|
||||
this.asCfgNode().(CallNode).getFunction() =
|
||||
os_attr(["execl", "execle", "execlp", "execlpe", "execv", "execve", "execvp", "execvpe"])
|
||||
.asCfgNode()
|
||||
}
|
||||
|
||||
override DataFlow::Node getCommand() {
|
||||
result.asCfgNode() = this.asCfgNode().(CallNode).getArg(0)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to any of the `os.spawn*` functions
|
||||
* See https://docs.python.org/3.8/library/os.html#os.spawnl
|
||||
*/
|
||||
private class OsSpawnCall extends SystemCommandExecution::Range {
|
||||
OsSpawnCall() {
|
||||
this.asCfgNode().(CallNode).getFunction() =
|
||||
os_attr(["spawnl", "spawnle", "spawnlp", "spawnlpe", "spawnv", "spawnve", "spawnvp",
|
||||
"spawnvpe"]).asCfgNode()
|
||||
}
|
||||
|
||||
override DataFlow::Node getCommand() {
|
||||
result.asCfgNode() = this.asCfgNode().(CallNode).getArg(1)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to any of the `os.posix_spawn*` functions
|
||||
* See https://docs.python.org/3.8/library/os.html#os.posix_spawn
|
||||
*/
|
||||
private class OsPosixSpawnCall extends SystemCommandExecution::Range {
|
||||
OsPosixSpawnCall() {
|
||||
this.asCfgNode().(CallNode).getFunction() =
|
||||
os_attr(["posix_spawn", "posix_spawnp"]).asCfgNode()
|
||||
}
|
||||
|
||||
override DataFlow::Node getCommand() {
|
||||
result.asCfgNode() = this.asCfgNode().(CallNode).getArg(0)
|
||||
|
||||
@@ -29,38 +29,38 @@ def os_members():
|
||||
|
||||
|
||||
########################################
|
||||
# https://docs.python.org/3.8/library/os.html#os.execv
|
||||
# https://docs.python.org/3.8/library/os.html#os.execl
|
||||
#
|
||||
# VS Code extension will ignore rest of program if encountering one of these, which we
|
||||
# don't want. We could use `if False`, but just to be 100% sure we don't do anything too
|
||||
# clever in our analysis that discards that code, I used `if UNKNOWN` instead
|
||||
if UNKNOWN:
|
||||
env = {"FOO": "foo"}
|
||||
os.execl("executable", "<progname>", "arg0") # $f-:SystemCommandExecution_getCommand="executable"
|
||||
os.execle("executable", "<progname>", "arg0", env) # $f-:SystemCommandExecution_getCommand="executable"
|
||||
os.execlp("executable", "<progname>", "arg0") # $f-:SystemCommandExecution_getCommand="executable"
|
||||
os.execlpe("executable", "<progname>", "arg0", env) # $f-:SystemCommandExecution_getCommand="executable"
|
||||
os.execv("executable", ["<progname>", "arg0"]) # $f-:SystemCommandExecution_getCommand="executable"
|
||||
os.execve("executable", ["<progname>", "arg0"], env) # $f-:SystemCommandExecution_getCommand="executable"
|
||||
os.execvp("executable", ["<progname>", "arg0"]) # $f-:SystemCommandExecution_getCommand="executable"
|
||||
os.execvpe("executable", ["<progname>", "arg0"], env) # $f-:SystemCommandExecution_getCommand="executable"
|
||||
os.execl("executable", "<progname>", "arg0") # $SystemCommandExecution_getCommand="executable"
|
||||
os.execle("executable", "<progname>", "arg0", env) # $SystemCommandExecution_getCommand="executable"
|
||||
os.execlp("executable", "<progname>", "arg0") # $SystemCommandExecution_getCommand="executable"
|
||||
os.execlpe("executable", "<progname>", "arg0", env) # $SystemCommandExecution_getCommand="executable"
|
||||
os.execv("executable", ["<progname>", "arg0"]) # $SystemCommandExecution_getCommand="executable"
|
||||
os.execve("executable", ["<progname>", "arg0"], env) # $SystemCommandExecution_getCommand="executable"
|
||||
os.execvp("executable", ["<progname>", "arg0"]) # $SystemCommandExecution_getCommand="executable"
|
||||
os.execvpe("executable", ["<progname>", "arg0"], env) # $SystemCommandExecution_getCommand="executable"
|
||||
|
||||
|
||||
########################################
|
||||
# https://docs.python.org/3.8/library/os.html#os.spawnl
|
||||
env = {"FOO": "foo"}
|
||||
os.spawnl(os.P_WAIT, "executable", "<progname>", "arg0") # $f-:SystemCommandExecution_getCommand="executable"
|
||||
os.spawnle(os.P_WAIT, "executable", "<progname>", "arg0", env) # $f-:SystemCommandExecution_getCommand="executable"
|
||||
os.spawnlp(os.P_WAIT, "executable", "<progname>", "arg0") # $f-:SystemCommandExecution_getCommand="executable"
|
||||
os.spawnlpe(os.P_WAIT, "executable", "<progname>", "arg0", env) # $f-:SystemCommandExecution_getCommand="executable"
|
||||
os.spawnv(os.P_WAIT, "executable", ["<progname>", "arg0"]) # $f-:SystemCommandExecution_getCommand="executable"
|
||||
os.spawnve(os.P_WAIT, "executable", ["<progname>", "arg0"], env) # $f-:SystemCommandExecution_getCommand="executable"
|
||||
os.spawnvp(os.P_WAIT, "executable", ["<progname>", "arg0"]) # $f-:SystemCommandExecution_getCommand="executable"
|
||||
os.spawnvpe(os.P_WAIT, "executable", ["<progname>", "arg0"], env) # $f-:SystemCommandExecution_getCommand="executable"
|
||||
os.spawnl(os.P_WAIT, "executable", "<progname>", "arg0") # $SystemCommandExecution_getCommand="executable"
|
||||
os.spawnle(os.P_WAIT, "executable", "<progname>", "arg0", env) # $SystemCommandExecution_getCommand="executable"
|
||||
os.spawnlp(os.P_WAIT, "executable", "<progname>", "arg0") # $SystemCommandExecution_getCommand="executable"
|
||||
os.spawnlpe(os.P_WAIT, "executable", "<progname>", "arg0", env) # $SystemCommandExecution_getCommand="executable"
|
||||
os.spawnv(os.P_WAIT, "executable", ["<progname>", "arg0"]) # $SystemCommandExecution_getCommand="executable"
|
||||
os.spawnve(os.P_WAIT, "executable", ["<progname>", "arg0"], env) # $SystemCommandExecution_getCommand="executable"
|
||||
os.spawnvp(os.P_WAIT, "executable", ["<progname>", "arg0"]) # $SystemCommandExecution_getCommand="executable"
|
||||
os.spawnvpe(os.P_WAIT, "executable", ["<progname>", "arg0"], env) # $SystemCommandExecution_getCommand="executable"
|
||||
|
||||
# Added in Python 3.8
|
||||
os.posix_spawn("executable", ["<progname>", "arg0"], env) # $f-:SystemCommandExecution_getCommand="executable"
|
||||
os.posix_spawnp("executable", ["<progname>", "arg0"], env) # $f-:SystemCommandExecution_getCommand="executable"
|
||||
os.posix_spawn("executable", ["<progname>", "arg0"], env) # $SystemCommandExecution_getCommand="executable"
|
||||
os.posix_spawnp("executable", ["<progname>", "arg0"], env) # $SystemCommandExecution_getCommand="executable"
|
||||
|
||||
########################################
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ class SystemCommandExecutionTest extends InlineExpectationsTest {
|
||||
|
||||
override predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
exists(SystemCommandExecution sce, DataFlow::Node command |
|
||||
exists(location.getFile().getRelativePath()) and
|
||||
command = sce.getCommand() and
|
||||
location = command.getLocation() and
|
||||
element = command.toString() and
|
||||
|
||||
Reference in New Issue
Block a user