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

Skip to content

Commit d2cfa91

Browse files
committed
Python: Add some tricky tests of return in flask route handler
In these cases the `return` might end up creating a new HTTP response, so they need to be modeled as such. Initially I created a very naive solution that didn't handle either tricky_return1 or tricky_return2. The interaction in tricky_return2/helper highlighted for me that to handle this properly, due to the fact that the flow is across functions, we either need to use a global dataflow/taint-tracking configuration, or some clever use of type-trackers. In the end, this extra effort for not modeling all returns in a flask route handler as a creation of a HTTP response doesn't really seem to be worth it (at least not right now). Sicne we use it with taint-tracking for the Reflected XSS query, and use a HTTP response _creation_ as the sink (without propagating taint to the HTTP response), we won't get into trouble where we report a path to BOTH `make_response(...)` and the `return` ``` resp = make_response(...) return resp ``` If we change this setup in the future, we will probably need to do something to avoid this double-path reporting.
1 parent d60221b commit d2cfa91

2 files changed

Lines changed: 41 additions & 66 deletions

File tree

Lines changed: 0 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +0,0 @@
1-
| response_test.py:19:12:19:15 | ControlFlowNode for resp | Unexpected result: HttpResponse= |
2-
| response_test.py:19:12:19:15 | ControlFlowNode for resp | Unexpected result: mimetype=text/html |
3-
| response_test.py:19:12:19:15 | ControlFlowNode for resp | Unexpected result: responseBody=resp |
4-
| response_test.py:25:12:25:15 | ControlFlowNode for resp | Unexpected result: HttpResponse= |
5-
| response_test.py:25:12:25:15 | ControlFlowNode for resp | Unexpected result: mimetype=text/html |
6-
| response_test.py:25:12:25:15 | ControlFlowNode for resp | Unexpected result: responseBody=resp |
7-
| response_test.py:35:12:35:15 | ControlFlowNode for resp | Unexpected result: HttpResponse= |
8-
| response_test.py:35:12:35:15 | ControlFlowNode for resp | Unexpected result: mimetype=text/html |
9-
| response_test.py:35:12:35:15 | ControlFlowNode for resp | Unexpected result: responseBody=resp |
10-
| response_test.py:43:12:43:15 | ControlFlowNode for resp | Unexpected result: HttpResponse= |
11-
| response_test.py:43:12:43:15 | ControlFlowNode for resp | Unexpected result: mimetype=text/html |
12-
| response_test.py:43:12:43:15 | ControlFlowNode for resp | Unexpected result: responseBody=resp |
13-
| response_test.py:51:12:51:15 | ControlFlowNode for resp | Unexpected result: HttpResponse= |
14-
| response_test.py:51:12:51:15 | ControlFlowNode for resp | Unexpected result: mimetype=text/html |
15-
| response_test.py:51:12:51:15 | ControlFlowNode for resp | Unexpected result: responseBody=resp |
16-
| response_test.py:58:12:58:19 | ControlFlowNode for response | Unexpected result: HttpResponse= |
17-
| response_test.py:58:12:58:19 | ControlFlowNode for response | Unexpected result: mimetype=text/html |
18-
| response_test.py:58:12:58:19 | ControlFlowNode for response | Unexpected result: responseBody=response |
19-
| response_test.py:70:12:70:15 | ControlFlowNode for resp | Unexpected result: HttpResponse= |
20-
| response_test.py:70:12:70:15 | ControlFlowNode for resp | Unexpected result: mimetype=text/html |
21-
| response_test.py:70:12:70:15 | ControlFlowNode for resp | Unexpected result: responseBody=resp |
22-
| response_test.py:77:12:77:15 | ControlFlowNode for resp | Unexpected result: HttpResponse= |
23-
| response_test.py:77:12:77:15 | ControlFlowNode for resp | Unexpected result: mimetype=text/html |
24-
| response_test.py:77:12:77:15 | ControlFlowNode for resp | Unexpected result: responseBody=resp |
25-
| response_test.py:87:12:87:15 | ControlFlowNode for resp | Unexpected result: HttpResponse= |
26-
| response_test.py:87:12:87:15 | ControlFlowNode for resp | Unexpected result: mimetype=text/html |
27-
| response_test.py:87:12:87:15 | ControlFlowNode for resp | Unexpected result: responseBody=resp |
28-
| response_test.py:93:12:93:15 | ControlFlowNode for resp | Unexpected result: HttpResponse= |
29-
| response_test.py:93:12:93:15 | ControlFlowNode for resp | Unexpected result: mimetype=text/html |
30-
| response_test.py:93:12:93:15 | ControlFlowNode for resp | Unexpected result: responseBody=resp |
31-
| response_test.py:100:12:100:15 | ControlFlowNode for resp | Unexpected result: HttpResponse= |
32-
| response_test.py:100:12:100:15 | ControlFlowNode for resp | Unexpected result: mimetype=text/html |
33-
| response_test.py:100:12:100:15 | ControlFlowNode for resp | Unexpected result: responseBody=resp |
34-
| response_test.py:107:12:107:15 | ControlFlowNode for resp | Unexpected result: HttpResponse= |
35-
| response_test.py:107:12:107:15 | ControlFlowNode for resp | Unexpected result: mimetype=text/html |
36-
| response_test.py:107:12:107:15 | ControlFlowNode for resp | Unexpected result: responseBody=resp |
37-
| response_test.py:115:12:115:15 | ControlFlowNode for resp | Unexpected result: HttpResponse= |
38-
| response_test.py:115:12:115:15 | ControlFlowNode for resp | Unexpected result: mimetype=text/html |
39-
| response_test.py:115:12:115:15 | ControlFlowNode for resp | Unexpected result: responseBody=resp |
40-
| response_test.py:123:12:123:15 | ControlFlowNode for resp | Unexpected result: HttpResponse= |
41-
| response_test.py:123:12:123:15 | ControlFlowNode for resp | Unexpected result: mimetype=text/html |
42-
| response_test.py:123:12:123:15 | ControlFlowNode for resp | Unexpected result: responseBody=resp |
43-
| response_test.py:131:12:131:15 | ControlFlowNode for resp | Unexpected result: HttpResponse= |
44-
| response_test.py:131:12:131:15 | ControlFlowNode for resp | Unexpected result: mimetype=text/html |
45-
| response_test.py:131:12:131:15 | ControlFlowNode for resp | Unexpected result: responseBody=resp |
46-
| response_test.py:139:12:139:15 | ControlFlowNode for resp | Unexpected result: HttpResponse= |
47-
| response_test.py:139:12:139:15 | ControlFlowNode for resp | Unexpected result: mimetype=text/html |
48-
| response_test.py:139:12:139:15 | ControlFlowNode for resp | Unexpected result: responseBody=resp |

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

