[Zope-Checkins] SVN: Zope/branches/Zope-2_8-branch/ Ensure that response header values cannot embed CRLF pairs, which violate the

Tres Seaver tseaver at palladion.com
Thu Oct 23 15:14:54 EDT 2008


Log message for revision 92516:
  Ensure that response header values cannot embed CRLF pairs, which violate the
  HTTP spec (RFC 2616).
  

Changed:
  U   Zope/branches/Zope-2_8-branch/doc/CHANGES.txt
  U   Zope/branches/Zope-2_8-branch/lib/python/ZPublisher/HTTPResponse.py
  U   Zope/branches/Zope-2_8-branch/lib/python/ZPublisher/tests/testHTTPResponse.py

-=-
Modified: Zope/branches/Zope-2_8-branch/doc/CHANGES.txt
===================================================================
--- Zope/branches/Zope-2_8-branch/doc/CHANGES.txt	2008-10-23 18:49:44 UTC (rev 92515)
+++ Zope/branches/Zope-2_8-branch/doc/CHANGES.txt	2008-10-23 19:14:53 UTC (rev 92516)
@@ -8,6 +8,9 @@
 
     Bugs fixed
 
+      - Ensure that response header values cannot embed CRLF pairs, which
+        violate the HTTP spec (RFC 2616).
+
       - 'AccessControl.ZopeGuards.guarded_import' mapped some Unauthorized
         exceptions onto ImportErrors:  don't do that!  Also, removed
         mutable defaults from argument list, improved tests.

Modified: Zope/branches/Zope-2_8-branch/lib/python/ZPublisher/HTTPResponse.py
===================================================================
--- Zope/branches/Zope-2_8-branch/lib/python/ZPublisher/HTTPResponse.py	2008-10-23 18:49:44 UTC (rev 92515)
+++ Zope/branches/Zope-2_8-branch/lib/python/ZPublisher/HTTPResponse.py	2008-10-23 19:14:53 UTC (rev 92516)
@@ -122,7 +122,11 @@
 if otherTypes:
     uncompressableMimeMajorTypes += tuple(otherTypes.split(','))
 
+_CRLF = re.compile(r'\r[\n]?')
 
+def _scrubHeader(name, value):
+    return ''.join(_CRLF.split(str(name))), ''.join(_CRLF.split(str(value)))
+
 class HTTPResponse(BaseResponse):
     """\
     An object representation of an HTTP response.
@@ -249,8 +253,7 @@
         literal flag is true, the case of the header name is preserved,
         otherwise word-capitalization will be performed on the header
         name on output.'''
-        name = str(name)
-        value = str(value)
+        name, value = _scrubHeader(name, value)
         key = name.lower()
         if accumulate_header(key):
             self.accumulated_headers = (
@@ -263,8 +266,7 @@
         '''\
         Set a new HTTP return header with the given value, while retaining
         any previously set headers with the same name.'''
-        name = str(name)
-        value = str(value)
+        name, value = _scrubHeader(name, value)
         self.accumulated_headers = (
             "%s%s: %s\n" % (self.accumulated_headers, name, value))
 
@@ -547,8 +549,8 @@
         Sets an HTTP return header "name" with value "value",
         appending it following a comma if there was a previous value
         set for the header. '''
-        name = str(name).lower()
-        value = str(value)
+        name, value = _scrubHeader(name, value)
+        name = name.lower()
 
         headers = self.headers
         if headers.has_key(name):

Modified: Zope/branches/Zope-2_8-branch/lib/python/ZPublisher/tests/testHTTPResponse.py
===================================================================
--- Zope/branches/Zope-2_8-branch/lib/python/ZPublisher/tests/testHTTPResponse.py	2008-10-23 18:49:44 UTC (rev 92515)
+++ Zope/branches/Zope-2_8-branch/lib/python/ZPublisher/tests/testHTTPResponse.py	2008-10-23 19:14:53 UTC (rev 92516)
@@ -80,7 +80,40 @@
         response.setStatus(ResourceLockedError)
         self.assertEqual(response.status, 423)
 
+    def test_addHeader_drops_CRLF(self):
+        # RFC2616 disallows CRLF in a header value.
+        response = self._makeOne()
+        response.addHeader('Location',
+                           'http://www.ietf.org/rfc/\r\nrfc2616.txt')
+        self.assertEqual(response.accumulated_headers,
+                         'Location: http://www.ietf.org/rfc/rfc2616.txt\n')
 
+    def test_appendHeader_drops_CRLF(self):
+        # RFC2616 disallows CRLF in a header value.
+        response = self._makeOne()
+        response.appendHeader('Location',
+                               'http://www.ietf.org/rfc/\r\nrfc2616.txt')
+        self.assertEqual(response.headers['location'],
+                         'http://www.ietf.org/rfc/rfc2616.txt')
+
+    def test_setHeader_drops_CRLF(self):
+        # RFC2616 disallows CRLF in a header value.
+        response = self._makeOne()
+        response.setHeader('Location',
+                           'http://www.ietf.org/rfc/\r\nrfc2616.txt')
+        self.assertEqual(response.headers['location'],
+                         'http://www.ietf.org/rfc/rfc2616.txt')
+
+    def test_setHeader_drops_CRLF_when_accumulating(self):
+        # RFC2616 disallows CRLF in a header value.
+        response = self._makeOne()
+        response.setHeader('Set-Cookie', 'allowed="OK"')
+        response.setHeader('Set-Cookie',
+                       'violation="http://www.ietf.org/rfc/\r\nrfc2616.txt"')
+        self.assertEqual(response.accumulated_headers,
+                'Set-Cookie: allowed="OK"\n' +
+                'Set-Cookie: violation="http://www.ietf.org/rfc/rfc2616.txt"\n')
+
 def test_suite():
     suite = unittest.TestSuite()
     suite.addTest(unittest.makeSuite(HTTPResponseTests, 'test'))



More information about the Zope-Checkins mailing list