mirror of
https://github.com/github/codeql.git
synced 2025-12-22 11:46:32 +01:00
Merge pull request #12636 from RasmusWL/sql-modeling
Python: Some more SQL modeling
This commit is contained in:
@@ -223,7 +223,9 @@ and the CodeQL library pack ``codeql/python-all`` (`changelog <https://github.co
|
||||
aioch, Database
|
||||
aiomysql, Database
|
||||
aiopg, Database
|
||||
aiosqlite, Database
|
||||
asyncpg, Database
|
||||
cassandra-driver, Database
|
||||
clickhouse-driver, Database
|
||||
cx_Oracle, Database
|
||||
mysql-connector-python, Database
|
||||
@@ -233,9 +235,9 @@ and the CodeQL library pack ``codeql/python-all`` (`changelog <https://github.co
|
||||
oracledb, Database
|
||||
phoenixdb, Database
|
||||
psycopg2, Database
|
||||
pyodbc, Database
|
||||
pymssql, Database
|
||||
PyMySQL, Database
|
||||
pyodbc, Database
|
||||
sqlite3, Database
|
||||
Flask-SQLAlchemy, Database ORM
|
||||
peewee, Database ORM
|
||||
@@ -276,4 +278,3 @@ and the CodeQL library pack ``codeql/ruby-all`` (`changelog <https://github.com/
|
||||
Ruby on Rails, Web framework
|
||||
rubyzip, Compression library
|
||||
typhoeus, HTTP client
|
||||
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Added modeling of SQL execution in the packages `sqlite3.dbapi2`, `cassandra-driver`, `aiosqlite`, and the functions `sqlite3.Connection.executescript`/`sqlite3.Cursor.executescript` and `asyncpg.connection.connect()`.
|
||||
@@ -3,12 +3,14 @@
|
||||
*/
|
||||
|
||||
// If you add modeling of a new framework/library, remember to add it to the docs in
|
||||
// `docs/codeql/support/reusables/frameworks.rst`
|
||||
// `docs/codeql/reusables/supported-frameworks.rst`
|
||||
private import semmle.python.frameworks.Aioch
|
||||
private import semmle.python.frameworks.Aiohttp
|
||||
private import semmle.python.frameworks.Aiomysql
|
||||
private import semmle.python.frameworks.Aiosqlite
|
||||
private import semmle.python.frameworks.Aiopg
|
||||
private import semmle.python.frameworks.Asyncpg
|
||||
private import semmle.python.frameworks.CassandraDriver
|
||||
private import semmle.python.frameworks.ClickhouseDriver
|
||||
private import semmle.python.frameworks.Cryptodome
|
||||
private import semmle.python.frameworks.Cryptography
|
||||
|
||||
@@ -9,11 +9,10 @@ private import python
|
||||
private import semmle.python.dataflow.new.DataFlow
|
||||
private import semmle.python.Concepts
|
||||
private import semmle.python.ApiGraphs
|
||||
private import semmle.python.frameworks.PEP249
|
||||
|
||||
/** Provides models for the `aiomysql` PyPI package. */
|
||||
private module Aiomysql {
|
||||
private import semmle.python.internal.Awaited
|
||||
|
||||
/**
|
||||
* Gets a `ConnectionPool` that is created when the result of `aiomysql.create_pool()` is awaited.
|
||||
* See https://aiomysql.readthedocs.io/en/stable/pool.html
|
||||
@@ -23,49 +22,29 @@ private module Aiomysql {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a `Connection` that is created when
|
||||
* A Connection that is created when
|
||||
* - the result of `aiomysql.connect()` is awaited.
|
||||
* - the result of calling `acquire` on a `ConnectionPool` is awaited.
|
||||
* See https://aiomysql.readthedocs.io/en/stable/connection.html#connection
|
||||
* See
|
||||
* - https://aiomysql.readthedocs.io/en/stable/connection.html#connection
|
||||
* - https://aiomysql.readthedocs.io/en/stable/pool.html#Pool.acquire
|
||||
*/
|
||||
API::Node connection() {
|
||||
result = API::moduleImport("aiomysql").getMember("connect").getReturn().getAwaited()
|
||||
or
|
||||
result = connectionPool().getMember("acquire").getReturn().getAwaited()
|
||||
class AiomysqlConnection extends PEP249::AsyncDatabaseConnection {
|
||||
AiomysqlConnection() {
|
||||
this = API::moduleImport("aiomysql").getMember("connect").getReturn().getAwaited()
|
||||
or
|
||||
this = connectionPool().getMember("acquire").getReturn().getAwaited()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a `Cursor` that is created when
|
||||
* An additional cursor, that is created when
|
||||
* - the result of calling `cursor` on a `ConnectionPool` is awaited.
|
||||
* - the result of calling `cursor` on a `Connection` is awaited.
|
||||
* See https://aiomysql.readthedocs.io/en/stable/cursors.html
|
||||
* See
|
||||
* - https://aiomysql.readthedocs.io/en/stable/pool.html##Pool.cursor
|
||||
*/
|
||||
API::Node cursor() {
|
||||
result = connectionPool().getMember("cursor").getReturn().getAwaited()
|
||||
or
|
||||
result = connection().getMember("cursor").getReturn().getAwaited()
|
||||
}
|
||||
|
||||
/**
|
||||
* A query. Calling `execute` on a `Cursor` constructs a query.
|
||||
* See https://aiomysql.readthedocs.io/en/stable/cursors.html#Cursor.execute
|
||||
*/
|
||||
class CursorExecuteCall extends SqlConstruction::Range, API::CallNode {
|
||||
CursorExecuteCall() { this = cursor().getMember("execute").getACall() }
|
||||
|
||||
override DataFlow::Node getSql() { result = this.getParameter(0, "operation").asSink() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An awaited query. Awaiting the result of calling `execute` executes the query.
|
||||
* See https://aiomysql.readthedocs.io/en/stable/cursors.html#Cursor.execute
|
||||
*/
|
||||
class AwaitedCursorExecuteCall extends SqlExecution::Range {
|
||||
CursorExecuteCall executeCall;
|
||||
|
||||
AwaitedCursorExecuteCall() { this = executeCall.getReturn().getAwaited().asSource() }
|
||||
|
||||
override DataFlow::Node getSql() { result = executeCall.getSql() }
|
||||
class AiomysqlCursor extends PEP249::AsyncDatabaseCursor {
|
||||
AiomysqlCursor() { this = connectionPool().getMember("cursor").getReturn().getAwaited() }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -9,11 +9,10 @@ private import python
|
||||
private import semmle.python.dataflow.new.DataFlow
|
||||
private import semmle.python.Concepts
|
||||
private import semmle.python.ApiGraphs
|
||||
private import semmle.python.frameworks.PEP249
|
||||
|
||||
/** Provides models for the `aiopg` PyPI package. */
|
||||
private module Aiopg {
|
||||
private import semmle.python.internal.Awaited
|
||||
|
||||
/**
|
||||
* Gets a `ConnectionPool` that is created when the result of `aiopg.create_pool()` is awaited.
|
||||
* See https://aiopg.readthedocs.io/en/stable/core.html#pool
|
||||
@@ -23,49 +22,29 @@ private module Aiopg {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a `Connection` that is created when
|
||||
* A Connection that is created when
|
||||
* - the result of `aiopg.connect()` is awaited.
|
||||
* - the result of calling `acquire` on a `ConnectionPool` is awaited.
|
||||
* See https://aiopg.readthedocs.io/en/stable/core.html#connection
|
||||
* See
|
||||
* - https://aiopg.readthedocs.io/en/stable/core.html#connection
|
||||
* - https://aiopg.readthedocs.io/en/stable/core.html#aiopg.Pool.acquire
|
||||
*/
|
||||
API::Node connection() {
|
||||
result = API::moduleImport("aiopg").getMember("connect").getReturn().getAwaited()
|
||||
or
|
||||
result = connectionPool().getMember("acquire").getReturn().getAwaited()
|
||||
class AiopgConnection extends PEP249::AsyncDatabaseConnection {
|
||||
AiopgConnection() {
|
||||
this = API::moduleImport("aiopg").getMember("connect").getReturn().getAwaited()
|
||||
or
|
||||
this = connectionPool().getMember("acquire").getReturn().getAwaited()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a `Cursor` that is created when
|
||||
* An additional cursor, that is created when
|
||||
* - the result of calling `cursor` on a `ConnectionPool` is awaited.
|
||||
* - the result of calling `cursor` on a `Connection` is awaited.
|
||||
* See https://aiopg.readthedocs.io/en/stable/core.html#cursor
|
||||
* See
|
||||
* - https://aiopg.readthedocs.io/en/stable/core.html#aiopg.Pool.cursor
|
||||
*/
|
||||
API::Node cursor() {
|
||||
result = connectionPool().getMember("cursor").getReturn().getAwaited()
|
||||
or
|
||||
result = connection().getMember("cursor").getReturn().getAwaited()
|
||||
}
|
||||
|
||||
/**
|
||||
* A query. Calling `execute` on a `Cursor` constructs a query.
|
||||
* See https://aiopg.readthedocs.io/en/stable/core.html#aiopg.Cursor.execute
|
||||
*/
|
||||
class CursorExecuteCall extends SqlConstruction::Range, API::CallNode {
|
||||
CursorExecuteCall() { this = cursor().getMember("execute").getACall() }
|
||||
|
||||
override DataFlow::Node getSql() { result = this.getParameter(0, "operation").asSink() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An awaited query. Awaiting the result of calling `execute` executes the query.
|
||||
* See https://aiopg.readthedocs.io/en/stable/core.html#aiopg.Cursor.execute
|
||||
*/
|
||||
class AwaitedCursorExecuteCall extends SqlExecution::Range {
|
||||
CursorExecuteCall execute;
|
||||
|
||||
AwaitedCursorExecuteCall() { this = execute.getReturn().getAwaited().asSource() }
|
||||
|
||||
override DataFlow::Node getSql() { result = execute.getSql() }
|
||||
class AiopgCursor extends PEP249::AsyncDatabaseCursor {
|
||||
AiopgCursor() { this = connectionPool().getMember("cursor").getReturn().getAwaited() }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
39
python/ql/lib/semmle/python/frameworks/Aiosqlite.qll
Normal file
39
python/ql/lib/semmle/python/frameworks/Aiosqlite.qll
Normal file
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* Provides classes modeling security-relevant aspects of the `aiosqlite` PyPI package.
|
||||
* See
|
||||
* - https://pypi.org/project/aiosqlite/
|
||||
*/
|
||||
|
||||
private import python
|
||||
private import semmle.python.dataflow.new.DataFlow
|
||||
private import semmle.python.Concepts
|
||||
private import semmle.python.ApiGraphs
|
||||
private import semmle.python.frameworks.PEP249
|
||||
|
||||
/** Provides models for the `aiosqlite` PyPI package. */
|
||||
private module Aiosqlite {
|
||||
/**
|
||||
* A model of `aiosqlite` as a module that implements PEP 249 using asyncio, providing
|
||||
* ways to execute SQL statements against a database.
|
||||
*/
|
||||
class AiosqlitePEP249 extends PEP249::AsyncPEP249ModuleApiNode {
|
||||
AiosqlitePEP249() { this = API::moduleImport("aiosqlite") }
|
||||
}
|
||||
|
||||
/**
|
||||
* An additional cursor, that is return from the coroutine Connection.execute,
|
||||
* see https://aiosqlite.omnilib.dev/en/latest/api.html#aiosqlite.Connection.execute
|
||||
*/
|
||||
class AiosqliteCursor extends PEP249::AsyncDatabaseCursor {
|
||||
AiosqliteCursor() {
|
||||
this =
|
||||
API::moduleImport("aiosqlite")
|
||||
.getMember("connect")
|
||||
.getReturn()
|
||||
.getAwaited()
|
||||
.getMember("execute")
|
||||
.getReturn()
|
||||
.getAwaited()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,7 @@ private module Asyncpg {
|
||||
// * - the result of `asyncpg.connect()` is awaited.
|
||||
// * - the result of calling `acquire` on a `ConnectionPool` is awaited.
|
||||
"asyncpg.Connection;asyncpg;Member[connect].ReturnValue.Awaited",
|
||||
"asyncpg.Connection;asyncpg;Member[connection].Member[connect].ReturnValue.Awaited",
|
||||
"asyncpg.Connection;asyncpg.ConnectionPool;Member[acquire].ReturnValue.Awaited",
|
||||
// Creating an internal `~Connection` type that contains both `Connection` and `ConnectionPool`.
|
||||
"asyncpg.~Connection;asyncpg.Connection;", //
|
||||
|
||||
61
python/ql/lib/semmle/python/frameworks/CassandraDriver.qll
Normal file
61
python/ql/lib/semmle/python/frameworks/CassandraDriver.qll
Normal file
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* Provides classes modeling security-relevant aspects of the `cassandra-driver` PyPI package.
|
||||
* See https://pypi.org/project/cassandra-driver/
|
||||
*/
|
||||
|
||||
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
|
||||
private import semmle.python.frameworks.PEP249
|
||||
|
||||
/**
|
||||
* Provides models for the `cassandra-driver` PyPI package.
|
||||
* See https://pypi.org/project/cassandra-driver/
|
||||
*/
|
||||
private module CassandraDriver {
|
||||
/**
|
||||
* A cassandra cluster session.
|
||||
*
|
||||
* see
|
||||
* - https://docs.datastax.com/en/developer/python-driver/3.25/api/cassandra/cluster/#cassandra.cluster.Cluster.connect
|
||||
* - https://docs.datastax.com/en/developer/python-driver/3.25/api/cassandra/cluster/#cassandra.cluster.Session
|
||||
*/
|
||||
API::Node session() {
|
||||
result =
|
||||
API::moduleImport("cassandra")
|
||||
.getMember("cluster")
|
||||
.getMember("Cluster")
|
||||
.getReturn()
|
||||
.getMember("connect")
|
||||
.getReturn()
|
||||
}
|
||||
|
||||
/**
|
||||
* see https://docs.datastax.com/en/developer/python-driver/3.25/api/cassandra/cluster/#cassandra.cluster.Session.execute
|
||||
*/
|
||||
class CassandraSessionExecuteCall extends SqlExecution::Range, API::CallNode {
|
||||
CassandraSessionExecuteCall() { this = session().getMember("execute").getACall() }
|
||||
|
||||
override DataFlow::Node getSql() { result = this.getParameter(0, "query").asSink() }
|
||||
}
|
||||
|
||||
/**
|
||||
* see https://docs.datastax.com/en/developer/python-driver/3.25/api/cassandra/cluster/#cassandra.cluster.Session.execute_async
|
||||
*/
|
||||
class CassandraSessionExecuteAsyncCall extends SqlConstruction::Range, API::CallNode {
|
||||
CassandraSessionExecuteAsyncCall() { this = session().getMember("execute_async").getACall() }
|
||||
|
||||
override DataFlow::Node getSql() { result = this.getParameter(0, "query").asSink() }
|
||||
}
|
||||
|
||||
/**
|
||||
* see https://docs.datastax.com/en/developer/python-driver/3.25/api/cassandra/cluster/#cassandra.cluster.Session.prepare
|
||||
*/
|
||||
class CassandraSessionPrepareCall extends SqlConstruction::Range, API::CallNode {
|
||||
CassandraSessionPrepareCall() { this = session().getMember("prepare").getACall() }
|
||||
|
||||
override DataFlow::Node getSql() { result = this.getParameter(0, "query").asSink() }
|
||||
}
|
||||
}
|
||||
@@ -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() }
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@@ -22,6 +22,148 @@ 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", "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()
|
||||
@@ -147,7 +289,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 +315,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())]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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() }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -2435,9 +2435,14 @@ private module StdlibPrivate {
|
||||
* against a database.
|
||||
*
|
||||
* See https://devdocs.io/python~3.9/library/sqlite3
|
||||
* https://github.com/python/cpython/blob/3.11/Lib/sqlite3/dbapi2.py
|
||||
*/
|
||||
class Sqlite3 extends PEP249::PEP249ModuleApiNode {
|
||||
Sqlite3() { this = API::moduleImport("sqlite3") }
|
||||
Sqlite3() {
|
||||
this = API::moduleImport("sqlite3")
|
||||
or
|
||||
this = API::moduleImport("sqlite3").getMember("dbapi2")
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
import python
|
||||
import experimental.meta.ConceptsTest
|
||||
27
python/ql/test/library-tests/frameworks/aiosqlite/test.py
Normal file
27
python/ql/test/library-tests/frameworks/aiosqlite/test.py
Normal file
@@ -0,0 +1,27 @@
|
||||
import aiosqlite
|
||||
|
||||
# see https://pypi.org/project/aiosqlite/
|
||||
|
||||
async def test():
|
||||
db = await aiosqlite.connect(...)
|
||||
|
||||
await db.execute("sql") # $ getSql="sql" constructedSql="sql"
|
||||
await db.execute(sql="sql") # $ getSql="sql" constructedSql="sql"
|
||||
|
||||
cursor = await db.cursor()
|
||||
cursor.execute("sql") # $ constructedSql="sql"
|
||||
|
||||
cursor = await db.execute("sql") # $ getSql="sql" constructedSql="sql"
|
||||
cursor.execute("sql") # $ constructedSql="sql"
|
||||
|
||||
async with aiosqlite.connect(...) as db:
|
||||
db.row_factory = aiosqlite.Row
|
||||
async with db.execute("sql") as cursor: # $ getSql="sql" constructedSql="sql"
|
||||
async for row in cursor:
|
||||
print(row['column'])
|
||||
|
||||
# nonstandard
|
||||
await db.execute_insert("sql") # $ getSql="sql" constructedSql="sql"
|
||||
await db.execute_fetchall("sql") # $ getSql="sql" constructedSql="sql"
|
||||
await db.executescript("sql") # $ getSql="sql" constructedSql="sql"
|
||||
await db.executescript(sql_script="sql") # $ getSql="sql" constructedSql="sql"
|
||||
@@ -22,6 +22,9 @@ async def test_connection():
|
||||
finally:
|
||||
await conn.close()
|
||||
|
||||
conn = await asyncpg.connection.connect()
|
||||
conn.execute("sql") # $ mad-sink[sql-injection]="sql"
|
||||
|
||||
|
||||
async def test_prepared_statement():
|
||||
conn = await asyncpg.connect()
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
import python
|
||||
import experimental.meta.ConceptsTest
|
||||
@@ -0,0 +1,12 @@
|
||||
from cassandra.cluster import Cluster
|
||||
|
||||
cluster = Cluster(...)
|
||||
session = cluster.connect()
|
||||
|
||||
session.execute("sql") # $ getSql="sql"
|
||||
|
||||
future = session.execute_async("sql") # $ constructedSql="sql"
|
||||
future.result()
|
||||
|
||||
prepared = session.prepare("sql") # $ constructedSql="sql"
|
||||
session.execute(prepared) # $ SPURIOUS: getSql=prepared
|
||||
@@ -6,3 +6,10 @@ db.execute("some sql", (42,)) # $ getSql="some sql"
|
||||
|
||||
cursor = db.cursor()
|
||||
cursor.execute("some sql", (42,)) # $ getSql="some sql"
|
||||
cursor.executescript("sql") # $ getSql="sql"
|
||||
cursor.executescript(sql_script="sql") # $ getSql="sql"
|
||||
|
||||
import sqlite3.dbapi2
|
||||
conn = sqlite3.dbapi2.connect()
|
||||
cursor = conn.cursor()
|
||||
cursor.execute("some sql") # $ getSql="some sql"
|
||||
|
||||
Reference in New Issue
Block a user