mirror of
https://github.com/github/codeql.git
synced 2025-12-21 19:26:31 +01:00
Python: Split modelling of query operators
`$where` and `$function` behave quite differently.
This commit is contained in:
@@ -99,9 +99,6 @@ private module NoSql {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Gets the name of a mongo query operator that will interpret JavaScript. */
|
|
||||||
private string mongoQueryOperator() { result in ["$where", "$function"] }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a reference to a `Mongo` collection method call
|
* Gets a reference to a `Mongo` collection method call
|
||||||
*
|
*
|
||||||
@@ -125,12 +122,34 @@ private module NoSql {
|
|||||||
override predicate vulnerableToStrings() { none() }
|
override predicate vulnerableToStrings() { none() }
|
||||||
}
|
}
|
||||||
|
|
||||||
private class MongoCollectionQueryOperator extends API::CallNode, NoSqlQuery::Range {
|
/** The `$where` query operator executes a string as JavaScript. */
|
||||||
|
private class WhereQueryOperator extends API::CallNode, NoSqlQuery::Range {
|
||||||
DataFlow::Node query;
|
DataFlow::Node query;
|
||||||
|
|
||||||
MongoCollectionQueryOperator() {
|
WhereQueryOperator() {
|
||||||
this = mongoCollection().getMember(mongoCollectionMethodName()).getACall() and
|
this = mongoCollection().getMember(mongoCollectionMethodName()).getACall() and
|
||||||
query = this.getParameter(0).getSubscript(mongoQueryOperator()).asSink()
|
query = this.getParameter(0).getSubscript("$where").asSink()
|
||||||
|
}
|
||||||
|
|
||||||
|
override DataFlow::Node getQuery() { result = query }
|
||||||
|
|
||||||
|
override predicate interpretsDict() { none() }
|
||||||
|
|
||||||
|
override predicate vulnerableToStrings() { any() }
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The `$function` query operator executes its `body` string as JavaScript. */
|
||||||
|
private class FunctionQueryOperator extends API::CallNode, NoSqlQuery::Range {
|
||||||
|
DataFlow::Node query;
|
||||||
|
|
||||||
|
FunctionQueryOperator() {
|
||||||
|
this = mongoCollection().getMember(mongoCollectionMethodName()).getACall() and
|
||||||
|
query =
|
||||||
|
this.getParameter(0)
|
||||||
|
.getASubscript*()
|
||||||
|
.getSubscript("$function")
|
||||||
|
.getSubscript("body")
|
||||||
|
.asSink()
|
||||||
}
|
}
|
||||||
|
|
||||||
override DataFlow::Node getQuery() { result = query }
|
override DataFlow::Node getQuery() { result = query }
|
||||||
|
|||||||
@@ -46,30 +46,29 @@ def by_where():
|
|||||||
post = posts.find_one({'$where': 'this.author === "'+author+'"'}) # $ result=BAD
|
post = posts.find_one({'$where': 'this.author === "'+author+'"'}) # $ result=BAD
|
||||||
return show_post(post, author)
|
return show_post(post, author)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/byFunction', methods=['GET'])
|
@app.route('/byFunction', methods=['GET'])
|
||||||
def by_function():
|
def by_function():
|
||||||
author = request.args['author']
|
author = request.args['author']
|
||||||
search = {
|
search = {
|
||||||
"body": 'function(author) { return(author === "'+author+'") }',
|
"body": 'function(author) { return(author === "'+author+'") }', # $ result=BAD
|
||||||
"args": [ "$author" ],
|
"args": [ "$author" ],
|
||||||
"lang": "js"
|
"lang": "js"
|
||||||
}
|
}
|
||||||
# Use `" | "a" === "a` as author
|
# Use `" | "a" === "a` as author
|
||||||
# making the query `this.author === "" | "a" === "a"`
|
# making the query `this.author === "" | "a" === "a"`
|
||||||
# Found by http://127.0.0.1:5000/byFunction?author=%22%20|%20%22a%22%20===%20%22a
|
# Found by http://127.0.0.1:5000/byFunction?author=%22%20|%20%22a%22%20===%20%22a
|
||||||
post = posts.find_one({'$expr': {'$function': search}}) # $ MISING: result=BAD
|
post = posts.find_one({'$expr': {'$function': search}}) # $ result=BAD
|
||||||
return show_post(post, author)
|
return show_post(post, author)
|
||||||
|
|
||||||
@app.route('/byFunctionArg', methods=['GET'])
|
@app.route('/byFunctionArg', methods=['GET'])
|
||||||
def by_function_arg():
|
def by_function_arg():
|
||||||
author = request.args['author']
|
author = request.args['author']
|
||||||
search = {
|
search = {
|
||||||
"body": 'function(author, target) { return(author === target) }',
|
"body": 'function(author, target) { return(author === target) }', # $ result=OK
|
||||||
"args": [ "$author", author ],
|
"args": [ "$author", author ],
|
||||||
"lang": "js"
|
"lang": "js"
|
||||||
}
|
}
|
||||||
post = posts.find_one({'$expr': {'$function': search}}) # $ result=OK
|
post = posts.find_one({'$expr': {'$function': search}}) # $ SPURIOUS: result=BAD
|
||||||
return show_post(post, author)
|
return show_post(post, author)
|
||||||
|
|
||||||
@app.route('/', methods=['GET'])
|
@app.route('/', methods=['GET'])
|
||||||
|
|||||||
Reference in New Issue
Block a user