Merge pull request #10752 from RasmusWL/pymssql

Python: DB Modeling: Add `pymssql` and `executemany` in general
This commit is contained in:
Rasmus Wriedt Larsen
2022-10-11 15:55:04 +02:00
committed by GitHub
11 changed files with 85 additions and 16 deletions

View File

@@ -226,6 +226,8 @@ and the CodeQL library pack ``codeql/python-all`` (`changelog <https://github.co
MySQL-python, Database MySQL-python, Database
mysqlclient, Database mysqlclient, Database
psycopg2, Database psycopg2, Database
pymssql, Database
PyMySQL, Database
sqlite3, Database sqlite3, Database
Flask-SQLAlchemy, Database ORM Flask-SQLAlchemy, Database ORM
peewee, Database ORM peewee, Database ORM

View File

@@ -37,6 +37,7 @@ private import semmle.python.frameworks.Peewee
private import semmle.python.frameworks.Psycopg2 private import semmle.python.frameworks.Psycopg2
private import semmle.python.frameworks.Pycurl private import semmle.python.frameworks.Pycurl
private import semmle.python.frameworks.Pydantic private import semmle.python.frameworks.Pydantic
private import semmle.python.frameworks.Pymssql
private import semmle.python.frameworks.PyMySQL private import semmle.python.frameworks.PyMySQL
private import semmle.python.frameworks.Requests private import semmle.python.frameworks.Requests
private import semmle.python.frameworks.RestFramework private import semmle.python.frameworks.RestFramework
@@ -51,6 +52,6 @@ private import semmle.python.frameworks.Tornado
private import semmle.python.frameworks.Twisted private import semmle.python.frameworks.Twisted
private import semmle.python.frameworks.Ujson private import semmle.python.frameworks.Ujson
private import semmle.python.frameworks.Urllib3 private import semmle.python.frameworks.Urllib3
private import semmle.python.frameworks.Xmltodict
private import semmle.python.frameworks.Yaml private import semmle.python.frameworks.Yaml
private import semmle.python.frameworks.Yarl private import semmle.python.frameworks.Yarl
private import semmle.python.frameworks.Xmltodict

View File

@@ -21,17 +21,11 @@ private import semmle.python.frameworks.PEP249
* - https://dev.mysql.com/doc/connector-python/en/connector-python-example-connecting.html * - https://dev.mysql.com/doc/connector-python/en/connector-python-example-connecting.html
*/ */
private module Mysql { private module Mysql {
// --------------------------------------------------------------------------- /**
// mysql * The mysql.connector module
// --------------------------------------------------------------------------- * See https://dev.mysql.com/doc/connector-python/en/connector-python-example-connecting.html
/** Provides models for the `mysql` module. */ */
module MysqlMod { class MysqlConnector extends PEP249::PEP249ModuleApiNode {
/** MysqlConnector() { this = API::moduleImport("mysql").getMember("connector") }
* The mysql.connector module
* See https://dev.mysql.com/doc/connector-python/en/connector-python-example-connecting.html
*/
class MysqlConnector extends PEP249::PEP249ModuleApiNode {
MysqlConnector() { this = API::moduleImport("mysql").getMember("connector") }
}
} }
} }

View File

@@ -0,0 +1,25 @@
/**
* Provides classes modeling security-relevant aspects of the `pymssql` PyPI package.
* See https://pypi.org/project/pymssql/
*/
private import python
private import semmle.python.dataflow.new.DataFlow
private import semmle.python.dataflow.new.RemoteFlowSources
private import semmle.python.Concepts
private import semmle.python.ApiGraphs
private import semmle.python.frameworks.PEP249
/**
* Provides models for the `pymssql` PyPI package.
* See https://pypi.org/project/pymssql/
*/
private module Pymssql {
/**
* A model of `pymssql` as a module that implements PEP 249, providing ways to execute SQL statements
* against a database.
*/
class PymssqlPEP249 extends PEP249::PEP249ModuleApiNode {
PymssqlPEP249() { this = API::moduleImport("pymssql") }
}
}

View File

@@ -146,7 +146,7 @@ module PEP249 {
* Note: while `execute` method on a connection is not part of PEP249, if it is used, we * Note: while `execute` method on a connection is not part of PEP249, if it is used, we
* recognize it as an alias for constructing a cursor and calling `execute` on it. * recognize it as an alias for constructing a cursor and calling `execute` on it.
* *
* See https://www.python.org/dev/peps/pep-0249/#id15. * See https://peps.python.org/pep-0249/#execute.
*/ */
private DataFlow::TypeTrackingNode execute(DataFlow::TypeTracker t) { private DataFlow::TypeTrackingNode execute(DataFlow::TypeTracker t) {
t.startInAttr("execute") and t.startInAttr("execute") and
@@ -161,14 +161,44 @@ module PEP249 {
* Note: while `execute` method on a connection is not part of PEP249, if it is used, we * Note: while `execute` method on a connection is not part of PEP249, if it is used, we
* recognize it as an alias for constructing a cursor and calling `execute` on it. * recognize it as an alias for constructing a cursor and calling `execute` on it.
* *
* See https://www.python.org/dev/peps/pep-0249/#id15. * See https://peps.python.org/pep-0249/#execute.
*/ */
DataFlow::Node execute() { execute(DataFlow::TypeTracker::end()).flowsTo(result) } DataFlow::Node execute() { execute(DataFlow::TypeTracker::end()).flowsTo(result) }
/** A call to the `execute` method on a cursor (or on a connection). */ /**
* A call to the `execute` method on a cursor or a connection.
*
* See https://peps.python.org/pep-0249/#execute
*
* Note: While `execute` method on a connection is not part of PEP249, if it is used, we
* recognize it as an alias for constructing a cursor and calling `execute` on it.
*/
private class ExecuteCall extends SqlExecution::Range, DataFlow::CallCfgNode { private class ExecuteCall extends SqlExecution::Range, DataFlow::CallCfgNode {
ExecuteCall() { this.getFunction() = execute() } ExecuteCall() { this.getFunction() = execute() }
override DataFlow::Node getSql() { result in [this.getArg(0), this.getArgByName("sql")] } override DataFlow::Node getSql() { result in [this.getArg(0), this.getArgByName("sql")] }
} }
private DataFlow::TypeTrackingNode executemany(DataFlow::TypeTracker t) {
t.startInAttr("executemany") and
result in [Cursor::instance(), Connection::instance()]
or
exists(DataFlow::TypeTracker t2 | result = executemany(t2).track(t2, t))
}
private DataFlow::Node executemany() { executemany(DataFlow::TypeTracker::end()).flowsTo(result) }
/**
* A call to the `executemany` method on a cursor or a connection.
*
* See https://peps.python.org/pep-0249/#executemany
*
* Note: While `executemany` method on a connection is not part of PEP249, if it is used, we
* recognize it as an alias for constructing a cursor and calling `executemany` on it.
*/
private class ExecutemanyCall extends SqlExecution::Range, DataFlow::CallCfgNode {
ExecutemanyCall() { this.getFunction() = executemany() }
override DataFlow::Node getSql() { result in [this.getArg(0), this.getArgByName("sql")] }
}
} }

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* Added model of `executemany` calls on PEP-249 compliant database APIs, resulting in additional sinks for `py/sql-injection`.

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* Added model of `pymssql` PyPI package as a SQL interface following PEP249, resulting in additional sinks for `py/sql-injection`.

View File

@@ -0,0 +1,2 @@
import python
import experimental.meta.ConceptsTest

View File

@@ -0,0 +1,6 @@
import pymssql
connection = pymssql.connect(host="localhost", user="user", password="passwd")
cursor = connection.cursor()
cursor.execute("some sql", (42,)) # $ getSql="some sql"
cursor.executemany("some sql", [(42,)]) # $ getSql="some sql"

View File

@@ -3,3 +3,4 @@ connection = pymysql.connect(host="localhost", user="user", password="passwd")
cursor = connection.cursor() cursor = connection.cursor()
cursor.execute("some sql", (42,)) # $ getSql="some sql" cursor.execute("some sql", (42,)) # $ getSql="some sql"
cursor.executemany("some sql", [(42,)]) # $ getSql="some sql"