File: CVE-2014-0472.patch

package info (click to toggle)
python-django 1.2.3-3%2Bsqueeze15
  • links: PTS, VCS
  • area: main
  • in suites: squeeze-lts
  • size: 29,720 kB
  • ctags: 21,538
  • sloc: python: 101,631; xml: 574; makefile: 149; sh: 121; sql: 7
file content (133 lines) | stat: -rw-r--r-- 5,426 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
Description: fix unexpected code execution using reverse()
Origin: backport, https://github.com/django/django/commit/c1a8c420fe4b27fb2caf5e46d23b5712fc0ac535
Bug-Ubuntu: https://bugs.launchpad.net/ubuntu/+source/python-django/+bug/1309779
Forwarded: not-needed
Last-Update: 2014-05-09

--- a/django/core/urlresolvers.py
+++ b/django/core/urlresolvers.py
@@ -151,6 +151,10 @@
         self._reverse_dict = None
         self._namespace_dict = None
         self._app_dict = None
+        # set of dotted paths to all functions and classes that are used in
+        # urlpatterns
+        self._callback_strs = set()
+        self._populated = False
 
     def __repr__(self):
         return '<%s %s (%s:%s) %s>' % (self.__class__.__name__, self.urlconf_name, self.app_name, self.namespace, self.regex.pattern)
@@ -160,6 +164,15 @@
         namespaces = {}
         apps = {}
         for pattern in reversed(self.url_patterns):
+            if hasattr(pattern, '_callback_str'):
+                self._callback_strs.add(pattern._callback_str)
+            elif hasattr(pattern, '_callback'):
+                callback = pattern._callback
+                if not hasattr(callback, '__name__'):
+                    lookup_str = callback.__module__ + "." + callback.__class__.__name__
+                else:
+                    lookup_str = callback.__module__ + "." + callback.__name__
+                self._callback_strs.add(lookup_str)
             p_pattern = pattern.regex.pattern
             if p_pattern.startswith('^'):
                 p_pattern = p_pattern[1:]
@@ -180,6 +193,7 @@
                         namespaces[namespace] = (p_pattern + prefix, sub_pattern)
                     for app_name, namespace_list in pattern.app_dict.items():
                         apps.setdefault(app_name, []).extend(namespace_list)
+                    self._callback_strs.update(pattern._callback_strs)
             else:
                 bits = normalize(p_pattern)
                 lookups.appendlist(pattern.callback, (bits, p_pattern))
@@ -188,6 +202,7 @@
         self._reverse_dict = lookups
         self._namespace_dict = namespaces
         self._app_dict = apps
+        self._populated = True
 
     def _get_reverse_dict(self):
         if self._reverse_dict is None:
@@ -265,8 +280,13 @@
     def reverse(self, lookup_view, *args, **kwargs):
         if args and kwargs:
             raise ValueError("Don't mix *args and **kwargs in call to reverse()!")
+
+        if not self._populated:
+            self._populate()
+
         try:
-            lookup_view = get_callable(lookup_view, True)
+            if lookup_view in self._callback_strs:
+                lookup_view = get_callable(lookup_view, True)
         except (ImportError, AttributeError), e:
             raise NoReverseMatch("Error importing '%s': %s." % (lookup_view, e))
         possibilities = self.reverse_dict.getlist(lookup_view)
--- /dev/null
+++ b/tests/regressiontests/urlpatterns_reverse/nonimported_module.py
@@ -0,0 +1,3 @@
+def view(request):
+    """Stub view"""
+    pass
--- a/tests/regressiontests/urlpatterns_reverse/tests.py
+++ b/tests/regressiontests/urlpatterns_reverse/tests.py
@@ -1,3 +1,4 @@
+# -*- coding: utf-8 -*-
 """
 Unit tests for reverse URL lookups.
 """
@@ -14,6 +15,8 @@
 ImproperlyConfigured: The included urlconf regressiontests.urlpatterns_reverse.no_urls doesn't have any patterns in it
 """}
 
+import sys
+
 import unittest
 
 from django.conf import settings
@@ -169,6 +172,25 @@
         self.assertEqual(res['Location'], '/foo/')
         res = redirect('http://example.com/')
         self.assertEqual(res['Location'], 'http://example.com/')
+        # Assert that we can redirect using UTF-8 strings
+        res = redirect('/æøå/abc/')
+        self.assertEqual(res['Location'], '/%C3%A6%C3%B8%C3%A5/abc/')
+        # Assert that no imports are attempted when dealing with a relative path
+        # (previously, the below would resolve in a UnicodeEncodeError from __import__ )
+        res = redirect('/æøå.abc/')
+        self.assertEqual(res['Location'], '/%C3%A6%C3%B8%C3%A5.abc/')
+        res = redirect('os.path')
+        self.assertEqual(res['Location'], 'os.path')
+
+    def test_no_illegal_imports(self):
+        # modules that are not listed in urlpatterns should not be importable
+        redirect("urlpatterns_reverse.nonimported_module.view")
+        self.failIf("urlpatterns_reverse.nonimported_module" in sys.modules)
+
+    def test_reverse_by_path_nested(self):
+        # Views that are added to urlpatterns using include() should be
+        # reversable by doted path.
+        self.assertEqual(reverse('regressiontests.urlpatterns_reverse.views.nested_view'), '/includes/nested_path/')
 
     def test_redirect_view_object(self):
         from views import absolute_kwargs_view
--- a/tests/regressiontests/urlpatterns_reverse/urls.py
+++ b/tests/regressiontests/urlpatterns_reverse/urls.py
@@ -3,6 +3,7 @@
 
 other_patterns = patterns('',
     url(r'non_path_include/$', empty_view, name='non_path_include'),
+    url(r'nested_path/$', 'regressiontests.urlpatterns_reverse.views.nested_view'),
 )
 
 urlpatterns = patterns('',
--- a/tests/regressiontests/urlpatterns_reverse/views.py
+++ b/tests/regressiontests/urlpatterns_reverse/views.py
@@ -6,3 +6,6 @@
 
 def absolute_kwargs_view(request, arg1=1, arg2=2):
     pass
+
+def nested_view(request):
+    pass