Skip to content

Commit dba42d6

Browse files
committed
Python: Model executemany on PEP-249 DB APIs
Note: I kept the modeling using the old approach with type-trackers instead of `DataFlow::MethodCallNode`. I would like a meta query for DCA to show sinks before doing this, so I can be absolutely sure we don't loose out on any important sinks on this... so will postpone this work to a small one-off task (added to my todo list).
1 parent 669f4f3 commit dba42d6

File tree

4 files changed

+29
-1
lines changed

4 files changed

+29
-1
lines changed

python/ql/lib/semmle/python/frameworks/internal/PEP249Impl.qll

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,4 +178,27 @@ module PEP249 {
178178

179179
override DataFlow::Node getSql() { result in [this.getArg(0), this.getArgByName("sql")] }
180180
}
181+
182+
private DataFlow::TypeTrackingNode executemany(DataFlow::TypeTracker t) {
183+
t.startInAttr("executemany") and
184+
result in [Cursor::instance(), Connection::instance()]
185+
or
186+
exists(DataFlow::TypeTracker t2 | result = executemany(t2).track(t2, t))
187+
}
188+
189+
private DataFlow::Node executemany() { executemany(DataFlow::TypeTracker::end()).flowsTo(result) }
190+
191+
/**
192+
* A call to the `executemany` method on a cursor or a connection.
193+
*
194+
* See https://peps.python.org/pep-0249/#executemany
195+
*
196+
* Note: While `executemany` method on a connection is not part of PEP249, if it is used, we
197+
* recognize it as an alias for constructing a cursor and calling `executemany` on it.
198+
*/
199+
private class ExecutemanyCall extends SqlExecution::Range, DataFlow::CallCfgNode {
200+
ExecutemanyCall() { this.getFunction() = executemany() }
201+
202+
override DataFlow::Node getSql() { result in [this.getArg(0), this.getArgByName("sql")] }
203+
}
181204
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: minorAnalysis
3+
---
4+
* Added model of `executemany` calls on PEP-249 compliant Database APIs, resulting in additional sinks for `py/sql-injection`.

python/ql/test/library-tests/frameworks/pymssql/pep249.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@
33

44
cursor = connection.cursor()
55
cursor.execute("some sql", (42,)) # $ getSql="some sql"
6-
cursor.executemany("some sql", [(42,)]) # $ MISSING: getSql="some sql"
6+
cursor.executemany("some sql", [(42,)]) # $ getSql="some sql"

python/ql/test/library-tests/frameworks/pymysql/pep249.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@
33

44
cursor = connection.cursor()
55
cursor.execute("some sql", (42,)) # $ getSql="some sql"
6+
cursor.executemany("some sql", [(42,)]) # $ getSql="some sql"

0 commit comments

Comments
 (0)