diff --git a/python/ql/test/query-tests/Security/CWE-601-UrlRedirect/UrlRedirect.expected b/python/ql/test/query-tests/Security/CWE-601-UrlRedirect/UrlRedirect.expected index 167e5270c95..21f498c5d94 100644 --- a/python/ql/test/query-tests/Security/CWE-601-UrlRedirect/UrlRedirect.expected +++ b/python/ql/test/query-tests/Security/CWE-601-UrlRedirect/UrlRedirect.expected @@ -8,6 +8,7 @@ edges | test.py:1:26:1:32 | GSSA Variable request | test.py:67:17:67:23 | ControlFlowNode for request | | test.py:1:26:1:32 | GSSA Variable request | test.py:74:17:74:23 | ControlFlowNode for request | | test.py:1:26:1:32 | GSSA Variable request | test.py:81:17:81:23 | ControlFlowNode for request | +| test.py:1:26:1:32 | GSSA Variable request | test.py:90:17:90:23 | ControlFlowNode for request | | test.py:7:5:7:10 | SSA variable target | test.py:8:21:8:26 | ControlFlowNode for target | | test.py:7:14:7:20 | ControlFlowNode for request | test.py:7:14:7:25 | ControlFlowNode for Attribute | | test.py:7:14:7:25 | ControlFlowNode for Attribute | test.py:7:14:7:43 | ControlFlowNode for Attribute() | @@ -47,6 +48,11 @@ edges | test.py:81:17:81:28 | ControlFlowNode for Attribute | test.py:81:17:81:46 | ControlFlowNode for Attribute() | | test.py:81:17:81:46 | ControlFlowNode for Attribute() | test.py:81:5:81:13 | SSA variable untrusted | | test.py:82:5:82:10 | SSA variable unsafe | test.py:83:21:83:26 | ControlFlowNode for unsafe | +| test.py:90:5:90:13 | SSA variable untrusted | test.py:93:18:93:26 | ControlFlowNode for untrusted | +| test.py:90:5:90:13 | SSA variable untrusted | test.py:95:25:95:33 | ControlFlowNode for untrusted | +| test.py:90:17:90:23 | ControlFlowNode for request | test.py:90:17:90:28 | ControlFlowNode for Attribute | +| test.py:90:17:90:28 | ControlFlowNode for Attribute | test.py:90:17:90:46 | ControlFlowNode for Attribute() | +| test.py:90:17:90:46 | ControlFlowNode for Attribute() | test.py:90:5:90:13 | SSA variable untrusted | nodes | test.py:1:26:1:32 | ControlFlowNode for ImportMember | semmle.label | ControlFlowNode for ImportMember | | test.py:1:26:1:32 | GSSA Variable request | semmle.label | GSSA Variable request | @@ -97,6 +103,12 @@ nodes | test.py:81:17:81:46 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | | test.py:82:5:82:10 | SSA variable unsafe | semmle.label | SSA variable unsafe | | test.py:83:21:83:26 | ControlFlowNode for unsafe | semmle.label | ControlFlowNode for unsafe | +| test.py:90:5:90:13 | SSA variable untrusted | semmle.label | SSA variable untrusted | +| test.py:90:17:90:23 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | +| test.py:90:17:90:28 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | +| test.py:90:17:90:46 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | +| test.py:93:18:93:26 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted | +| test.py:95:25:95:33 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted | subpaths #select | test.py:8:21:8:26 | ControlFlowNode for target | test.py:1:26:1:32 | ControlFlowNode for ImportMember | test.py:8:21:8:26 | ControlFlowNode for target | Untrusted URL redirection depends on a $@. | test.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value | @@ -107,3 +119,5 @@ subpaths | test.py:69:21:69:26 | ControlFlowNode for unsafe | test.py:1:26:1:32 | ControlFlowNode for ImportMember | test.py:69:21:69:26 | ControlFlowNode for unsafe | Untrusted URL redirection depends on a $@. | test.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value | | test.py:76:21:76:26 | ControlFlowNode for unsafe | test.py:1:26:1:32 | ControlFlowNode for ImportMember | test.py:76:21:76:26 | ControlFlowNode for unsafe | Untrusted URL redirection depends on a $@. | test.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value | | test.py:83:21:83:26 | ControlFlowNode for unsafe | test.py:1:26:1:32 | ControlFlowNode for ImportMember | test.py:83:21:83:26 | ControlFlowNode for unsafe | Untrusted URL redirection depends on a $@. | test.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value | +| test.py:93:18:93:26 | ControlFlowNode for untrusted | test.py:1:26:1:32 | ControlFlowNode for ImportMember | test.py:93:18:93:26 | ControlFlowNode for untrusted | Untrusted URL redirection depends on a $@. | test.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value | +| test.py:95:25:95:33 | ControlFlowNode for untrusted | test.py:1:26:1:32 | ControlFlowNode for ImportMember | test.py:95:25:95:33 | ControlFlowNode for untrusted | Untrusted URL redirection depends on a $@. | test.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value | diff --git a/python/ql/test/query-tests/Security/CWE-601-UrlRedirect/test.py b/python/ql/test/query-tests/Security/CWE-601-UrlRedirect/test.py index 381eb3a4bae..419ae428edd 100644 --- a/python/ql/test/query-tests/Security/CWE-601-UrlRedirect/test.py +++ b/python/ql/test/query-tests/Security/CWE-601-UrlRedirect/test.py @@ -81,3 +81,17 @@ def not_ok4(): untrusted = request.args.get('target', '') unsafe = "%s?login=success" % untrusted return redirect(unsafe, code=302) + +from django.utils.http import url_has_allowed_host_and_scheme +import math + +@app.route('/ok6') +def ok6(): + untrusted = request.args.get('target', '') + # random chance. + if math.random() > 0.5: + redirect(untrusted, code=302) # NOT OK + if url_has_allowed_host_and_scheme(untrusted, allowed_hosts=None): + return redirect(untrusted, code=302) # OK - but is flagged! + + return redirect("https://example.com", code=302) # OK \ No newline at end of file