Skip to content

Commit 0bc4d52

Browse files
committed
Python: Update more tests annotations. It looks like we need to allow single-quote strings to support the existing Python use-cases, but let's do that in the next commit.
1 parent ed9ad8b commit 0bc4d52

File tree

6 files changed

+69
-59
lines changed

6 files changed

+69
-59
lines changed
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
| response_test.py:16:12:16:86 | ControlFlowNode for HttpResponse() | Unexpected result: responseBody='<img src="0" onerror="alert(1)">' |
2+
| response_test.py:16:89:16:171 | Comment # $HttpResponse mimetype=text/plain responseBody='<img src="0" onerror="alert(1)">' | Missing result:responseBody='<img |
3+
| response_test.py:21:12:21:56 | ControlFlowNode for HttpResponseRedirect() | Unexpected result: mimetype=text/html; charset=utf-8 |
4+
| response_test.py:21:59:21:132 | Comment # $HttpResponse mimetype=text/html; charset=utf-8 responseBody=Attribute() | Missing result:mimetype=text/html; |
5+
| response_test.py:25:12:25:56 | ControlFlowNode for HttpResponseNotFound() | Unexpected result: mimetype=text/html; charset=utf-8 |
6+
| response_test.py:25:59:25:132 | Comment # $HttpResponse mimetype=text/html; charset=utf-8 responseBody=Attribute() | Missing result:mimetype=text/html; |
7+
| response_test.py:32:16:32:29 | ControlFlowNode for HttpResponse() | Unexpected result: mimetype=text/html; charset=utf-8 |
8+
| response_test.py:32:32:32:80 | Comment # $HttpResponse mimetype=text/html; charset=utf-8 | Missing result:mimetype=text/html; |
9+
| response_test.py:33:5:33:43 | ControlFlowNode for Attribute() | Unexpected result: mimetype=text/html; charset=utf-8 |
10+
| response_test.py:33:46:33:119 | Comment # $HttpResponse mimetype=text/html; charset=utf-8 responseBody=Attribute() | Missing result:mimetype=text/html; |

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

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,50 +4,50 @@
44
# FP reported in https://github.com/github/codeql-python-team/issues/38
55
def safe__json_response(request):
66
# implicitly sets Content-Type to "application/json"
7-
return JsonResponse({"foo": request.GET.get("foo")}) # $HttpResponse $mimetype=application/json $responseBody=Dict
7+
return JsonResponse({"foo": request.GET.get("foo")}) # $HttpResponse mimetype=application/json responseBody=Dict
88

99
# Not an XSS sink, since the Content-Type is not "text/html"
1010
def safe__manual_json_response(request):
1111
json_data = '{"json": "{}"}'.format(request.GET.get("foo"))
12-
return HttpResponse(json_data, content_type="application/json") # $HttpResponse $mimetype=application/json $responseBody=json_data
12+
return HttpResponse(json_data, content_type="application/json") # $HttpResponse mimetype=application/json responseBody=json_data
1313

1414
# Not an XSS sink, since the Content-Type is not "text/html"
1515
def safe__manual_content_type(request):
16-
return HttpResponse('<img src="0" onerror="alert(1)">', content_type="text/plain") # $HttpResponse $mimetype=text/plain $responseBody='<img src="0" onerror="alert(1)">'
16+
return HttpResponse('<img src="0" onerror="alert(1)">', content_type="text/plain") # $HttpResponse mimetype=text/plain responseBody='<img src="0" onerror="alert(1)">'
1717

1818
# XSS FP reported in https://github.com/github/codeql/issues/3466
1919
# Note: This should be an open-redirect sink, but not an XSS sink.
2020
def or__redirect(request):
21-
return HttpResponseRedirect(request.GET.get("next")) # $HttpResponse $mimetype=text/html; charset=utf-8 $responseBody=Attribute()
21+
return HttpResponseRedirect(request.GET.get("next")) # $HttpResponse mimetype=text/html; charset=utf-8 responseBody=Attribute()
2222

2323
# Ensure that simple subclasses are still vuln to XSS
2424
def xss__not_found(request):
25-
return HttpResponseNotFound(request.GET.get("name")) # $HttpResponse $mimetype=text/html; charset=utf-8 $responseBody=Attribute()
25+
return HttpResponseNotFound(request.GET.get("name")) # $HttpResponse mimetype=text/html; charset=utf-8 responseBody=Attribute()
2626

