summaryrefslogtreecommitdiffstats
path: root/nova/api
diff options
context:
space:
mode:
authorJenkins <jenkins@review.openstack.org>2013-03-14 18:52:42 +0000
committerGerrit Code Review <review@openstack.org>2013-03-14 18:52:42 +0000
commit01eb75a6a7479f197fecc0b69667a9755fc70cbd (patch)
tree6dd8cc35357aa404a32f166c8d26645c6ac032ff /nova/api
parent9df61c0b06dd81f34d97fbc02030f92928e21a78 (diff)
parent3478f1e121d84d15558d338a32315f13250cf3bb (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.py9
-rw-r--r--nova/api/openstack/compute/contrib/cells.py8
-rw-r--r--nova/api/openstack/compute/contrib/hosts.py8
-rw-r--r--nova/api/openstack/compute/contrib/security_groups.py5
-rw-r--r--nova/api/openstack/compute/contrib/volumes.py2
-rw-r--r--nova/api/openstack/compute/servers.py4
-rw-r--r--nova/api/openstack/wsgi.py27
-rw-r--r--nova/api/openstack/xmlutil.py61
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))