mirror of
https://github.com/github/codeql.git
synced 2026-04-30 11:15:13 +02:00
Python: Model mimetype instead of content-type for HTTP Response
Since that's really what we're after (at least for now)
This commit is contained in:
@@ -26,7 +26,7 @@ class ReflectedXssConfiguration extends TaintTracking::Configuration {
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
exists(HTTP::Server::HttpResponse response |
|
||||
response.getContentType().toLowerCase().matches("text/html%") and
|
||||
response.getMimetype().toLowerCase() = "text/html" and
|
||||
sink = response.getBody()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -237,8 +237,8 @@ module HTTP {
|
||||
/** Gets the data-flow node that specifies the body of this HTTP response. */
|
||||
DataFlow::Node getBody() { result = range.getBody() }
|
||||
|
||||
/** Gets the content-type of this HTTP response, if it can be statically determined. */
|
||||
string getContentType() { result = range.getContentType() }
|
||||
/** Gets the mimetype of this HTTP response, if it can be statically determined. */
|
||||
string getMimetype() { result = range.getMimetype() }
|
||||
}
|
||||
|
||||
/** Provides a class for modeling new HTTP response APIs. */
|
||||
@@ -256,21 +256,21 @@ module HTTP {
|
||||
/** Gets the data-flow node that specifies the body of this HTTP response. */
|
||||
abstract DataFlow::Node getBody();
|
||||
|
||||
/** Gets the data-flow node that specifies the content-type of this HTTP response, if any. */
|
||||
abstract DataFlow::Node getContentTypeArg();
|
||||
/** Gets the data-flow node that specifies the content-type/mimetype of this HTTP response, if any. */
|
||||
abstract DataFlow::Node getMimetypeOrContentTypeArg();
|
||||
|
||||
/** Gets the default content-type that should be used if `getContentTypeArg` has no results. */
|
||||
abstract string getContentTypeDefault();
|
||||
/** Gets the default mimetype that should be used if `getMimetypeOrContentTypeArg` has no results. */
|
||||
abstract string getMimetypeDefault();
|
||||
|
||||
/** Gets the content-type of this HTTP response, if it can be statically determined. */
|
||||
string getContentType() {
|
||||
/** Gets the mimetype of this HTTP response, if it can be statically determined. */
|
||||
string getMimetype() {
|
||||
exists(StrConst str |
|
||||
DataFlow::localFlow(DataFlow::exprNode(str), this.getContentTypeArg()) and
|
||||
result = str.getText()
|
||||
DataFlow::localFlow(DataFlow::exprNode(str), this.getMimetypeOrContentTypeArg()) and
|
||||
result = str.getText().splitAt(";", 0)
|
||||
)
|
||||
or
|
||||
not exists(this.getContentTypeArg()) and
|
||||
result = this.getContentTypeDefault()
|
||||
not exists(this.getMimetypeOrContentTypeArg()) and
|
||||
result = this.getMimetypeDefault()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -210,27 +210,23 @@ private module FlaskModel {
|
||||
|
||||
override DataFlow::Node getBody() { result.asCfgNode() = node.getArg(0) }
|
||||
|
||||
override string getContentTypeDefault() { result = "text/html" }
|
||||
override string getMimetypeDefault() { result = "text/html" }
|
||||
|
||||
/** Gets the argument passed to the `mimetype` parameter, if any. */
|
||||
private DataFlow::Node getMimetypeArg() {
|
||||
result.asCfgNode() in [node.getArg(3), node.getArgByName("mimetype")]
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the actual argument passed to the `content_type` parameter, if any.
|
||||
* This helper method exists since `getContentTypeArg` is the method exposed by
|
||||
* `HttpResponse::Range`)
|
||||
*/
|
||||
private DataFlow::Node actualContentTypeArg() {
|
||||
/** Gets the argument passed to the `content_type` parameter, if any. */
|
||||
private DataFlow::Node getContentTypeArg() {
|
||||
result.asCfgNode() in [node.getArg(4), node.getArgByName("content_type")]
|
||||
}
|
||||
|
||||
override DataFlow::Node getContentTypeArg() {
|
||||
result = this.actualContentTypeArg()
|
||||
override DataFlow::Node getMimetypeOrContentTypeArg() {
|
||||
result = this.getContentTypeArg()
|
||||
or
|
||||
// content_type argument takes priority over mimetype argument
|
||||
not exists(this.actualContentTypeArg()) and
|
||||
not exists(this.getContentTypeArg()) and
|
||||
result = this.getMimetypeArg()
|
||||
}
|
||||
}
|
||||
@@ -464,8 +460,8 @@ private module FlaskModel {
|
||||
|
||||
override DataFlow::Node getBody() { result.asCfgNode() = node.getArg(0) }
|
||||
|
||||
override string getContentTypeDefault() { result = "text/html" }
|
||||
override string getMimetypeDefault() { result = "text/html" }
|
||||
|
||||
override DataFlow::Node getContentTypeArg() { none() }
|
||||
override DataFlow::Node getMimetypeOrContentTypeArg() { none() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
| response_test.py:92:12:92:79 | ControlFlowNode for Response() | Unexpected result: contentType=text/plain; charset=utf-8 |
|
||||
| response_test.py:99:12:99:101 | ControlFlowNode for Response() | Unexpected result: contentType=text/plain; charset=utf-8 |
|
||||
| response_test.py:114:12:114:118 | ControlFlowNode for Response() | Unexpected result: contentType=text/plain; charset=utf-8 |
|
||||
|
||||
@@ -7,7 +7,7 @@ app = Flask(__name__)
|
||||
|
||||
@app.route("/html1") # $routeSetup="/html1"
|
||||
def html1(): # $routeHandler
|
||||
return "<h1>hello</h1>" # $f-:HttpResponse $f-:contentType=text/html $f-:responseBody="<h1>hello</h1>"
|
||||
return "<h1>hello</h1>" # $f-:HttpResponse $f-:mimetype=text/html $f-:responseBody="<h1>hello</h1>"
|
||||
|
||||
|
||||
@app.route("/html2") # $routeSetup="/html2"
|
||||
@@ -15,13 +15,13 @@ def html2(): # $routeHandler
|
||||
# note that response saved in a variable intentionally -- we wan the annotations to
|
||||
# show that we recognize the response creation, and not the return (hopefully). (and
|
||||
# do the same in the following of the file)
|
||||
resp = make_response("<h1>hello</h1>") # $HttpResponse $contentType=text/html $responseBody="<h1>hello</h1>"
|
||||
resp = make_response("<h1>hello</h1>") # $HttpResponse $mimetype=text/html $responseBody="<h1>hello</h1>"
|
||||
return resp
|
||||
|
||||
|
||||
@app.route("/html3") # $routeSetup="/html3"
|
||||
def html3(): # $routeHandler
|
||||
resp = app.make_response("<h1>hello</h1>") # $HttpResponse $contentType=text/html $responseBody="<h1>hello</h1>"
|
||||
resp = app.make_response("<h1>hello</h1>") # $HttpResponse $mimetype=text/html $responseBody="<h1>hello</h1>"
|
||||
return resp
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ def html3(): # $routeHandler
|
||||
|
||||
@app.route("/html4") # $routeSetup="/html4"
|
||||
def html4(): # $routeHandler
|
||||
resp = Response("<h1>hello</h1>") # $HttpResponse $contentType=text/html $responseBody="<h1>hello</h1>"
|
||||
resp = Response("<h1>hello</h1>") # $HttpResponse $mimetype=text/html $responseBody="<h1>hello</h1>"
|
||||
return resp
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ def html4(): # $routeHandler
|
||||
def html5(): # $routeHandler
|
||||
# note: flask.Flask.response_class is set to `flask.Response` by default.
|
||||
# it can be overridden, but we don't try to handle that right now.
|
||||
resp = Flask.response_class("<h1>hello</h1>") # $f-:HttpResponse $f-:contentType=text/html $f-:responseBody="<h1>hello</h1>"
|
||||
resp = Flask.response_class("<h1>hello</h1>") # $f-:HttpResponse $f-:mimetype=text/html $f-:responseBody="<h1>hello</h1>"
|
||||
return resp
|
||||
|
||||
|
||||
@@ -47,14 +47,14 @@ def html5(): # $routeHandler
|
||||
def html6(): # $routeHandler
|
||||
# note: app.response_class (flask.Flask.response_class) is set to `flask.Response` by default.
|
||||
# it can be overridden, but we don't try to handle that right now.
|
||||
resp = app.response_class("<h1>hello</h1>") # $f-:HttpResponse $f-:contentType=text/html $f-:responseBody="<h1>hello</h1>"
|
||||
resp = app.response_class("<h1>hello</h1>") # $f-:HttpResponse $f-:mimetype=text/html $f-:responseBody="<h1>hello</h1>"
|
||||
return resp
|
||||
|
||||
|
||||
@app.route("/jsonify") # $routeSetup="/jsonify"
|
||||
def jsonify_route(): # $routeHandler
|
||||
data = {"foo": "bar"}
|
||||
response = jsonify(data) # $f-:HttpResponse $f-:contentType=application/json $f-:responseBody=data
|
||||
response = jsonify(data) # $f-:HttpResponse $f-:mimetype=application/json $f-:responseBody=data
|
||||
return response
|
||||
|
||||
|
||||
@@ -65,15 +65,15 @@ def jsonify_route(): # $routeHandler
|
||||
|
||||
@app.route("/content-type/response-modification1") # $routeSetup="/content-type/response-modification1"
|
||||
def response_modification1(): # $routeHandler
|
||||
resp = make_response("<h1>hello</h1>") # $HttpResponse $contentType=text/html $responseBody="<h1>hello</h1>"
|
||||
resp.content_type = "text/plain" # $f-:HttpResponse $f-:contentType=text/plain
|
||||
resp = make_response("<h1>hello</h1>") # $HttpResponse $mimetype=text/html $responseBody="<h1>hello</h1>"
|
||||
resp.content_type = "text/plain" # $f-:HttpResponse $f-:mimetype=text/plain
|
||||
return resp
|
||||
|
||||
|
||||
@app.route("/content-type/response-modification2") # $routeSetup="/content-type/response-modification2"
|
||||
def response_modification2(): # $routeHandler
|
||||
resp = make_response("<h1>hello</h1>") # $HttpResponse $contentType=text/html $responseBody="<h1>hello</h1>"
|
||||
resp.headers["content-type"] = "text/plain" # $f-:HttpResponse $f-:contentType=text/plain
|
||||
resp = make_response("<h1>hello</h1>") # $HttpResponse $mimetype=text/html $responseBody="<h1>hello</h1>"
|
||||
resp.headers["content-type"] = "text/plain" # $f-:HttpResponse $f-:mimetype=text/plain
|
||||
return resp
|
||||
|
||||
|
||||
@@ -83,27 +83,27 @@ def response_modification2(): # $routeHandler
|
||||
|
||||
@app.route("/content-type/Response1") # $routeSetup="/content-type/Response1"
|
||||
def Response1(): # $routeHandler
|
||||
resp = Response("<h1>hello</h1>", mimetype="text/plain") # $HttpResponse $contentType=text/plain $responseBody="<h1>hello</h1>"
|
||||
resp = Response("<h1>hello</h1>", mimetype="text/plain") # $HttpResponse $mimetype=text/plain $responseBody="<h1>hello</h1>"
|
||||
return resp
|
||||
|
||||
|
||||
@app.route("/content-type/Response2") # $routeSetup="/content-type/Response2"
|
||||
def Response2(): # $routeHandler
|
||||
resp = Response("<h1>hello</h1>", content_type="text/plain; charset=utf-8") # $HttpResponse $f-:contentType=text/plain $responseBody="<h1>hello</h1>"
|
||||
resp = Response("<h1>hello</h1>", content_type="text/plain; charset=utf-8") # $HttpResponse $mimetype=text/plain $responseBody="<h1>hello</h1>"
|
||||
return resp
|
||||
|
||||
|
||||
@app.route("/content-type/Response3") # $routeSetup="/content-type/Response3"
|
||||
def Response3(): # $routeHandler
|
||||
# content_type argument takes priority (and result is text/plain)
|
||||
resp = Response("<h1>hello</h1>", content_type="text/plain; charset=utf-8", mimetype="text/html") # $HttpResponse $f-:contentType=text/plain $responseBody="<h1>hello</h1>"
|
||||
resp = Response("<h1>hello</h1>", content_type="text/plain; charset=utf-8", mimetype="text/html") # $HttpResponse $mimetype=text/plain $responseBody="<h1>hello</h1>"
|
||||
return resp
|
||||
|
||||
|
||||
@app.route("/content-type/Response4") # $routeSetup="/content-type/Response4"
|
||||
def Response4(): # $routeHandler
|
||||
# note: capitalization of Content-Type does not matter
|
||||
resp = Response("<h1>hello</h1>", headers={"Content-TYPE": "text/plain"}) # $HttpResponse $f+:contentType=text/html $f-:contentType=text/plain $responseBody="<h1>hello</h1>"
|
||||
resp = Response("<h1>hello</h1>", headers={"Content-TYPE": "text/plain"}) # $HttpResponse $f+:mimetype=text/html $f-:mimetype=text/plain $responseBody="<h1>hello</h1>"
|
||||
return resp
|
||||
|
||||
|
||||
@@ -111,7 +111,7 @@ def Response4(): # $routeHandler
|
||||
def Response5(): # $routeHandler
|
||||
# content_type argument takes priority (and result is text/plain)
|
||||
# note: capitalization of Content-Type does not matter
|
||||
resp = Response("<h1>hello</h1>", headers={"Content-TYPE": "text/html"}, content_type="text/plain; charset=utf-8") # $HttpResponse $f-:contentType=text/plain $responseBody="<h1>hello</h1>"
|
||||
resp = Response("<h1>hello</h1>", headers={"Content-TYPE": "text/html"}, content_type="text/plain; charset=utf-8") # $HttpResponse $mimetype=text/plain $responseBody="<h1>hello</h1>"
|
||||
return resp
|
||||
|
||||
|
||||
@@ -119,7 +119,7 @@ def Response5(): # $routeHandler
|
||||
def Response6(): # $routeHandler
|
||||
# mimetype argument takes priority over header (and result is text/plain)
|
||||
# note: capitalization of Content-Type does not matter
|
||||
resp = Response("<h1>hello</h1>", headers={"Content-TYPE": "text/html"}, mimetype="text/plain") # $HttpResponse $contentType=text/plain $responseBody="<h1>hello</h1>"
|
||||
resp = Response("<h1>hello</h1>", headers={"Content-TYPE": "text/html"}, mimetype="text/plain") # $HttpResponse $mimetype=text/plain $responseBody="<h1>hello</h1>"
|
||||
return resp
|
||||
|
||||
|
||||
@@ -127,7 +127,7 @@ def Response6(): # $routeHandler
|
||||
def Flask_response_class(): # $routeHandler
|
||||
# note: flask.Flask.response_class is set to `flask.Response` by default.
|
||||
# it can be overridden, but we don't try to handle that right now.
|
||||
resp = Flask.response_class("<h1>hello</h1>", mimetype="text/plain") # $f-:HttpResponse $f-:contentType=text/plain $f-:responseBody="<h1>hello</h1>"
|
||||
resp = Flask.response_class("<h1>hello</h1>", mimetype="text/plain") # $f-:HttpResponse $f-:mimetype=text/plain $f-:responseBody="<h1>hello</h1>"
|
||||
return resp
|
||||
|
||||
|
||||
@@ -135,7 +135,7 @@ def Flask_response_class(): # $routeHandler
|
||||
def app_response_class(): # $routeHandler
|
||||
# note: app.response_class (flask.Flask.response_class) is set to `flask.Response` by default.
|
||||
# it can be overridden, but we don't try to handle that right now.
|
||||
resp = app.response_class("<h1>hello</h1>", mimetype="text/plain") # $f-:HttpResponse $f-:contentType=text/plain $f-:responseBody="<h1>hello</h1>"
|
||||
resp = app.response_class("<h1>hello</h1>", mimetype="text/plain") # $f-:HttpResponse $f-:mimetype=text/plain $f-:responseBody="<h1>hello</h1>"
|
||||
return resp
|
||||
|
||||
|
||||
|
||||
@@ -148,7 +148,7 @@ class HttpServerHttpResponseTest extends InlineExpectationsTest {
|
||||
|
||||
HttpServerHttpResponseTest() { this = "HttpServerHttpResponseTest: " + file }
|
||||
|
||||
override string getARelevantTag() { result in ["HttpResponse", "responseBody", "contentType"] }
|
||||
override string getARelevantTag() { result in ["HttpResponse", "responseBody", "mimetype"] }
|
||||
|
||||
override predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
// By adding `file` as a class field, and these two restrictions, it's possible to
|
||||
@@ -175,8 +175,8 @@ class HttpServerHttpResponseTest extends InlineExpectationsTest {
|
||||
exists(HTTP::Server::HttpResponse response |
|
||||
location = response.getLocation() and
|
||||
element = response.toString() and
|
||||
value = response.getContentType() and
|
||||
tag = "contentType"
|
||||
value = response.getMimetype() and
|
||||
tag = "mimetype"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user