]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
Prevent DoS through XML entity expansion
authorDavanum Srinivas <dims@linux.vnet.ibm.com>
Tue, 26 Feb 2013 20:43:50 +0000 (15:43 -0500)
committerDavanum Srinivas <dims@linux.vnet.ibm.com>
Tue, 5 Mar 2013 20:51:34 +0000 (15:51 -0500)
Add a ProtectedXMLParser that overrides the
doctype declaration handler. The handler simply
throws an exception and prevents any further
parsing of the incoming xml.

Fixes LP Bug #1100282

Change-Id: I6488e1a6a52326006e7e7927ece5b5939b72e83e

quantum/tests/unit/test_wsgi.py
quantum/wsgi.py

index 0c09c1cd4df2b9c791de7a0e0b289c0d78dfaffd..7e66c0a91a3150820ab8ce5fb2b2acf46858a404 100644 (file)
@@ -175,6 +175,27 @@ class XMLDeserializerTest(testtools.TestCase):
         self.assertRaises(
             exception.MalformedRequestBody, deserializer.default, data_string)
 
+    def test_entity_expansion(self):
+        def killer_body():
+            return (("""<!DOCTYPE x [
+                    <!ENTITY a "%(a)s">
+                    <!ENTITY b "%(b)s">
+                    <!ENTITY c "%(c)s">]>
+                <foo>
+                    <bar>
+                        <v1>%(d)s</v1>
+                    </bar>
+                </foo>""") % {
+                'a': 'A' * 10,
+                'b': '&a;' * 10,
+                'c': '&b;' * 10,
+                'd': '&c;' * 9999,
+            }).strip()
+
+        deserializer = wsgi.XMLDeserializer()
+        self.assertRaises(
+            ValueError, deserializer.default, killer_body())
+
 
 class JSONDeserializerTest(testtools.TestCase):
     def test_default_raise_Maiformed_Exception(self):
index a78c88ffe047be910d72834d85469f52b5a34b68..58b9d156a4d2e64267ff9977854fe41f9ac7cd73 100644 (file)
@@ -452,6 +452,18 @@ class JSONDeserializer(TextDeserializer):
         return {'body': self._from_json(datastring)}
 
 
+class ProtectedXMLParser(etree.XMLParser):
+    def __init__(self, *args, **kwargs):
+        etree.XMLParser.__init__(self, *args, **kwargs)
+        self._parser.StartDoctypeDeclHandler = self.start_doctype_decl
+
+    def start_doctype_decl(self, name, sysid, pubid, internal):
+        raise ValueError(_("Inline DTD forbidden"))
+
+    def doctype(self, name, pubid, system):
+        raise ValueError(_("Inline DTD forbidden"))
+
+
 class XMLDeserializer(TextDeserializer):
 
     def __init__(self, metadata=None):
@@ -493,12 +505,17 @@ class XMLDeserializer(TextDeserializer):
             node.remove(link)
         return link_list and {link_key: link_list} or {}
 
+    def _parseXML(self, text):
+        parser = ProtectedXMLParser()
+        parser.feed(text)
+        return parser.close()
+
     def _from_xml(self, datastring):
         if datastring is None:
             return None
         plurals = set(self.metadata.get('plurals', {}))
         try:
-            node = etree.fromstring(datastring)
+            node = self._parseXML(datastring)
             root_tag = self._get_key(node.tag)
             # Deserialize link node was needed by unit test for verifying
             # the request's response