Skip to content

Commit 5c7229c

Browse files
committed
Optimize Type Tracking stuff
1 parent 81505fb commit 5c7229c

File tree

1 file changed

+55
-101
lines changed
  • python/ql/src/experimental/semmle/python/frameworks

1 file changed

+55
-101
lines changed

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

Lines changed: 55 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -11,106 +11,100 @@ private import experimental.semmle.python.Concepts
1111
private import semmle.python.ApiGraphs
1212

1313
private module NoSQL {
14-
/** Gets a reference to a `MongoClient` instance. */
15-
private API::Node mongoClientInstance() {
16-
result = API::moduleImport("pymongo").getMember("MongoClient").getReturn() or
17-
result =
18-
API::moduleImport("flask_mongoengine")
19-
.getMember("MongoEngine")
20-
.getReturn()
21-
.getMember("get_db")
22-
.getReturn() or
23-
result =
24-
API::moduleImport(["mongoengine", "mongoengine.connection"])
25-
.getMember(["get_db", "connect"])
26-
.getReturn() or
14+
/** API Nodes returning `Mongo` instances. */
15+
private API::Node pyMongo() {
16+
result = API::moduleImport("pymongo").getMember("MongoClient").getReturn()
17+
}
18+
19+
private API::Node flask_PyMongo() {
2720
result = API::moduleImport("flask_pymongo").getMember("PyMongo").getReturn()
2821
}
2922

30-
/** Gets a reference to a `MongoClient` DB. */
31-
private DataFlow::LocalSourceNode mongoClientDB(DataFlow::TypeTracker t) {
23+
private API::Node mongoEngine() { result = API::moduleImport("mongoengine") }
24+
25+
private API::Node flask_MongoEngine() {
26+
result = API::moduleImport("flask_mongoengine").getMember("MongoEngine").getReturn()
27+
}
28+
29+
/** Gets a reference to a initialized `Mongo` instance. */
30+
private API::Node mongoInstance() {
31+
result = pyMongo() or
32+
result = flask_PyMongo()
33+
}
34+
35+
/** Gets a reference to a initialized `Mongo` DB instance. */
36+
private API::Node mongoDBInstance() {
37+
result = mongoEngine().getMember(["get_db", "connect"]).getReturn() or
38+
result = mongoEngine().getMember("connection").getMember(["get_db", "connect"]).getReturn() or
39+
result = flask_MongoEngine().getMember("get_db").getReturn()
40+
}
41+
42+
/** Gets a reference to a `Mongo` DB use. */
43+
private DataFlow::LocalSourceNode mongoDB(DataFlow::TypeTracker t) {
3244
t.start() and
3345
(
34-
exists(SubscriptNode subscript | result.asCfgNode() = subscript |
35-
subscript.getObject() = mongoClientInstance().getAUse().asCfgNode()
46+
exists(SubscriptNode subscript |
47+
subscript.getObject() = mongoInstance().getAUse().asCfgNode() and
48+
result.asCfgNode() = subscript
3649
)
3750
or
38-
result.(DataFlow::AttrRead).getObject() = mongoClientInstance().getAUse()
51+
result.(DataFlow::AttrRead).getObject() = mongoInstance().getAUse()
52+
or
53+
result = mongoDBInstance().getAUse()
3954
)
4055
or
41-
exists(DataFlow::TypeTracker t2 | result = mongoClientDB(t2).track(t2, t))
56+
exists(DataFlow::TypeTracker t2 | result = mongoDB(t2).track(t2, t))
4257
}
4358

44-
/** Gets a reference to a `MongoClient` DB. */
45-
private DataFlow::Node mongoClientDB() {
46-
mongoClientDB(DataFlow::TypeTracker::end()).flowsTo(result)
47-
}
59+
/** Gets a reference to a `Mongo` DB use. */
60+
private DataFlow::Node mongoDB() { mongoDB(DataFlow::TypeTracker::end()).flowsTo(result) }
4861

49-
/** Gets a reference to a `MongoClient` collection. */
50-
private DataFlow::LocalSourceNode mongoClientCollection(DataFlow::TypeTracker t) {
62+
/** Gets a reference to a `Mongo` collection use. */
63+
private DataFlow::LocalSourceNode mongoCollection(DataFlow::TypeTracker t) {
5164
t.start() and
5265
(
5366
exists(SubscriptNode subscript | result.asCfgNode() = subscript |
54-
subscript.getObject() = mongoClientDB().asCfgNode()
67+
subscript.getObject() = mongoDB().asCfgNode()
5568
)
5669
or
57-
result.(DataFlow::AttrRead).getObject() = mongoClientDB()
70+
result.(DataFlow::AttrRead).getObject() = mongoDB()
5871
)
5972
or
60-
exists(DataFlow::TypeTracker t2 | result = mongoClientCollection(t2).track(t2, t))
73+
exists(DataFlow::TypeTracker t2 | result = mongoCollection(t2).track(t2, t))
6174
}
6275

63-
/** Gets a reference to a `MongoClient` collection. */
64-
private DataFlow::Node mongoClientCollection() {
65-
mongoClientCollection(DataFlow::TypeTracker::end()).flowsTo(result)
76+
/** Gets a reference to a `Mongo` collection use. */
77+
private DataFlow::Node mongoCollection() {
78+
mongoCollection(DataFlow::TypeTracker::end()).flowsTo(result)
6679
}
6780

68-
/** This class represents names of find_* relevant MongoClient collection level operation methods. */
69-
private class MongoClientMethodNames extends string {
70-
MongoClientMethodNames() {
71-
// the find_one_or_404 method is only found in the Pymongo Flask library.
81+
/** This class represents names of find_* relevant `Mongo` collection-level operation methods. */
82+
private class MongoCollectionMethodNames extends string {
83+
MongoCollectionMethodNames() {
7284
this in [
7385
"find", "find_raw_batches", "find_one", "find_one_and_delete", "find_and_modify",
7486
"find_one_and_replace", "find_one_and_update", "find_one_or_404"
7587
]
7688
}
7789
}
7890

79-
/** Gets a reference to a `MongoClient` Collection method. */
80-
private DataFlow::Node mongoClientMethod() {
81-
result.(DataFlow::AttrRead).getAttributeName() instanceof MongoClientMethodNames and
82-
(
83-
result.(DataFlow::AttrRead).getObject() = mongoClientCollection() or
84-
result.(DataFlow::AttrRead) = mongoClientCollection()
85-
)
91+
/** Gets a reference to a `Mongo` collection method. */
92+
private DataFlow::Node mongoCollectionMethod() {
93+
mongoCollection() in [result.(DataFlow::AttrRead), result.(DataFlow::AttrRead).getObject()] and
94+
result.(DataFlow::AttrRead).getAttributeName() instanceof MongoCollectionMethodNames
8695
}
8796

88-
/** Gets a reference to a `MongoClient` call */
89-
private class MongoClientCall extends DataFlow::CallCfgNode, NoSQLQuery::Range {
90-
MongoClientCall() { this.getFunction() = mongoClientMethod() }
97+
/** Gets a reference to a `Mongo` collection method call */
98+
private class MongoCollectionCall extends DataFlow::CallCfgNode, NoSQLQuery::Range {
99+
MongoCollectionCall() { this.getFunction() = mongoCollectionMethod() }
91100

92101
override DataFlow::Node getQuery() { result = this.getArg(0) }
93102
}
94103

95104
private class MongoEngineObjectsCall extends DataFlow::CallCfgNode, NoSQLQuery::Range {
96105
MongoEngineObjectsCall() {
97106
this =
98-
API::moduleImport("mongoengine")
99-
.getMember(["Document", "EmbeddedDocument"])
100-
.getASubclass()
101-
.getMember("objects")
102-
.getACall()
103-
}
104-
105-
override DataFlow::Node getQuery() { result = this.getArgByName(_) }
106-
}
107-
108-
private class FlaskMongoEngineObjectsCall extends DataFlow::CallCfgNode, NoSQLQuery::Range {
109-
FlaskMongoEngineObjectsCall() {
110-
this =
111-
API::moduleImport("flask_mongoengine")
112-
.getMember("MongoEngine")
113-
.getReturn()
107+
[mongoEngine(), flask_MongoEngine()]
114108
.getMember(["Document", "EmbeddedDocument"])
115109
.getASubclass()
116110
.getMember("objects")
@@ -120,46 +114,6 @@ private module NoSQL {
120114
override DataFlow::Node getQuery() { result = this.getArgByName(_) }
121115
}
122116

123-
private DataFlow::Node flaskMongoEngineInstance() {
124-
result = API::moduleImport("flask_mongoengine").getMember("MongoEngine").getReturn().getAUse()
125-
}
126-
127-
/**
128-
* A MongoEngine.Document or MongoEngine.EmbeddedDocument subclass which represents a MongoDB document.
129-
*/
130-
private class FlaskMongoEngineDocumentClass extends ClassValue {
131-
FlaskMongoEngineDocumentClass() {
132-
this.getASuperType().getName() in ["Document", "EmbeddedDocument"] and
133-
exists(AttrNode documentClass |
134-
documentClass.getName() in ["Document", "EmbeddedDocument"] and
135-
documentClass.getObject() = flaskMongoEngineInstance().asCfgNode() and
136-
// This is super hacky. It checks to see if the class is a subclass of a flaskMongoEngineInstance.Document
137-
this.getASuperType()
138-
.getAReference()
139-
.getNode()
140-
.(ClassExpr)
141-
.contains(documentClass.getNode().getObject())
142-
)
143-
}
144-
}
145-
146-
private class FlaskMongoEngineDocumentSubclassInstanceCall extends DataFlow::CallCfgNode,
147-
NoSQLQuery::Range {
148-
FlaskMongoEngineDocumentSubclassInstanceCall() {
149-
exists(
150-
DataFlow::CallCfgNode objectsCall,
151-
FlaskMongoEngineDocumentClass flaskMongoEngineDocumentClass
152-
|
153-
objectsCall.getFunction().asExpr().(Attribute).getObject().getAFlowNode() =
154-
flaskMongoEngineDocumentClass.getAReference() and
155-
objectsCall.asCfgNode().(CallNode).getNode().getFunc().(Attribute).getName() = "objects" and
156-
this = objectsCall
157-
)
158-
}
159-
160-
override DataFlow::Node getQuery() { result = this.getArgByName(_) }
161-
}
162-
163117
private class MongoSanitizerCall extends DataFlow::CallCfgNode, NoSQLSanitizer::Range {
164118
MongoSanitizerCall() {
165119
this =

0 commit comments

Comments
 (0)