mirror of
https://github.com/github/codeql.git
synced 2025-12-21 03:06:31 +01:00
Merge pull request #4588 from yoff/python-pep-249
Python: Model PEP 249
This commit is contained in:
@@ -7,5 +7,7 @@ private import experimental.semmle.python.frameworks.Django
|
||||
private import experimental.semmle.python.frameworks.Fabric
|
||||
private import experimental.semmle.python.frameworks.Flask
|
||||
private import experimental.semmle.python.frameworks.Invoke
|
||||
private import experimental.semmle.python.frameworks.MySQLdb
|
||||
private import experimental.semmle.python.frameworks.MysqlConnectorPython
|
||||
private import experimental.semmle.python.frameworks.Stdlib
|
||||
private import experimental.semmle.python.frameworks.Yaml
|
||||
|
||||
@@ -8,6 +8,7 @@ private import experimental.dataflow.DataFlow
|
||||
private import experimental.dataflow.RemoteFlowSources
|
||||
private import experimental.dataflow.TaintTracking
|
||||
private import experimental.semmle.python.Concepts
|
||||
private import experimental.semmle.python.frameworks.PEP249
|
||||
private import semmle.python.regex
|
||||
|
||||
/**
|
||||
@@ -76,6 +77,10 @@ private module Django {
|
||||
/** Gets a reference to the `django.db` module. */
|
||||
DataFlow::Node db() { result = django_attr("db") }
|
||||
|
||||
class DjangoDb extends PEP249Module {
|
||||
DjangoDb() { this = db() }
|
||||
}
|
||||
|
||||
/** Provides models for the `django.db` module. */
|
||||
module db {
|
||||
/** Gets a reference to the `django.db.connection` object. */
|
||||
@@ -92,45 +97,10 @@ private module Django {
|
||||
/** Gets a reference to the `django.db.connection` object. */
|
||||
DataFlow::Node connection() { result = connection(DataFlow::TypeTracker::end()) }
|
||||
|
||||
/** Provides models for the `django.db.connection.cursor` method. */
|
||||
module cursor {
|
||||
/** Gets a reference to the `django.db.connection.cursor` metod. */
|
||||
private DataFlow::Node methodRef(DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
result = DataFlow::importNode("django.db.connection.cursor")
|
||||
or
|
||||
t.startInAttr("cursor") and
|
||||
result = connection()
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = methodRef(t2).track(t2, t))
|
||||
}
|
||||
|
||||
/** Gets a reference to the `django.db.connection.cursor` metod. */
|
||||
DataFlow::Node methodRef() { result = methodRef(DataFlow::TypeTracker::end()) }
|
||||
|
||||
/** Gets a reference to a result of calling `django.db.connection.cursor`. */
|
||||
private DataFlow::Node methodResult(DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
result.asCfgNode().(CallNode).getFunction() = methodRef().asCfgNode()
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = methodResult(t2).track(t2, t))
|
||||
}
|
||||
|
||||
/** Gets a reference to a result of calling `django.db.connection.cursor`. */
|
||||
DataFlow::Node methodResult() { result = methodResult(DataFlow::TypeTracker::end()) }
|
||||
class DjangoDbConnection extends Connection::InstanceSource {
|
||||
DjangoDbConnection() { this = connection() }
|
||||
}
|
||||
|
||||
/** Gets a reference to the `django.db.connection.cursor.execute` function. */
|
||||
private DataFlow::Node execute(DataFlow::TypeTracker t) {
|
||||
t.startInAttr("execute") and
|
||||
result = cursor::methodResult()
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = execute(t2).track(t2, t))
|
||||
}
|
||||
|
||||
/** Gets a reference to the `django.db.connection.cursor.execute` function. */
|
||||
DataFlow::Node execute() { result = execute(DataFlow::TypeTracker::end()) }
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// django.db.models
|
||||
// -------------------------------------------------------------------------
|
||||
@@ -276,23 +246,6 @@ private module Django {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to the `django.db.connection.cursor.execute` function.
|
||||
*
|
||||
* See
|
||||
* - https://docs.djangoproject.com/en/3.1/topics/db/sql/#executing-custom-sql-directly
|
||||
* - https://docs.djangoproject.com/en/3.1/topics/db/sql/#connections-and-cursors
|
||||
*/
|
||||
private class DbConnectionExecute extends SqlExecution::Range, DataFlow::CfgNode {
|
||||
override CallNode node;
|
||||
|
||||
DbConnectionExecute() { node.getFunction() = django::db::execute().asCfgNode() }
|
||||
|
||||
override DataFlow::Node getSql() {
|
||||
result.asCfgNode() in [node.getArg(0), node.getArgByName("sql")]
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to the `annotate` function on a model using a `RawSQL` argument.
|
||||
*
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* Provides classes modeling security-relevant aspects of the `MySQLdb` PyPI package.
|
||||
* See
|
||||
* - https://mysqlclient.readthedocs.io/index.html
|
||||
* - https://pypi.org/project/MySQL-python/
|
||||
*/
|
||||
|
||||
private import python
|
||||
private import experimental.dataflow.DataFlow
|
||||
private import experimental.dataflow.RemoteFlowSources
|
||||
private import experimental.semmle.python.Concepts
|
||||
private import PEP249
|
||||
|
||||
/**
|
||||
* Provides models for the `MySQLdb` PyPI package.
|
||||
* See
|
||||
* - https://mysqlclient.readthedocs.io/index.html
|
||||
* - https://pypi.org/project/MySQL-python/
|
||||
*/
|
||||
module MySQLdb {
|
||||
// ---------------------------------------------------------------------------
|
||||
// MySQLdb
|
||||
// ---------------------------------------------------------------------------
|
||||
/** Gets a reference to the `MySQLdb` module. */
|
||||
private DataFlow::Node moduleMySQLdb(DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
result = DataFlow::importNode("MySQLdb")
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = moduleMySQLdb(t2).track(t2, t))
|
||||
}
|
||||
|
||||
/** Gets a reference to the `MySQLdb` module. */
|
||||
DataFlow::Node moduleMySQLdb() { result = moduleMySQLdb(DataFlow::TypeTracker::end()) }
|
||||
|
||||
class MySQLdb extends PEP249Module {
|
||||
MySQLdb() { this = moduleMySQLdb() }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
/**
|
||||
* Provides classes modeling security-relevant aspects of the `mysql-connector-python` package.
|
||||
* See
|
||||
* - https://dev.mysql.com/doc/connector-python/en/
|
||||
* - https://dev.mysql.com/doc/connector-python/en/connector-python-example-connecting.html
|
||||
*/
|
||||
|
||||
private import python
|
||||
private import experimental.dataflow.DataFlow
|
||||
private import experimental.dataflow.RemoteFlowSources
|
||||
private import experimental.semmle.python.Concepts
|
||||
private import PEP249
|
||||
|
||||
/**
|
||||
* Provides models for the `mysql-connector-python` package.
|
||||
* See
|
||||
* - https://dev.mysql.com/doc/connector-python/en/
|
||||
* - https://dev.mysql.com/doc/connector-python/en/connector-python-example-connecting.html
|
||||
*/
|
||||
module MysqlConnectorPython {
|
||||
// ---------------------------------------------------------------------------
|
||||
// mysql
|
||||
// ---------------------------------------------------------------------------
|
||||
/** Gets a reference to the `mysql` module. */
|
||||
private DataFlow::Node mysql(DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
result = DataFlow::importNode("mysql")
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = mysql(t2).track(t2, t))
|
||||
}
|
||||
|
||||
/** Gets a reference to the `mysql` module. */
|
||||
DataFlow::Node mysql() { result = mysql(DataFlow::TypeTracker::end()) }
|
||||
|
||||
/**
|
||||
* Gets a reference to the attribute `attr_name` of the `mysql` module.
|
||||
* WARNING: Only holds for a few predefined attributes.
|
||||
*/
|
||||
private DataFlow::Node mysql_attr(DataFlow::TypeTracker t, string attr_name) {
|
||||
attr_name in ["connector"] and
|
||||
(
|
||||
t.start() and
|
||||
result = DataFlow::importNode("mysql" + "." + attr_name)
|
||||
or
|
||||
t.startInAttr(attr_name) and
|
||||
result = mysql()
|
||||
)
|
||||
or
|
||||
// Due to bad performance when using normal setup with `mysql_attr(t2, attr_name).track(t2, t)`
|
||||
// we have inlined that code and forced a join
|
||||
exists(DataFlow::TypeTracker t2 |
|
||||
exists(DataFlow::StepSummary summary |
|
||||
mysql_attr_first_join(t2, attr_name, result, summary) and
|
||||
t = t2.append(summary)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate mysql_attr_first_join(
|
||||
DataFlow::TypeTracker t2, string attr_name, DataFlow::Node res, DataFlow::StepSummary summary
|
||||
) {
|
||||
DataFlow::StepSummary::step(mysql_attr(t2, attr_name), res, summary)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a reference to the attribute `attr_name` of the `mysql` module.
|
||||
* WARNING: Only holds for a few predefined attributes.
|
||||
*/
|
||||
private DataFlow::Node mysql_attr(string attr_name) {
|
||||
result = mysql_attr(DataFlow::TypeTracker::end(), attr_name)
|
||||
}
|
||||
|
||||
/** Provides models for the `mysql` module. */
|
||||
module mysql {
|
||||
/**
|
||||
* The mysql.connector module
|
||||
* See https://dev.mysql.com/doc/connector-python/en/connector-python-example-connecting.html
|
||||
*/
|
||||
class MysqlConnector extends PEP249Module {
|
||||
MysqlConnector() { this = mysql_attr("connector") }
|
||||
}
|
||||
}
|
||||
}
|
||||
117
python/ql/src/experimental/semmle/python/frameworks/PEP249.qll
Normal file
117
python/ql/src/experimental/semmle/python/frameworks/PEP249.qll
Normal file
@@ -0,0 +1,117 @@
|
||||
/**
|
||||
* Provides classes modeling PEP 249.
|
||||
* See https://www.python.org/dev/peps/pep-0249/.
|
||||
*/
|
||||
|
||||
private import python
|
||||
private import experimental.dataflow.DataFlow
|
||||
private import experimental.dataflow.RemoteFlowSources
|
||||
private import experimental.semmle.python.Concepts
|
||||
|
||||
/** A module implementing PEP 249. Extend this class for implementations. */
|
||||
abstract class PEP249Module extends DataFlow::Node { }
|
||||
|
||||
/** Gets a reference to a connect call. */
|
||||
private DataFlow::Node connect(DataFlow::TypeTracker t) {
|
||||
t.startInAttr("connect") and
|
||||
result instanceof PEP249Module
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = connect(t2).track(t2, t))
|
||||
}
|
||||
|
||||
/** Gets a reference to a connect call. */
|
||||
DataFlow::Node connect() { result = connect(DataFlow::TypeTracker::end()) }
|
||||
|
||||
/**
|
||||
* Provides models for the `db.Connection` class
|
||||
*
|
||||
* See https://www.python.org/dev/peps/pep-0249/#connection-objects.
|
||||
*/
|
||||
module Connection {
|
||||
/**
|
||||
* A source of an instance of `db.Connection`.
|
||||
*
|
||||
* This can include instantiation of the class, return value from function
|
||||
* calls, or a special parameter that will be set when functions are called by external
|
||||
* libraries.
|
||||
*
|
||||
* Use `Connection::instance()` predicate to get references to instances of `db.Connection`.
|
||||
*
|
||||
* Extend this class if the module implementing PEP 249 offers more direct ways to obtain
|
||||
* a connection than going through `connect`.
|
||||
*/
|
||||
abstract class InstanceSource extends DataFlow::Node { }
|
||||
|
||||
/** A direct instantiation of `db.Connection`. */
|
||||
private class ClassInstantiation extends InstanceSource, DataFlow::CfgNode {
|
||||
override CallNode node;
|
||||
|
||||
ClassInstantiation() { node.getFunction() = connect().asCfgNode() }
|
||||
}
|
||||
|
||||
/** Gets a reference to an instance of `db.Connection`. */
|
||||
private DataFlow::Node instance(DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
result instanceof InstanceSource
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = instance(t2).track(t2, t))
|
||||
}
|
||||
|
||||
/** Gets a reference to an instance of `db.Connection`. */
|
||||
DataFlow::Node instance() { result = instance(DataFlow::TypeTracker::end()) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides models for the `db.Connection.cursor` method.
|
||||
* See https://www.python.org/dev/peps/pep-0249/#cursor.
|
||||
*/
|
||||
module cursor {
|
||||
/** Gets a reference to the `db.connection.cursor` method. */
|
||||
private DataFlow::Node methodRef(DataFlow::TypeTracker t) {
|
||||
t.startInAttr("cursor") and
|
||||
result = Connection::instance()
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = methodRef(t2).track(t2, t))
|
||||
}
|
||||
|
||||
/** Gets a reference to the `db.connection.cursor` metod. */
|
||||
DataFlow::Node methodRef() { result = methodRef(DataFlow::TypeTracker::end()) }
|
||||
|
||||
/** Gets a reference to a result of calling `db.connection.cursor`. */
|
||||
private DataFlow::Node methodResult(DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
result.asCfgNode().(CallNode).getFunction() = methodRef().asCfgNode()
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = methodResult(t2).track(t2, t))
|
||||
}
|
||||
|
||||
/** Gets a reference to a result of calling `db.connection.cursor`. */
|
||||
DataFlow::Node methodResult() { result = methodResult(DataFlow::TypeTracker::end()) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a reference to the `db.Connection.Cursor.execute` function.
|
||||
* See https://www.python.org/dev/peps/pep-0249/#id15.
|
||||
*/
|
||||
private DataFlow::Node execute(DataFlow::TypeTracker t) {
|
||||
t.startInAttr("execute") and
|
||||
result = cursor::methodResult()
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = execute(t2).track(t2, t))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a reference to the `db.Connection.Cursor.execute` function.
|
||||
* See https://www.python.org/dev/peps/pep-0249/#id15.
|
||||
*/
|
||||
DataFlow::Node execute() { result = execute(DataFlow::TypeTracker::end()) }
|
||||
|
||||
private class DbConnectionExecute extends SqlExecution::Range, DataFlow::CfgNode {
|
||||
override CallNode node;
|
||||
|
||||
DbConnectionExecute() { node.getFunction() = execute().asCfgNode() }
|
||||
|
||||
override DataFlow::Node getSql() {
|
||||
result.asCfgNode() in [node.getArg(0), node.getArgByName("sql")]
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user