|
1 Patches taken from: |
|
2 https://github.com/django/django/commit/382ab137312961ad62feb8109d70a5a581fe8350 |
|
3 https://github.com/django/django/commit/552f03869ea7f3072b3fa19ffb6cb2d957fd8447 |
|
4 |
|
5 This is fixed in Django 1.8.11 and 1.9.4 |
|
6 |
|
7 --- Django-1.4.22/django/utils/http.py.orig 2016-03-09 19:31:50.474600452 -0800 |
|
8 +++ Django-1.4.22/django/utils/http.py 2016-03-09 19:37:10.433043007 -0800 |
|
9 @@ -7,8 +7,9 @@ import urlparse |
|
10 import unicodedata |
|
11 from email.utils import formatdate |
|
12 |
|
13 +from django.utils import six |
|
14 from django.utils.datastructures import MultiValueDict |
|
15 -from django.utils.encoding import smart_str, force_unicode |
|
16 +from django.utils.encoding import smart_str, force_unicode, force_text |
|
17 from django.utils.functional import allow_lazy |
|
18 |
|
19 ETAG_MATCH = re.compile(r'(?:W/)?"((?:\\.|[^"])*)"') |
|
20 @@ -237,8 +238,16 @@ def is_safe_url(url, host=None): |
|
21 url = url.strip() |
|
22 if not url: |
|
23 return False |
|
24 - # Chrome treats \ completely as / |
|
25 - url = url.replace('\\', '/') |
|
26 + if six.PY2: |
|
27 + try: |
|
28 + url = force_text(url) |
|
29 + except UnicodeDecodeError: |
|
30 + return False |
|
31 + # Chrome treats \ completely as / in paths but it could be part of some |
|
32 + # basic auth credentials so we need to check both URLs. |
|
33 + return _is_safe_url(url, host) and _is_safe_url(url.replace('\\', '/'), host) |
|
34 + |
|
35 +def _is_safe_url(url, host): |
|
36 # Chrome considers any URL with more than two slashes to be absolute, but |
|
37 # urlaprse is not so flexible. Treat any url with three slashes as unsafe. |
|
38 if url.startswith('///'): |
|
39 |
|
40 |
|
41 --- Django-1.4.22/tests/regressiontests/utils/http.py.orig 2016-03-09 19:40:41.664196629 -0800 |
|
42 +++ Django-1.4.22/tests/regressiontests/utils/http.py 2016-03-09 19:42:38.347335015 -0800 |
|
43 @@ -1,3 +1,6 @@ |
|
44 +# -*- encoding: utf-8 -*- |
|
45 +from __future__ import unicode_literals |
|
46 + |
|
47 import sys |
|
48 |
|
49 from django.utils import http |
|
50 @@ -100,6 +103,11 @@ class TestUtilsHttp(unittest.TestCase): |
|
51 'javascript:alert("XSS")' |
|
52 '\njavascript:alert(x)', |
|
53 '\x08//example.com', |
|
54 + r'http://otherserver\@example.com', |
|
55 + r'http:\\testserver\@example.com', |
|
56 + r'http://testserver\me:[email protected]', |
|
57 + r'http://testserver\@example.com', |
|
58 + r'http:\\testserver\confirm\[email protected]', |
|
59 '\n'): |
|
60 self.assertFalse(http.is_safe_url(bad_url, host='testserver'), "%s should be blocked" % bad_url) |
|
61 for good_url in ('/view/?param=http://example.com', |
|
62 @@ -109,5 +117,14 @@ class TestUtilsHttp(unittest.TestCase): |
|
63 'https://testserver/', |
|
64 'HTTPS://testserver/', |
|
65 '//testserver/', |
|
66 + 'http://testserver/[email protected]', |
|
67 '/url%20with%20spaces/'): |
|
68 self.assertTrue(http.is_safe_url(good_url, host='testserver'), "%s should be allowed" % good_url) |
|
69 + self.assertFalse(http.is_safe_url('Ã view'.encode('latin-1'), host='testserver')) |
|
70 + |
|
71 + # Valid basic auth credentials are allowed. |
|
72 + self.assertTrue(http.is_safe_url(r'http://user:pass@testserver/', host='user:pass@testserver')) |
|
73 + # A path without host is allowed. |
|
74 + self.assertTrue(http.is_safe_url('/confirm/[email protected]')) |
|
75 + # Basic auth without host is not allowed. |
|
76 + self.assertFalse(http.is_safe_url(r'http://testserver\@example.com')) |