Lines changed: 41 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import json
22

3-
from flask import Flask, make_response, jsonify, Response
3+
from flask import Flask, make_response, jsonify, Response, request
44

55
app = Flask(__name__)
66

@@ -16,13 +16,13 @@ def html2(): # $routeHandler
1616
# show that we recognize the response creation, and not the return (hopefully). (and
1717
# do the same in the following of the file)
1818
resp = make_response("<h1>hello</h1>") # $HttpResponse $mimetype=text/html $responseBody="<h1>hello</h1>"
19-
return resp
19+
return resp # $f+:HttpResponse $f+:mimetype=text/html $f+:responseBody=resp
2020

2121

2222
@app.route("/html3") # $routeSetup="/html3"
2323
def html3(): # $routeHandler
2424
resp = app.make_response("<h1>hello</h1>") # $HttpResponse $mimetype=text/html $responseBody="<h1>hello</h1>"
25-
return resp
25+
return resp # $f+:HttpResponse $f+:mimetype=text/html $f+:responseBody=resp
2626

2727

2828
# TODO: Create test-cases for the many ways that `make_response` can be used
@@ -32,30 +32,53 @@ def html3(): # $routeHandler
3232
@app.route("/html4") # $routeSetup="/html4"
3333
def html4(): # $routeHandler
3434
resp = Response("<h1>hello</h1>") # $HttpResponse $mimetype=text/html $responseBody="<h1>hello</h1>"
35-
return resp
35+
return resp # $f+:HttpResponse $f+:mimetype=text/html $f+:responseBody=resp
3636

