Python: Make API graph version of PEP249 modeling

This will allow us to more easily handle the executescript method, which
we'll do in next commit.
This commit is contained in:
Rasmus Wriedt Larsen
2023-03-22 15:07:03 +01:00
parent 5930499f1d
commit eb43fa2644
3 changed files with 78 additions and 11 deletions

View File

@@ -561,8 +561,8 @@ module PrivateDjango {
API::Node connection() { result = db().getMember("connection") }
/** A `django.db.connection` is a PEP249 compliant DB connection. */
class DjangoDbConnection extends PEP249::Connection::InstanceSource {
DjangoDbConnection() { this = connection().asSource() }
class DjangoDbConnection extends PEP249::DatabaseConnection {
DjangoDbConnection() { this = connection() }
}
// -------------------------------------------------------------------------

View File

@@ -22,6 +22,67 @@ module PEP249 {
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"]
}
/**
* A call to `execute` or `executemany` method on a database cursor or a connection.
*
* 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(["execute", "executemany"]).getACall()
)
}
override DataFlow::Node getSql() {
result in [this.getArg(0), this.getArgByName(getSqlKwargName()),]
}
}
// ---------------------------------------------------------------------------
// 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()
@@ -147,7 +208,10 @@ module PEP249 {
* 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() }
ExecuteCall() {
this.getFunction() = execute() and
not this instanceof ExecuteMethodCall
}
override DataFlow::Node getSql() { result in [this.getArg(0), this.getArgByName("sql")] }
}
@@ -170,8 +234,13 @@ module PEP249 {
* 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() }
ExecutemanyCall() {
this.getFunction() = executemany() and
not this instanceof ExecuteMethodCall
}
override DataFlow::Node getSql() { result in [this.getArg(0), this.getArgByName("sql")] }
override DataFlow::Node getSql() {
result in [this.getArg(0), this.getArgByName(getSqlKwargName())]
}
}
}

View File

@@ -163,11 +163,9 @@ private module Peewee {
* A call to the `connection` method on a `peewee.Database` instance.
* https://docs.peewee-orm.com/en/latest/peewee/api.html#Database.connection.
*/
class PeeweeDatabaseConnectionCall extends PEP249::Connection::InstanceSource,
DataFlow::CallCfgNode
{
class PeeweeDatabaseConnectionCall extends PEP249::DatabaseConnection {
PeeweeDatabaseConnectionCall() {
this = Database::instance().getMember("connection").getACall()
this = Database::instance().getMember("connection").getReturn()
}
}
@@ -175,8 +173,8 @@ private module Peewee {
* A call to the `cursor` method on a `peewee.Database` instance.
* https://docs.peewee-orm.com/en/latest/peewee/api.html#Database.cursor.
*/
class PeeweeDatabaseCursorCall extends PEP249::Cursor::InstanceSource, DataFlow::CallCfgNode {
PeeweeDatabaseCursorCall() { this = Database::instance().getMember("cursor").getACall() }
class PeeweeDatabaseCursorCall extends PEP249::DatabaseCursor {
PeeweeDatabaseCursorCall() { this = Database::instance().getMember("cursor").getReturn() }
}
/**