Skip to content

Commit 50a4345

Browse files
authored
Merge pull request github#6161 from RasmusWL/peewee-modeling
Python: Add modeling of `peewee`
2 parents 92d1bec + 3231ae7 commit 50a4345

File tree

19 files changed

+413
-104
lines changed

19 files changed

+413
-104
lines changed

docs/codeql/support/reusables/frameworks.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,7 @@ Python built-in support
176176
mysqlclient, Database
177177
psycopg2, Database
178178
sqlite3, Database
179+
peewee, Database ORM
179180
cryptography, Cryptography library
180181
pycryptodome, Cryptography library
181182
pycryptodomex, Cryptography library
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
lgtm,codescanning
2+
* Added modeling of raw SQL execution from the PyPI package `peewee`.

python/ql/src/semmle/python/Frameworks.qll

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ private import semmle.python.frameworks.Rsa
2626
private import semmle.python.frameworks.Simplejson
2727
private import semmle.python.frameworks.Stdlib
2828
private import semmle.python.frameworks.Tornado
29+
private import semmle.python.frameworks.Peewee
2930
private import semmle.python.frameworks.Twisted
3031
private import semmle.python.frameworks.Ujson
3132
private import semmle.python.frameworks.Yaml

python/ql/src/semmle/python/frameworks/Aioch.qll

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
private import python
99
private import semmle.python.Concepts
1010
private import semmle.python.ApiGraphs
11-
private import semmle.python.frameworks.PEP249
1211
private import semmle.python.frameworks.ClickhouseDriver
1312

