mirror of
https://github.com/github/codeql.git
synced 2026-04-29 10:45:15 +02:00
Implemented sinks for bulk header updates, and added corresponding tests.
This commit is contained in:
@@ -1084,6 +1084,55 @@ module Http {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A data-flow node that sets multiple headers in an HTTP response using a dict.
|
||||
*
|
||||
* Extend this class to model new APIs. If you want to refine existing API models,
|
||||
* extend `ResponseHeaderBulkWrite::Range` instead.
|
||||
*/
|
||||
class ResponseHeaderBulkWrite extends DataFlow::Node instanceof ResponseHeaderBulkWrite::Range {
|
||||
/**
|
||||
* Gets the argument containing the headers dictionary.
|
||||
*/
|
||||
DataFlow::Node geBulkArg() { result = super.getBulkArg() }
|
||||
|
||||
/**
|
||||
* Holds if newlines are accepted in the header name argument.
|
||||
*/
|
||||
predicate nameAllowsNewline() { super.nameAllowsNewline() }
|
||||
|
||||
/**
|
||||
* Holds if newlines are accepted in the header value argument.
|
||||
*/
|
||||
predicate valueAllowsNewline() { super.valueAllowsNewline() }
|
||||
}
|
||||
|
||||
/** Provides a class for modelling bulk header writes on HTTP responses. */
|
||||
module ResponseHeaderBulkWrite {
|
||||
/**
|
||||
*sets multiple headers in an HTTP response using a dict.
|
||||
*
|
||||
* Extend this class to model new APIs. If you want to refine existing API models,
|
||||
* extend `ResponseHeaderBulkWrite` instead.
|
||||
*/
|
||||
abstract class Range extends DataFlow::Node {
|
||||
/**
|
||||
* Gets the argument containing the headers dictionary.
|
||||
*/
|
||||
abstract DataFlow::Node getBulkArg();
|
||||
|
||||
/**
|
||||
* Holds if newlines are accepted in the header name argument.
|
||||
*/
|
||||
abstract predicate nameAllowsNewline();
|
||||
|
||||
/**
|
||||
* Holds if newlines are accepted in the header value argument.
|
||||
*/
|
||||
abstract predicate valueAllowsNewline();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A data-flow node that sets a cookie in an HTTP response.
|
||||
*
|
||||
|
||||
@@ -222,14 +222,41 @@ module Flask {
|
||||
DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }
|
||||
|
||||
/** An `Headers` instance that is part of a Flask response. */
|
||||
private class FlaskResponseHeadersInstances extends Werkzeug::Headers::InstanceSource
|
||||
{
|
||||
private class FlaskResponseHeadersInstances extends Werkzeug::Headers::InstanceSource {
|
||||
FlaskResponseHeadersInstances() {
|
||||
this.(DataFlow::AttrRead).getObject() = instance() and
|
||||
this.(DataFlow::AttrRead).getAttributeName() = "headers"
|
||||
}
|
||||
}
|
||||
// TODO: headers arg to make_response
|
||||
|
||||
/** A class instantiation of `Response` that sets response headers. */
|
||||
private class ResponseClassHeadersWrite extends Http::Server::ResponseHeaderBulkWrite::Range,
|
||||
ClassInstantiation
|
||||
{
|
||||
override DataFlow::Node getBulkArg() {
|
||||
result = [this.getArg(2), this.getArgByName("headers")]
|
||||
}
|
||||
|
||||
override predicate nameAllowsNewline() { any() }
|
||||
|
||||
override predicate valueAllowsNewline() { none() }
|
||||
}
|
||||
|
||||
/** A call to `make_response that sets response headers. */
|
||||
private class MakeResponseHeadersWrite extends Http::Server::ResponseHeaderBulkWrite::Range,
|
||||
FlaskMakeResponseCall
|
||||
{
|
||||
override DataFlow::Node getBulkArg() {
|
||||
result = this.getArg(2)
|
||||
or
|
||||
strictcount(this.getArg(_)) = 2 and
|
||||
result = this.getArg(1)
|
||||
}
|
||||
|
||||
override predicate nameAllowsNewline() { any() }
|
||||
|
||||
override predicate valueAllowsNewline() { none() }
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
@@ -237,9 +237,21 @@ module Werkzeug {
|
||||
|
||||
override predicate valueAllowsNewline() { none() }
|
||||
}
|
||||
|
||||
/** A call to `Headers.extend`, assumed to be a response header. */
|
||||
private class HeaderExtendCall extends Http::Server::ResponseHeaderBulkWrite::Range,
|
||||
DataFlow::MethodCallNode
|
||||
{
|
||||
HeaderExtendCall() { this.calls(instance(), "extend") }
|
||||
|
||||
override DataFlow::Node getBulkArg() { result = this.getArg(0) }
|
||||
|
||||
override predicate nameAllowsNewline() { any() }
|
||||
|
||||
override predicate valueAllowsNewline() { none() }
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: `extend` bulk header update
|
||||
/**
|
||||
* Provides models for the `werkzeug.datastructures.Authorization` class
|
||||
*
|
||||
|
||||
@@ -50,4 +50,25 @@ module HttpHeaderInjection {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** A key-value pair in a literal for a bulk header update, considered as a single header update. */
|
||||
// TODO: We could instead consider bulk writes as sinks with an implicit read step of DictionaryKey/DictionaryValue content as needed.
|
||||
private class HeaderBulkWriteDictLiteral extends Http::Server::ResponseHeaderWrite::Range instanceof Http::Server::ResponseHeaderBulkWrite
|
||||
{
|
||||
KeyValuePair item;
|
||||
|
||||
HeaderBulkWriteDictLiteral() { item = super.geBulkArg().asExpr().(Dict).getAnItem() }
|
||||
|
||||
override DataFlow::Node getNameArg() { result.asExpr() = item.getKey() }
|
||||
|
||||
override DataFlow::Node getValueArg() { result.asExpr() = item.getValue() }
|
||||
|
||||
override predicate nameAllowsNewline() {
|
||||
Http::Server::ResponseHeaderBulkWrite.super.nameAllowsNewline()
|
||||
}
|
||||
|
||||
override predicate valueAllowsNewline() {
|
||||
Http::Server::ResponseHeaderBulkWrite.super.valueAllowsNewline()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user