3737

3838
@app.route("/html5") # $routeSetup="/html5"
3939
def html5(): # $routeHandler
4040
# note: flask.Flask.response_class is set to `flask.Response` by default.
4141
# it can be overridden, but we don't try to handle that right now.
4242
resp = Flask.response_class("<h1>hello</h1>") # $HttpResponse $mimetype=text/html $responseBody="<h1>hello</h1>"
43-
return resp
43+
return resp # $f+:HttpResponse $f+:mimetype=text/html $f+:responseBody=resp
4444

4545

4646
@app.route("/html6") # $routeSetup="/html6"
4747
def html6(): # $routeHandler
4848
# note: app.response_class (flask.Flask.response_class) is set to `flask.Response` by default.
4949
# it can be overridden, but we don't try to handle that right now.
5050
resp = app.response_class("<h1>hello</h1>") # $HttpResponse $mimetype=text/html $responseBody="<h1>hello</h1>"
51-
return resp
51+
return resp # $f+:HttpResponse $f+:mimetype=text/html $f+:responseBody=resp
5252

5353

5454
@app.route("/jsonify") # $routeSetup="/jsonify"
5555
def jsonify_route(): # $routeHandler
5656
data = {"foo": "bar"}
57-
response = jsonify(data) # $f-:HttpResponse $f-:mimetype=application/json $f-:responseBody=data
58-
return response
57+
resp = jsonify(data) # $f-:HttpResponse $f-:mimetype=application/json $f-:responseBody=data
58+
return resp # $f+:HttpResponse $f+:mimetype=text/html $f+:responseBody=resp
59+
60+
################################################################################
61+
# Tricky return handling
62+
################################################################################
63+
64+
@app.route("/tricky-return1") # $routeSetup="/tricky-return1"
65+
def tricky_return1(): # $routeHandler
66+
if "raw" in request.args:
67+
resp = "<h1>hellu</h1>"
68+
else:
69+
resp = make_response("<h1>hello</h1>") # $HttpResponse $mimetype=text/html $responseBody="<h1>hello</h1>"
70+
return resp # $HttpResponse $mimetype=text/html $responseBody=resp
71+
72+
def helper():
73+
if "raw" in request.args:
74+
return "<h1>hellu</h1>"
75+
else:
76+
return make_response("<h1>hello</h1>") # $HttpResponse $mimetype=text/html $responseBody="<h1>hello</h1>"
77+
78+
@app.route("/tricky-return2") # $routeSetup="/tricky-return2"
79+
def tricky_return2(): # $routeHandler
80+
resp = helper()
81+
return resp # $HttpResponse $mimetype=text/html $responseBody=resp
5982

6083

6184
################################################################################
@@ -67,14 +90,14 @@ def jsonify_route(): # $routeHandler
6790
def response_modification1(): # $routeHandler
6891
resp = make_response("<h1>hello</h1>") # $HttpResponse $mimetype=text/html $responseBody="<h1>hello</h1>"
6992
resp.content_type = "text/plain" # $f-:HttpResponse $f-:mimetype=text/plain
70-
return resp
93+
return resp # $f+:HttpResponse $f+:mimetype=text/html $f+:responseBody=resp
7194

7295

7396
@app.route("/content-type/response-modification2") # $routeSetup="/content-type/response-modification2"
7497
def response_modification2(): # $routeHandler
7598
resp = make_response("<h1>hello</h1>") # $HttpResponse $mimetype=text/html $responseBody="<h1>hello</h1>"
7699
resp.headers["content-type"] = "text/plain" # $f-:HttpResponse $f-:mimetype=text/plain
77-
return resp
100+
return resp # $f+:HttpResponse $f+:mimetype=text/html $f+:responseBody=resp
78101

79102

