mirror of
https://github.com/github/codeql.git
synced 2026-04-27 01:35:13 +02:00
Python: Enrich the NoSql concept
This allows us to make more precise modelling The query tests now pass. I do wonder, if there is a cleaner approach, similar to `TaintedObject` in JavaScript. I want the option to get this query in the hands of the custumors before such an investigation, though.
This commit is contained in:
@@ -389,6 +389,12 @@ module NoSqlQuery {
|
||||
abstract class Range extends DataFlow::Node {
|
||||
/** Gets the argument that specifies the NoSql query to be executed. */
|
||||
abstract DataFlow::Node getQuery();
|
||||
|
||||
/** Holds if this query will unpack/interpret a dictionary */
|
||||
abstract predicate interpretsDict();
|
||||
|
||||
/** Holds if this query can be dangerous when run on a user-controlled string */
|
||||
abstract predicate vulnerableToStrings();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -401,6 +407,12 @@ module NoSqlQuery {
|
||||
class NoSqlQuery extends DataFlow::Node instanceof NoSqlQuery::Range {
|
||||
/** Gets the argument that specifies the NoSql query to be executed. */
|
||||
DataFlow::Node getQuery() { result = super.getQuery() }
|
||||
|
||||
/** Holds if this query will unpack/interpret a dictionary */
|
||||
predicate interpretsDict() { super.interpretsDict() }
|
||||
|
||||
/** Holds if this query can be dangerous when run on a user-controlled string */
|
||||
predicate vulnerableToStrings() { super.vulnerableToStrings() }
|
||||
}
|
||||
|
||||
/** Provides classes for modeling NoSql sanitization-related APIs. */
|
||||
|
||||
@@ -91,16 +91,17 @@ private module NoSql {
|
||||
result = mongoDBInstance().getMember(["get_collection", "create_collection"]).getReturn()
|
||||
}
|
||||
|
||||
/** This class represents names of find_* relevant `Mongo` collection-level operation methods. */
|
||||
private class MongoCollectionMethodNames extends string {
|
||||
MongoCollectionMethodNames() {
|
||||
this in [
|
||||
"find", "find_raw_batches", "find_one", "find_one_and_delete", "find_and_modify",
|
||||
"find_one_and_replace", "find_one_and_update", "find_one_or_404"
|
||||
]
|
||||
}
|
||||
/** Gets the name of a find_* relevant `Mongo` collection-level operation method. */
|
||||
private string mongoCollectionMethodName() {
|
||||
result in [
|
||||
"find", "find_raw_batches", "find_one", "find_one_and_delete", "find_and_modify",
|
||||
"find_one_and_replace", "find_one_and_update", "find_one_or_404"
|
||||
]
|
||||
}
|
||||
|
||||
/** 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
|
||||
*
|
||||
@@ -114,10 +115,29 @@ private module NoSql {
|
||||
*/
|
||||
private class MongoCollectionCall extends DataFlow::CallCfgNode, NoSqlQuery::Range {
|
||||
MongoCollectionCall() {
|
||||
this = mongoCollection().getMember(any(MongoCollectionMethodNames m)).getACall()
|
||||
this = mongoCollection().getMember(mongoCollectionMethodName()).getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getQuery() { result = this.getArg(0) }
|
||||
|
||||
override predicate interpretsDict() { any() }
|
||||
|
||||
override predicate vulnerableToStrings() { none() }
|
||||
}
|
||||
|
||||
private class MongoCollectionQueryOperator extends API::CallNode, NoSqlQuery::Range {
|
||||
DataFlow::Node query;
|
||||
|
||||
MongoCollectionQueryOperator() {
|
||||
this = mongoCollection().getMember(mongoCollectionMethodName()).getACall() and
|
||||
query = this.getParameter(0).getSubscript(mongoQueryOperator()).asSink()
|
||||
}
|
||||
|
||||
override DataFlow::Node getQuery() { result = query }
|
||||
|
||||
override predicate interpretsDict() { none() }
|
||||
|
||||
override predicate vulnerableToStrings() { any() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -146,6 +166,10 @@ private module NoSql {
|
||||
}
|
||||
|
||||
override DataFlow::Node getQuery() { result = this.getArgByName(_) }
|
||||
|
||||
override predicate interpretsDict() { any() }
|
||||
|
||||
override predicate vulnerableToStrings() { none() }
|
||||
}
|
||||
|
||||
/** Gets a reference to `mongosanitizer.sanitizer.sanitize` */
|
||||
@@ -176,4 +200,23 @@ private module NoSql {
|
||||
|
||||
override DataFlow::Node getAnInput() { result = this.getArg(0) }
|
||||
}
|
||||
|
||||
/**
|
||||
* An equality operator can protect against dictionary interpretation.
|
||||
* For instance, in `{'password': {"$eq": password} }`, if a dictionary is injected into
|
||||
* `password`, it will not match.
|
||||
*/
|
||||
private class EqualityOperator extends DataFlow::Node, NoSqlSanitizer::Range {
|
||||
EqualityOperator() {
|
||||
this =
|
||||
mongoCollection()
|
||||
.getMember(mongoCollectionMethodName())
|
||||
.getParameter(0)
|
||||
.getASubscript*()
|
||||
.getSubscript("$eq")
|
||||
.asSink()
|
||||
}
|
||||
|
||||
override DataFlow::Node getAnInput() { result = this }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,6 +36,14 @@ module NoSqlInjection {
|
||||
|
||||
class RemoteFlowSourceAsStringSource extends RemoteFlowSource, StringSource { }
|
||||
|
||||
class NoSqlQueryAsStringSink extends StringSink {
|
||||
NoSqlQueryAsStringSink() {
|
||||
exists(NoSqlQuery noSqlQuery | this = noSqlQuery.getQuery() |
|
||||
noSqlQuery.vulnerableToStrings()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class NoSqlQueryAsDictSink extends DictSink {
|
||||
NoSqlQueryAsDictSink() { this = any(NoSqlQuery noSqlQuery).getQuery() }
|
||||
}
|
||||
|
||||
@@ -17,7 +17,12 @@ module Config implements DataFlow::StateConfigSig {
|
||||
|
||||
predicate isSink(DataFlow::Node source, FlowState state) {
|
||||
source instanceof C::StringSink and
|
||||
state instanceof C::StringInput
|
||||
(
|
||||
state instanceof C::StringInput
|
||||
or
|
||||
// since dictionaries can encode strings
|
||||
state instanceof C::DictInput
|
||||
)
|
||||
or
|
||||
source instanceof C::DictSink and
|
||||
state instanceof C::DictInput
|
||||
|
||||
Reference in New Issue
Block a user