mirror of
https://github.com/github/codeql.git
synced 2026-04-29 10:45:15 +02:00
JS: model Mongoose Document for additional js/nosql-injection sinks
This commit is contained in:
@@ -277,6 +277,16 @@ private module Mongoose {
|
||||
name = "updateOne" or
|
||||
name = "where"
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if Model method `name` returns one or more documents, the
|
||||
* documents are wrapped in an array if `asArray` is true.
|
||||
*/
|
||||
predicate returnsDocument(string name, boolean asArray) {
|
||||
asArray = false and name = "findOne"
|
||||
or
|
||||
asArray = true and name = "find"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -296,6 +306,18 @@ private module Mongoose {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A Mongoose query object as a result of a Document method call.
|
||||
*/
|
||||
private class QueryFromDocument extends DataFlow::MethodCallNode {
|
||||
QueryFromDocument() {
|
||||
exists(string name |
|
||||
Document::MethodSignatures::returnsQuery(name) and
|
||||
Document::ref().getAMethodCall(name) = this
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A Mongoose query object as a result of a Query constructor invocation.
|
||||
*/
|
||||
@@ -312,6 +334,7 @@ private module Mongoose {
|
||||
(
|
||||
result instanceof QueryFromConstructor or
|
||||
result instanceof QueryFromModel or
|
||||
result instanceof QueryFromDocument or
|
||||
result.hasUnderlyingType("mongoose", "Query")
|
||||
) and
|
||||
t.start()
|
||||
@@ -450,6 +473,138 @@ private module Mongoose {
|
||||
name = "within" or
|
||||
name = "wtimeout"
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if Query method `name` returns one or more documents, the
|
||||
* documents are wrapped in an array if `asArray` is true.
|
||||
*/
|
||||
predicate returnsDocument(string name, boolean asArray) {
|
||||
asArray = false and name = "findOne"
|
||||
or
|
||||
asArray = true and name = "find"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides classes modeling the Mongoose Document class
|
||||
*/
|
||||
module Document {
|
||||
/**
|
||||
* A Mongoose Document that is retrieved from the backing database.
|
||||
*/
|
||||
class RetrievedDocument extends DataFlow::SourceNode {
|
||||
RetrievedDocument() {
|
||||
exists(boolean asArray, DataFlow::ParameterNode param |
|
||||
exists(DataFlow::SourceNode base, DataFlow::MethodCallNode call, string name |
|
||||
base = Query::ref() and Query::MethodSignatures::returnsDocument(name, asArray)
|
||||
or
|
||||
base = Model::ref() and Model::MethodSignatures::returnsDocument(name, asArray)
|
||||
or
|
||||
base = ref() and MethodSignatures::returnsDocument(name, asArray)
|
||||
|
|
||||
call = base.getAMethodCall(name) and
|
||||
param = call.getCallback(call.getNumArgument() - 1).getParameter(1)
|
||||
)
|
||||
or
|
||||
exists(
|
||||
DataFlow::SourceNode base, DataFlow::MethodCallNode call, string executor,
|
||||
int paramIndex
|
||||
|
|
||||
executor = "then" and paramIndex = 0
|
||||
or
|
||||
executor = "exec" and paramIndex = 1
|
||||
|
|
||||
base = Query::ref() and
|
||||
call = base.getAMethodCall(executor) and
|
||||
param = call.getCallback(0).getParameter(paramIndex) and
|
||||
exists(DataFlow::MethodCallNode pred |
|
||||
// limitation: look at the previous method call
|
||||
Query::MethodSignatures::returnsDocument(pred.getMethodName(), asArray) and
|
||||
pred.getAMethodCall() = call
|
||||
)
|
||||
)
|
||||
|
|
||||
asArray = false and this = param
|
||||
or
|
||||
asArray = true and
|
||||
exists(DataFlow::PropRead access |
|
||||
// limitation: look for direct accesses
|
||||
access = param.getAPropertyRead() and
|
||||
not exists(access.getPropertyName()) and
|
||||
this = access
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a data flow node referring to a Mongoose Document object.
|
||||
*/
|
||||
private DataFlow::SourceNode ref(DataFlow::TypeTracker t) {
|
||||
(
|
||||
result instanceof RetrievedDocument or
|
||||
result.hasUnderlyingType("mongoose", "Document")
|
||||
) 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::returnsDocument(name, true))) and
|
||||
t = t2.continue()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a data flow node referring to a Mongoose Document object.
|
||||
*/
|
||||
DataFlow::SourceNode ref() { result = ref(DataFlow::TypeTracker::end()) }
|
||||
|
||||
module MethodSignatures {
|
||||
/**
|
||||
* Holds if Document method `name` returns a Query.
|
||||
*/
|
||||
predicate returnsQuery(string name) {
|
||||
// Documents are subtypes of Models
|
||||
Model::MethodSignatures::returnsQuery(name) or
|
||||
name = "replaceOne" or
|
||||
name = "update" or
|
||||
name = "updateOne"
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if Document method `name` interprets parameter `n` as a query.
|
||||
*/
|
||||
predicate interpretsArgumentAsQuery(string name, int n) {
|
||||
// Documents are subtypes of Models
|
||||
Model::MethodSignatures::interpretsArgumentAsQuery(name, n)
|
||||
or
|
||||
n = 0 and
|
||||
(
|
||||
name = "replaceOne" or
|
||||
name = "update" or
|
||||
name = "updateOne"
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if Document method `name` returns one or more documents, the
|
||||
* documents are wrapped in an array if `asArray` is true.
|
||||
*/
|
||||
predicate returnsDocument(string name, boolean asArray) {
|
||||
// Documents are subtypes of Models
|
||||
Model::MethodSignatures::returnsDocument(name, asArray)
|
||||
or
|
||||
asArray = false and
|
||||
(
|
||||
name = "depopulate" or
|
||||
name = "init" or
|
||||
name = "populate" or
|
||||
name = "overwrite"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -486,6 +641,12 @@ private module Mongoose {
|
||||
exists(string method, int n | Query::MethodSignatures::interpretsArgumentAsQuery(method, n) |
|
||||
this = Query::ref().getAMethodCall(method).getArgument(n).asExpr()
|
||||
)
|
||||
or
|
||||
exists(DataFlow::MethodCallNode mcn, string method, int n |
|
||||
Document::MethodSignatures::interpretsArgumentAsQuery(method, n) and
|
||||
mcn = Document::ref().getAMethodCall(method) and
|
||||
this = mcn.getArgument(n).asExpr()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -515,6 +676,13 @@ private module Mongoose {
|
||||
// callback provided to a Query method call
|
||||
exists(mcn.getCallback(mcn.getNumArgument() - 1))
|
||||
)
|
||||
or
|
||||
exists(string method |
|
||||
Document::MethodSignatures::returnsQuery(method) and
|
||||
mcn = Document::ref().getAMethodCall(method) and
|
||||
// callback provided to a Document method call
|
||||
exists(mcn.getCallback(mcn.getNumArgument() - 1))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -90,6 +90,18 @@ nodes
|
||||
| mongoose.js:81:46:81:50 | query |
|
||||
| mongoose.js:82:47:82:51 | query |
|
||||
| mongoose.js:82:47:82:51 | query |
|
||||
| mongoose.js:84:46:84:50 | query |
|
||||
| mongoose.js:84:46:84:50 | query |
|
||||
| mongoose.js:86:51:86:55 | query |
|
||||
| mongoose.js:86:51:86:55 | query |
|
||||
| mongoose.js:88:46:88:50 | query |
|
||||
| mongoose.js:88:46:88:50 | query |
|
||||
| mongoose.js:91:46:91:50 | query |
|
||||
| mongoose.js:91:46:91:50 | query |
|
||||
| mongoose.js:93:51:93:55 | query |
|
||||
| mongoose.js:93:51:93:55 | query |
|
||||
| mongoose.js:95:46:95:50 | query |
|
||||
| mongoose.js:95:46:95:50 | query |
|
||||
| mongooseJsonParse.js:19:11:19:20 | query |
|
||||
| mongooseJsonParse.js:19:19:19:20 | {} |
|
||||
| mongooseJsonParse.js:20:19:20:44 | JSON.pa ... y.data) |
|
||||
@@ -236,6 +248,18 @@ edges
|
||||
| mongoose.js:20:11:20:20 | query | mongoose.js:81:46:81:50 | query |
|
||||
| mongoose.js:20:11:20:20 | query | mongoose.js:82:47:82:51 | query |
|
||||
| mongoose.js:20:11:20:20 | query | mongoose.js:82:47:82:51 | query |
|
||||
| mongoose.js:20:11:20:20 | query | mongoose.js:84:46:84:50 | query |
|
||||
| mongoose.js:20:11:20:20 | query | mongoose.js:84:46:84:50 | query |
|
||||
| mongoose.js:20:11:20:20 | query | mongoose.js:86:51:86:55 | query |
|
||||
| mongoose.js:20:11:20:20 | query | mongoose.js:86:51:86:55 | query |
|
||||
| mongoose.js:20:11:20:20 | query | mongoose.js:88:46:88:50 | query |
|
||||
| mongoose.js:20:11:20:20 | query | mongoose.js:88:46:88:50 | query |
|
||||
| mongoose.js:20:11:20:20 | query | mongoose.js:91:46:91:50 | query |
|
||||
| mongoose.js:20:11:20:20 | query | mongoose.js:91:46:91:50 | query |
|
||||
| mongoose.js:20:11:20:20 | query | mongoose.js:93:51:93:55 | query |
|
||||
| mongoose.js:20:11:20:20 | query | mongoose.js:93:51:93:55 | query |
|
||||
| mongoose.js:20:11:20:20 | query | mongoose.js:95:46:95:50 | query |
|
||||
| mongoose.js:20:11:20:20 | query | mongoose.js:95:46:95:50 | query |
|
||||
| mongoose.js:20:19:20:20 | {} | mongoose.js:20:11:20:20 | query |
|
||||
| mongoose.js:21:19:21:26 | req.body | mongoose.js:21:19:21:32 | req.body.title |
|
||||
| mongoose.js:21:19:21:26 | req.body | mongoose.js:21:19:21:32 | req.body.title |
|
||||
@@ -285,6 +309,18 @@ edges
|
||||
| mongoose.js:21:19:21:32 | req.body.title | mongoose.js:81:46:81:50 | query |
|
||||
| mongoose.js:21:19:21:32 | req.body.title | mongoose.js:82:47:82:51 | query |
|
||||
| mongoose.js:21:19:21:32 | req.body.title | mongoose.js:82:47:82:51 | query |
|
||||
| mongoose.js:21:19:21:32 | req.body.title | mongoose.js:84:46:84:50 | query |
|
||||
| mongoose.js:21:19:21:32 | req.body.title | mongoose.js:84:46:84:50 | query |
|
||||
| mongoose.js:21:19:21:32 | req.body.title | mongoose.js:86:51:86:55 | query |
|
||||
| mongoose.js:21:19:21:32 | req.body.title | mongoose.js:86:51:86:55 | query |
|
||||
| mongoose.js:21:19:21:32 | req.body.title | mongoose.js:88:46:88:50 | query |
|
||||
| mongoose.js:21:19:21:32 | req.body.title | mongoose.js:88:46:88:50 | query |
|
||||
| mongoose.js:21:19:21:32 | req.body.title | mongoose.js:91:46:91:50 | query |
|
||||
| mongoose.js:21:19:21:32 | req.body.title | mongoose.js:91:46:91:50 | query |
|
||||
| mongoose.js:21:19:21:32 | req.body.title | mongoose.js:93:51:93:55 | query |
|
||||
| mongoose.js:21:19:21:32 | req.body.title | mongoose.js:93:51:93:55 | query |
|
||||
| mongoose.js:21:19:21:32 | req.body.title | mongoose.js:95:46:95:50 | query |
|
||||
| mongoose.js:21:19:21:32 | req.body.title | mongoose.js:95:46:95:50 | query |
|
||||
| mongooseJsonParse.js:19:11:19:20 | query | mongooseJsonParse.js:23:19:23:23 | query |
|
||||
| mongooseJsonParse.js:19:11:19:20 | query | mongooseJsonParse.js:23:19:23:23 | query |
|
||||
| mongooseJsonParse.js:19:19:19:20 | {} | mongooseJsonParse.js:19:11:19:20 | query |
|
||||
@@ -357,6 +393,12 @@ edges
|
||||
| mongoose.js:76:10:76:14 | query | mongoose.js:21:19:21:26 | req.body | mongoose.js:76:10:76:14 | query | This query depends on $@. | mongoose.js:21:19:21:26 | req.body | a user-provided value |
|
||||
| mongoose.js:81:46:81:50 | query | mongoose.js:21:19:21:26 | req.body | mongoose.js:81:46:81:50 | query | This query depends on $@. | mongoose.js:21:19:21:26 | req.body | a user-provided value |
|
||||
| mongoose.js:82:47:82:51 | query | mongoose.js:21:19:21:26 | req.body | mongoose.js:82:47:82:51 | query | This query depends on $@. | mongoose.js:21:19:21:26 | req.body | a user-provided value |
|
||||
| mongoose.js:84:46:84:50 | query | mongoose.js:21:19:21:26 | req.body | mongoose.js:84:46:84:50 | query | This query depends on $@. | mongoose.js:21:19:21:26 | req.body | a user-provided value |
|
||||
| mongoose.js:86:51:86:55 | query | mongoose.js:21:19:21:26 | req.body | mongoose.js:86:51:86:55 | query | This query depends on $@. | mongoose.js:21:19:21:26 | req.body | a user-provided value |
|
||||
| mongoose.js:88:46:88:50 | query | mongoose.js:21:19:21:26 | req.body | mongoose.js:88:46:88:50 | query | This query depends on $@. | mongoose.js:21:19:21:26 | req.body | a user-provided value |
|
||||
| mongoose.js:91:46:91:50 | query | mongoose.js:21:19:21:26 | req.body | mongoose.js:91:46:91:50 | query | This query depends on $@. | mongoose.js:21:19:21:26 | req.body | a user-provided value |
|
||||
| mongoose.js:93:51:93:55 | query | mongoose.js:21:19:21:26 | req.body | mongoose.js:93:51:93:55 | query | This query depends on $@. | mongoose.js:21:19:21:26 | req.body | a user-provided value |
|
||||
| mongoose.js:95:46:95:50 | query | mongoose.js:21:19:21:26 | req.body | mongoose.js:95:46:95:50 | query | This query depends on $@. | mongoose.js:21:19:21:26 | req.body | a user-provided value |
|
||||
| mongooseJsonParse.js:23:19:23:23 | query | mongooseJsonParse.js:20:30:20:43 | req.query.data | mongooseJsonParse.js:23:19:23:23 | query | This query depends on $@. | mongooseJsonParse.js:20:30:20:43 | req.query.data | a user-provided value |
|
||||
| mongooseModelClient.js:11:16:11:24 | { id: v } | mongooseModelClient.js:10:22:10:29 | req.body | mongooseModelClient.js:11:16:11:24 | { id: v } | This query depends on $@. | mongooseModelClient.js:10:22:10:29 | req.body | a user-provided value |
|
||||
| mongooseModelClient.js:12:16:12:34 | { id: req.body.id } | mongooseModelClient.js:12:22:12:29 | req.body | mongooseModelClient.js:12:16:12:34 | { id: req.body.id } | This query depends on $@. | mongooseModelClient.js:12:22:12:29 | req.body | a user-provided value |
|
||||
|
||||
Reference in New Issue
Block a user