Fixed #12470 - django.contrib.messages CookieStorage failing silently in safari when comma is used in message
This issue was fixed by changing the underlying cookie storage mechanism. This will fix other bugs with cookies for Internet Explorer and Safari, but could also cause backwards incompatibilities with existing javascript that may parse cookie values that contain commas or semi-colons, and, very rarely, with existing cookie values. git-svn-id: http://code.djangoproject.com/svn/django/trunk@12282 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
e71b10efb7
commit
088f717077
|
@ -248,12 +248,40 @@ class QueryDict(MultiValueDict):
|
||||||
output.extend([urlencode({k: smart_str(v, self.encoding)}) for v in list_])
|
output.extend([urlencode({k: smart_str(v, self.encoding)}) for v in list_])
|
||||||
return '&'.join(output)
|
return '&'.join(output)
|
||||||
|
|
||||||
|
class CompatCookie(SimpleCookie):
|
||||||
|
"""
|
||||||
|
Cookie class that handles some issues with browser compatibility.
|
||||||
|
"""
|
||||||
|
def value_encode(self, val):
|
||||||
|
# Some browsers do not support quoted-string from RFC 2109,
|
||||||
|
# including some versions of Safari and Internet Explorer.
|
||||||
|
# These browsers split on ';', and some versions of Safari
|
||||||
|
# are known to split on ', '. Therefore, we encode ';' and ','
|
||||||
|
|
||||||
|
# SimpleCookie already does the hard work of encoding and decoding.
|
||||||
|
# It uses octal sequences like '\\012' for newline etc.
|
||||||
|
# and non-ASCII chars. We just make use of this mechanism, to
|
||||||
|
# avoid introducing two encoding schemes which would be confusing
|
||||||
|
# and especially awkward for javascript.
|
||||||
|
|
||||||
|
# NB, contrary to Python docs, value_encode returns a tuple containing
|
||||||
|
# (real val, encoded_val)
|
||||||
|
val, encoded = super(CompatCookie, self).value_encode(val)
|
||||||
|
|
||||||
|
encoded = encoded.replace(";", "\\073").replace(",","\\054")
|
||||||
|
# If encoded now contains any quoted chars, we need double quotes
|
||||||
|
# around the whole string.
|
||||||
|
if "\\" in encoded and not encoded.startswith('"'):
|
||||||
|
encoded = '"' + encoded + '"'
|
||||||
|
|
||||||
|
return val, encoded
|
||||||
|
|
||||||
def parse_cookie(cookie):
|
def parse_cookie(cookie):
|
||||||
if cookie == '':
|
if cookie == '':
|
||||||
return {}
|
return {}
|
||||||
if not isinstance(cookie, BaseCookie):
|
if not isinstance(cookie, BaseCookie):
|
||||||
try:
|
try:
|
||||||
c = SimpleCookie()
|
c = CompatCookie()
|
||||||
c.load(cookie)
|
c.load(cookie)
|
||||||
except CookieError:
|
except CookieError:
|
||||||
# Invalid cookie
|
# Invalid cookie
|
||||||
|
@ -288,7 +316,7 @@ class HttpResponse(object):
|
||||||
else:
|
else:
|
||||||
self._container = [content]
|
self._container = [content]
|
||||||
self._is_string = True
|
self._is_string = True
|
||||||
self.cookies = SimpleCookie()
|
self.cookies = CompatCookie()
|
||||||
if status:
|
if status:
|
||||||
self.status_code = status
|
self.status_code = status
|
||||||
|
|
||||||
|
|
|
@ -240,6 +240,15 @@ status code for the test runner is now 0 for success (no failing tests) and 1
|
||||||
for any number of test failures. If needed, the number of test failures can be
|
for any number of test failures. If needed, the number of test failures can be
|
||||||
found at the end of the test runner's output.
|
found at the end of the test runner's output.
|
||||||
|
|
||||||
|
Cookie quoting
|
||||||
|
--------------
|
||||||
|
|
||||||
|
Due to bugs and variations in web browsers, we've slightly changed the way that
|
||||||
|
cookie values are encoded. Comma (',') and semi-colon (';') now use the same
|
||||||
|
octal encoding mechanism that is used for other special characters. If you
|
||||||
|
were not previously storing either of these two characters in cookies you are
|
||||||
|
not affected.
|
||||||
|
|
||||||
.. _deprecated-features-1.2:
|
.. _deprecated-features-1.2:
|
||||||
|
|
||||||
Features deprecated in 1.2
|
Features deprecated in 1.2
|
||||||
|
|
|
@ -465,7 +465,40 @@ BadHeaderError: Header values can't contain newlines (got 'test\\nstr')
|
||||||
[u'1', u'2', u'3', u'4']
|
[u'1', u'2', u'3', u'4']
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from django.http import QueryDict, HttpResponse
|
from django.http import QueryDict, HttpResponse, CompatCookie
|
||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
|
||||||
|
class Cookies(TestCase):
|
||||||
|
|
||||||
|
def test_encode(self):
|
||||||
|
"""
|
||||||
|
Test that we don't output tricky characters in encoded value
|
||||||
|
"""
|
||||||
|
c = CompatCookie()
|
||||||
|
c['test'] = "An,awkward;value"
|
||||||
|
self.assert_(";" not in c.output()) # IE compat
|
||||||
|
self.assert_("," not in c.output()) # Safari compat
|
||||||
|
|
||||||
|
def test_decode(self):
|
||||||
|
"""
|
||||||
|
Test that we can still preserve semi-colons and commas
|
||||||
|
"""
|
||||||
|
c = CompatCookie()
|
||||||
|
c['test'] = "An,awkward;value"
|
||||||
|
c2 = CompatCookie()
|
||||||
|
c2.load(c.output())
|
||||||
|
self.assertEqual(c['test'].value, c2['test'].value)
|
||||||
|
|
||||||
|
def test_decode_2(self):
|
||||||
|
"""
|
||||||
|
Test that we haven't broken normal encoding
|
||||||
|
"""
|
||||||
|
c = CompatCookie()
|
||||||
|
c['test'] = "\xf0"
|
||||||
|
c2 = CompatCookie()
|
||||||
|
c2.load(c.output())
|
||||||
|
self.assertEqual(c['test'].value, c2['test'].value)
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
import doctest
|
import doctest
|
||||||
|
|
Loading…
Reference in New Issue