Commit cac3d5bf authored by Florent Guillaume's avatar Florent Guillaume

Collector #1498: Don't choke on malformed cookies. Cookies of the form

"foo=bar; hmm; baz=gee" will give an empty value for 'hmm' instead of
silently discarding it and the rest of the string. (Thanks to 'sirilyan'
for the patch.)
parent 066b9e9e
...@@ -45,11 +45,16 @@ Zope Changes ...@@ -45,11 +45,16 @@ Zope Changes
text/<foo> types text/<foo> types
Bugs fixed Bugs fixed
- Collector #1498: Don't choke on malformed cookies. Cookies of
the form "foo=bar; hmm; baz=gee" will give an empty value for
'hmm' instead of silently discarding it and the rest of the
string. (Thanks to 'sirilyan' for the patch.)
- bin/zopectl test now uses os.execv, instead os os.system, - bin/zopectl test now uses os.execv, instead os os.system,
so that options with characters that needs shell quoting so that options with characters that needs shell quoting
doesn't break the command. doesn't break the command.
- Collector #945: Allow adding empty PythonScript instances - Collector #945: Allow adding empty PythonScript instances
programmatically. programmatically.
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
$Id$ $Id$
""" """
import re, sys, Globals, Moniker, tempfile, ExtensionClass import re, sys, Globals, Moniker, tempfile, ExtensionClass
from marshal import loads, dumps from marshal import loads, dumps
from urllib import quote, unquote from urllib import quote, unquote
from zlib import compress, decompress from zlib import compress, decompress
...@@ -29,6 +29,8 @@ from cgi import escape ...@@ -29,6 +29,8 @@ from cgi import escape
CopyError='Copy Error' CopyError='Copy Error'
copy_re = re.compile('^copy([0-9]*)_of_(.*)')
_marker=[] _marker=[]
class CopyContainer(ExtensionClass.Base): class CopyContainer(ExtensionClass.Base):
"""Interface for containerish objects which allow cut/copy/paste""" """Interface for containerish objects which allow cut/copy/paste"""
...@@ -113,19 +115,17 @@ class CopyContainer(ExtensionClass.Base): ...@@ -113,19 +115,17 @@ class CopyContainer(ExtensionClass.Base):
return self.manage_main(self, REQUEST) return self.manage_main(self, REQUEST)
return cp return cp
copy_re=re.compile('^copy[0-9]*_of_')
def _get_id(self, id): def _get_id(self, id):
# Allow containers to override the generation of # Allow containers to override the generation of
# object copy id by attempting to call its _get_id # object copy id by attempting to call its _get_id
# method, if it exists. # method, if it exists.
copy_match=self.copy_re.match(id) match = copy_re.match(id)
if (copy_match) and (copy_match.end() < len(id)): if match:
n=1 n = int(match.group(1) or '1')
orig_id=self.copy_re.sub('', id) orig_id = match.group(2)
else: else:
n=0 n = 0
orig_id=id orig_id = id
while 1: while 1:
if self._getOb(id, None) is None: if self._getOb(id, None) is None:
return id return id
......
...@@ -183,6 +183,43 @@ class TestCopySupport( CopySupportTestBase ): ...@@ -183,6 +183,43 @@ class TestCopySupport( CopySupportTestBase ):
self.failUnless( 'copy_of_file' in self.folder2.objectIds() ) self.failUnless( 'copy_of_file' in self.folder2.objectIds() )
self.failUnless( result == [{'id':'file', 'new_id':'copy_of_file'}]) self.failUnless( result == [{'id':'file', 'new_id':'copy_of_file'}])
def testPasteSingleSameIDMultipleTimes(self):
cookie = self.folder1.manage_copyObjects(ids=('file',))
result = self.folder1.manage_pasteObjects(cookie)
self.assertEqual(self.folder1.objectIds(), ['file', 'copy_of_file'])
self.assertEqual(result, [{'id':'file', 'new_id':'copy_of_file'}])
# make another copy of file
cookie = self.folder1.manage_copyObjects(ids=('file',))
result = self.folder1.manage_pasteObjects(cookie)
self.assertEqual(self.folder1.objectIds(),
['file', 'copy_of_file', 'copy2_of_file'])
self.assertEqual(result, [{'id':'file', 'new_id':'copy2_of_file'}])
# now copy the copy
cookie = self.folder1.manage_copyObjects(ids=('copy_of_file',))
result = self.folder1.manage_pasteObjects(cookie)
self.assertEqual(self.folder1.objectIds(),
['file', 'copy_of_file', 'copy2_of_file',
'copy3_of_file'])
self.assertEqual(result, [{'id':'copy_of_file',
'new_id':'copy3_of_file'}])
# or copy another copy
cookie = self.folder1.manage_copyObjects(ids=('copy2_of_file',))
result = self.folder1.manage_pasteObjects(cookie)
self.assertEqual(self.folder1.objectIds(),
['file', 'copy_of_file', 'copy2_of_file',
'copy3_of_file', 'copy4_of_file'])
self.assertEqual(result, [{'id':'copy2_of_file',
'new_id':'copy4_of_file'}])
def testPasteSpecialName(self):
manage_addFile(self.folder1, 'copy_of_',
file='', content_type='text/plain')
cookie = self.folder1.manage_copyObjects(ids=('copy_of_',))
result = self.folder1.manage_pasteObjects(cookie)
self.assertEqual(self.folder1.objectIds(),
['file', 'copy_of_', 'copy2_of_'])
self.assertEqual(result, [{'id':'copy_of_', 'new_id':'copy2_of_'}])
def testPasteMultiNotSameID( self ): def testPasteMultiNotSameID( self ):
self.failUnless( 'file' in self.folder1.objectIds() ) self.failUnless( 'file' in self.folder1.objectIds() )
self.failIf( 'file1' in self.folder1.objectIds() ) self.failIf( 'file1' in self.folder1.objectIds() )
......
...@@ -1438,6 +1438,8 @@ def parse_cookie(text, ...@@ -1438,6 +1438,8 @@ def parse_cookie(text,
'([\x00- ]*([^\x00- ;,="]+)="([^"]*)"([\x00- ]*[;,])?[\x00- ]*)'), '([\x00- ]*([^\x00- ;,="]+)="([^"]*)"([\x00- ]*[;,])?[\x00- ]*)'),
parmre=re.compile( parmre=re.compile(
'([\x00- ]*([^\x00- ;,="]+)=([^\x00- ;,"]*)([\x00- ]*[;,])?[\x00- ]*)'), '([\x00- ]*([^\x00- ;,="]+)=([^\x00- ;,"]*)([\x00- ]*[;,])?[\x00- ]*)'),
paramlessre=re.compile(
'([\x00- ]*([^\x00- ;,="]+)[\x00- ]*[;,][\x00- ]*)'),
acquire=parse_cookie_lock.acquire, acquire=parse_cookie_lock.acquire,
release=parse_cookie_lock.release, release=parse_cookie_lock.release,
...@@ -1469,7 +1471,15 @@ def parse_cookie(text, ...@@ -1469,7 +1471,15 @@ def parse_cookie(text,
value = mo_p.group(3) value = mo_p.group(3)
else: else:
return result # Broken Cookie without = nor value.
broken_p = paramlessre.match(text)
if broken_p:
l = len(broken_p.group(1))
name = broken_p.group(2)
value = ''
else:
return result
finally: release() finally: release()
......
...@@ -564,6 +564,25 @@ class ProcessInputsTests(unittest.TestCase): ...@@ -564,6 +564,25 @@ class ProcessInputsTests(unittest.TestCase):
self._noTaintedValues(req) self._noTaintedValues(req)
self._onlyTaintedformHoldsTaintedStrings(req) self._onlyTaintedformHoldsTaintedStrings(req)
def testCookieParsing(self):
env = {'SERVER_NAME': 'testingharnas', 'SERVER_PORT': '80'}
env['HTTP_COOKIE'] = 'foo=bar; baz=gee'
req = self._getHTTPRequest(env)
self.assertEquals(req.cookies['foo'], 'bar')
self.assertEquals(req.cookies['baz'], 'gee')
env['HTTP_COOKIE'] = 'foo=bar; baz="gee, like, e=mc^2"'
req = self._getHTTPRequest(env)
self.assertEquals(req.cookies['foo'], 'bar')
self.assertEquals(req.cookies['baz'], 'gee, like, e=mc^2')
# Collector #1498: empty cookies
env['HTTP_COOKIE'] = 'foo=bar; hmm; baz=gee'
req = self._getHTTPRequest(env)
self.assertEquals(req.cookies['foo'], 'bar')
self.assertEquals(req.cookies['hmm'], '')
self.assertEquals(req.cookies['baz'], 'gee')
TEST_ENVIRON = { TEST_ENVIRON = {
'CONTENT_TYPE': 'multipart/form-data; boundary=12345', 'CONTENT_TYPE': 'multipart/form-data; boundary=12345',
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment