Skip to content

Commit ec66f26

Browse files
committed
Python: Handle get_collection on pymongo DB
1 parent 89eeaf8 commit ec66f26

File tree

3 files changed

+18
-11
lines changed

3 files changed

+18
-11
lines changed

python/ql/src/experimental/semmle/python/frameworks/NoSQL.qll

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,12 @@ private module NoSql {
9696
)
9797
or
9898
result.(DataFlow::AttrRead).getObject() = mongoDBInstance()
99+
or
100+
// see https://pymongo.readthedocs.io/en/stable/api/pymongo/database.html#pymongo.database.Database.get_collection
101+
// see https://pymongo.readthedocs.io/en/stable/api/pymongo/database.html#pymongo.database.Database.create_collection
102+
result
103+
.(DataFlow::MethodCallNode)
104+
.calls(mongoDBInstance(), ["get_collection", "create_collection"])
99105
)
100106
or
101107
exists(DataFlow::TypeTracker t2 | result = mongoCollection(t2).track(t2, t))

python/ql/test/experimental/query-tests/Security/CWE-943/NoSQLInjection.expected

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,10 @@ edges
5353
| pymongo_test.py:29:27:29:33 | ControlFlowNode for request | pymongo_test.py:29:27:29:38 | ControlFlowNode for Attribute |
5454
| pymongo_test.py:29:27:29:38 | ControlFlowNode for Attribute | pymongo_test.py:29:27:29:50 | ControlFlowNode for Subscript |
5555
| pymongo_test.py:29:27:29:50 | ControlFlowNode for Subscript | pymongo_test.py:29:16:29:51 | ControlFlowNode for Attribute() |
56-
| pymongo_test.py:38:16:38:51 | ControlFlowNode for Attribute() | pymongo_test.py:42:34:42:73 | ControlFlowNode for Dict |
57-
| pymongo_test.py:38:27:38:33 | ControlFlowNode for request | pymongo_test.py:38:27:38:38 | ControlFlowNode for Attribute |
58-
| pymongo_test.py:38:27:38:38 | ControlFlowNode for Attribute | pymongo_test.py:38:27:38:50 | ControlFlowNode for Subscript |
59-
| pymongo_test.py:38:27:38:50 | ControlFlowNode for Subscript | pymongo_test.py:38:16:38:51 | ControlFlowNode for Attribute() |
56+
| pymongo_test.py:39:16:39:51 | ControlFlowNode for Attribute() | pymongo_test.py:43:34:43:73 | ControlFlowNode for Dict |
57+
| pymongo_test.py:39:27:39:33 | ControlFlowNode for request | pymongo_test.py:39:27:39:38 | ControlFlowNode for Attribute |
58+
| pymongo_test.py:39:27:39:38 | ControlFlowNode for Attribute | pymongo_test.py:39:27:39:50 | ControlFlowNode for Subscript |
59+
| pymongo_test.py:39:27:39:50 | ControlFlowNode for Subscript | pymongo_test.py:39:16:39:51 | ControlFlowNode for Attribute() |
6060
nodes
6161
| flask_mongoengine_bad.py:19:21:19:27 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
6262
| flask_mongoengine_bad.py:19:21:19:32 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
@@ -123,11 +123,11 @@ nodes
123123
| pymongo_test.py:29:27:29:38 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
124124
| pymongo_test.py:29:27:29:50 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
125125
| pymongo_test.py:33:34:33:73 | ControlFlowNode for Dict | semmle.label | ControlFlowNode for Dict |
126-
| pymongo_test.py:38:16:38:51 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
127-
| pymongo_test.py:38:27:38:33 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
128-
| pymongo_test.py:38:27:38:38 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
129-
| pymongo_test.py:38:27:38:50 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
130-
| pymongo_test.py:42:34:42:73 | ControlFlowNode for Dict | semmle.label | ControlFlowNode for Dict |
126+
| pymongo_test.py:39:16:39:51 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
127+
| pymongo_test.py:39:27:39:33 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
128+
| pymongo_test.py:39:27:39:38 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
129+
| pymongo_test.py:39:27:39:50 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
130+
| pymongo_test.py:43:34:43:73 | ControlFlowNode for Dict | semmle.label | ControlFlowNode for Dict |
131131
subpaths
132132
#select
133133
| flask_mongoengine_bad.py:22:34:22:44 | ControlFlowNode for json_search | flask_mongoengine_bad.py:19:21:19:27 | ControlFlowNode for request | flask_mongoengine_bad.py:22:34:22:44 | ControlFlowNode for json_search | $@ NoSQL query contains an unsanitized $@ | flask_mongoengine_bad.py:22:34:22:44 | ControlFlowNode for json_search | This | flask_mongoengine_bad.py:19:21:19:27 | ControlFlowNode for request | user-provided value |
@@ -141,4 +141,4 @@ subpaths
141141
| mongoengine_bad.py:61:29:61:49 | ControlFlowNode for Dict | mongoengine_bad.py:57:21:57:27 | ControlFlowNode for request | mongoengine_bad.py:61:29:61:49 | ControlFlowNode for Dict | $@ NoSQL query contains an unsanitized $@ | mongoengine_bad.py:61:29:61:49 | ControlFlowNode for Dict | This | mongoengine_bad.py:57:21:57:27 | ControlFlowNode for request | user-provided value |
142142
| pymongo_test.py:15:42:15:62 | ControlFlowNode for Dict | pymongo_test.py:12:21:12:27 | ControlFlowNode for request | pymongo_test.py:15:42:15:62 | ControlFlowNode for Dict | $@ NoSQL query contains an unsanitized $@ | pymongo_test.py:15:42:15:62 | ControlFlowNode for Dict | This | pymongo_test.py:12:21:12:27 | ControlFlowNode for request | user-provided value |
143143
| pymongo_test.py:33:34:33:73 | ControlFlowNode for Dict | pymongo_test.py:29:27:29:33 | ControlFlowNode for request | pymongo_test.py:33:34:33:73 | ControlFlowNode for Dict | $@ NoSQL query contains an unsanitized $@ | pymongo_test.py:33:34:33:73 | ControlFlowNode for Dict | This | pymongo_test.py:29:27:29:33 | ControlFlowNode for request | user-provided value |
144-
| pymongo_test.py:42:34:42:73 | ControlFlowNode for Dict | pymongo_test.py:38:27:38:33 | ControlFlowNode for request | pymongo_test.py:42:34:42:73 | ControlFlowNode for Dict | $@ NoSQL query contains an unsanitized $@ | pymongo_test.py:42:34:42:73 | ControlFlowNode for Dict | This | pymongo_test.py:38:27:38:33 | ControlFlowNode for request | user-provided value |
144+
| pymongo_test.py:43:34:43:73 | ControlFlowNode for Dict | pymongo_test.py:39:27:39:33 | ControlFlowNode for request | pymongo_test.py:43:34:43:73 | ControlFlowNode for Dict | $@ NoSQL query contains an unsanitized $@ | pymongo_test.py:43:34:43:73 | ControlFlowNode for Dict | This | pymongo_test.py:39:27:39:33 | ControlFlowNode for request | user-provided value |

python/ql/test/experimental/query-tests/Security/CWE-943/pymongo_test.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,11 @@ def bad2():
3535

3636
@app.route("/bad3")
3737
def bad3():
38+
# using `get_` methods instead of subscript/attribute lookups
3839
event_id = json.loads(request.args['event_id'])
3940
client = MongoClient("localhost", 27017, maxPoolSize=50)
4041
db = client.get_database(name="localhost")
41-
collection = db['collection']
42+
collection = db.get_collection("collection")
4243
cursor = collection.find_one({"$where": f"this._id == '${event_id}'"})
4344

4445

0 commit comments

Comments
 (0)