80103
# Exploration of mimetype/content_type/headers arguments to `app.response_class` -- things can get tricky
@@ -84,59 +107,59 @@ def response_modification2(): # $routeHandler
84107
@app.route("/content-type/Response1") # $routeSetup="/content-type/Response1"
85108
def Response1(): # $routeHandler
86109
resp = Response("<h1>hello</h1>", mimetype="text/plain") # $HttpResponse $mimetype=text/plain $responseBody="<h1>hello</h1>"
87-
return resp
110+
return resp # $f+:HttpResponse $f+:mimetype=text/html $f+:responseBody=resp
88111

89112

90113
@app.route("/content-type/Response2") # $routeSetup="/content-type/Response2"
91114
def Response2(): # $routeHandler
92115
resp = Response("<h1>hello</h1>", content_type="text/plain; charset=utf-8") # $HttpResponse $mimetype=text/plain $responseBody="<h1>hello</h1>"
93-
return resp
116+
return resp # $f+:HttpResponse $f+:mimetype=text/html $f+:responseBody=resp
94117

95118

96119
@app.route("/content-type/Response3") # $routeSetup="/content-type/Response3"
97120
def Response3(): # $routeHandler
98121
# content_type argument takes priority (and result is text/plain)
99122
resp = Response("<h1>hello</h1>", content_type="text/plain; charset=utf-8", mimetype="text/html") # $HttpResponse $mimetype=text/plain $responseBody="<h1>hello</h1>"
100-
return resp
123+
return resp # $f+:HttpResponse $f+:mimetype=text/html $f+:responseBody=resp
101124

102125

103126
@app.route("/content-type/Response4") # $routeSetup="/content-type/Response4"
104127
def Response4(): # $routeHandler
105128
# note: capitalization of Content-Type does not matter
106129
resp = Response("<h1>hello</h1>", headers={"Content-TYPE": "text/plain"}) # $HttpResponse $f+:mimetype=text/html $f-:mimetype=text/plain $responseBody="<h1>hello</h1>"
107-
return resp
130+
return resp # $f+:HttpResponse $f+:mimetype=text/html $f+:responseBody=resp
108131

109132

110133
@app.route("/content-type/Response5") # $routeSetup="/content-type/Response5"
111134
def Response5(): # $routeHandler
112135
# content_type argument takes priority (and result is text/plain)
113136
# note: capitalization of Content-Type does not matter
114137
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>"
115-
return resp
138+
return resp # $f+:HttpResponse $f+:mimetype=text/html $f+:responseBody=resp
116139

117140

118141
@app.route("/content-type/Response6") # $routeSetup="/content-type/Response6"
119142
def Response6(): # $routeHandler
120143
# mimetype argument takes priority over header (and result is text/plain)
121144
# note: capitalization of Content-Type does not matter
122145
resp = Response("<h1>hello</h1>", headers={"Content-TYPE": "text/html"}, mimetype="text/plain") # $HttpResponse $mimetype=text/plain $responseBody="<h1>hello</h1>"
123-
return resp
146+
return resp # $f+:HttpResponse $f+:mimetype=text/html $f+:responseBody=resp
124147

125148

126149
@app.route("/content-type/Flask-response-class") # $routeSetup="/content-type/Flask-response-class"
127150
def Flask_response_class(): # $routeHandler
128151
# note: flask.Flask.response_class is set to `flask.Response` by default.
129152
# it can be overridden, but we don't try to handle that right now.
130153
resp = Flask.response_class("<h1>hello</h1>", mimetype="text/plain") # $HttpResponse $mimetype=text/plain $responseBody="<h1>hello</h1>"
131-
return resp
154+
return resp # $f+:HttpResponse $f+:mimetype=text/html $f+:responseBody=resp
132155

133156

134157
@app.route("/content-type/app-response-class") # $routeSetup="/content-type/app-response-class"
135158
def app_response_class(): # $routeHandler
136159
# note: app.response_class (flask.Flask.response_class) is set to `flask.Response` by default.
137160
# it can be overridden, but we don't try to handle that right now.
138161
resp = app.response_class("<h1>hello</h1>", mimetype="text/plain") # $HttpResponse $mimetype=text/plain $responseBody="<h1>hello</h1>"
139-
return resp
162+
return resp # $f+:HttpResponse $f+:mimetype=text/html $f+:responseBody=resp
140163

141164

142165
# TODO: add tests for setting status code

0 commit comments

Comments
 (0)