Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

[1.1.X] Fixed #12470 - encoding of comma and semi-colon in cookies.

This is a backport of r12282 from trunk.

The original bug was about CookieStorage, which does not exist in 1.1.X,
but the fix involved the underlying cookie storage.

The change fixes other bugs with cookies for Internet Explorer and Safari,
hence it is backported.  It could, however, 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
manipulated server side.


git-svn-id: http://code.djangoproject.com/svn/django/branches/releases/1.1.X@12283 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit a3f4402853b7beb7a89cc88a1500818f94f8d313 1 parent da954e8
Luke Plant authored January 23, 2010
32  django/http/__init__.py
@@ -248,12 +248,40 @@ def urlencode(self):
248 248
             output.extend([urlencode({k: smart_str(v, self.encoding)}) for v in list_])
249 249
         return '&'.join(output)
250 250
 
  251
+class CompatCookie(SimpleCookie):
  252
+    """
  253
+    Cookie class that handles some issues with browser compatibility.
  254
+    """
  255
+    def value_encode(self, val):
  256
+        # Some browsers do not support quoted-string from RFC 2109,
  257
+        # including some versions of Safari and Internet Explorer.
  258
+        # These browsers split on ';', and some versions of Safari
  259
+        # are known to split on ', '. Therefore, we encode ';' and ','
  260
+
  261
+        # SimpleCookie already does the hard work of encoding and decoding.
  262
+        # It uses octal sequences like '\\012' for newline etc.
  263
+        # and non-ASCII chars.  We just make use of this mechanism, to
  264
+        # avoid introducing two encoding schemes which would be confusing
  265
+        # and especially awkward for javascript.
  266
+
  267
+        # NB, contrary to Python docs, value_encode returns a tuple containing
  268
+        # (real val, encoded_val)
  269
+        val, encoded = super(CompatCookie, self).value_encode(val)
  270
+
  271
+        encoded = encoded.replace(";", "\\073").replace(",","\\054")
  272
+        # If encoded now contains any quoted chars, we need double quotes
  273
+        # around the whole string.
  274
+        if "\\" in encoded and not encoded.startswith('"'):
  275
+            encoded = '"' + encoded + '"'
  276
+
  277
+        return val, encoded
  278
+
251 279
 def parse_cookie(cookie):
252 280
     if cookie == '':
253 281
         return {}
254 282
     if not isinstance(cookie, BaseCookie):
255 283
         try:
256  
-            c = SimpleCookie()
  284
+            c = CompatCookie()
257 285
             c.load(cookie)
258 286
         except CookieError:
259 287
             # Invalid cookie
@@ -288,7 +316,7 @@ def __init__(self, content='', mimetype=None, status=None,
288 316
         else:
289 317
             self._container = [content]
290 318
             self._is_string = True
291  
-        self.cookies = SimpleCookie()
  319
+        self.cookies = CompatCookie()
292 320
         if status:
293 321
             self.status_code = status
294 322
 
35  tests/regressiontests/httpwrappers/tests.py
@@ -465,7 +465,40 @@
465 465
 [u'1', u'2', u'3', u'4']
466 466
 """
467 467
 
468  
-from django.http import QueryDict, HttpResponse
  468
+from django.http import QueryDict, HttpResponse, CompatCookie
  469
+from django.test import TestCase
  470
+
  471
+
  472
+class Cookies(TestCase):
  473
+
  474
+    def test_encode(self):
  475
+        """
  476
+        Test that we don't output tricky characters in encoded value
  477
+        """
  478
+        c = CompatCookie()
  479
+        c['test'] = "An,awkward;value"
  480
+        self.assert_(";" not in c.output()) # IE compat
  481
+        self.assert_("," not in c.output()) # Safari compat
  482
+
  483
+    def test_decode(self):
  484
+        """
  485
+        Test that we can still preserve semi-colons and commas
  486
+        """
  487
+        c = CompatCookie()
  488
+        c['test'] = "An,awkward;value"
  489
+        c2 = CompatCookie()
  490
+        c2.load(c.output())
  491
+        self.assertEqual(c['test'].value, c2['test'].value)
  492
+
  493
+    def test_decode_2(self):
  494
+        """
  495
+        Test that we haven't broken normal encoding
  496
+        """
  497
+        c = CompatCookie()
  498
+        c['test'] = "\xf0"
  499
+        c2 = CompatCookie()
  500
+        c2.load(c.output())
  501
+        self.assertEqual(c['test'].value, c2['test'].value)
469 502
 
470 503
 if __name__ == "__main__":
471 504
     import doctest

0 notes on commit a3f4402

Please sign in to comment.
Something went wrong with that request. Please try again.