Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 8b0b87a

Browse files
committed
Python: Model flask.make_response
1 parent e93c20a commit 8b0b87a

3 files changed

Lines changed: 71 additions & 4 deletions

File tree

python/ql/src/experimental/semmle/python/frameworks/Flask.qll

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ private module FlaskModel {
3434
* WARNING: Only holds for a few predefined attributes.
3535
*/
3636
private DataFlow::Node flask_attr(DataFlow::TypeTracker t, string attr_name) {
37-
attr_name in ["request"] and
37+
attr_name in ["request", "make_response"] and
3838
(
3939
t.start() and
4040
result = DataFlow::importNode("flask" + "." + attr_name)
@@ -73,6 +73,9 @@ private module FlaskModel {
7373
/** Gets a reference to the `flask.request` object. */
7474
DataFlow::Node request() { result = flask_attr("request") }
7575

76+
/** Gets a reference to the `flask.make_response` function. */
77+
DataFlow::Node make_response() { result = flask_attr("make_response") }
78+
7679
/**
7780
* Provides models for the `flask.Flask` class
7881
*
@@ -355,8 +358,32 @@ private module FlaskModel {
355358
private class RequestInputFiles extends RequestInputMultiDict {
356359
RequestInputFiles() { attr_name = "files" }
357360
}
361+
358362
// TODO: Somehow specify that elements of `RequestInputFiles` are
359363
// Werkzeug::werkzeug::datastructures::FileStorage and should have those additional taint steps
360364
// AND that the 0-indexed argument to its' save method is a sink for path-injection.
361365
// https://werkzeug.palletsprojects.com/en/1.0.x/datastructures/#werkzeug.datastructures.FileStorage.save
366+
// ---------------------------------------------------------------------------
367+
// Response modeling
368+
// ---------------------------------------------------------------------------
369+
/**
370+
* A call to the `flask.make_response` function.
371+
*
372+
* See https://flask.palletsprojects.com/en/1.1.x/api/#flask.make_response
373+
*/
374+
private class FlaskMakeResponseCall extends HTTP::Server::HttpResponse::Range, DataFlow::CfgNode {
375+
override CallNode node;
376+
377+
FlaskMakeResponseCall() { node.getFunction() = flask::make_response().asCfgNode() }
378+
379+
override DataFlow::Node getBody() { result.asCfgNode() = node.getArg(0) }
380+
381+
override string getContentTypeDefault() { result = "text/html" }
382+
383+
override DataFlow::Node getContentTypeArg() { none() }
384+
385+
override int getStatusCodeDefault() { result = 200 }
386+
387+
override DataFlow::Node getStatusCodeArg() { result.asCfgNode() = node.getArg(1) }
388+
}
362389
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
| old_test.py:41:12:41:54 | ControlFlowNode for make_response() | Unexpected result: HttpResponse= |
2+
| old_test.py:41:12:41:54 | ControlFlowNode for make_response() | Unexpected result: contentType=text/html |
3+
| old_test.py:41:12:41:54 | ControlFlowNode for make_response() | Unexpected result: responseBody=BinaryExpr |
4+
| old_test.py:41:12:41:54 | ControlFlowNode for make_response() | Unexpected result: statusCode=200 |
5+
| old_test.py:46:12:46:62 | ControlFlowNode for make_response() | Unexpected result: HttpResponse= |
6+
| old_test.py:46:12:46:62 | ControlFlowNode for make_response() | Unexpected result: contentType=text/html |
7+
| old_test.py:46:12:46:62 | ControlFlowNode for make_response() | Unexpected result: responseBody=BinaryExpr |
8+
| old_test.py:46:12:46:62 | ControlFlowNode for make_response() | Unexpected result: statusCode=200 |
9+
| old_test.py:50:12:50:48 | ControlFlowNode for make_response() | Unexpected result: HttpResponse= |
10+
| old_test.py:50:12:50:48 | ControlFlowNode for make_response() | Unexpected result: contentType=text/html |
11+
| old_test.py:50:12:50:48 | ControlFlowNode for make_response() | Unexpected result: responseBody=BinaryExpr |
12+
| old_test.py:50:12:50:48 | ControlFlowNode for make_response() | Unexpected result: statusCode=200 |
13+
| old_test.py:54:12:54:53 | ControlFlowNode for make_response() | Unexpected result: HttpResponse= |
14+
| old_test.py:54:12:54:53 | ControlFlowNode for make_response() | Unexpected result: contentType=text/html |
15+
| old_test.py:54:12:54:53 | ControlFlowNode for make_response() | Unexpected result: responseBody=BinaryExpr |
16+
| old_test.py:54:12:54:53 | ControlFlowNode for make_response() | Unexpected result: statusCode=200 |
17+
| old_test.py:60:12:60:62 | ControlFlowNode for make_response() | Unexpected result: HttpResponse= |
18+
| old_test.py:60:12:60:62 | ControlFlowNode for make_response() | Unexpected result: contentType=text/html |
19+
| old_test.py:60:12:60:62 | ControlFlowNode for make_response() | Unexpected result: responseBody=Attribute() |
20+
| old_test.py:60:12:60:62 | ControlFlowNode for make_response() | Unexpected result: statusCode=200 |
21+
| old_test.py:64:12:64:58 | ControlFlowNode for make_response() | Unexpected result: HttpResponse= |
22+
| old_test.py:64:12:64:58 | ControlFlowNode for make_response() | Unexpected result: contentType=text/html |
23+
| old_test.py:64:12:64:58 | ControlFlowNode for make_response() | Unexpected result: responseBody=Attribute() |
24+
| old_test.py:64:12:64:58 | ControlFlowNode for make_response() | Unexpected result: statusCode=200 |
25+
| routing_test.py:10:12:10:38 | ControlFlowNode for make_response() | Unexpected result: HttpResponse= |
26+
| routing_test.py:10:12:10:38 | ControlFlowNode for make_response() | Unexpected result: contentType=text/html |
27+
| routing_test.py:10:12:10:38 | ControlFlowNode for make_response() | Unexpected result: responseBody="some_route" |
28+
| routing_test.py:10:12:10:38 | ControlFlowNode for make_response() | Unexpected result: statusCode=200 |
29+
| routing_test.py:14:12:14:33 | ControlFlowNode for make_response() | Unexpected result: HttpResponse= |
30+
| routing_test.py:14:12:14:33 | ControlFlowNode for make_response() | Unexpected result: contentType=text/html |
31+
| routing_test.py:14:12:14:33 | ControlFlowNode for make_response() | Unexpected result: responseBody="index" |
32+
| routing_test.py:14:12:14:33 | ControlFlowNode for make_response() | Unexpected result: statusCode=200 |
33+
| routing_test.py:20:12:20:37 | ControlFlowNode for make_response() | Unexpected result: HttpResponse= |
34+
| routing_test.py:20:12:20:37 | ControlFlowNode for make_response() | Unexpected result: contentType=text/html |
35+
| routing_test.py:20:12:20:37 | ControlFlowNode for make_response() | Unexpected result: responseBody="later_set" |
36+
| routing_test.py:20:12:20:37 | ControlFlowNode for make_response() | Unexpected result: statusCode=200 |
37+
| routing_test.py:27:12:27:40 | ControlFlowNode for make_response() | Unexpected result: HttpResponse= |
38+
| routing_test.py:27:12:27:40 | ControlFlowNode for make_response() | Unexpected result: contentType=text/html |
39+
| routing_test.py:27:12:27:40 | ControlFlowNode for make_response() | Unexpected result: responseBody="unkown_route" |
40+
| routing_test.py:27:12:27:40 | ControlFlowNode for make_response() | Unexpected result: statusCode=200 |

python/ql/test/experimental/library-tests/frameworks/flask/response_test.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ def html2(): # $routeHandler
1515
# note that response saved in a variable intentionally -- we wan the annotations to
1616
# show that we recognize the response creation, and not the return (hopefully). (and
1717
# do the same in the following of the file)
18-
resp = make_response("<h1>hello</h1>") # $f-:HttpResponse $f-:contentType=text/html $f-:statusCode=200 $f-:responseBody="<h1>hello</h1>"
18+
resp = make_response("<h1>hello</h1>") # $HttpResponse $contentType=text/html $statusCode=200 $responseBody="<h1>hello</h1>"
1919
return resp
2020

2121

@@ -65,14 +65,14 @@ def jsonify_route(): # $routeHandler
6565

6666
@app.route("/content-type/response-modification1") # $routeSetup="/content-type/response-modification1"
6767
def response_modification1(): # $routeHandler
68-
resp = make_response("<h1>hello</h1>") # $f-:HttpResponse $f-:contentType=text/html $f-:statusCode=200 $f-:responseBody="<h1>hello</h1>"
68+
resp = make_response("<h1>hello</h1>") # $HttpResponse $contentType=text/html $statusCode=200 $responseBody="<h1>hello</h1>"
6969
resp.content_type = "text/plain" # $f-:HttpResponse $f-:contentType=text/plain
7070
return resp
7171

7272

7373
@app.route("/content-type/response-modification2") # $routeSetup="/content-type/response-modification2"
7474
def response_modification2(): # $routeHandler
75-
resp = make_response("<h1>hello</h1>") # $f-:HttpResponse $f-:contentType=text/html $f-:statusCode=200 $f-:responseBody="<h1>hello</h1>"
75+
resp = make_response("<h1>hello</h1>") # $HttpResponse $contentType=text/html $statusCode=200 $responseBody="<h1>hello</h1>"
7676
resp.headers["content-type"] = "text/plain" # $f-:HttpResponse $f-:contentType=text/plain
7777
return resp
7878

0 commit comments

Comments
 (0)