Prevent DoS through XML entity expansion
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
This commit is contained in:
parent
5376c174bd
commit
6446b4ecd2
@ -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):
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user