Commit 722547c7 authored by Tres Seaver's avatar Tres Seaver

Defend against minidom-based DoS in webdav.

Patch from Christian Heimes.

Addresses LP #1114688.
parent 21da65a2
......@@ -8,6 +8,9 @@ http://docs.zope.org/zope2/releases/.
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.
......
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 @@ class TestNode(unittest.TestCase):
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),
))
......@@ -35,7 +35,9 @@ TODO:
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 = {'"': '&quot;',
"'": '&apos;',
......@@ -170,6 +172,36 @@ class Element(Node):
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 @@ class XmlParser:
pass
def parse(self, data):
self.dom = minidom.parseString(data)
self.dom = minidom.parseString(data, parser=ProtectedExpatParser())
return Node(self.dom)
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