[Zope-Checkins] SVN: Zope/branches/2.13/ Defend against minidom-based DoS in webdav.
Tres Seaver
cvs-admin at zope.org
Wed Feb 20 23:41:01 UTC 2013
Log message for revision 129554:
Defend against minidom-based DoS in webdav.
Patch from Christian Heimes.
Addresses LP #1114688.
Changed:
_U Zope/branches/2.13/
U Zope/branches/2.13/doc/CHANGES.rst
U Zope/branches/2.13/src/webdav/tests/test_xmltools.py
U Zope/branches/2.13/src/webdav/xmltools.py
-=-
Property changes on: Zope/branches/2.13
___________________________________________________________________
Modified: svn:mergeinfo
- /Zope/branches/2.12:109929
+ /Zope/branches/2.12:109929
Modified: Zope/branches/2.13/doc/CHANGES.rst
===================================================================
--- Zope/branches/2.13/doc/CHANGES.rst 2013-02-20 23:37:08 UTC (rev 129553)
+++ Zope/branches/2.13/doc/CHANGES.rst 2013-02-20 23:41:00 UTC (rev 129554)
@@ -8,6 +8,9 @@
2.13.20 (unreleased)
--------------------
+- LP #1114688: Defend against minidom-based DoS in webdav. (Patch from
+ Christian Heimes).
+
- LP #978980: Protect views of ZPT source with 'View Management Screens'
permision.
Modified: Zope/branches/2.13/src/webdav/tests/test_xmltools.py
===================================================================
--- Zope/branches/2.13/src/webdav/tests/test_xmltools.py 2013-02-20 23:37:08 UTC (rev 129553)
+++ Zope/branches/2.13/src/webdav/tests/test_xmltools.py 2013-02-20 23:41:00 UTC (rev 129554)
@@ -1,19 +1,16 @@
import unittest
-class TestNode(unittest.TestCase):
+class NodeTests(unittest.TestCase):
def _getTargetClass(self):
from webdav.xmltools import Node
return Node
def _makeOne(self, wrapped):
- klass = self._getTargetClass()
- return klass(wrapped)
+ return self._getTargetClass()(wrapped)
def test_remove_namespace_attrs(self):
- """ A method added in Zope 2.11 which removes any attributes
- which appear to be XML namespace declarations """
- class DummyMinidomNode:
+ class DummyMinidomNode(object):
def __init__(self):
self.attributes = {'xmlns:foo':'foo', 'xmlns':'bar', 'a':'b'}
def hasAttributes(self):
@@ -27,7 +24,36 @@
self.assertEqual(wrapped.attributes, {'a':'b'})
+class XmlParserTests(unittest.TestCase):
+
+ def _getTargetClass(self):
+ from webdav.xmltools import XmlParser
+ return XmlParser
+
+ def _makeOne(self):
+ return self._getTargetClass()()
+
+ def test_parse_rejects_entities(self):
+ XML = '\n'.join([
+ '<!DOCTYPE dt_test [',
+ '<!ENTITY entity "1234567890" >',
+ ']>',
+ '<test>&entity;</test>'
+ ])
+ parser = self._makeOne()
+ self.assertRaises(ValueError, parser.parse, XML)
+
+ def test_parse_rejects_doctype_wo_entities(self):
+ XML = '\n'.join([
+ '<!DOCTYPE dt_test []>',
+ '<test/>'
+ ])
+ parser = self._makeOne()
+ self.assertRaises(ValueError, parser.parse, XML)
+
+
def test_suite():
return unittest.TestSuite((
- unittest.makeSuite(TestNode),
- ))
+ unittest.makeSuite(NodeTests),
+ unittest.makeSuite(XmlParserTests),
+ ))
Modified: Zope/branches/2.13/src/webdav/xmltools.py
===================================================================
--- Zope/branches/2.13/src/webdav/xmltools.py 2013-02-20 23:37:08 UTC (rev 129553)
+++ Zope/branches/2.13/src/webdav/xmltools.py 2013-02-20 23:41:00 UTC (rev 129554)
@@ -35,7 +35,9 @@
from StringIO import StringIO
from xml.dom import minidom
-from xml.sax.saxutils import escape as _escape, unescape as _unescape
+from xml.sax.expatreader import ExpatParser
+from xml.sax.saxutils import escape as _escape
+from xml.sax.saxutils import unescape as _unescape
escape_entities = {'"': '"',
"'": ''',
@@ -170,6 +172,36 @@
writer.write(value)
return writer.getvalue()
+
+class ProtectedExpatParser(ExpatParser):
+ """ See https://bugs.launchpad.net/zope2/+bug/1114688
+ """
+ def __init__(self, forbid_dtd=True, forbid_entities=True,
+ *args, **kwargs):
+ # Python 2.x old style class
+ ExpatParser.__init__(self, *args, **kwargs)
+ self.forbid_dtd = forbid_dtd
+ self.forbid_entities = forbid_entities
+
+ def start_doctype_decl(self, name, sysid, pubid, has_internal_subset):
+ raise ValueError("Inline DTD forbidden")
+
+ def entity_decl(self, entityName, is_parameter_entity, value, base, systemId, publicId, notationName):
+ raise ValueError("<!ENTITY> forbidden")
+
+ def unparsed_entity_decl(self, name, base, sysid, pubid, notation_name):
+ # expat 1.2
+ raise ValueError("<!ENTITY> forbidden")
+
+ def reset(self):
+ ExpatParser.reset(self)
+ if self.forbid_dtd:
+ self._parser.StartDoctypeDeclHandler = self.start_doctype_decl
+ if self.forbid_entities:
+ self._parser.EntityDeclHandler = self.entity_decl
+ self._parser.UnparsedEntityDeclHandler = self.unparsed_entity_decl
+
+
class XmlParser:
""" Simple wrapper around minidom to support the required
interfaces for zope.webdav
@@ -181,5 +213,5 @@
pass
def parse(self, data):
- self.dom = minidom.parseString(data)
+ self.dom = minidom.parseString(data, parser=ProtectedExpatParser())
return Node(self.dom)
More information about the Zope-Checkins
mailing list