JS: refactoring: make separate modules for mongoose Model and Query

This commit is contained in:
Esben Sparre Andreasen
2020-03-10 09:55:16 +01:00
parent 480be06d86
commit 3bfda6cd38

View File

@@ -210,233 +210,243 @@ private module Mongoose {
}
/**
* Gets a data flow node referring to a Mongoose model object.
* Provides classes modeling the Mongoose Model class
*/
private DataFlow::SourceNode getAModel(DataFlow::TypeTracker t) {
(
result = getAMongooseInstance().getAMemberCall("model") or
result.hasUnderlyingType("mongoose", "Model")
) and
t.start()
or
exists(DataFlow::TypeTracker t2 | result = getAModel(t2).track(t2, t))
}
/**
* Gets a data flow node referring to a Mongoose model object.
*/
DataFlow::SourceNode getAModel() { result = getAModel(DataFlow::TypeTracker::end()) }
/**
* Provides signatures for the Model methods.
*/
module ModelMethodSignatures {
module Model {
/**
* Holds if Model method `name` interprets parameter `n` as a query.
* Gets a data flow node referring to a Mongoose Model object.
*/
predicate interpretsArgumentAsQuery(string name, int n) {
// implement lots of the MongoDB collection interface
MongoDB::CollectionMethodSignatures::interpretsArgumentAsQuery(name, n)
or
name = "findByIdAndUpdate" and n = 1
}
/**
* Holds if Model method `name` returns a Query.
*/
predicate returnsQuery(string name) {
name = "$where" or
name = "count" or
name = "countDocuments" or
name = "deleteMany" or
name = "deleteOne" or
name = "find" or
name = "findById" or
name = "findByIdAndDelete" or
name = "findByIdAndRemove" or
name = "findByIdAndUpdate" or
name = "findOne" or
name = "findOneAndDelete" or
name = "findOneAndRemove" or
name = "findOneAndReplace" or
name = "findOneAndUpdate" or
name = "geosearch" or
name = "replaceOne" or
name = "update" or
name = "updateMany" or
name = "updateOne" or
name = "where"
}
}
/**
* Provides signatures for the Query methods.
*/
module QueryMethodSignatures {
/**
* Holds if Query method `name` interprets parameter `n` as a query.
*/
predicate interpretsArgumentAsQuery(string name, int n) {
n = 0 and
private DataFlow::SourceNode ref(DataFlow::TypeTracker t) {
(
name = "and" or
result = getAMongooseInstance().getAMemberCall("model") or
result.hasUnderlyingType("mongoose", "Model")
) and
t.start()
or
exists(DataFlow::TypeTracker t2 | result = ref(t2).track(t2, t))
}
/**
* Gets a data flow node referring to a Mongoose model object.
*/
DataFlow::SourceNode ref() { result = ref(DataFlow::TypeTracker::end()) }
/**
* Provides signatures for the Model methods.
*/
module MethodSignatures {
/**
* Holds if Model method `name` interprets parameter `n` as a query.
*/
predicate interpretsArgumentAsQuery(string name, int n) {
// implement lots of the MongoDB collection interface
MongoDB::CollectionMethodSignatures::interpretsArgumentAsQuery(name, n)
or
name = "findByIdAndUpdate" and n = 1
}
/**
* Holds if Model method `name` returns a Query.
*/
predicate returnsQuery(string name) {
name = "$where" or
name = "count" or
name = "countDocuments" or
name = "deleteMany" or
name = "deleteOne" or
name = "elemMatch" or
name = "find" or
name = "findById" or
name = "findByIdAndDelete" or
name = "findByIdAndRemove" or
name = "findByIdAndUpdate" or
name = "findOne" or
name = "findOneAndDelete" or
name = "findOneAndRemove" or
name = "findOneAndReplace" or
name = "findOneAndUpdate" or
name = "merge" or
name = "nor" or
name = "or" or
name = "remove" or
name = "geosearch" or
name = "replaceOne" or
name = "setQuery" or
name = "setUpdate" or
name = "update" or
name = "updateMany" or
name = "updateOne" or
name = "where"
)
or
n = 1 and
}
}
}
/**
* Provides classes modeling the Mongoose Query class
*/
module Query {
/**
* A Mongoose query object as a result of a Model method call.
*/
private class QueryFromModel extends DataFlow::MethodCallNode {
QueryFromModel() {
exists(string name |
Model::MethodSignatures::returnsQuery(name) and
Model::ref().getAMethodCall(name) = this
)
}
}
/**
* A Mongoose query object as a result of a Query constructor invocation.
*/
class QueryFromConstructor extends DataFlow::NewNode {
QueryFromConstructor() {
this = getAMongooseInstance().getAPropertyRead("Query").getAnInstantiation()
}
}
/**
* Gets a data flow node referring to a Mongoose query object.
*/
private DataFlow::SourceNode ref(DataFlow::TypeTracker t) {
(
name = "distinct" or
name = "findOneAndUpdate" or
name = "update" or
name = "updateMany" or
name = "updateOne"
result instanceof QueryFromConstructor or
result instanceof QueryFromModel or
result.hasUnderlyingType("mongoose", "Query")
) and
t.start()
or
exists(DataFlow::TypeTracker t2, DataFlow::SourceNode succ | succ = ref(t2) |
result = succ.track(t2, t)
or
result = succ.getAMethodCall(any(string name | MethodSignatures::returnsQuery(name))) and
t = t2.continue()
)
}
/**
* Holds if Query method `name` returns a Query.
* Gets a data flow node referring to a Mongoose query object.
*/
predicate returnsQuery(string name) {
name = "$where" or
name = "J" or
name = "all" or
name = "and" or
name = "batchsize" or
name = "box" or
name = "center" or
name = "centerSphere" or
name = "circle" or
name = "collation" or
name = "comment" or
name = "count" or
name = "countDocuments" or
name = "distinct" or
name = "elemMatch" or
name = "equals" or
name = "error" or
name = "estimatedDocumentCount" or
name = "exists" or
name = "explain" or
name = "find" or
name = "findById" or
name = "findOne" or
name = "findOneAndRemove" or
name = "findOneAndUpdate" or
name = "geometry" or
name = "get" or
name = "gt" or
name = "gte" or
name = "hint" or
name = "in" or
name = "intersects" or
name = "lean" or
name = "limit" or
name = "lt" or
name = "lte" or
name = "map" or
name = "map" or
name = "maxDistance" or
name = "maxTimeMS" or
name = "maxscan" or
name = "mod" or
name = "ne" or
name = "near" or
name = "nearSphere" or
name = "nin" or
name = "or" or
name = "orFail" or
name = "polygon" or
name = "populate" or
name = "read" or
name = "readConcern" or
name = "regexp" or
name = "remove" or
name = "select" or
name = "session" or
name = "set" or
name = "setOptions" or
name = "setQuery" or
name = "setUpdate" or
name = "size" or
name = "skip" or
name = "slaveOk" or
name = "slice" or
name = "snapshot" or
name = "sort" or
name = "update" or
name = "w" or
name = "where" or
name = "within" or
name = "wtimeout"
DataFlow::SourceNode ref() { result = ref(DataFlow::TypeTracker::end()) }
/**
* Provides signatures for the Query methods.
*/
module MethodSignatures {
/**
* Holds if Query method `name` interprets parameter `n` as a query.
*/
predicate interpretsArgumentAsQuery(string name, int n) {
n = 0 and
(
name = "and" or
name = "count" or
name = "countDocuments" or
name = "deleteMany" or
name = "deleteOne" or
name = "elemMatch" or
name = "find" or
name = "findOne" or
name = "findOneAndDelete" or
name = "findOneAndRemove" or
name = "findOneAndReplace" or
name = "findOneAndUpdate" or
name = "merge" or
name = "nor" or
name = "or" or
name = "remove" or
name = "replaceOne" or
name = "setQuery" or
name = "setUpdate" or
name = "update" or
name = "updateMany" or
name = "updateOne" or
name = "where"
)
or
n = 1 and
(
name = "distinct" or
name = "findOneAndUpdate" or
name = "update" or
name = "updateMany" or
name = "updateOne"
)
}
/**
* Holds if Query method `name` returns a Query.
*/
predicate returnsQuery(string name) {
name = "$where" or
name = "J" or
name = "all" or
name = "and" or
name = "batchsize" or
name = "box" or
name = "center" or
name = "centerSphere" or
name = "circle" or
name = "collation" or
name = "comment" or
name = "count" or
name = "countDocuments" or
name = "distinct" or
name = "elemMatch" or
name = "equals" or
name = "error" or
name = "estimatedDocumentCount" or
name = "exists" or
name = "explain" or
name = "find" or
name = "findById" or
name = "findOne" or
name = "findOneAndRemove" or
name = "findOneAndUpdate" or
name = "geometry" or
name = "get" or
name = "gt" or
name = "gte" or
name = "hint" or
name = "in" or
name = "intersects" or
name = "lean" or
name = "limit" or
name = "lt" or
name = "lte" or
name = "map" or
name = "map" or
name = "maxDistance" or
name = "maxTimeMS" or
name = "maxscan" or
name = "mod" or
name = "ne" or
name = "near" or
name = "nearSphere" or
name = "nin" or
name = "or" or
name = "orFail" or
name = "polygon" or
name = "populate" or
name = "read" or
name = "readConcern" or
name = "regexp" or
name = "remove" or
name = "select" or
name = "session" or
name = "set" or
name = "setOptions" or
name = "setQuery" or
name = "setUpdate" or
name = "size" or
name = "skip" or
name = "slaveOk" or
name = "slice" or
name = "snapshot" or
name = "sort" or
name = "update" or
name = "w" or
name = "where" or
name = "within" or
name = "wtimeout"
}
}
}
/**
* A Mongoose query object as a result of a Model method call.
*/
private class QueryFromModel extends DataFlow::MethodCallNode {
QueryFromModel() {
exists(string name |
ModelMethodSignatures::returnsQuery(name) and
getAModel().getAMethodCall(name) = this
)
}
}
/**
* A Mongoose query object as a result of a Query constructor invocation.
*/
private class QueryFromConstructor extends DataFlow::NewNode {
QueryFromConstructor() {
this = getAMongooseInstance().getAPropertyRead("Query").getAnInstantiation()
}
}
/**
* Gets a data flow node referring to a Mongoose query object.
*/
private DataFlow::SourceNode getAQuery(DataFlow::TypeTracker t) {
(
result instanceof QueryFromConstructor or
result instanceof QueryFromModel or
result.hasUnderlyingType("mongoose", "Query")
) and
t.start()
or
exists(DataFlow::TypeTracker t2, DataFlow::SourceNode succ | succ = getAQuery(t2) |
result = succ.track(t2, t)
or
result = succ.getAMethodCall(any(string name | QueryMethodSignatures::returnsQuery(name))) and
t = t2.continue()
)
}
/**
* Gets a data flow node referring to a Mongoose query object.
*/
private DataFlow::SourceNode getAQuery() { result = getAQuery(DataFlow::TypeTracker::end()) }
/**
* An expression passed to `mongoose.createConnection` to supply credentials.
*/
@@ -460,15 +470,15 @@ private module Mongoose {
class MongoDBQueryPart extends NoSQL::Query {
MongoDBQueryPart() {
exists(DataFlow::MethodCallNode mcn, string method, int n |
ModelMethodSignatures::interpretsArgumentAsQuery(method, n) and
mcn = getAModel().getAMethodCall(method) and
Model::MethodSignatures::interpretsArgumentAsQuery(method, n) and
mcn = Model::ref().getAMethodCall(method) and
this = mcn.getArgument(n).asExpr()
)
or
this = any(QueryFromConstructor c).getArgument(2).asExpr()
this = any(Query::QueryFromConstructor c).getArgument(2).asExpr()
or
exists(string method, int n | QueryMethodSignatures::interpretsArgumentAsQuery(method, n) |
this = getAQuery().getAMethodCall(method).getArgument(n).asExpr()
exists(string method, int n | Query::MethodSignatures::interpretsArgumentAsQuery(method, n) |
this = Query::ref().getAMethodCall(method).getArgument(n).asExpr()
)
}
}
@@ -483,13 +493,13 @@ private module Mongoose {
this = mcn and
(
exists(string method |
ModelMethodSignatures::returnsQuery(method) and
mcn = getAModel().getAMethodCall(method) and
Model::MethodSignatures::returnsQuery(method) and
mcn = Model::ref().getAMethodCall(method) and
// callback provided to a Model method call
exists(mcn.getCallback(mcn.getNumArgument() - 1))
)
or
getAQuery().getAMethodCall() = mcn and
Query::ref().getAMethodCall() = mcn and
(
// explicit execution using a Query method call
exists(string executor | executor = "exec" or executor = "then" or executor = "catch" |