2727
# Ensure we still have an XSS sink when manually setting the content_type to HTML
2828
def xss__manual_response_type(request):
29-
return HttpResponse(request.GET.get("name"), content_type="text/html; charset=utf-8") # $HttpResponse $mimetype=text/html $responseBody=Attribute()
29+
return HttpResponse(request.GET.get("name"), content_type="text/html; charset=utf-8") # $HttpResponse mimetype=text/html responseBody=Attribute()
3030

3131
def xss__write(request):
32-
response = HttpResponse() # $HttpResponse $mimetype=text/html; charset=utf-8
33-
response.write(request.GET.get("name")) # $HttpResponse $mimetype=text/html; charset=utf-8 $responseBody=Attribute()
32+
response = HttpResponse() # $HttpResponse mimetype=text/html; charset=utf-8
33+
response.write(request.GET.get("name")) # $HttpResponse mimetype=text/html; charset=utf-8 responseBody=Attribute()
3434

3535
# This is safe but probably a bug if the argument to `write` is not a result of `json.dumps` or similar.
3636
def safe__write_json(request):
37-
response = JsonResponse() # $HttpResponse $mimetype=application/json
38-
response.write(request.GET.get("name")) # $HttpResponse $mimetype=application/json $responseBody=Attribute()
37+
response = JsonResponse() # $HttpResponse mimetype=application/json
38+
response.write(request.GET.get("name")) # $HttpResponse mimetype=application/json responseBody=Attribute()
3939

4040
# Ensure manual subclasses are vulnerable
4141
class CustomResponse(HttpResponse):
4242
def __init__(self, banner, content, *args, **kwargs):
4343
super().__init__(content, *args, content_type="text/html", **kwargs)
4444

4545
def xss__custom_response(request):
46-
return CustomResponse("ACME Responses", request.GET("name")) # $HttpResponse $f-:mimetype=text/html $f-:responseBody=Attribute() $f+:responseBody="ACME Responses"
46+
return CustomResponse("ACME Responses", request.GET("name")) # $HttpResponse MISSING: mimetype=text/html responseBody=Attribute() SPURIOUS: responseBody="ACME Responses"
4747

4848
class CustomJsonResponse(JsonResponse):
4949
def __init__(self, banner, content, *args, **kwargs):
5050
super().__init__(content, *args, content_type="text/html", **kwargs)
5151

5252
def safe__custom_json_response(request):
53-
return CustomJsonResponse("ACME Responses", {"foo": request.GET.get("foo")}) # $HttpResponse $mimetype=application/json $f-:responseBody=Dict $f+:responseBody="ACME Responses"
53+
return CustomJsonResponse("ACME Responses", {"foo": request.GET.get("foo")}) # $HttpResponse mimetype=application/json MISSING: responseBody=Dict SPURIOUS: responseBody="ACME Responses"

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ def dangerous2(): # $routeHandler
3333
x = request.form['param0']
3434
if request.method == "POST":
3535
return request.form['param1'] # $HttpResponse
36-
return None # $f+:HttpResponse
36+
return None # $ SPURIOUS: HttpResponse
3737

3838
@app.route("/unsafe") # $routeSetup="/unsafe"
3939
def unsafe(): # $routeHandler

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

Lines changed: 42 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -7,22 +7,22 @@
77

88
@app.route("/html1") # $routeSetup="/html1"
99
def html1(): # $routeHandler
10-
return "<h1>hello</h1>" # $HttpResponse $mimetype=text/html $responseBody="<h1>hello</h1>"
10+
return "<h1>hello</h1>" # $HttpResponse mimetype=text/html responseBody="<h1>hello</h1>"
1111

1212

1313
@app.route("/html2") # $routeSetup="/html2"
1414
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>") # $HttpResponse $mimetype=text/html $responseBody="<h1>hello</h1>"
19-
return resp # $f+:HttpResponse $f+:mimetype=text/html $f+:responseBody=resp
18+
resp = make_response("<h1>hello</h1>") # $HttpResponse mimetype=text/html responseBody="<h1>hello</h1>"
19+
return resp # $ SPURIOUS: HttpResponse mimetype=text/html responseBody=resp
2020

2121

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

2727

2828
# TODO: Create test-cases for the many ways that `make_response` can be used
@@ -31,38 +31,38 @@ def html3(): # $routeHandler
3131

