Ticket #13751: openredirect.diff
File openredirect.diff, 8.9 KB (added by , 14 years ago) |
---|
-
django/http/__init__.py
1 1 import os 2 2 import re 3 import logging 3 4 from Cookie import BaseCookie, SimpleCookie, CookieError 4 5 from pprint import pformat 5 6 from urllib import urlencode 6 from urlparse import urljoin 7 from urlparse import urljoin, urlparse 7 8 try: 8 9 # The mod_python version is more efficient, so try importing it first. 9 10 from mod_python.util import parse_qsl … … 189 190 for key, value in dict.items(self): 190 191 dict.__setitem__(result, copy.deepcopy(key, memo), copy.deepcopy(value, memo)) 191 192 return result 192 193 193 194 def setlist(self, key, list_): 194 195 self._assert_mutable() 195 196 key = str_to_unicode(key, self.encoding) … … 432 433 class HttpResponseRedirect(HttpResponse): 433 434 status_code = 302 434 435 435 def __init__(self, redirect_to ):436 def __init__(self, redirect_to, whitelist=[], fallback_to=None): 436 437 HttpResponse.__init__(self) 437 438 self['Location'] = iri_to_uri(redirect_to) 438 439 440 if urlparse(self['Location']).scheme: 441 effective_whitelist = whitelist + getattr(settings, 'REDIRECT_WHITELIST', []) 442 443 matched = False 444 445 for pattern in effective_whitelist: 446 matched = re.compile(pattern).match(self['Location']) 447 448 if matched: 449 break 450 451 if not matched: 452 logging.warn("found open redirect attack to %s", self['Location']) 453 454 self['Location'] = fallback_to or settings.LOGIN_REDIRECT_URL 455 439 456 class HttpResponsePermanentRedirect(HttpResponse): 440 457 status_code = 301 441 458 … … 493 510 return unicode(s, encoding, 'replace') 494 511 else: 495 512 return s 496 -
tests/regressiontests/httpwrappers/tests.py
1 1 import copy 2 2 import pickle 3 3 import unittest 4 from django.http import QueryDict, HttpResponse, CompatCookie, BadHeaderError 4 from django.conf import settings 5 from django.http import QueryDict, HttpResponse, CompatCookie, BadHeaderError, HttpResponseRedirect 5 6 6 7 class QueryDictTests(unittest.TestCase): 7 8 def test_missing_key(self): … … 17 18 self.assertRaises(AttributeError, q.pop, 'foo') 18 19 self.assertRaises(AttributeError, q.popitem) 19 20 self.assertRaises(AttributeError, q.clear) 20 21 21 22 def test_immutable_get_with_default(self): 22 23 q = QueryDict('') 23 24 self.assertEqual(q.get('foo', 'default'), 'default') … … 34 35 self.assertEqual(q.values(), []) 35 36 self.assertEqual(len(q), 0) 36 37 self.assertEqual(q.urlencode(), '') 37 38 38 39 def test_single_key_value(self): 39 40 """Test QueryDict with one key/value pair""" 40 41 … … 47 48 self.assertEqual(q.get('bar', 'default'), 'default') 48 49 self.assertEqual(q.getlist('foo'), ['bar']) 49 50 self.assertEqual(q.getlist('bar'), []) 50 51 51 52 self.assertRaises(AttributeError, q.setlist, 'foo', ['bar']) 52 53 self.assertRaises(AttributeError, q.appendlist, 'foo', ['bar']) 53 54 … … 67 68 self.assertRaises(AttributeError, q.popitem) 68 69 self.assertRaises(AttributeError, q.clear) 69 70 self.assertRaises(AttributeError, q.setdefault, 'foo', 'bar') 70 71 71 72 self.assertEqual(q.urlencode(), 'foo=bar') 72 73 73 74 def test_mutable_copy(self): 74 75 """A copy of a QueryDict is mutable.""" 75 76 q = QueryDict('').copy() 76 77 self.assertRaises(KeyError, q.__getitem__, "foo") 77 78 q['name'] = 'john' 78 79 self.assertEqual(q['name'], 'john') 79 80 80 81 def test_mutable_delete(self): 81 82 q = QueryDict('').copy() 82 83 q['name'] = 'john' … … 126 127 """Test QueryDict with two key/value pairs with same keys.""" 127 128 128 129 q = QueryDict('vote=yes&vote=no') 129 130 130 131 self.assertEqual(q['vote'], u'no') 131 132 self.assertRaises(AttributeError, q.__setitem__, 'something', 'bar') 132 133 133 134 self.assertEqual(q.get('vote', 'default'), u'no') 134 135 self.assertEqual(q.get('foo', 'default'), 'default') 135 136 self.assertEqual(q.getlist('vote'), [u'yes', u'no']) 136 137 self.assertEqual(q.getlist('foo'), []) 137 138 138 139 self.assertRaises(AttributeError, q.setlist, 'foo', ['bar', 'baz']) 139 140 self.assertRaises(AttributeError, q.setlist, 'foo', ['bar', 'baz']) 140 141 self.assertRaises(AttributeError, q.appendlist, 'foo', ['bar']) 141 142 142 self.assertEqual(q.has_key('vote'), True) 143 self.assertEqual(q.has_key('vote'), True) 143 144 self.assertEqual('vote' in q, True) 144 145 self.assertEqual(q.has_key('foo'), False) 145 146 self.assertEqual('foo' in q, False) … … 148 149 self.assertEqual(q.keys(), [u'vote']) 149 150 self.assertEqual(q.values(), [u'no']) 150 151 self.assertEqual(len(q), 1) 151 152 152 153 self.assertRaises(AttributeError, q.update, {'foo': 'bar'}) 153 154 self.assertRaises(AttributeError, q.pop, 'foo') 154 155 self.assertRaises(AttributeError, q.popitem) 155 156 self.assertRaises(AttributeError, q.clear) 156 157 self.assertRaises(AttributeError, q.setdefault, 'foo', 'bar') 157 158 self.assertRaises(AttributeError, q.__delitem__, 'vote') 158 159 159 160 def test_invalid_input_encoding(self): 160 161 """ 161 162 QueryDicts must be able to handle invalid input encoding (in this 162 163 case, bad UTF-8 encoding). 163 164 """ 164 165 q = QueryDict('foo=bar&foo=\xff') 165 self.assertEqual(q['foo'], u'\ufffd') 166 self.assertEqual(q['foo'], u'\ufffd') 166 167 self.assertEqual(q.getlist('foo'), [u'bar', u'\ufffd']) 167 168 168 169 def test_pickle(self): 169 170 q = QueryDict('') 170 171 q1 = pickle.loads(pickle.dumps(q, 2)) … … 172 173 q = QueryDict('a=b&c=d') 173 174 q1 = pickle.loads(pickle.dumps(q, 2)) 174 175 self.assertEqual(q == q1, True) 175 q = QueryDict('a=b&c=d&a=1') 176 q = QueryDict('a=b&c=d&a=1') 176 177 q1 = pickle.loads(pickle.dumps(q, 2)) 177 178 self.assertEqual(q == q1 , True) 178 179 … … 181 182 x = QueryDict("a=1&a=2", mutable=True) 182 183 y = QueryDict("a=3&a=4") 183 184 x.update(y) 184 self.assertEqual(x.getlist('a'), [u'1', u'2', u'3', u'4']) 185 self.assertEqual(x.getlist('a'), [u'1', u'2', u'3', u'4']) 185 186 186 187 def test_non_default_encoding(self): 187 188 """#13572 - QueryDict with a non-default encoding""" 188 q = QueryDict('sbb=one', encoding='rot_13') 189 q = QueryDict('sbb=one', encoding='rot_13') 189 190 self.assertEqual(q.encoding , 'rot_13' ) 190 191 self.assertEqual(q.items() , [(u'foo', u'bar')] ) 191 192 self.assertEqual(q.urlencode() , 'sbb=one' ) 192 q = q.copy() 193 q = q.copy() 193 194 self.assertEqual(q.encoding , 'rot_13' ) 194 195 self.assertEqual(q.items() , [(u'foo', u'bar')] ) 195 196 self.assertEqual(q.urlencode() , 'sbb=one' ) 196 197 self.assertEqual(copy.copy(q).encoding , 'rot_13' ) 197 198 self.assertEqual(copy.deepcopy(q).encoding , 'rot_13') 198 199 199 200 class HttpResponseTests(unittest.TestCase): 200 201 def test_unicode_headers(self): 201 202 r = HttpResponse() … … 203 204 # If we insert a unicode value it will be converted to an ascii 204 205 r['value'] = u'test value' 205 206 self.failUnless(isinstance(r['value'], str)) 206 207 207 208 # An error is raised When a unicode object with non-ascii is assigned. 208 209 self.assertRaises(UnicodeEncodeError, r.__setitem__, 'value', u't\xebst value') 209 210 # The response also converts unicode keys to strings.) 210 211 # The response also converts unicode keys to strings.) 211 212 r[u'test'] = 'testing key' 212 213 l = list(r.items()) 213 214 l.sort() 214 215 self.assertEqual(l[1], ('test', 'testing key')) 215 216 216 217 # It will also raise errors for keys with non-ascii data. 217 218 self.assertRaises(UnicodeEncodeError, r.__setitem__, u't\xebst key', 'value') 218 219 … … 254 255 c2 = CompatCookie() 255 256 c2.load(c.output()) 256 257 self.assertEqual(c['test'].value, c2['test'].value) 258 259 class HttpResponseRedirectTests(unittest.TestCase): 260 def test_open_redirect(self): 261 self.assertEqual('/test', HttpResponseRedirect('/test')['Location']) 262 self.assertEqual(settings.LOGIN_REDIRECT_URL, HttpResponseRedirect('http://djangoproject.com')['Location']) 263 264 r = HttpResponseRedirect('http://djangoproject.com', whitelist=[r'.*']) 265 self.assertEqual('http://djangoproject.com', r['Location']) 266 267 setattr(settings, 'REDIRECT_WHITELIST', [r'.*']) 268 self.assertEqual('http://djangoproject.com', HttpResponseRedirect('http://djangoproject.com')['Location'])