Skip to content

Commit b3f1031

Browse files
authored
Merge pull request github#10752 from RasmusWL/pymssql
Python: DB Modeling: Add `pymssql` and `executemany` in general
2 parents 8523d21 + ac30cfa commit b3f1031

File tree

11 files changed

+85
-16
lines changed

11 files changed

+85
-16
lines changed

docs/codeql/support/reusables/frameworks.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,8 @@ and the CodeQL library pack ``codeql/python-all`` (`changelog <https://github.co
226226
MySQL-python, Database
227227
mysqlclient, Database
228228
psycopg2, Database
229+
pymssql, Database
230+
PyMySQL, Database
229231
sqlite3, Database
230232
Flask-SQLAlchemy, Database ORM
231233
peewee, Database ORM

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ private import semmle.python.frameworks.Peewee
3737
private import semmle.python.frameworks.Psycopg2
3838
private import semmle.python.frameworks.Pycurl
3939
private import semmle.python.frameworks.Pydantic
40+
private import semmle.python.frameworks.Pymssql
4041
private import semmle.python.frameworks.PyMySQL
4142
private import semmle.python.frameworks.Requests
4243
private import semmle.python.frameworks.RestFramework
@@ -51,6 +52,6 @@ private import semmle.python.frameworks.Tornado
5152
private import semmle.python.frameworks.Twisted
5253
private import semmle.python.frameworks.Ujson
5354
private import semmle.python.frameworks.Urllib3
55+
private import semmle.python.frameworks.Xmltodict
5456
private import semmle.python.frameworks.Yaml
5557
private import semmle.python.frameworks.Yarl
56-
private import semmle.python.frameworks.Xmltodict

python/ql/lib/semmle/python/frameworks/Mysql.qll

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,11 @@ private import semmle.python.frameworks.PEP249
2121
* - https://dev.mysql.com/doc/connector-python/en/connector-python-example-connecting.html
2222
*/
2323
private module Mysql {
24-
// ---------------------------------------------------------------------------
25-
// mysql
26-
// ---------------------------------------------------------------------------
27-
/** Provides models for the `mysql` module. */
28-
module MysqlMod {
29-
/**
30-
* The mysql.connector module
31-
* See https://dev.mysql.com/doc/connector-python/en/connector-python-example-connecting.html
32-
*/
33-
class MysqlConnector extends PEP249::PEP249ModuleApiNode {
34-
MysqlConnector() { this = API::moduleImport("mysql").getMember("connector") }
35-
}
24+
/**
25+
* The mysql.connector module
26+
* See https://dev.mysql.com/doc/connector-python/en/connector-python-example-connecting.html
27+
*/
28+
class MysqlConnector extends PEP249::PEP249ModuleApiNode {
29+
MysqlConnector() { this = API::moduleImport("mysql").getMember("connector") }
3630
}
3731
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/**
2+
* Provides classes modeling security-relevant aspects of the `pymssql` PyPI package.
3+
* See https://pypi.org/project/pymssql/
4+
*/
5+
6+
private import python
7+
private import semmle.python.dataflow.new.DataFlow
8+
private import semmle.python.dataflow.new.RemoteFlowSources
9+
private import semmle.python.Concepts
10+
private import semmle.python.ApiGraphs
11+
private import semmle.python.frameworks.PEP249
12+
13+
/**
14+
* Provides models for the `pymssql` PyPI package.
15+
* See https://pypi.org/project/pymssql/
16+
*/
17+
private module Pymssql {
18+
/**
19+
* A model of `pymssql` as a module that implements PEP 249, providing ways to execute SQL statements
20+
* against a database.
21+
*/
22+
class PymssqlPEP249 extends PEP249::PEP249ModuleApiNode {
23+
PymssqlPEP249() { this = API::moduleImport("pymssql") }
24+
}
25+
}

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

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ module PEP249 {
146146
* Note: while `execute` method on a connection is not part of PEP249, if it is used, we
147147
* recognize it as an alias for constructing a cursor and calling `execute` on it.
148148
*
149-
* See https://www.python.org/dev/peps/pep-0249/#id15.
149+
* See https://peps.python.org/pep-0249/#execute.
150150
*/
151151
private DataFlow::TypeTrackingNode execute(DataFlow::TypeTracker t) {
152152
t.startInAttr("execute") and
@@ -161,14 +161,44 @@ module PEP249 {
161161
* Note: while `execute` method on a connection is not part of PEP249, if it is used, we
162162
* recognize it as an alias for constructing a cursor and calling `execute` on it.
163163
*
164-
* See https://www.python.org/dev/peps/pep-0249/#id15.
164+
* See https://peps.python.org/pep-0249/#execute.
165165
*/
166166
DataFlow::Node execute() { execute(DataFlow::TypeTracker::end()).flowsTo(result) }
167167

168-
/** A call to the `execute` method on a cursor (or on a connection). */
168+
/**
169+
* A call to the `execute` method on a cursor or a connection.
170+
*
171+
* See https://peps.python.org/pep-0249/#execute
172+
*
173+
* Note: While `execute` method on a connection is not part of PEP249, if it is used, we
174+
* recognize it as an alias for constructing a cursor and calling `execute` on it.
175+
*/
169176
private class ExecuteCall extends SqlExecution::Range, DataFlow::CallCfgNode {
170177
ExecuteCall() { this.getFunction() = execute() }
171178

172179
override DataFlow::Node getSql() { result in [this.getArg(0), this.getArgByName("sql")] }
173180
}
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+
}
174204
}
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`.
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 `pymssql` PyPI package as a SQL interface following PEP249, resulting in additional sinks for `py/sql-injection`.

python/ql/test/library-tests/frameworks/pymssql/ConceptsTest.expected

Whitespace-only changes.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
import python
2+
import experimental.meta.ConceptsTest
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import pymssql
2+
connection = pymssql.connect(host="localhost", user="user", password="passwd")
3+
4+
cursor = connection.cursor()
5+
cursor.execute("some sql", (42,)) # $ getSql="some sql"
6+
cursor.executemany("some sql", [(42,)]) # $ getSql="some sql"

0 commit comments

Comments
 (0)