3232
@app.route("/html4") # $routeSetup="/html4"
3333
def html4(): # $routeHandler
34-
resp = Response("<h1>hello</h1>") # $HttpResponse $mimetype=text/html $responseBody="<h1>hello</h1>"
35-
return resp # $f+:HttpResponse $f+:mimetype=text/html $f+:responseBody=resp
34+
resp = Response("<h1>hello</h1>") # $HttpResponse mimetype=text/html responseBody="<h1>hello</h1>"
35+
return resp # $ SPURIOUS: HttpResponse mimetype=text/html 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.
42-
resp = Flask.response_class("<h1>hello</h1>") # $HttpResponse $mimetype=text/html $responseBody="<h1>hello</h1>"
43-
return resp # $f+:HttpResponse $f+:mimetype=text/html $f+:responseBody=resp
42+
resp = Flask.response_class("<h1>hello</h1>") # $HttpResponse mimetype=text/html responseBody="<h1>hello</h1>"
43+
return resp # $ SPURIOUS: HttpResponse mimetype=text/html 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.
50-
resp = app.response_class("<h1>hello</h1>") # $HttpResponse $mimetype=text/html $responseBody="<h1>hello</h1>"
51-
return resp # $f+:HttpResponse $f+:mimetype=text/html $f+:responseBody=resp
50+
resp = app.response_class("<h1>hello</h1>") # $HttpResponse mimetype=text/html responseBody="<h1>hello</h1>"
51+
return resp # $ SPURIOUS: HttpResponse mimetype=text/html responseBody=resp
5252

5353

5454
@app.route("/html7") # $routeSetup="/html7"
5555
def html7(): # $routeHandler
56-
resp = make_response() # $HttpResponse $mimetype=text/html
57-
resp.set_data("<h1>hello</h1>") # $f-:responseBody="<h1>hello</h1>"
58-
return resp # $f+:HttpResponse $f+:mimetype=text/html $f+:responseBody=resp
56+
resp = make_response() # $HttpResponse mimetype=text/html
57+
resp.set_data("<h1>hello</h1>") # $ MISSING: responseBody="<h1>hello</h1>"
58+
return resp # $ SPURIOUS: HttpResponse mimetype=text/html responseBody=resp
5959

6060

6161
@app.route("/jsonify") # $routeSetup="/jsonify"
6262
def jsonify_route(): # $routeHandler
6363
data = {"foo": "bar"}
64-
resp = jsonify(data) # $f-:HttpResponse $f-:mimetype=application/json $f-:responseBody=data
65-
return resp # $f+:HttpResponse $f+:mimetype=text/html $f+:responseBody=resp
64+
resp = jsonify(data) # $ MISSING: HttpResponse mimetype=application/json responseBody=data
65+
return resp # $ SPURIOUS: HttpResponse mimetype=text/html responseBody=resp
6666

6767
################################################################################
6868
# Tricky return handling
@@ -73,19 +73,19 @@ def tricky_return1(): # $routeHandler
7373
if "raw" in request.args:
7474
resp = "<h1>hellu</h1>"
7575
else:
76-
resp = make_response("<h1>hello</h1>") # $HttpResponse $mimetype=text/html $responseBody="<h1>hello</h1>"
77-
return resp # $HttpResponse $mimetype=text/html $responseBody=resp
76+
resp = make_response("<h1>hello</h1>") # $HttpResponse mimetype=text/html responseBody="<h1>hello</h1>"
77+
return resp # $HttpResponse mimetype=text/html responseBody=resp
7878

7979
def helper():
8080
if "raw" in request.args:
8181
return "<h1>hellu</h1>"
8282
else:
83-
return make_response("<h1>hello</h1>") # $HttpResponse $mimetype=text/html $responseBody="<h1>hello</h1>"
83+
return make_response("<h1>hello</h1>") # $HttpResponse mimetype=text/html responseBody="<h1>hello</h1>"
8484

8585
@app.route("/tricky-return2") # $routeSetup="/tricky-return2"
8686
def tricky_return2(): # $routeHandler
8787
resp = helper()
88-
return resp # $HttpResponse $mimetype=text/html $responseBody=resp
88+
return resp # $HttpResponse mimetype=text/html responseBody=resp
8989

9090

9191
################################################################################
@@ -95,16 +95,16 @@ def tricky_return2(): # $routeHandler
9595

