Skip to content

Commit f174489

Browse files
committed
Python: Add tests for Flask-SQLAlchemy
1 parent c34d6d1 commit f174489

File tree

7 files changed

+96
-31
lines changed

7 files changed

+96
-31
lines changed

python/ql/test/library-tests/frameworks/flask_sqlalchemy/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: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
argumentToEnsureNotTaintedNotMarkedAsSpurious
2+
untaintedArgumentToEnsureTaintedNotMarkedAsMissing
3+
failures
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
import experimental.meta.InlineTaintTest
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# pip install Flask-SQLAlchemy
2+
from flask import Flask
3+
from flask_sqlalchemy import SQLAlchemy
4+
import sqlalchemy
5+
6+
app = Flask(__name__)
7+
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite+pysqlite:///:memory:"
8+
db = SQLAlchemy(app)
9+
10+
# re-exports all things from `sqlalchemy` and `sqlalchemy.orm` under instances of `SQLAlchemy`
11+
# see
12+
# - https://github.com/pallets/flask-sqlalchemy/blob/931ec00d1e27f51508e05706eef41cc4419a0b32/src/flask_sqlalchemy/__init__.py#L765
13+
# - https://github.com/pallets/flask-sqlalchemy/blob/931ec00d1e27f51508e05706eef41cc4419a0b32/src/flask_sqlalchemy/__init__.py#L99-L109
14+
15+
assert str(type(db.text("Foo"))) == "<class 'sqlalchemy.sql.elements.TextClause'>"
16+
17+
# also has engine/session instantiated
18+
19+
raw_sql = "SELECT 'Foo'"
20+
21+
conn = db.engine.connect()
22+
result = conn.execute(raw_sql) # $ MISSING: getSql=raw_sql
23+
assert result.fetchall() == [("Foo",)]
24+
25+
conn = db.get_engine().connect()
26+
result = conn.execute(raw_sql) # $ MISSING: getSql=raw_sql
27+
assert result.fetchall() == [("Foo",)]
28+
29+
result = db.session.execute(raw_sql) # $ MISSING: getSql=raw_sql
30+
assert result.fetchall() == [("Foo",)]
31+
32+
Session = db.create_session(options={})
33+
session = Session()
34+
result = session.execute(raw_sql) # $ MISSING: getSql=raw_sql
35+
assert result.fetchall() == [("Foo",)]
36+
37+
Session = db.create_session(options={})
38+
with Session.begin() as session:
39+
result = session.execute(raw_sql) # $ MISSING: getSql=raw_sql
40+
assert result.fetchall() == [("Foo",)]
41+
42+
result = db.create_scoped_session().execute(raw_sql) # $ MISSING: getSql=raw_sql
43+
assert result.fetchall() == [("Foo",)]
44+
45+
46+
# text
47+
t = db.text("foo")
48+
assert isinstance(t, sqlalchemy.sql.expression.TextClause)
49+
50+
t = db.text(text="foo")
51+
assert isinstance(t, sqlalchemy.sql.expression.TextClause)
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,34 @@
11
edges
2-
| test.py:18:15:18:22 | ControlFlowNode for username | test.py:22:28:22:87 | ControlFlowNode for Attribute() |
3-
| test.py:18:15:18:22 | ControlFlowNode for username | test.py:26:50:26:72 | ControlFlowNode for Attribute() |
4-
| test.py:18:15:18:22 | ControlFlowNode for username | test.py:36:26:36:33 | ControlFlowNode for username |
5-
| test.py:18:15:18:22 | ControlFlowNode for username | test.py:37:31:37:38 | ControlFlowNode for username |
6-
| test.py:18:15:18:22 | ControlFlowNode for username | test.py:38:30:38:37 | ControlFlowNode for username |
7-
| test.py:18:15:18:22 | ControlFlowNode for username | test.py:39:35:39:42 | ControlFlowNode for username |
8-
| test.py:18:15:18:22 | ControlFlowNode for username | test.py:40:41:40:48 | ControlFlowNode for username |
9-
| test.py:18:15:18:22 | ControlFlowNode for username | test.py:41:46:41:53 | ControlFlowNode for username |
10-
| test.py:18:15:18:22 | ControlFlowNode for username | test.py:42:47:42:54 | ControlFlowNode for username |
11-
| test.py:18:15:18:22 | ControlFlowNode for username | test.py:43:52:43:59 | ControlFlowNode for username |
2+
| test.py:23:15:23:22 | ControlFlowNode for username | test.py:27:28:27:87 | ControlFlowNode for Attribute() |
3+
| test.py:23:15:23:22 | ControlFlowNode for username | test.py:31:50:31:72 | ControlFlowNode for Attribute() |
4+
| test.py:23:15:23:22 | ControlFlowNode for username | test.py:41:26:41:33 | ControlFlowNode for username |
5+
| test.py:23:15:23:22 | ControlFlowNode for username | test.py:42:31:42:38 | ControlFlowNode for username |
6+
| test.py:23:15:23:22 | ControlFlowNode for username | test.py:43:30:43:37 | ControlFlowNode for username |
7+
| test.py:23:15:23:22 | ControlFlowNode for username | test.py:44:35:44:42 | ControlFlowNode for username |
8+
| test.py:23:15:23:22 | ControlFlowNode for username | test.py:45:41:45:48 | ControlFlowNode for username |
9+
| test.py:23:15:23:22 | ControlFlowNode for username | test.py:46:46:46:53 | ControlFlowNode for username |
10+
| test.py:23:15:23:22 | ControlFlowNode for username | test.py:47:47:47:54 | ControlFlowNode for username |
11+
| test.py:23:15:23:22 | ControlFlowNode for username | test.py:48:52:48:59 | ControlFlowNode for username |
1212
nodes
13-
| test.py:18:15:18:22 | ControlFlowNode for username | semmle.label | ControlFlowNode for username |
14-
| test.py:22:28:22:87 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
15-
| test.py:26:50:26:72 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
16-
| test.py:36:26:36:33 | ControlFlowNode for username | semmle.label | ControlFlowNode for username |
17-
| test.py:37:31:37:38 | ControlFlowNode for username | semmle.label | ControlFlowNode for username |
18-
| test.py:38:30:38:37 | ControlFlowNode for username | semmle.label | ControlFlowNode for username |
19-
| test.py:39:35:39:42 | ControlFlowNode for username | semmle.label | ControlFlowNode for username |
20-
| test.py:40:41:40:48 | ControlFlowNode for username | semmle.label | ControlFlowNode for username |
21-
| test.py:41:46:41:53 | ControlFlowNode for username | semmle.label | ControlFlowNode for username |
22-
| test.py:42:47:42:54 | ControlFlowNode for username | semmle.label | ControlFlowNode for username |
23-
| test.py:43:52:43:59 | ControlFlowNode for username | semmle.label | ControlFlowNode for username |
13+
| test.py:23:15:23:22 | ControlFlowNode for username | semmle.label | ControlFlowNode for username |
14+
| test.py:27:28:27:87 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
15+
| test.py:31:50:31:72 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
16+
| test.py:41:26:41:33 | ControlFlowNode for username | semmle.label | ControlFlowNode for username |
17+
| test.py:42:31:42:38 | ControlFlowNode for username | semmle.label | ControlFlowNode for username |
18+
| test.py:43:30:43:37 | ControlFlowNode for username | semmle.label | ControlFlowNode for username |
19+
| test.py:44:35:44:42 | ControlFlowNode for username | semmle.label | ControlFlowNode for username |
20+
| test.py:45:41:45:48 | ControlFlowNode for username | semmle.label | ControlFlowNode for username |
21+
| test.py:46:46:46:53 | ControlFlowNode for username | semmle.label | ControlFlowNode for username |
22+
| test.py:47:47:47:54 | ControlFlowNode for username | semmle.label | ControlFlowNode for username |
23+
| test.py:48:52:48:59 | ControlFlowNode for username | semmle.label | ControlFlowNode for username |
2424
#select
25-
| test.py:22:28:22:87 | ControlFlowNode for Attribute() | test.py:18:15:18:22 | ControlFlowNode for username | test.py:22:28:22:87 | ControlFlowNode for Attribute() | This SQLAlchemy TextClause depends on $@, which could lead to SQL injection. | test.py:18:15:18:22 | ControlFlowNode for username | a user-provided value |
26-
| test.py:26:50:26:72 | ControlFlowNode for Attribute() | test.py:18:15:18:22 | ControlFlowNode for username | test.py:26:50:26:72 | ControlFlowNode for Attribute() | This SQLAlchemy TextClause depends on $@, which could lead to SQL injection. | test.py:18:15:18:22 | ControlFlowNode for username | a user-provided value |
27-
| test.py:36:26:36:33 | ControlFlowNode for username | test.py:18:15:18:22 | ControlFlowNode for username | test.py:36:26:36:33 | ControlFlowNode for username | This SQLAlchemy TextClause depends on $@, which could lead to SQL injection. | test.py:18:15:18:22 | ControlFlowNode for username | a user-provided value |
28-
| test.py:37:31:37:38 | ControlFlowNode for username | test.py:18:15:18:22 | ControlFlowNode for username | test.py:37:31:37:38 | ControlFlowNode for username | This SQLAlchemy TextClause depends on $@, which could lead to SQL injection. | test.py:18:15:18:22 | ControlFlowNode for username | a user-provided value |
29-
| test.py:38:30:38:37 | ControlFlowNode for username | test.py:18:15:18:22 | ControlFlowNode for username | test.py:38:30:38:37 | ControlFlowNode for username | This SQLAlchemy TextClause depends on $@, which could lead to SQL injection. | test.py:18:15:18:22 | ControlFlowNode for username | a user-provided value |
30-
| test.py:39:35:39:42 | ControlFlowNode for username | test.py:18:15:18:22 | ControlFlowNode for username | test.py:39:35:39:42 | ControlFlowNode for username | This SQLAlchemy TextClause depends on $@, which could lead to SQL injection. | test.py:18:15:18:22 | ControlFlowNode for username | a user-provided value |
31-
| test.py:40:41:40:48 | ControlFlowNode for username | test.py:18:15:18:22 | ControlFlowNode for username | test.py:40:41:40:48 | ControlFlowNode for username | This SQLAlchemy TextClause depends on $@, which could lead to SQL injection. | test.py:18:15:18:22 | ControlFlowNode for username | a user-provided value |
32-
| test.py:41:46:41:53 | ControlFlowNode for username | test.py:18:15:18:22 | ControlFlowNode for username | test.py:41:46:41:53 | ControlFlowNode for username | This SQLAlchemy TextClause depends on $@, which could lead to SQL injection. | test.py:18:15:18:22 | ControlFlowNode for username | a user-provided value |
33-
| test.py:42:47:42:54 | ControlFlowNode for username | test.py:18:15:18:22 | ControlFlowNode for username | test.py:42:47:42:54 | ControlFlowNode for username | This SQLAlchemy TextClause depends on $@, which could lead to SQL injection. | test.py:18:15:18:22 | ControlFlowNode for username | a user-provided value |
34-
| test.py:43:52:43:59 | ControlFlowNode for username | test.py:18:15:18:22 | ControlFlowNode for username | test.py:43:52:43:59 | ControlFlowNode for username | This SQLAlchemy TextClause depends on $@, which could lead to SQL injection. | test.py:18:15:18:22 | ControlFlowNode for username | a user-provided value |
25+
| test.py:27:28:27:87 | ControlFlowNode for Attribute() | test.py:23:15:23:22 | ControlFlowNode for username | test.py:27:28:27:87 | ControlFlowNode for Attribute() | This SQLAlchemy TextClause depends on $@, which could lead to SQL injection. | test.py:23:15:23:22 | ControlFlowNode for username | a user-provided value |
26+
| test.py:31:50:31:72 | ControlFlowNode for Attribute() | test.py:23:15:23:22 | ControlFlowNode for username | test.py:31:50:31:72 | ControlFlowNode for Attribute() | This SQLAlchemy TextClause depends on $@, which could lead to SQL injection. | test.py:23:15:23:22 | ControlFlowNode for username | a user-provided value |
27+
| test.py:41:26:41:33 | ControlFlowNode for username | test.py:23:15:23:22 | ControlFlowNode for username | test.py:41:26:41:33 | ControlFlowNode for username | This SQLAlchemy TextClause depends on $@, which could lead to SQL injection. | test.py:23:15:23:22 | ControlFlowNode for username | a user-provided value |
28+
| test.py:42:31:42:38 | ControlFlowNode for username | test.py:23:15:23:22 | ControlFlowNode for username | test.py:42:31:42:38 | ControlFlowNode for username | This SQLAlchemy TextClause depends on $@, which could lead to SQL injection. | test.py:23:15:23:22 | ControlFlowNode for username | a user-provided value |
29+
| test.py:43:30:43:37 | ControlFlowNode for username | test.py:23:15:23:22 | ControlFlowNode for username | test.py:43:30:43:37 | ControlFlowNode for username | This SQLAlchemy TextClause depends on $@, which could lead to SQL injection. | test.py:23:15:23:22 | ControlFlowNode for username | a user-provided value |
30+
| test.py:44:35:44:42 | ControlFlowNode for username | test.py:23:15:23:22 | ControlFlowNode for username | test.py:44:35:44:42 | ControlFlowNode for username | This SQLAlchemy TextClause depends on $@, which could lead to SQL injection. | test.py:23:15:23:22 | ControlFlowNode for username | a user-provided value |
31+
| test.py:45:41:45:48 | ControlFlowNode for username | test.py:23:15:23:22 | ControlFlowNode for username | test.py:45:41:45:48 | ControlFlowNode for username | This SQLAlchemy TextClause depends on $@, which could lead to SQL injection. | test.py:23:15:23:22 | ControlFlowNode for username | a user-provided value |
32+
| test.py:46:46:46:53 | ControlFlowNode for username | test.py:23:15:23:22 | ControlFlowNode for username | test.py:46:46:46:53 | ControlFlowNode for username | This SQLAlchemy TextClause depends on $@, which could lead to SQL injection. | test.py:23:15:23:22 | ControlFlowNode for username | a user-provided value |
33+
| test.py:47:47:47:54 | ControlFlowNode for username | test.py:23:15:23:22 | ControlFlowNode for username | test.py:47:47:47:54 | ControlFlowNode for username | This SQLAlchemy TextClause depends on $@, which could lead to SQL injection. | test.py:23:15:23:22 | ControlFlowNode for username | a user-provided value |
34+
| test.py:48:52:48:59 | ControlFlowNode for username | test.py:23:15:23:22 | ControlFlowNode for username | test.py:48:52:48:59 | ControlFlowNode for username | This SQLAlchemy TextClause depends on $@, which could lead to SQL injection. | test.py:23:15:23:22 | ControlFlowNode for username | a user-provided value |

python/ql/test/query-tests/Security/CWE-089-SQLAlchemyTextClauseInjection/test.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
11
from flask import Flask, request
22
import sqlalchemy
33
import sqlalchemy.orm
4+
from flask_sqlalchemy import SQLAlchemy
45

56
app = Flask(__name__)
67
engine = sqlalchemy.create_engine(...)
78
Base = sqlalchemy.orm.declarative_base()
89

10+
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite+pysqlite:///:memory:"
11+
db = SQLAlchemy(app)
12+
13+
914

1015
class User(Base):
1116
__tablename__ = "users"
@@ -41,3 +46,6 @@ def show_user(username):
4146
t6 = sqlalchemy.sql.expression.text(text=username)
4247
t7 = sqlalchemy.sql.expression.TextClause(username)
4348
t8 = sqlalchemy.sql.expression.TextClause(text=username)
49+
50+
t9 = db.text(username)
51+
t10 = db.text(text=username)

0 commit comments

Comments
 (0)