Python: sinks -> decodings

Query operators that interpret JavaScript
are no longer considered sinks.
Instead they are considered decodings
and the output is the tainted dictionary.
The state changes to `DictInput` to reflect
that the user now controls a dangerous dictionary.

This fixes the spurious result and moves the error reporting
to a more logical place.
This commit is contained in:
Rasmus Lerchedahl Petersen
2023-09-11 16:33:20 +02:00
parent d9f63e1ed3
commit a063d7d510
4 changed files with 44 additions and 27 deletions

View File

@@ -123,40 +123,49 @@ private module NoSql {
}
/** The `$where` query operator executes a string as JavaScript. */
private class WhereQueryOperator extends API::CallNode, NoSqlQuery::Range {
private class WhereQueryOperator extends DataFlow::Node, Decoding::Range {
API::Node dictionary;
DataFlow::Node query;
WhereQueryOperator() {
this = mongoCollection().getMember(mongoCollectionMethodName()).getACall() and
query = this.getParameter(0).getSubscript("$where").asSink()
dictionary =
mongoCollection().getMember(mongoCollectionMethodName()).getACall().getParameter(0) and
query = dictionary.getSubscript("$where").asSink() and
this = dictionary.asSink()
}
override DataFlow::Node getQuery() { result = query }
override DataFlow::Node getAnInput() { result = query }
override predicate interpretsDict() { none() }
override DataFlow::Node getOutput() { result = this }
override predicate vulnerableToStrings() { any() }
override string getFormat() { result = "NoSQL" }
override predicate mayExecuteInput() { any() }
}
/** The `$function` query operator executes its `body` string as JavaScript. */
private class FunctionQueryOperator extends API::CallNode, NoSqlQuery::Range {
private class FunctionQueryOperator extends DataFlow::Node, Decoding::Range {
API::Node dictionary;
DataFlow::Node query;
FunctionQueryOperator() {
this = mongoCollection().getMember(mongoCollectionMethodName()).getACall() and
query =
this.getParameter(0)
.getASubscript*()
.getSubscript("$function")
.getSubscript("body")
.asSink()
dictionary =
mongoCollection()
.getMember(mongoCollectionMethodName())
.getACall()
.getParameter(0)
.getASubscript*() and
query = dictionary.getSubscript("$function").getSubscript("body").asSink() and
this = dictionary.asSink()
}
override DataFlow::Node getQuery() { result = query }
override DataFlow::Node getAnInput() { result = query }
override predicate interpretsDict() { none() }
override DataFlow::Node getOutput() { result = this }
override predicate vulnerableToStrings() { any() }
override string getFormat() { result = "NoSQL" }
override predicate mayExecuteInput() { any() }
}
/**

View File

@@ -75,7 +75,7 @@ module NoSqlInjection {
/** A JSON decoding converts a string to a dictionary. */
class JsonDecoding extends Decoding, StringToDictConversion {
JsonDecoding() { this.getFormat() = "JSON" }
JsonDecoding() { this.getFormat() in ["JSON", "NoSQL"] }
override DataFlow::Node getAnInput() { result = Decoding.super.getAnInput() }