mirror of
				https://github.com/django/django.git
				synced 2025-10-31 01:25:32 +00:00 
			
		
		
		
	Fixed #26158 -- Rewrote http.parse_cookie() to better match browsers.
This commit is contained in:
		
				
					committed by
					
						 Tim Graham
						Tim Graham
					
				
			
			
				
	
			
			
			
						parent
						
							e7e5d9b338
						
					
				
				
					commit
					93a135d111
				
			| @@ -57,18 +57,21 @@ else: | ||||
|  | ||||
|  | ||||
| def parse_cookie(cookie): | ||||
|     if cookie == '': | ||||
|         return {} | ||||
|     if not isinstance(cookie, http_cookies.BaseCookie): | ||||
|         try: | ||||
|             c = SimpleCookie() | ||||
|             c.load(cookie) | ||||
|         except http_cookies.CookieError: | ||||
|             # Invalid cookie | ||||
|             return {} | ||||
|     else: | ||||
|         c = cookie | ||||
|     """ | ||||
|     Return a dictionary parsed from a `Cookie:` header string. | ||||
|     """ | ||||
|     cookiedict = {} | ||||
|     for key in c.keys(): | ||||
|         cookiedict[key] = c.get(key).value | ||||
|     if six.PY2: | ||||
|         cookie = force_str(cookie) | ||||
|     for chunk in cookie.split(str(';')): | ||||
|         if str('=') in chunk: | ||||
|             key, val = chunk.split(str('='), 1) | ||||
|         else: | ||||
|             # Assume an empty name per | ||||
|             # https://bugzilla.mozilla.org/show_bug.cgi?id=169091 | ||||
|             key, val = str(''), chunk | ||||
|         key, val = key.strip(), val.strip() | ||||
|         if key or val: | ||||
|             # unquote using Python's algorithm. | ||||
|             cookiedict[key] = http_cookies._unquote(val) | ||||
|     return cookiedict | ||||
|   | ||||
| @@ -354,6 +354,10 @@ Requests and Responses | ||||
|   :attr:`~django.http.HttpRequest.content_params` attributes which are | ||||
|   parsed from the ``CONTENT_TYPE`` header. | ||||
|  | ||||
| * The parser for ``request.COOKIES`` is simplified to better match the behavior | ||||
|   of browsers. ``request.COOKIES`` may now contain cookies that are invalid | ||||
|   according to :rfc:`6265` but are possible to set via ``document.cookie``. | ||||
|  | ||||
| Serialization | ||||
| ~~~~~~~~~~~~~ | ||||
|  | ||||
|   | ||||
| @@ -676,6 +676,8 @@ class CookieTests(unittest.TestCase): | ||||
|         c2 = SimpleCookie() | ||||
|         c2.load(c.output()[12:]) | ||||
|         self.assertEqual(c['test'].value, c2['test'].value) | ||||
|         c3 = parse_cookie(c.output()[12:]) | ||||
|         self.assertEqual(c['test'].value, c3['test']) | ||||
|  | ||||
|     def test_decode_2(self): | ||||
|         """ | ||||
| @@ -686,6 +688,8 @@ class CookieTests(unittest.TestCase): | ||||
|         c2 = SimpleCookie() | ||||
|         c2.load(c.output()[12:]) | ||||
|         self.assertEqual(c['test'].value, c2['test'].value) | ||||
|         c3 = parse_cookie(c.output()[12:]) | ||||
|         self.assertEqual(c['test'].value, c3['test']) | ||||
|  | ||||
|     def test_nonstandard_keys(self): | ||||
|         """ | ||||
| @@ -699,6 +703,52 @@ class CookieTests(unittest.TestCase): | ||||
|         """ | ||||
|         self.assertIn('good_cookie', parse_cookie('a:=b; a:=c; good_cookie=yes').keys()) | ||||
|  | ||||
|     def test_python_cookies(self): | ||||
|         """ | ||||
|         Test cases copied from Python's Lib/test/test_http_cookies.py | ||||
|         """ | ||||
|         self.assertEqual(parse_cookie('chips=ahoy; vienna=finger'), {'chips': 'ahoy', 'vienna': 'finger'}) | ||||
|         # Here parse_cookie() differs from Python's cookie parsing in that it | ||||
|         # treats all semicolons as delimiters, even within quotes. | ||||
|         self.assertEqual( | ||||
|             parse_cookie('keebler="E=mc2; L=\\"Loves\\"; fudge=\\012;"'), | ||||
|             {'keebler': '"E=mc2', 'L': '\\"Loves\\"', 'fudge': '\\012', '': '"'} | ||||
|         ) | ||||
|         # Illegal cookies that have an '=' char in an unquoted value. | ||||
|         self.assertEqual(parse_cookie('keebler=E=mc2'), {'keebler': 'E=mc2'}) | ||||
|         # Cookies with ':' character in their name. | ||||
|         self.assertEqual(parse_cookie('key:term=value:term'), {'key:term': 'value:term'}) | ||||
|         # Cookies with '[' and ']'. | ||||
|         self.assertEqual(parse_cookie('a=b; c=[; d=r; f=h'), {'a': 'b', 'c': '[', 'd': 'r', 'f': 'h'}) | ||||
|  | ||||
|     def test_cookie_edgecases(self): | ||||
|         # Cookies that RFC6265 allows. | ||||
|         self.assertEqual(parse_cookie('a=b; Domain=example.com'), {'a': 'b', 'Domain': 'example.com'}) | ||||
|         # parse_cookie() has historically kept only the last cookie with the | ||||
|         # same name. | ||||
|         self.assertEqual(parse_cookie('a=b; h=i; a=c'), {'a': 'c', 'h': 'i'}) | ||||
|  | ||||
|     def test_invalid_cookies(self): | ||||
|         """ | ||||
|         Cookie strings that go against RFC6265 but browsers will send if set | ||||
|         via document.cookie. | ||||
|         """ | ||||
|         # Chunks without an equals sign appear as unnamed values per | ||||
|         # https://bugzilla.mozilla.org/show_bug.cgi?id=169091 | ||||
|         self.assertIn('django_language', parse_cookie('abc=def; unnamed; django_language=en').keys()) | ||||
|         # Even a double quote may be an unamed value. | ||||
|         self.assertEqual(parse_cookie('a=b; "; c=d'), {'a': 'b', '': '"', 'c': 'd'}) | ||||
|         # Spaces in names and values, and an equals sign in values. | ||||
|         self.assertEqual(parse_cookie('a b c=d e = f; gh=i'), {'a b c': 'd e = f', 'gh': 'i'}) | ||||
|         # More characters the spec forbids. | ||||
|         self.assertEqual(parse_cookie('a   b,c<>@:/[]?{}=d  "  =e,f g'), {'a   b,c<>@:/[]?{}': 'd  "  =e,f g'}) | ||||
|         # Unicode characters. The spec only allows ASCII. | ||||
|         self.assertEqual(parse_cookie('saint=André Bessette'), {'saint': force_str('André Bessette')}) | ||||
|         # Browsers don't send extra whitespace or semicolons in Cookie headers, | ||||
|         # but parse_cookie() should parse whitespace the same way | ||||
|         # document.cookie parses whitespace. | ||||
|         self.assertEqual(parse_cookie('  =  b  ;  ;  =  ;   c  =  ;  '), {'': 'b', 'c': ''}) | ||||
|  | ||||
|     def test_httponly_after_load(self): | ||||
|         """ | ||||
|         Test that we can use httponly attribute on cookies that we load | ||||
|   | ||||
| @@ -10,7 +10,6 @@ from django.core.exceptions import SuspiciousOperation | ||||
| from django.core.handlers.wsgi import LimitedStream, WSGIRequest | ||||
| from django.http import ( | ||||
|     HttpRequest, HttpResponse, RawPostDataException, UnreadablePostError, | ||||
|     parse_cookie, | ||||
| ) | ||||
| from django.test import RequestFactory, SimpleTestCase, override_settings | ||||
| from django.test.client import FakePayload | ||||
| @@ -183,9 +182,6 @@ class RequestsTests(SimpleTestCase): | ||||
|         request = WSGIRequest({'PATH_INFO': wsgi_str("/سلام/"), 'REQUEST_METHOD': 'get', 'wsgi.input': BytesIO(b'')}) | ||||
|         self.assertEqual(request.path, "/سلام/") | ||||
|  | ||||
|     def test_parse_cookie(self): | ||||
|         self.assertEqual(parse_cookie('invalid@key=true'), {}) | ||||
|  | ||||
|     def test_httprequest_location(self): | ||||
|         request = HttpRequest() | ||||
|         self.assertEqual(request.build_absolute_uri(location="https://www.example.com/asdf"), | ||||
|   | ||||
		Reference in New Issue
	
	Block a user