9696
@app.route("/content-type/response-modification1") # $routeSetup="/content-type/response-modification1"
9797
def response_modification1(): # $routeHandler
98-
resp = make_response("<h1>hello</h1>") # $HttpResponse $mimetype=text/html $responseBody="<h1>hello</h1>"
99-
resp.content_type = "text/plain" # $f-:HttpResponse $f-:mimetype=text/plain
100-
return resp # $f+:HttpResponse $f+:mimetype=text/html $f+:responseBody=resp
98+
resp = make_response("<h1>hello</h1>") # $HttpResponse mimetype=text/html responseBody="<h1>hello</h1>"
99+
resp.content_type = "text/plain" # $ MISSING: HttpResponse mimetype=text/plain
100+
return resp # $ SPURIOUS: HttpResponse mimetype=text/html responseBody=resp
101101

102102

103103
@app.route("/content-type/response-modification2") # $routeSetup="/content-type/response-modification2"
104104
def response_modification2(): # $routeHandler
105-
resp = make_response("<h1>hello</h1>") # $HttpResponse $mimetype=text/html $responseBody="<h1>hello</h1>"
106-
resp.headers["content-type"] = "text/plain" # $f-:HttpResponse $f-:mimetype=text/plain
107-
return resp # $f+:HttpResponse $f+:mimetype=text/html $f+:responseBody=resp
105+
resp = make_response("<h1>hello</h1>") # $HttpResponse mimetype=text/html responseBody="<h1>hello</h1>"
106+
resp.headers["content-type"] = "text/plain" # $ MISSING: HttpResponse mimetype=text/plain
107+
return resp # $ SPURIOUS: HttpResponse mimetype=text/html responseBody=resp
108108

109109

110110
# Exploration of mimetype/content_type/headers arguments to `app.response_class` -- things can get tricky
@@ -113,60 +113,60 @@ def response_modification2(): # $routeHandler
113113

114114
@app.route("/content-type/Response1") # $routeSetup="/content-type/Response1"
115115
def Response1(): # $routeHandler
116-
resp = Response("<h1>hello</h1>", mimetype="text/plain") # $HttpResponse $mimetype=text/plain $responseBody="<h1>hello</h1>"
117-
return resp # $f+:HttpResponse $f+:mimetype=text/html $f+:responseBody=resp
116+
resp = Response("<h1>hello</h1>", mimetype="text/plain") # $HttpResponse mimetype=text/plain responseBody="<h1>hello</h1>"
117+
return resp # $ SPURIOUS: HttpResponse mimetype=text/html responseBody=resp
118118

119119

120120
@app.route("/content-type/Response2") # $routeSetup="/content-type/Response2"
121121
def Response2(): # $routeHandler
122-
resp = Response("<h1>hello</h1>", content_type="text/plain; charset=utf-8") # $HttpResponse $mimetype=text/plain $responseBody="<h1>hello</h1>"
123-
return resp # $f+:HttpResponse $f+:mimetype=text/html $f+:responseBody=resp
122+
resp = Response("<h1>hello</h1>", content_type="text/plain; charset=utf-8") # $HttpResponse mimetype=text/plain responseBody="<h1>hello</h1>"
123+
return resp # $ SPURIOUS: HttpResponse mimetype=text/html responseBody=resp
124124

125125

126126
@app.route("/content-type/Response3") # $routeSetup="/content-type/Response3"
127127
def Response3(): # $routeHandler
128128
# content_type argument takes priority (and result is text/plain)
129-
resp = Response("<h1>hello</h1>", content_type="text/plain; charset=utf-8", mimetype="text/html") # $HttpResponse $mimetype=text/plain $responseBody="<h1>hello</h1>"
130-
return resp # $f+:HttpResponse $f+:mimetype=text/html $f+:responseBody=resp
129+
resp = Response("<h1>hello</h1>", content_type="text/plain; charset=utf-8", mimetype="text/html") # $HttpResponse mimetype=text/plain responseBody="<h1>hello</h1>"
130+
return resp # $ SPURIOUS: HttpResponse mimetype=text/html responseBody=resp
131131

132132

133133
@app.route("/content-type/Response4") # $routeSetup="/content-type/Response4"
134134
def Response4(): # $routeHandler
135135
# note: capitalization of Content-Type does not matter
136-
resp = Response("<h1>hello</h1>", headers={"Content-TYPE": "text/plain"}) # $HttpResponse $f+:mimetype=text/html $f-:mimetype=text/plain $responseBody="<h1>hello</h1>"
137-
return resp # $f+:HttpResponse $f+:mimetype=text/html $f+:responseBody=resp
136+
resp = Response("<h1>hello</h1>", headers={"Content-TYPE": "text/plain"}) # $HttpResponse responseBody="<h1>hello</h1>" SPURIOUS: mimetype=text/html MISSING: mimetype=text/plain
137+
return resp # $ SPURIOUS: HttpResponse mimetype=text/html responseBody=resp
138138

