Merge pull request #12636 from RasmusWL/sql-modeling

Python: Some more SQL modeling
This commit is contained in:
Rasmus Wriedt Larsen
2023-03-27 15:52:30 +02:00
committed by GitHub
20 changed files with 361 additions and 89 deletions

View File

@@ -223,7 +223,9 @@ and the CodeQL library pack ``codeql/python-all`` (`changelog <https://github.co
aioch, Database aioch, Database
aiomysql, Database aiomysql, Database
aiopg, Database aiopg, Database
aiosqlite, Database
asyncpg, Database asyncpg, Database
cassandra-driver, Database
clickhouse-driver, Database clickhouse-driver, Database
cx_Oracle, Database cx_Oracle, Database
mysql-connector-python, Database mysql-connector-python, Database
@@ -233,9 +235,9 @@ and the CodeQL library pack ``codeql/python-all`` (`changelog <https://github.co
oracledb, Database oracledb, Database
phoenixdb, Database phoenixdb, Database
psycopg2, Database psycopg2, Database
pyodbc, Database
pymssql, Database pymssql, Database
PyMySQL, Database PyMySQL, Database
pyodbc, Database
sqlite3, Database sqlite3, Database
Flask-SQLAlchemy, Database ORM Flask-SQLAlchemy, Database ORM
peewee, 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 Ruby on Rails, Web framework
rubyzip, Compression library rubyzip, Compression library
typhoeus, HTTP client typhoeus, HTTP client

View File

@@ -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()`.

View File

@@ -3,12 +3,14 @@
*/ */
// If you add modeling of a new framework/library, remember to add it to the docs in // 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.Aioch
private import semmle.python.frameworks.Aiohttp private import semmle.python.frameworks.Aiohttp
private import semmle.python.frameworks.Aiomysql private import semmle.python.frameworks.Aiomysql
private import semmle.python.frameworks.Aiosqlite
private import semmle.python.frameworks.Aiopg private import semmle.python.frameworks.Aiopg
private import semmle.python.frameworks.Asyncpg private import semmle.python.frameworks.Asyncpg
private import semmle.python.frameworks.CassandraDriver
private import semmle.python.frameworks.ClickhouseDriver private import semmle.python.frameworks.ClickhouseDriver
private import semmle.python.frameworks.Cryptodome private import semmle.python.frameworks.Cryptodome
private import semmle.python.frameworks.Cryptography private import semmle.python.frameworks.Cryptography

View File

@@ -9,11 +9,10 @@ private import python
private import semmle.python.dataflow.new.DataFlow private import semmle.python.dataflow.new.DataFlow
private import semmle.python.Concepts private import semmle.python.Concepts
private import semmle.python.ApiGraphs private import semmle.python.ApiGraphs
private import semmle.python.frameworks.PEP249
/** Provides models for the `aiomysql` PyPI package. */ /** Provides models for the `aiomysql` PyPI package. */
private module Aiomysql { private module Aiomysql {
private import semmle.python.internal.Awaited
/** /**
* Gets a `ConnectionPool` that is created when the result of `aiomysql.create_pool()` is 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 * 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 `aiomysql.connect()` is awaited.
* - the result of calling `acquire` on a `ConnectionPool` 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() { class AiomysqlConnection extends PEP249::AsyncDatabaseConnection {
result = API::moduleImport("aiomysql").getMember("connect").getReturn().getAwaited() AiomysqlConnection() {
or this = API::moduleImport("aiomysql").getMember("connect").getReturn().getAwaited()
result = connectionPool().getMember("acquire").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 `ConnectionPool` is awaited.
* - the result of calling `cursor` on a `Connection` is awaited. * See
* See https://aiomysql.readthedocs.io/en/stable/cursors.html * - https://aiomysql.readthedocs.io/en/stable/pool.html##Pool.cursor
*/ */
API::Node cursor() { class AiomysqlCursor extends PEP249::AsyncDatabaseCursor {
result = connectionPool().getMember("cursor").getReturn().getAwaited() AiomysqlCursor() { this = 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() }
} }
/** /**

View File

@@ -9,11 +9,10 @@ private import python
private import semmle.python.dataflow.new.DataFlow private import semmle.python.dataflow.new.DataFlow
private import semmle.python.Concepts private import semmle.python.Concepts
private import semmle.python.ApiGraphs private import semmle.python.ApiGraphs
private import semmle.python.frameworks.PEP249
/** Provides models for the `aiopg` PyPI package. */ /** Provides models for the `aiopg` PyPI package. */
private module Aiopg { private module Aiopg {
private import semmle.python.internal.Awaited
/** /**
* Gets a `ConnectionPool` that is created when the result of `aiopg.create_pool()` is 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 * 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 `aiopg.connect()` is awaited.
* - the result of calling `acquire` on a `ConnectionPool` 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() { class AiopgConnection extends PEP249::AsyncDatabaseConnection {
result = API::moduleImport("aiopg").getMember("connect").getReturn().getAwaited() AiopgConnection() {
or this = API::moduleImport("aiopg").getMember("connect").getReturn().getAwaited()
result = connectionPool().getMember("acquire").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 `ConnectionPool` is awaited.
* - the result of calling `cursor` on a `Connection` is awaited. * See
* See https://aiopg.readthedocs.io/en/stable/core.html#cursor * - https://aiopg.readthedocs.io/en/stable/core.html#aiopg.Pool.cursor
*/ */
API::Node cursor() { class AiopgCursor extends PEP249::AsyncDatabaseCursor {
result = connectionPool().getMember("cursor").getReturn().getAwaited() AiopgCursor() { this = 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() }
} }
/** /**

View 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()
}
}
}

View File

@@ -22,6 +22,7 @@ private module Asyncpg {
// * - the result of `asyncpg.connect()` is awaited. // * - the result of `asyncpg.connect()` is awaited.
// * - the result of calling `acquire` on a `ConnectionPool` is awaited. // * - the result of calling `acquire` on a `ConnectionPool` is awaited.
"asyncpg.Connection;asyncpg;Member[connect].ReturnValue.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", "asyncpg.Connection;asyncpg.ConnectionPool;Member[acquire].ReturnValue.Awaited",
// Creating an internal `~Connection` type that contains both `Connection` and `ConnectionPool`. // Creating an internal `~Connection` type that contains both `Connection` and `ConnectionPool`.
"asyncpg.~Connection;asyncpg.Connection;", // "asyncpg.~Connection;asyncpg.Connection;", //

View 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() }
}
}

View File

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

View File

@@ -22,6 +22,148 @@ module PEP249 {
override string toString() { result = this.(API::Node).toString() } 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. */ /** Gets a reference to the `connect` function of a module that implements PEP 249. */
DataFlow::Node connect() { DataFlow::Node connect() {
result = any(PEP249ModuleApiNode a).getMember("connect").getAValueReachableFromSource() 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. * recognize it as an alias for constructing a cursor and calling `execute` on it.
*/ */
private class ExecuteCall extends SqlExecution::Range, DataFlow::CallCfgNode { 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")] } 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. * recognize it as an alias for constructing a cursor and calling `executemany` on it.
*/ */
private class ExecutemanyCall extends SqlExecution::Range, DataFlow::CallCfgNode { 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. * A call to the `connection` method on a `peewee.Database` instance.
* https://docs.peewee-orm.com/en/latest/peewee/api.html#Database.connection. * https://docs.peewee-orm.com/en/latest/peewee/api.html#Database.connection.
*/ */
class PeeweeDatabaseConnectionCall extends PEP249::Connection::InstanceSource, class PeeweeDatabaseConnectionCall extends PEP249::DatabaseConnection {
DataFlow::CallCfgNode
{
PeeweeDatabaseConnectionCall() { 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. * A call to the `cursor` method on a `peewee.Database` instance.
* https://docs.peewee-orm.com/en/latest/peewee/api.html#Database.cursor. * https://docs.peewee-orm.com/en/latest/peewee/api.html#Database.cursor.
*/ */
class PeeweeDatabaseCursorCall extends PEP249::Cursor::InstanceSource, DataFlow::CallCfgNode { class PeeweeDatabaseCursorCall extends PEP249::DatabaseCursor {
PeeweeDatabaseCursorCall() { this = Database::instance().getMember("cursor").getACall() } PeeweeDatabaseCursorCall() { this = Database::instance().getMember("cursor").getReturn() }
} }
/** /**

View File

@@ -2435,9 +2435,14 @@ private module StdlibPrivate {
* against a database. * against a database.
* *
* See https://devdocs.io/python~3.9/library/sqlite3 * 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 { class Sqlite3 extends PEP249::PEP249ModuleApiNode {
Sqlite3() { this = API::moduleImport("sqlite3") } Sqlite3() {
this = API::moduleImport("sqlite3")
or
this = API::moduleImport("sqlite3").getMember("dbapi2")
}
} }
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------

View File

@@ -0,0 +1,2 @@
import python
import experimental.meta.ConceptsTest

View 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"

View File

@@ -22,6 +22,9 @@ async def test_connection():
finally: finally:
await conn.close() await conn.close()
conn = await asyncpg.connection.connect()
conn.execute("sql") # $ mad-sink[sql-injection]="sql"
async def test_prepared_statement(): async def test_prepared_statement():
conn = await asyncpg.connect() conn = await asyncpg.connect()

View File

@@ -0,0 +1,2 @@
import python
import experimental.meta.ConceptsTest

View File

@@ -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

View File

@@ -6,3 +6,10 @@ db.execute("some sql", (42,)) # $ getSql="some sql"
cursor = db.cursor() cursor = db.cursor()
cursor.execute("some sql", (42,)) # $ getSql="some sql" 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"