diff options
| author | Jenkins <jenkins@review.openstack.org> | 2013-03-14 18:52:42 +0000 |
|---|---|---|
| committer | Gerrit Code Review <review@openstack.org> | 2013-03-14 18:52:42 +0000 |
| commit | 01eb75a6a7479f197fecc0b69667a9755fc70cbd (patch) | |
| tree | 6dd8cc35357aa404a32f166c8d26645c6ac032ff /nova/api | |
| parent | 9df61c0b06dd81f34d97fbc02030f92928e21a78 (diff) | |
| parent | 3478f1e121d84d15558d338a32315f13250cf3bb (diff) | |
Merge "Makes safe xml data calls raise 400 http error instead of 500"
Diffstat (limited to 'nova/api')
| -rw-r--r-- | nova/api/openstack/common.py | 9 | ||||
| -rw-r--r-- | nova/api/openstack/compute/contrib/cells.py | 8 | ||||
| -rw-r--r-- | nova/api/openstack/compute/contrib/hosts.py | 8 | ||||
| -rw-r--r-- | nova/api/openstack/compute/contrib/security_groups.py | 5 | ||||
| -rw-r--r-- | nova/api/openstack/compute/contrib/volumes.py | 2 | ||||
| -rw-r--r-- | nova/api/openstack/compute/servers.py | 4 | ||||
| -rw-r--r-- | nova/api/openstack/wsgi.py | 27 | ||||
| -rw-r--r-- | nova/api/openstack/xmlutil.py | 61 |
8 files changed, 78 insertions, 46 deletions
diff --git a/nova/api/openstack/common.py b/nova/api/openstack/common.py index c6473a648..6e3d7eabc 100644 --- a/nova/api/openstack/common.py +++ b/nova/api/openstack/common.py @@ -31,7 +31,6 @@ from nova.compute import vm_states from nova import exception from nova.openstack.common import log as logging from nova import quota -from nova import utils osapi_opts = [ cfg.IntOpt('osapi_max_limit', @@ -356,7 +355,7 @@ def raise_http_conflict_for_instance_invalid_state(exc, action): class MetadataDeserializer(wsgi.MetadataXMLDeserializer): def deserialize(self, text): - dom = utils.safe_minidom_parse_string(text) + dom = xmlutil.safe_minidom_parse_string(text) metadata_node = self.find_first_child_named(dom, "metadata") metadata = self.extract_metadata(metadata_node) return {'body': {'metadata': metadata}} @@ -364,7 +363,7 @@ class MetadataDeserializer(wsgi.MetadataXMLDeserializer): class MetaItemDeserializer(wsgi.MetadataXMLDeserializer): def deserialize(self, text): - dom = utils.safe_minidom_parse_string(text) + dom = xmlutil.safe_minidom_parse_string(text) metadata_item = self.extract_metadata(dom) return {'body': {'meta': metadata_item}} @@ -382,7 +381,7 @@ class MetadataXMLDeserializer(wsgi.XMLDeserializer): return metadata def _extract_metadata_container(self, datastring): - dom = utils.safe_minidom_parse_string(datastring) + dom = xmlutil.safe_minidom_parse_string(datastring) metadata_node = self.find_first_child_named(dom, "metadata") metadata = self.extract_metadata(metadata_node) return {'body': {'metadata': metadata}} @@ -394,7 +393,7 @@ class MetadataXMLDeserializer(wsgi.XMLDeserializer): return self._extract_metadata_container(datastring) def update(self, datastring): - dom = utils.safe_minidom_parse_string(datastring) + dom = xmlutil.safe_minidom_parse_string(datastring) metadata_item = self.extract_metadata(dom) return {'body': {'meta': metadata_item}} diff --git a/nova/api/openstack/compute/contrib/cells.py b/nova/api/openstack/compute/contrib/cells.py index efd2cd189..03597ff0e 100644 --- a/nova/api/openstack/compute/contrib/cells.py +++ b/nova/api/openstack/compute/contrib/cells.py @@ -19,7 +19,6 @@ from oslo.config import cfg from webob import exc -from xml.parsers import expat from nova.api.openstack import common from nova.api.openstack import extensions @@ -31,7 +30,6 @@ from nova import db from nova import exception from nova.openstack.common import log as logging from nova.openstack.common import timeutils -from nova import utils LOG = logging.getLogger(__name__) @@ -98,11 +96,7 @@ class CellDeserializer(wsgi.XMLDeserializer): def default(self, string): """Deserialize an xml-formatted cell create request.""" - try: - node = utils.safe_minidom_parse_string(string) - except expat.ExpatError: - msg = _("cannot understand XML") - raise exception.MalformedRequestBody(reason=msg) + node = xmlutil.safe_minidom_parse_string(string) return {'body': {'cell': self._extract_cell(node)}} diff --git a/nova/api/openstack/compute/contrib/hosts.py b/nova/api/openstack/compute/contrib/hosts.py index 3ecfb9965..a3b3538fd 100644 --- a/nova/api/openstack/compute/contrib/hosts.py +++ b/nova/api/openstack/compute/contrib/hosts.py @@ -16,7 +16,6 @@ """The hosts admin extension.""" import webob.exc -from xml.parsers import expat from nova.api.openstack import extensions from nova.api.openstack import wsgi @@ -24,7 +23,6 @@ from nova.api.openstack import xmlutil from nova import compute from nova import exception from nova.openstack.common import log as logging -from nova import utils LOG = logging.getLogger(__name__) authorize = extensions.extension_authorizer('compute', 'hosts') @@ -71,11 +69,7 @@ class HostShowTemplate(xmlutil.TemplateBuilder): class HostUpdateDeserializer(wsgi.XMLDeserializer): def default(self, string): - try: - node = utils.safe_minidom_parse_string(string) - except expat.ExpatError: - msg = _("cannot understand XML") - raise exception.MalformedRequestBody(reason=msg) + node = xmlutil.safe_minidom_parse_string(string) updates = {} updates_node = self.find_first_child_named(node, 'updates') diff --git a/nova/api/openstack/compute/contrib/security_groups.py b/nova/api/openstack/compute/contrib/security_groups.py index af97a2a6b..ce6f2687f 100644 --- a/nova/api/openstack/compute/contrib/security_groups.py +++ b/nova/api/openstack/compute/contrib/security_groups.py @@ -32,7 +32,6 @@ from nova import exception from nova.network.security_group import openstack_driver from nova.network.security_group import quantum_driver from nova.openstack.common import log as logging -from nova import utils from nova.virt import netutils @@ -113,7 +112,7 @@ class SecurityGroupXMLDeserializer(wsgi.MetadataXMLDeserializer): """ def default(self, string): """Deserialize an xml-formatted security group create request.""" - dom = utils.safe_minidom_parse_string(string) + dom = xmlutil.safe_minidom_parse_string(string) security_group = {} sg_node = self.find_first_child_named(dom, 'security_group') @@ -134,7 +133,7 @@ class SecurityGroupRulesXMLDeserializer(wsgi.MetadataXMLDeserializer): def default(self, string): """Deserialize an xml-formatted security group create request.""" - dom = utils.safe_minidom_parse_string(string) + dom = xmlutil.safe_minidom_parse_string(string) security_group_rule = self._extract_security_group_rule(dom) return {'body': {'security_group_rule': security_group_rule}} diff --git a/nova/api/openstack/compute/contrib/volumes.py b/nova/api/openstack/compute/contrib/volumes.py index 760dc953a..93d76495f 100644 --- a/nova/api/openstack/compute/contrib/volumes.py +++ b/nova/api/openstack/compute/contrib/volumes.py @@ -154,7 +154,7 @@ class CreateDeserializer(CommonDeserializer): def default(self, string): """Deserialize an xml-formatted volume create request.""" - dom = utils.safe_minidom_parse_string(string) + dom = xmlutil.safe_minidom_parse_string(string) vol = self._extract_volume(dom) return {'body': {'volume': vol}} diff --git a/nova/api/openstack/compute/servers.py b/nova/api/openstack/compute/servers.py index 00aa35538..ce40e087b 100644 --- a/nova/api/openstack/compute/servers.py +++ b/nova/api/openstack/compute/servers.py @@ -317,7 +317,7 @@ class ActionDeserializer(CommonDeserializer): """ def default(self, string): - dom = utils.safe_minidom_parse_string(string) + dom = xmlutil.safe_minidom_parse_string(string) action_node = dom.childNodes[0] action_name = action_node.tagName @@ -424,7 +424,7 @@ class CreateDeserializer(CommonDeserializer): def default(self, string): """Deserialize an xml-formatted server create request.""" - dom = utils.safe_minidom_parse_string(string) + dom = xmlutil.safe_minidom_parse_string(string) server = self._extract_server(dom) return {'body': {'server': server}} diff --git a/nova/api/openstack/wsgi.py b/nova/api/openstack/wsgi.py index 5b9900f72..88b203a33 100644 --- a/nova/api/openstack/wsgi.py +++ b/nova/api/openstack/wsgi.py @@ -19,15 +19,14 @@ import inspect import math import time from xml.dom import minidom -from xml.parsers import expat from lxml import etree import webob +from nova.api.openstack import xmlutil from nova import exception from nova.openstack.common import jsonutils from nova.openstack.common import log as logging -from nova import utils from nova import wsgi @@ -216,13 +215,8 @@ class XMLDeserializer(TextDeserializer): def _from_xml(self, datastring): plurals = set(self.metadata.get('plurals', {})) - - try: - node = utils.safe_minidom_parse_string(datastring).childNodes[0] - return {node.nodeName: self._from_xml_node(node, plurals)} - except expat.ExpatError: - msg = _("cannot understand XML") - raise exception.MalformedRequestBody(reason=msg) + node = xmlutil.safe_minidom_parse_string(datastring).childNodes[0] + return {node.nodeName: self._from_xml_node(node, plurals)} def _from_xml_node(self, node, listnames): """Convert a minidom node to a simple Python type. @@ -634,7 +628,7 @@ def action_peek_json(body): def action_peek_xml(body): """Determine action to invoke.""" - dom = utils.safe_minidom_parse_string(body) + dom = xmlutil.safe_minidom_parse_string(body) action_node = dom.childNodes[0] return action_node.tagName @@ -890,17 +884,8 @@ class Resource(wsgi.Application): # function. If we try to audit __call__(), we can # run into troubles due to the @webob.dec.wsgify() # decorator. - try: - return self._process_stack(request, action, action_args, - content_type, body, accept) - except expat.ExpatError: - msg = _("Invalid XML in request body") - return Fault(webob.exc.HTTPBadRequest(explanation=msg)) - except LookupError as e: - #NOTE(Vijaya Erukala): XML input such as - # <?xml version="1.0" encoding="TF-8"?> - # raises LookupError: unknown encoding: TF-8 - return Fault(webob.exc.HTTPBadRequest(explanation=unicode(e))) + return self._process_stack(request, action, action_args, + content_type, body, accept) def _process_stack(self, request, action, action_args, content_type, body, accept): diff --git a/nova/api/openstack/xmlutil.py b/nova/api/openstack/xmlutil.py index a2f5b7506..9bcce808c 100644 --- a/nova/api/openstack/xmlutil.py +++ b/nova/api/openstack/xmlutil.py @@ -18,7 +18,12 @@ import os.path from lxml import etree +from xml.dom import minidom +from xml.parsers import expat +from xml import sax +from xml.sax import expatreader +from nova import exception from nova import utils @@ -905,3 +910,59 @@ def make_flat_dict(name, selector=None, subselector=None, ns=None): # Return the template return root + + +class ProtectedExpatParser(expatreader.ExpatParser): + """An expat parser which disables DTD's and entities by default.""" + + def __init__(self, forbid_dtd=True, forbid_entities=True, + *args, **kwargs): + # Python 2.x old style class + expatreader.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> entity declaration forbidden") + + def unparsed_entity_decl(self, name, base, sysid, pubid, notation_name): + # expat 1.2 + raise ValueError("<!ENTITY> unparsed entity forbidden") + + def external_entity_ref(self, context, base, systemId, publicId): + raise ValueError("<!ENTITY> external entity forbidden") + + def notation_decl(self, name, base, sysid, pubid): + raise ValueError("<!ENTITY> notation forbidden") + + def reset(self): + expatreader.ExpatParser.reset(self) + if self.forbid_dtd: + self._parser.StartDoctypeDeclHandler = self.start_doctype_decl + self._parser.EndDoctypeDeclHandler = None + if self.forbid_entities: + self._parser.EntityDeclHandler = self.entity_decl + self._parser.UnparsedEntityDeclHandler = self.unparsed_entity_decl + self._parser.ExternalEntityRefHandler = self.external_entity_ref + self._parser.NotationDeclHandler = self.notation_decl + try: + self._parser.SkippedEntityHandler = None + except AttributeError: + # some pyexpat versions do not support SkippedEntity + pass + + +def safe_minidom_parse_string(xml_string): + """Parse an XML string using minidom safely.""" + try: + return minidom.parseString(xml_string, parser=ProtectedExpatParser()) + except (sax.SAXParseException, ValueError, + expat.ExpatError, LookupError) as e: + #NOTE(Vijaya Erukala): XML input such as + # <?xml version="1.0" encoding="TF-8"?> + # raises LookupError: unknown encoding: TF-8 + raise exception.MalformedRequestBody(reason=str(e)) |
