mirror of
https://github.com/github/codeql.git
synced 2025-12-31 16:16:34 +01:00
328 lines
12 KiB
Plaintext
328 lines
12 KiB
Plaintext
/**
|
|
* Provides classes modeling PEP 249.
|
|
* See https://www.python.org/dev/peps/pep-0249/.
|
|
*/
|
|
|
|
private import python
|
|
private import semmle.python.dataflow.new.DataFlow
|
|
private import semmle.python.dataflow.new.RemoteFlowSources
|
|
private import semmle.python.Concepts
|
|
private import semmle.python.ApiGraphs
|
|
|
|
/**
|
|
* Provides classes modeling database interfaces following PEP 249.
|
|
* See https://www.python.org/dev/peps/pep-0249/.
|
|
*/
|
|
module PEP249 {
|
|
/**
|
|
* An API graph node representing a module that implements PEP 249.
|
|
*/
|
|
abstract class PEP249ModuleApiNode extends API::Node {
|
|
/** Gets a string representation of this element. */
|
|
override string toString() { result = this.(API::Node).toString() }
|
|
}
|
|
|
|
/**
|
|
* An API graph node representing a database connection.
|
|
*/
|
|
abstract class DatabaseConnection extends API::Node {
|
|
/** Gets a string representation of this element. */
|
|
override string toString() { result = this.(API::Node).toString() }
|
|
}
|
|
|
|
private class DefaultDatabaseConnection extends DatabaseConnection {
|
|
DefaultDatabaseConnection() {
|
|
this = any(PEP249ModuleApiNode mod).getMember("connect").getReturn()
|
|
}
|
|
}
|
|
|
|
/**
|
|
* An API graph node representing a database cursor.
|
|
*/
|
|
abstract class DatabaseCursor extends API::Node {
|
|
/** Gets a string representation of this element. */
|
|
override string toString() { result = this.(API::Node).toString() }
|
|
}
|
|
|
|
private class DefaultDatabaseCursor extends DatabaseCursor {
|
|
DefaultDatabaseCursor() { this = any(DatabaseConnection conn).getMember("cursor").getReturn() }
|
|
}
|
|
|
|
private string getSqlKwargName() {
|
|
result in ["sql", "statement", "operation", "query", "query_string", "sql_script"]
|
|
}
|
|
|
|
private string getExecuteMethodName() {
|
|
result in ["execute", "executemany", "executescript", "execute_insert", "execute_fetchall"]
|
|
}
|
|
|
|
/**
|
|
* A call to an execute method on a database cursor or a connection, such as `execute`
|
|
* or `executemany`.
|
|
*
|
|
* See
|
|
* - https://peps.python.org/pep-0249/#execute
|
|
* - https://peps.python.org/pep-0249/#executemany
|
|
*
|
|
* Note: While `execute` method on a connection is not part of PEP249, if it is used, we
|
|
* recognize it as an alias for constructing a cursor and calling `execute` on it.
|
|
*/
|
|
private class ExecuteMethodCall extends SqlExecution::Range, API::CallNode {
|
|
ExecuteMethodCall() {
|
|
exists(API::Node start |
|
|
start instanceof DatabaseCursor or start instanceof DatabaseConnection
|
|
|
|
|
this = start.getMember(getExecuteMethodName()).getACall()
|
|
)
|
|
}
|
|
|
|
override DataFlow::Node getSql() {
|
|
result in [this.getArg(0), this.getArgByName(getSqlKwargName()),]
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// asyncio implementations
|
|
// ---------------------------------------------------------------------------
|
|
//
|
|
// we differentiate between normal and asyncio implementations, since we model the
|
|
// `execute` call differently -- as a SqlExecution vs SqlConstruction, since the SQL
|
|
// is only executed in asyncio after being awaited (which might happen in something
|
|
// like `asyncio.gather`)
|
|
/**
|
|
* An API graph node representing a module that implements PEP 249 using asyncio.
|
|
*/
|
|
abstract class AsyncPEP249ModuleApiNode extends API::Node {
|
|
/** Gets a string representation of this element. */
|
|
override string toString() { result = this.(API::Node).toString() }
|
|
}
|
|
|
|
/**
|
|
* An API graph node representing a asyncio database connection (after being awaited).
|
|
*/
|
|
abstract class AsyncDatabaseConnection extends API::Node {
|
|
/** Gets a string representation of this element. */
|
|
override string toString() { result = this.(API::Node).toString() }
|
|
}
|
|
|
|
private class DefaultAsyncDatabaseConnection extends AsyncDatabaseConnection {
|
|
DefaultAsyncDatabaseConnection() {
|
|
this = any(AsyncPEP249ModuleApiNode mod).getMember("connect").getReturn().getAwaited()
|
|
}
|
|
}
|
|
|
|
/**
|
|
* An API graph node representing a asyncio database cursor (after being awaited).
|
|
*/
|
|
abstract class AsyncDatabaseCursor extends API::Node {
|
|
/** Gets a string representation of this element. */
|
|
override string toString() { result = this.(API::Node).toString() }
|
|
}
|
|
|
|
private class DefaultAsyncDatabaseCursor extends AsyncDatabaseCursor {
|
|
DefaultAsyncDatabaseCursor() {
|
|
this = any(AsyncDatabaseConnection conn).getMember("cursor").getReturn().getAwaited()
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A call to an execute method on an asyncio database cursor or an asyncio connection,
|
|
* such as `execute` or `executemany`.
|
|
*
|
|
* (This is not an SqlExecution, since that only happens when the coroutine is
|
|
* awaited)
|
|
*
|
|
* See ExecuteMethodCall for more details.
|
|
*/
|
|
private class AsyncExecuteMethodCall extends SqlConstruction::Range, API::CallNode {
|
|
AsyncExecuteMethodCall() {
|
|
exists(API::Node start |
|
|
start instanceof AsyncDatabaseCursor or start instanceof AsyncDatabaseConnection
|
|
|
|
|
this = start.getMember(getExecuteMethodName()).getACall()
|
|
)
|
|
}
|
|
|
|
override DataFlow::Node getSql() {
|
|
result in [this.getArg(0), this.getArgByName(getSqlKwargName()),]
|
|
}
|
|
}
|
|
|
|
/** Actual execution of the AsyncExecuteMethodCall coroutine. */
|
|
private class AwaitedAsyncExecuteMethodCall extends SqlExecution::Range {
|
|
AsyncExecuteMethodCall execute;
|
|
|
|
AwaitedAsyncExecuteMethodCall() { this = execute.getReturn().getAwaited().asSource() }
|
|
|
|
override DataFlow::Node getSql() { result = execute.getSql() }
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// old impl
|
|
// ---------------------------------------------------------------------------
|
|
// the goal is to deprecate it in favour of the API graph version, but currently this
|
|
// requires a rewrite of the Peewee modeling, which depends on rewriting the
|
|
// instance/instance-source stuff to use API graphs instead.
|
|
// so is postponed for now.
|
|
/** Gets a reference to the `connect` function of a module that implements PEP 249. */
|
|
DataFlow::Node connect() {
|
|
result = any(PEP249ModuleApiNode a).getMember("connect").getAValueReachableFromSource()
|
|
}
|
|
|
|
/**
|
|
* Provides models for database connections (following PEP 249).
|
|
*
|
|
* See https://www.python.org/dev/peps/pep-0249/#connection-objects.
|
|
*/
|
|
module Connection {
|
|
/**
|
|
* A source of database connections (following PEP 249), extend this class to model new instances.
|
|
*
|
|
* This can include instantiations of the class, return values from function
|
|
* calls, or a special parameter that will be set when functions are called by external
|
|
* libraries.
|
|
*
|
|
* Use the predicate `Connection::instance()` to get references to database connections (following PEP 249).
|
|
*
|
|
* Extend this class if the module implementing PEP 249 offers more direct ways to obtain
|
|
* a connection than going through `connect`.
|
|
*/
|
|
abstract class InstanceSource extends DataFlow::Node { }
|
|
|
|
/** A call to the `connect` function of a module that implements PEP 249. */
|
|
private class ConnectCall extends InstanceSource, DataFlow::CallCfgNode {
|
|
ConnectCall() { this.getFunction() = connect() }
|
|
}
|
|
|
|
/** Gets a reference to a database connection (following PEP 249). */
|
|
private DataFlow::TypeTrackingNode instance(DataFlow::TypeTracker t) {
|
|
t.start() and
|
|
result instanceof InstanceSource
|
|
or
|
|
exists(DataFlow::TypeTracker t2 | result = instance(t2).track(t2, t))
|
|
}
|
|
|
|
/** Gets a reference to a database connection (following PEP 249). */
|
|
DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }
|
|
}
|
|
|
|
/**
|
|
* Provides models for database cursors (following PEP 249).
|
|
*
|
|
* These are returned by the `cursor` method on a database connection.
|
|
* See https://www.python.org/dev/peps/pep-0249/#cursor.
|
|
*/
|
|
module Cursor {
|
|
/**
|
|
* A source of database cursors (following PEP 249), extend this class to model new instances.
|
|
*
|
|
* This can include instantiations of the class, return values from function
|
|
* calls, or a special parameter that will be set when functions are called by external
|
|
* libraries.
|
|
*
|
|
* Use the predicate `Cursor::instance()` to get references to database cursors (following PEP 249).
|
|
*
|
|
* Extend this class if the module implementing PEP 249 offers more direct ways to obtain
|
|
* a connection than going through `connect`.
|
|
*/
|
|
abstract class InstanceSource extends DataFlow::LocalSourceNode { }
|
|
|
|
/** Gets a reference to a database cursor. */
|
|
private DataFlow::TypeTrackingNode instance(DataFlow::TypeTracker t) {
|
|
t.start() and
|
|
result instanceof InstanceSource
|
|
or
|
|
exists(DataFlow::TypeTracker t2 | result = instance(t2).track(t2, t))
|
|
}
|
|
|
|
/** Gets a reference to a database cursor. */
|
|
DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }
|
|
|
|
/** Gets a reference to the `cursor` method on a database connection. */
|
|
private DataFlow::TypeTrackingNode methodRef(DataFlow::TypeTracker t) {
|
|
t.startInAttr("cursor") and
|
|
result = Connection::instance()
|
|
or
|
|
exists(DataFlow::TypeTracker t2 | result = methodRef(t2).track(t2, t))
|
|
}
|
|
|
|
/** Gets a reference to the `cursor` method on a database connection. */
|
|
DataFlow::Node methodRef() { methodRef(DataFlow::TypeTracker::end()).flowsTo(result) }
|
|
|
|
/** A call to the `cursor` method on a database connection */
|
|
private class CursorCall extends InstanceSource, DataFlow::CallCfgNode {
|
|
CursorCall() { this.getFunction() = methodRef() }
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Gets a reference to the `execute` method on a cursor (or on a connection).
|
|
*
|
|
* Note: while `execute` method on a connection is not part of PEP249, if it is used, we
|
|
* recognize it as an alias for constructing a cursor and calling `execute` on it.
|
|
*
|
|
* See https://peps.python.org/pep-0249/#execute.
|
|
*/
|
|
private DataFlow::TypeTrackingNode execute(DataFlow::TypeTracker t) {
|
|
t.startInAttr("execute") and
|
|
result in [Cursor::instance(), Connection::instance()]
|
|
or
|
|
exists(DataFlow::TypeTracker t2 | result = execute(t2).track(t2, t))
|
|
}
|
|
|
|
/**
|
|
* Gets a reference to the `execute` method on a cursor (or on a connection).
|
|
*
|
|
* Note: while `execute` method on a connection is not part of PEP249, if it is used, we
|
|
* recognize it as an alias for constructing a cursor and calling `execute` on it.
|
|
*
|
|
* See https://peps.python.org/pep-0249/#execute.
|
|
*/
|
|
DataFlow::Node execute() { execute(DataFlow::TypeTracker::end()).flowsTo(result) }
|
|
|
|
/**
|
|
* A call to the `execute` method on a cursor or a connection.
|
|
*
|
|
* See https://peps.python.org/pep-0249/#execute
|
|
*
|
|
* Note: While `execute` method on a connection is not part of PEP249, if it is used, we
|
|
* recognize it as an alias for constructing a cursor and calling `execute` on it.
|
|
*/
|
|
private class ExecuteCall extends SqlExecution::Range, DataFlow::CallCfgNode {
|
|
ExecuteCall() {
|
|
this.getFunction() = execute() and
|
|
not this instanceof ExecuteMethodCall
|
|
}
|
|
|
|
override DataFlow::Node getSql() { result in [this.getArg(0), this.getArgByName("sql")] }
|
|
}
|
|
|
|
private DataFlow::TypeTrackingNode executemany(DataFlow::TypeTracker t) {
|
|
t.startInAttr("executemany") and
|
|
result in [Cursor::instance(), Connection::instance()]
|
|
or
|
|
exists(DataFlow::TypeTracker t2 | result = executemany(t2).track(t2, t))
|
|
}
|
|
|
|
private DataFlow::Node executemany() { executemany(DataFlow::TypeTracker::end()).flowsTo(result) }
|
|
|
|
/**
|
|
* A call to the `executemany` method on a cursor or a connection.
|
|
*
|
|
* See https://peps.python.org/pep-0249/#executemany
|
|
*
|
|
* Note: While `executemany` method on a connection is not part of PEP249, if it is used, we
|
|
* recognize it as an alias for constructing a cursor and calling `executemany` on it.
|
|
*/
|
|
private class ExecutemanyCall extends SqlExecution::Range, DataFlow::CallCfgNode {
|
|
ExecutemanyCall() {
|
|
this.getFunction() = executemany() and
|
|
not this instanceof ExecuteMethodCall
|
|
}
|
|
|
|
override DataFlow::Node getSql() {
|
|
result in [this.getArg(0), this.getArgByName(getSqlKwargName())]
|
|
}
|
|
}
|
|
}
|