mirror of
https://github.com/github/codeql.git
synced 2026-04-25 08:45:14 +02:00
Merge pull request #14145 from p-/p--asyncio-cmdi-exec
Python: Support for command injection sinks found in the `asyncio` module
This commit is contained in:
@@ -4396,6 +4396,117 @@ private module StdlibPrivate {
|
||||
preservesValue = true
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// asyncio
|
||||
// ---------------------------------------------------------------------------
|
||||
/** Provides models for the `asyncio` module. */
|
||||
module AsyncIO {
|
||||
/**
|
||||
* A call to the `asyncio.create_subprocess_exec` function (also accessible via the `subprocess` module of `asyncio`)
|
||||
*
|
||||
* See https://docs.python.org/3/library/asyncio-subprocess.html#creating-subprocesses
|
||||
*/
|
||||
private class CreateSubprocessExec extends SystemCommandExecution::Range,
|
||||
FileSystemAccess::Range, API::CallNode
|
||||
{
|
||||
CreateSubprocessExec() {
|
||||
this = API::moduleImport("asyncio").getMember("create_subprocess_exec").getACall()
|
||||
or
|
||||
this =
|
||||
API::moduleImport("asyncio")
|
||||
.getMember("subprocess")
|
||||
.getMember("create_subprocess_exec")
|
||||
.getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getCommand() { result = this.getParameter(0, "program").asSink() }
|
||||
|
||||
override DataFlow::Node getAPathArgument() { result = this.getCommand() }
|
||||
|
||||
override predicate isShellInterpreted(DataFlow::Node arg) {
|
||||
none() // this is a safe API.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to the `asyncio.create_subprocess_shell` function (also accessible via the `subprocess` module of `asyncio`)
|
||||
*
|
||||
* See https://docs.python.org/3/library/asyncio-subprocess.html#asyncio.create_subprocess_shell
|
||||
*/
|
||||
private class CreateSubprocessShell extends SystemCommandExecution::Range,
|
||||
FileSystemAccess::Range, API::CallNode
|
||||
{
|
||||
CreateSubprocessShell() {
|
||||
this = API::moduleImport("asyncio").getMember("create_subprocess_shell").getACall()
|
||||
or
|
||||
this =
|
||||
API::moduleImport("asyncio")
|
||||
.getMember("subprocess")
|
||||
.getMember("create_subprocess_shell")
|
||||
.getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getCommand() { result = this.getParameter(0, "cmd").asSink() }
|
||||
|
||||
override DataFlow::Node getAPathArgument() { result = this.getCommand() }
|
||||
|
||||
override predicate isShellInterpreted(DataFlow::Node arg) { arg = this.getCommand() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an asyncio event loop (an object with basetype `AbstractEventLoop`).
|
||||
*
|
||||
* See https://docs.python.org/3/library/asyncio-eventloop.html
|
||||
*/
|
||||
private API::Node getAsyncioEventLoop() {
|
||||
result = API::moduleImport("asyncio").getMember("get_running_loop").getReturn()
|
||||
or
|
||||
result = API::moduleImport("asyncio").getMember("get_event_loop").getReturn() // deprecated in Python 3.10.0 and later
|
||||
or
|
||||
result = API::moduleImport("asyncio").getMember("new_event_loop").getReturn()
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to `subprocess_exec` on an event loop instance.
|
||||
*
|
||||
* See https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.subprocess_exec
|
||||
*/
|
||||
private class EventLoopSubprocessExec extends API::CallNode, SystemCommandExecution::Range,
|
||||
FileSystemAccess::Range
|
||||
{
|
||||
EventLoopSubprocessExec() {
|
||||
this = getAsyncioEventLoop().getMember("subprocess_exec").getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getCommand() { result = this.getArg(1) }
|
||||
|
||||
override DataFlow::Node getAPathArgument() { result = this.getCommand() }
|
||||
|
||||
override predicate isShellInterpreted(DataFlow::Node arg) {
|
||||
none() // this is a safe API.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to `subprocess_shell` on an event loop instance.
|
||||
*
|
||||
* See https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.subprocess_shell
|
||||
*/
|
||||
private class EventLoopSubprocessShell extends API::CallNode, SystemCommandExecution::Range,
|
||||
FileSystemAccess::Range
|
||||
{
|
||||
EventLoopSubprocessShell() {
|
||||
this = getAsyncioEventLoop().getMember("subprocess_shell").getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getCommand() { result = this.getParameter(1, "cmd").asSink() }
|
||||
|
||||
override DataFlow::Node getAPathArgument() { result = this.getCommand() }
|
||||
|
||||
override predicate isShellInterpreted(DataFlow::Node arg) { arg = this.getCommand() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Extended the `py/command-line-injection` query with sinks from Python's `asyncio` module.
|
||||
@@ -158,3 +158,21 @@ safe_cmd = "ls {}".format(shlex.quote(tainted))
|
||||
wrong_use = shlex.quote("ls {}".format(tainted))
|
||||
# still dangerous, for example
|
||||
cmd = "sh -c " + wrong_use
|
||||
|
||||
########################################
|
||||
# Program/shell command execution via asyncio
|
||||
|
||||
import asyncio
|
||||
from asyncio import subprocess
|
||||
|
||||
asyncio.run(asyncio.create_subprocess_exec("executable", "arg0")) # $getCommand="executable" getAPathArgument="executable"
|
||||
asyncio.run(subprocess.create_subprocess_exec("executable", "arg0")) # $getCommand="executable" getAPathArgument="executable"
|
||||
|
||||
loop = asyncio.new_event_loop()
|
||||
loop.run_until_complete(loop.subprocess_exec(asyncio.SubprocessProtocol, "executable", "arg0")) # $getCommand="executable" getAPathArgument="executable"
|
||||
|
||||
asyncio.run(asyncio.create_subprocess_shell("shell_command")) # $getCommand="shell_command" getAPathArgument="shell_command"
|
||||
asyncio.run(subprocess.create_subprocess_shell("shell_command")) # $getCommand="shell_command" getAPathArgument="shell_command"
|
||||
|
||||
loop = asyncio.get_running_loop()
|
||||
loop.run_until_complete(loop.subprocess_shell(asyncio.SubprocessProtocol, "shell_command")) # $getCommand="shell_command" getAPathArgument="shell_command"
|
||||
|
||||
Reference in New Issue
Block a user