1413
/**

python/ql/src/semmle/python/frameworks/ClickhouseDriver.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ module ClickhouseDriver {
2323
* `clickhouse_driver` implements PEP249,
2424
* providing ways to execute SQL statements against a database.
2525
*/
26-
class ClickHouseDriverPEP249 extends PEP249ModuleApiNode {
26+
class ClickHouseDriverPEP249 extends PEP249::PEP249ModuleApiNode {
2727
ClickHouseDriverPEP249() { this = API::moduleImport("clickhouse_driver") }
2828
}
2929

python/ql/src/semmle/python/frameworks/Django.qll

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,7 @@ private module PrivateDjango {
313313
/**
314314
* `django.db` implements PEP249, providing ways to execute SQL statements against a database.
315315
*/
316-
private class DjangoDb extends PEP249ModuleApiNode {
316+
private class DjangoDb extends PEP249::PEP249ModuleApiNode {
317317
DjangoDb() { this = API::moduleImport("django").getMember("db") }
318318
}
319319

@@ -322,7 +322,7 @@ private module PrivateDjango {
322322
/** Gets a reference to the `django.db.connection` object. */
323323
API::Node connection() { result = db().getMember("connection") }
324324

325-
class DjangoDbConnection extends Connection::InstanceSource {
325+
class DjangoDbConnection extends PEP249::Connection::InstanceSource {
326326
DjangoDbConnection() { this = connection().getAUse() }
327327
}
328328

python/ql/src/semmle/python/frameworks/MySQLdb.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ private module MySQLdb {
2828
// MySQLdb
2929
// ---------------------------------------------------------------------------
3030
/** MySQLdb implements PEP 249, providing ways to execute SQL statements against a database. */
31-
class MySQLdb extends PEP249ModuleApiNode {
31+
class MySQLdb extends PEP249::PEP249ModuleApiNode {
3232
MySQLdb() { this = API::moduleImport("MySQLdb") }
3333
}
3434
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ private module Mysql {
3030
* The mysql.connector module
3131
* See https://dev.mysql.com/doc/connector-python/en/connector-python-example-connecting.html
3232
*/
33-
class MysqlConnector extends PEP249ModuleApiNode {
33+
class MysqlConnector extends PEP249::PEP249ModuleApiNode {
3434
MysqlConnector() { this = API::moduleImport("mysql").getMember("connector") }
3535
}
3636
}

python/ql/src/semmle/python/frameworks/PEP249.qll

Lines changed: 12 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -8,119 +8,36 @@ private import semmle.python.dataflow.new.DataFlow
88
private import semmle.python.dataflow.new.RemoteFlowSources
99
private import semmle.python.Concepts
1010
private import semmle.python.ApiGraphs
11+
import semmle.python.frameworks.internal.PEP249Impl
1112

1213
/**
1314
* A module implementing PEP 249. Extend this class for implementations.
1415
*
15-
* DEPRECATED: Extend `PEP249ModuleApiNode` instead.
16+
* DEPRECATED: Extend `PEP249::PEP249ModuleApiNode` instead.
1617
*/
1718
abstract deprecated class PEP249Module extends DataFlow::Node { }
1819

1920
/**
20-
* An abstract class encompassing API graph nodes that implement PEP 249.
21-
* Extend this class for implementations.
21+
* DEPRECATED: Use `PEP249::PEP249ModuleApiNode` instead.
2222
*/
23-
abstract class PEP249ModuleApiNode extends API::Node {
24-
/** Gets a string representation of this element. */
25-
override string toString() { result = this.(API::Node).toString() }
26-
}
27-
28-
/** Gets a reference to a connect call. */
29-
DataFlow::Node connect() { result = any(PEP249ModuleApiNode a).getMember("connect").getAUse() }
23+
deprecated class PEP249ModuleApiNode = PEP249::PEP249ModuleApiNode;
3024

3125
/**
32-
* Provides models for the `db.Connection` class
33-
*
34-
* See https://www.python.org/dev/peps/pep-0249/#connection-objects.
26+
* DEPRECATED: Use `PEP249::Connection` instead.
3527
*/
36-
module Connection {
37-
/**
38-
* A source of instances of `db.Connection`, extend this class to model new instances.
39-
*
40-
* This can include instantiations of the class, return values from function
41-
* calls, or a special parameter that will be set when functions are called by external
42-
* libraries.
43-
*
44-
* Use the predicate `Connection::instance()` to get references to instances of `db.Connection`.
45-
*
46-
* Extend this class if the module implementing PEP 249 offers more direct ways to obtain
47-
* a connection than going through `connect`.
48-
*/
49-
abstract class InstanceSource extends DataFlow::Node { }
50-
51-
/** A direct instantiation of `db.Connection`. */
52-
private class ClassInstantiation extends InstanceSource, DataFlow::CallCfgNode {
53-
ClassInstantiation() { this.getFunction() = connect() }
54-
}
55-
56-
/** Gets a reference to an instance of `db.Connection`. */
57-
private DataFlow::TypeTrackingNode instance(DataFlow::TypeTracker t) {
58-
t.start() and
59-
result instanceof InstanceSource
60-
or
61-
exists(DataFlow::TypeTracker t2 | result = instance(t2).track(t2, t))
62-
}
63-
64-
/** Gets a reference to an instance of `db.Connection`. */
65-
DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }
66-
}
28+
deprecated module Connection = PEP249::Connection;
6729

6830
/**
69-
* Provides models for the `cursor` method on a connection.
70-
* See https://www.python.org/dev/peps/pep-0249/#cursor.
31+
* DEPRECATED: Use `PEP249::Cursor` instead.
7132
*/
72-
module cursor {
73-
/** Gets a reference to the `cursor` method on a connection. */
74-
private DataFlow::TypeTrackingNode methodRef(DataFlow::TypeTracker t) {
75-
t.startInAttr("cursor") and
76-
result = Connection::instance()
77-
or
78-
exists(DataFlow::TypeTracker t2 | result = methodRef(t2).track(t2, t))
79-
}
80-
81-
/** Gets a reference to the `cursor` method on a connection. */
82-
DataFlow::Node methodRef() { methodRef(DataFlow::TypeTracker::end()).flowsTo(result) }
83-
84-
/** Gets a reference to a result of calling the `cursor` method on a connection. */
85-
private DataFlow::TypeTrackingNode methodResult(DataFlow::TypeTracker t) {
86-
t.start() and
87-
result.asCfgNode().(CallNode).getFunction() = methodRef().asCfgNode()
88-
or
89-
exists(DataFlow::TypeTracker t2 | result = methodResult(t2).track(t2, t))
90-
}
91-
92-
/** Gets a reference to a result of calling the `cursor` method on a connection. */
93-
DataFlow::Node methodResult() { methodResult(DataFlow::TypeTracker::end()).flowsTo(result) }
94-
}
33+
deprecated module cursor = PEP249::Cursor;
9534

9635
/**
97-
* Gets a reference to the `execute` method on a cursor (or on a connection).
98-
*
99-
* Note: while `execute` method on a connection is not part of PEP249, if it is used, we
100-
* recognize it as an alias for constructing a cursor and calling `execute` on it.
101-
*
102-
* See https://www.python.org/dev/peps/pep-0249/#id15.
36+
* DEPRECATED: Use `PEP249::execute` instead.
10337
*/
104-
private DataFlow::TypeTrackingNode execute(DataFlow::TypeTracker t) {
105-
t.startInAttr("execute") and
106-
result in [cursor::methodResult(), Connection::instance()]
107-
or
108-
exists(DataFlow::TypeTracker t2 | result = execute(t2).track(t2, t))
109-
}
38+
deprecated predicate execute = PEP249::execute/0;
11039

11140
/**
112-
* Gets a reference to the `execute` method on a cursor (or on a connection).
113-
*
114-
* Note: while `execute` method on a connection is not part of PEP249, if it is used, we
115-
* recognize it as an alias for constructing a cursor and calling `execute` on it.
116-
*
117-
* See https://www.python.org/dev/peps/pep-0249/#id15.
41+
* DEPRECATED: Use `PEP249::connect` instead.
11842
*/
119-
DataFlow::Node execute() { execute(DataFlow::TypeTracker::end()).flowsTo(result) }
120-
121-
/** A call to the `execute` method on a cursor (or on a connection). */
122-
private class ExecuteCall extends SqlExecution::Range, DataFlow::CallCfgNode {
123-
ExecuteCall() { this.getFunction() = execute() }
124-
125-
override DataFlow::Node getSql() { result in [this.getArg(0), this.getArgByName("sql")] }
126-
}
43+
deprecated predicate connect = PEP249::connect/0;
Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
/**
2+
* Provides classes modeling security-relevant aspects of the `peewee` PyPI package.
3+
* See
4+
* - https://pypi.org/project/peewee/
5+
* - https://docs.peewee-orm.com/en/latest/index.html
6+
*/
7+
8+
private import python
9+
private import semmle.python.dataflow.new.DataFlow
10+
private import semmle.python.dataflow.new.TaintTracking
11+
private import semmle.python.Concepts
12+
private import semmle.python.ApiGraphs
13+
private import semmle.python.frameworks.PEP249
14+
15+
/**
16+
* Provides models for the `peewee` PyPI package.
17+
* See
18+
* - https://pypi.org/project/peewee/
19+
* - https://docs.peewee-orm.com/en/latest/index.html
20+
*/
21+
private module Peewee {
22+
/** Provides models for the `peewee.Database` class and subclasses. */
23+
module Database {
24+
/** Gets a reference to the `peewee.Database` class or any subclass. */
25+
API::Node subclassRef() {
26+
result = API::moduleImport("peewee").getMember("Database").getASubclass*()
27+
or
28+
// known subclasses
29+
result =
30+
API::moduleImport("peewee")
31+
.getMember(["SqliteDatabase", "MySQLDatabase", "PostgresqlDatabase"])
32+
.getASubclass*()
33+
or
34+
// Ohter known subclasses, semi auto generated by using
35+
// ```codeql
36+
// class DBClass extends Class, SelfRefMixin {
37+
// DBClass() {
38+
// exists(this.getLocation().getFile().getRelativePath()) and
39+
// this.getName().matches("%Database") and
40+
// this.getABase().(Name).getId().matches("%Database")
41+
// }
42+
// }
43+
//
44+
// from DBClass dbClass, Module mod
45+
// where
46+
// dbClass.getScope() = mod
47+
// select mod.getName()+ "." + dbClass.getName()
48+
// ```
49+
result =
50+
API::moduleImport("playhouse")
51+
.getMember("apsw_ext")
52+
.getMember("APSWDatabase")
53+
.getASubclass*()
54+
or
55+
result =
56+
API::moduleImport("playhouse")
57+
.getMember("cockroachdb")
58+
.getMember("CockroachDatabase")
59+
.getASubclass*()
60+
or
61+
result =
62+
API::moduleImport("playhouse")
63+
.getMember("cockroachdb")
64+
.getMember("PooledCockroachDatabase")
65+
.getASubclass*()
66+
or
67+
result =
68+
API::moduleImport("playhouse")
69+
.getMember("mysql_ext")
70+
.getMember("MySQLConnectorDatabase")
71+
.getASubclass*()
72+
or
73+
result =
74+
API::moduleImport("playhouse")
75+
.getMember("pool")
76+
.getMember("PooledCSqliteExtDatabase")
77+
.getASubclass*()
78+
or
79+
result =
80+
API::moduleImport("playhouse")
81+
.getMember("pool")
82+
.getMember("PooledMySQLDatabase")
83+
.getASubclass*()
84+
or
85+
result =
86+
API::moduleImport("playhouse")
87+
.getMember("pool")
88+
.getMember("PooledPostgresqlDatabase")
89+
.getASubclass*()
90+
or
91+
result =
92+
API::moduleImport("playhouse")
93+
.getMember("pool")
94+
.getMember("PooledPostgresqlExtDatabase")
95+
.getASubclass*()
96+
or
97+
result =
98+
API::moduleImport("playhouse")
99+
.getMember("pool")
100+
.getMember("PooledSqliteDatabase")
101+
.getASubclass*()
102+
or
103+
result =
104+
API::moduleImport("playhouse")
105+
.getMember("pool")
106+
.getMember("PooledSqliteExtDatabase")
107+
.getASubclass*()
108+
or
109+
result =
110+
API::moduleImport("playhouse")
111+
.getMember("pool")
112+
.getMember("_PooledPostgresqlDatabase")
113+
.getASubclass*()
114+
or
115+
result =
116+
API::moduleImport("playhouse")
117+
.getMember("pool")
118+
.getMember("_PooledSqliteDatabase")
119+
.getASubclass*()
120+
or
121+
result =
122+
API::moduleImport("playhouse")
123+
.getMember("postgres_ext")
124+
.getMember("PostgresqlExtDatabase")
125+
.getASubclass*()
126+
or
127+
result =
128+
API::moduleImport("playhouse")
129+
.getMember("sqlcipher_ext")
130+
.getMember("SqlCipherDatabase")
131+
.getASubclass*()
132+
or
133+
result =
134+
API::moduleImport("playhouse")
135+
.getMember("sqlcipher_ext")
136+
.getMember("SqlCipherExtDatabase")
137+
.getASubclass*()
138+
or
139+
result =
140+
API::moduleImport("playhouse")
141+
.getMember("sqlite_ext")
142+
.getMember("CSqliteExtDatabase")
143+
.getASubclass*()
144+
or
145+
result =
146+
API::moduleImport("playhouse")
147+
.getMember("sqlite_ext")
148+
.getMember("SqliteExtDatabase")
149+
.getASubclass*()
150+
or
151+
result =
152+
API::moduleImport("playhouse")
153+
.getMember("sqliteq")
154+
.getMember("SqliteQueueDatabase")
155+
.getASubclass*()
156+
}
157+
158+
/** Gets a reference to an instance of `peewee.Database` or any subclass. */
159+
API::Node instance() { result = subclassRef().getReturn() }
160+
}
161+
162+
/**
163+
* A call to the `connection` method on a `peewee.Database` instance.
164+
* https://docs.peewee-orm.com/en/latest/peewee/api.html#Database.connection.
165+
*/
166+
class PeeweeDatabaseConnectionCall extends PEP249::Connection::InstanceSource,
167+
DataFlow::CallCfgNode {
168+
PeeweeDatabaseConnectionCall() {
169+
this = Database::instance().getMember("connection").getACall()
170+
}
171+
}
172+
173+
/**
174+
* A call to the `cursor` method on a `peewee.Database` instance.
175+
* https://docs.peewee-orm.com/en/latest/peewee/api.html#Database.cursor.
176+
*/
177+
class PeeweeDatabaseCursorCall extends PEP249::Cursor::InstanceSource, DataFlow::CallCfgNode {
178+
PeeweeDatabaseCursorCall() { this = Database::instance().getMember("cursor").getACall() }
179+
}
180+
181+
/**
182+
* A call to the `execute_sql` method on a `peewee.Database` instance.
183+
* See https://docs.peewee-orm.com/en/latest/peewee/api.html#Database.execute_sql.
184+
*/
185+
class PeeweeDatabaseExecuteSqlCall extends SqlExecution::Range, DataFlow::CallCfgNode {
186+
PeeweeDatabaseExecuteSqlCall() {
187+
this = Database::instance().getMember("execute_sql").getACall()
188+
}
189+
190+
override DataFlow::Node getSql() { result in [this.getArg(0), this.getArgByName("sql")] }
191+
}
192+
}

0 commit comments

Comments
 (0)