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

Skip to content

Commit 501e510

Browse files
committed
Python: Add redirect modeling tests (flask/django)
1 parent efb872a commit 501e510

3 files changed

Lines changed: 63 additions & 6 deletions

File tree

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

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from django.http.response import HttpResponse, HttpResponseRedirect, JsonResponse, HttpResponseNotFound
1+
from django.http.response import HttpResponse, HttpResponseRedirect, HttpResponsePermanentRedirect, JsonResponse, HttpResponseNotFound
22

33
# Not an XSS sink, since the Content-Type is not "text/html"
44
# FP reported in https://github.com/github/codeql-python-team/issues/38
@@ -18,15 +18,35 @@ def safe__manual_content_type(request):
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
21+
next = request.GET.get("next")
22+
return HttpResponseRedirect(next) # $HttpResponse mimetype=text/html MISSING: HttpRedirectResponse redirectLocation=next
2223

23-
def information_exposure_through_redirect(request, as_kw=False):
24+
def information_exposure_through_redirect(request, as_kw=False, perm_redirect=False):
2425
# This is a contrived example, but possible
2526
private = "private"
27+
next = request.GET.get("next")
2628
if as_kw:
27-
return HttpResponseRedirect(request.GET.get("next"), content=private) # $HttpResponse mimetype=text/html responseBody=private
29+
return HttpResponseRedirect(next, content=private) # $HttpResponse mimetype=text/html responseBody=private MISSING: HttpRedirectResponse redirectLocation=next
2830
else:
29-
return HttpResponseRedirect(request.GET.get("next"), private) # $HttpResponse mimetype=text/html responseBody=private
31+
return HttpResponseRedirect(next, private) # $ HttpResponse mimetype=text/html responseBody=private MISSING: HttpRedirectResponse redirectLocation=next
32+
33+
34+
def perm_redirect(request):
35+
private = "private"
36+
next = request.GET.get("next")
37+
return HttpResponsePermanentRedirect(next, private) # $ HttpResponse mimetype=text/html responseBody=private MISSING: HttpRedirectResponse redirectLocation=next
38+
39+
40+
def redirect_through_normal_response(request):
41+
private = "private"
42+
next = request.GET.get("next")
43+
44+
resp = HttpResponse() # $ HttpResponse mimetype=text/html
45+
resp.status_code = 302
46+
resp['Location'] = next # $ MISSING: redirectLocation=next
47+
resp.content = private # $ MISSING: responseBody=private
48+
return resp
49+
3050

3151
# Ensure that simple subclasses are still vuln to XSS
3252
def xss__not_found(request):

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

Lines changed: 13 additions & 1 deletion
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, request
3+
from flask import Flask, make_response, jsonify, Response, request, redirect
44

55
app = Flask(__name__)
66

@@ -172,6 +172,18 @@ def app_response_class(): # $requestHandler
172172
# TODO: add tests for setting status code
173173
# TODO: add test that manually creates a redirect by setting status code and suitable header.
174174

175+
################################################################################
176+
# Redirect
177+
################################################################################
178+
179+
180+
@app.route("/redirect-simple") # $routeSetup="/redirect-simple"
181+
def redirect_simple(): # $requestHandler
182+
next = request.args['next']
183+
resp = redirect(next) # $ MISSING: HttpResponse mimetype=text/html HttpRedirectResponse redirectLocation=next
184+
return resp # $ SPURIOUS: HttpResponse mimetype=text/html responseBody=resp
185+
186+
175187
################################################################################
176188

177189

python/ql/test/experimental/meta/ConceptsTest.qll

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,31 @@ class HttpServerHttpResponseTest extends InlineExpectationsTest {
239239
}
240240
}
241241

242+
class HttpServerHttpRedirectResponseTest extends InlineExpectationsTest {
243+
HttpServerHttpRedirectResponseTest() { this = "HttpServerHttpRedirectResponseTest" }
244+
245+
override string getARelevantTag() { result in ["HttpRedirectResponse", "redirectLocation"] }
246+
247+
override predicate hasActualResult(Location location, string element, string tag, string value) {
248+
exists(location.getFile().getRelativePath()) and
249+
(
250+
exists(HTTP::Server::HttpRedirectResponse redirect |
251+
location = redirect.getLocation() and
252+
element = redirect.toString() and
253+
value = "" and
254+
tag = "HttpRedirectResponse"
255+
)
256+
or
257+
exists(HTTP::Server::HttpRedirectResponse redirect |
258+
location = redirect.getLocation() and
259+
element = redirect.toString() and
260+
value = value_from_expr(redirect.getRedirectLocation().asExpr()) and
261+
tag = "redirectLocation"
262+
)
263+
)
264+
}
265+
}
266+
242267
class FileSystemAccessTest extends InlineExpectationsTest {
243268
FileSystemAccessTest() { this = "FileSystemAccessTest" }
244269

0 commit comments

Comments
 (0)