Skip to content

Commit cd332a7

Browse files
committed
Python: model aiopg
1 parent cb8f1b4 commit cd332a7

File tree

4 files changed

+79
-3
lines changed

4 files changed

+79
-3
lines changed

docs/codeql/support/reusables/frameworks.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,7 @@ Python built-in support
183183
pydantic, Utility library
184184
yarl, Utility library
185185
aioch, Database
186+
aiopg, Database
186187
asyncpg, Database
187188
clickhouse-driver, Database
188189
mysql-connector-python, Database

python/ql/lib/semmle/python/Frameworks.qll

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
// `docs/codeql/support/reusables/frameworks.rst`
77
private import semmle.python.frameworks.Aioch
88
private import semmle.python.frameworks.Aiohttp
9+
private import semmle.python.frameworks.Aiopg
910
private import semmle.python.frameworks.Asyncpg
1011
private import semmle.python.frameworks.ClickhouseDriver
1112
private import semmle.python.frameworks.Cryptodome
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/**
2+
* Provides classes modeling security-relevant aspects of the `aiopg` PyPI package.
3+
* See
4+
* - https://aiopg.readthedocs.io/en/stable/index.html
5+
* - https://pypi.org/project/aiopg/
6+
*/
7+
8+
private import python
9+
private import semmle.python.dataflow.new.DataFlow
10+
private import semmle.python.Concepts
11+
private import semmle.python.ApiGraphs
12+
13+
/** Provides models for the `aiopg` PyPI package. */
14+
private module Aiopg {
15+
private import semmle.python.internal.Awaited
16+
17+
/** A `ConectionPool` is created when the result of `aiopg.create_pool()` is awaited. */
18+
API::Node connectionPool() {
19+
result = API::moduleImport("aiopg").getMember("create_pool").getReturn().getAwaited()
20+
}
21+
22+
/**
23+
* A `Connection` is created when
24+
* - the result of `aiopg.connect()` is awaited.
25+
* - the result of calling `aquire` on a `ConnectionPool` is awaited.
26+
*/
27+
API::Node connection() {
28+
result = API::moduleImport("aiopg").getMember("connect").getReturn().getAwaited()
29+
or
30+
result = connectionPool().getMember("acquire").getReturn().getAwaited()
31+
}
32+
33+
/**
34+
* A `Cursor` is created when
35+
* - the result of calling `cursor` on a `ConnectionPool` is awaited.
36+
* - the result of calling `cursor` on a `Connection` is awaited.
37+
*/
38+
API::Node cursor() {
39+
result = connectionPool().getMember("cursor").getReturn().getAwaited()
40+
or
41+
result = connection().getMember("cursor").getReturn().getAwaited()
42+
}
43+
44+
class CursorExecuteCall extends SqlConstruction::Range, DataFlow::CallCfgNode {
45+
CursorExecuteCall() { this = cursor().getMember("execute").getACall() }
46+
47+
override DataFlow::Node getSql() { result in [this.getArg(0), this.getArgByName("operation")] }
48+
}
49+
50+
/**
51+
* This is only needed to connect the argument to the execute call with the subsequnt awaiting.
52+
* It should be obsolete once we have `API::CallNode` available.
53+
*/
54+
private DataFlow::TypeTrackingNode cursorExecuteCall(DataFlow::TypeTracker t, DataFlow::Node sql) {
55+
// cursor created from connection
56+
t.start() and
57+
sql = result.(CursorExecuteCall).getSql()
58+
or
59+
exists(DataFlow::TypeTracker t2 | result = cursorExecuteCall(t2, sql).track(t2, t))
60+
}
61+
62+
DataFlow::Node cursorExecuteCall(DataFlow::Node sql) {
63+
cursorExecuteCall(DataFlow::TypeTracker::end(), sql).flowsTo(result)
64+
}
65+
66+
/** Awaiting the result of calling `execute` executes the query. */
67+
class AwaitedCursorExecuteCall extends SqlExecution::Range {
68+
DataFlow::Node sql;
69+
70+
AwaitedCursorExecuteCall() { this = awaited(cursorExecuteCall(sql)) }
71+
72+
override DataFlow::Node getSql() { result = sql }
73+
}
74+
}

python/ql/test/library-tests/frameworks/aiopg/test.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,15 @@ async def test_cursor():
55
# Create connection directly
66
conn = await aiopg.connect()
77
cur = await conn.cursor()
8-
await cur.execute("sql") # $ MISSING: getSql="sql"
8+
await cur.execute("sql") # $ getSql="sql" constructedSql="sql"
99

1010
# Create connection via pool
1111
async with aiopg.create_pool() as pool:
1212
# Create Cursor via Connection
1313
async with pool.acquire() as conn:
1414
cur = await conn.cursor()
15-
await cur.execute("sql") # $ MISSING: getSql="sql"
15+
await cur.execute("sql") # $ getSql="sql" constructedSql="sql"
1616

1717
# Create Cursor directly
1818
async with pool.cursor() as cur:
19-
await cur.execute("sql") # $ MISSING: getSql="sql"
19+
await cur.execute("sql") # $ getSql="sql" constructedSql="sql"

0 commit comments

Comments
 (0)