139139

140140
@app.route("/content-type/Response5") # $routeSetup="/content-type/Response5"
141141
def Response5(): # $routeHandler
142142
# content_type argument takes priority (and result is text/plain)
143143
# note: capitalization of Content-Type does not matter
144-
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>"
145-
return resp # $f+:HttpResponse $f+:mimetype=text/html $f+:responseBody=resp
144+
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>"
145+
return resp # $ SPURIOUS: HttpResponse mimetype=text/html responseBody=resp
146146

147147

148148
@app.route("/content-type/Response6") # $routeSetup="/content-type/Response6"
149149
def Response6(): # $routeHandler
150150
# mimetype argument takes priority over header (and result is text/plain)
151151
# note: capitalization of Content-Type does not matter
152-
resp = Response("<h1>hello</h1>", headers={"Content-TYPE": "text/html"}, mimetype="text/plain") # $HttpResponse $mimetype=text/plain $responseBody="<h1>hello</h1>"
153-
return resp # $f+:HttpResponse $f+:mimetype=text/html $f+:responseBody=resp
152+
resp = Response("<h1>hello</h1>", headers={"Content-TYPE": "text/html"}, mimetype="text/plain") # $HttpResponse mimetype=text/plain responseBody="<h1>hello</h1>"
153+
return resp # $ SPURIOUS: HttpResponse mimetype=text/html responseBody=resp
154154

155155

156156
@app.route("/content-type/Flask-response-class") # $routeSetup="/content-type/Flask-response-class"
157157
def Flask_response_class(): # $routeHandler
158158
# note: flask.Flask.response_class is set to `flask.Response` by default.
159159
# it can be overridden, but we don't try to handle that right now.
160-
resp = Flask.response_class("<h1>hello</h1>", mimetype="text/plain") # $HttpResponse $mimetype=text/plain $responseBody="<h1>hello</h1>"
161-
return resp # $f+:HttpResponse $f+:mimetype=text/html $f+:responseBody=resp
160+
resp = Flask.response_class("<h1>hello</h1>", mimetype="text/plain") # $HttpResponse mimetype=text/plain responseBody="<h1>hello</h1>"
161+
return resp # $ SPURIOUS: HttpResponse mimetype=text/html responseBody=resp
162162

163163

164164
@app.route("/content-type/app-response-class") # $routeSetup="/content-type/app-response-class"
165165
def app_response_class(): # $routeHandler
166166
# note: app.response_class (flask.Flask.response_class) is set to `flask.Response` by default.
167167
# it can be overridden, but we don't try to handle that right now.
168-
resp = app.response_class("<h1>hello</h1>", mimetype="text/plain") # $HttpResponse $mimetype=text/plain $responseBody="<h1>hello</h1>"
169-
return resp # $f+:HttpResponse $f+:mimetype=text/html $f+:responseBody=resp
168+
resp = app.response_class("<h1>hello</h1>", mimetype="text/plain") # $HttpResponse mimetype=text/plain responseBody="<h1>hello</h1>"
169+
return resp # $ SPURIOUS: HttpResponse mimetype=text/html responseBody=resp
170170

171171

172172
# TODO: add tests for setting status code

python/ql/test/experimental/library-tests/frameworks/stdlib/FileSystemAccess.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,5 @@
33

44
o = open
55

6-
o("filepath") # f-:$getAPathArgument="filepath"
7-
o(file="filepath") # f-:$getAPathArgument="filepath"
6+
o("filepath") # $ MISSING: getAPathArgument="filepath"
7+
o(file="filepath") # $ MISSING: getAPathArgument="filepath"
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
s = "taintedString"
22

3-
if s.startswith("tainted"): # $checks=s $branch=true
3+
if s.startswith("tainted"): # $checks=s branch=true
44
pass
55

6-
sw = s.startswith # $f-:checks=s $f-:branch=true
6+
sw = s.startswith # $ MISSING: checks=s branch=true
77
if sw("safe"):
88
pass

0 commit comments

Comments
 (0)