From dcee027fa97f015ff3b87f0fd72b7995cdd6e155 Mon Sep 17 00:00:00 2001
From: Tom Christie
Date: Fri, 22 Feb 2013 13:17:22 +0000
Subject: defusedxml for security fix.
As per:
http://blog.python.org/2013/02/announcing-defusedxml-fixes-for-xml.html
---
 rest_framework/compat.py          | 17 ++++-------------
 rest_framework/parsers.py         | 14 ++++++++------
 rest_framework/tests/parsers.py   |  4 ++++
 rest_framework/tests/renderers.py | 13 ++++++-------
 4 files changed, 22 insertions(+), 26 deletions(-)
(limited to 'rest_framework')
diff --git a/rest_framework/compat.py b/rest_framework/compat.py
index 3fd865f8..07fdddce 100644
--- a/rest_framework/compat.py
+++ b/rest_framework/compat.py
@@ -421,17 +421,8 @@ except ImportError:
     yaml = None
 
 
-# xml.etree.parse only throws ParseError for python >= 2.7
+# XML is optional
 try:
-    from xml.etree import ParseError as ETParseError
-except ImportError:  # python < 2.7
-    ETParseError = None
-
-
-# XMLParser only takes an encoding arg from >= 2.7
-def ET_XMLParser(encoding=None):
-    from xml.etree import ElementTree as ET
-    try:
-        return ET.XMLParser(encoding=encoding)
-    except TypeError:
-        return ET.XMLParser()
+    import defusedxml.ElementTree as etree
+except ImportError:
+    etree = None
diff --git a/rest_framework/parsers.py b/rest_framework/parsers.py
index 06b02226..7bbb5f94 100644
--- a/rest_framework/parsers.py
+++ b/rest_framework/parsers.py
@@ -9,11 +9,9 @@ from django.conf import settings
 from django.http import QueryDict
 from django.http.multipartparser import MultiPartParser as DjangoMultiPartParser
 from django.http.multipartparser import MultiPartParserError
-from rest_framework.compat import yaml, ETParseError, ET_XMLParser
+from rest_framework.compat import yaml, etree
 from rest_framework.exceptions import ParseError
 from rest_framework.compat import six
-from xml.etree import ElementTree as ET
-from xml.parsers.expat import ExpatError
 import json
 import datetime
 import decimal
@@ -80,6 +78,8 @@ class YAMLParser(BaseParser):
         `data` will be an object which is the parsed content of the response.
         `files` will always be `None`.
         """
+        assert yaml, 'YAMLParser requires pyyaml to be installed'
+
         parser_context = parser_context or {}
         encoding = parser_context.get('encoding', settings.DEFAULT_CHARSET)
 
@@ -146,12 +146,14 @@ class XMLParser(BaseParser):
     media_type = 'application/xml'
 
     def parse(self, stream, media_type=None, parser_context=None):
+        assert etree, 'XMLParser requires defusedxml to be installed'
+
         parser_context = parser_context or {}
         encoding = parser_context.get('encoding', settings.DEFAULT_CHARSET)
-        parser = ET_XMLParser(encoding=encoding)
+        parser = etree.DefusedXMLParser(encoding=encoding)
         try:
-            tree = ET.parse(stream, parser=parser)
-        except (ExpatError, ETParseError, ValueError) as exc:
+            tree = etree.parse(stream, parser=parser)
+        except (etree.ParseError, ValueError) as exc:
             raise ParseError('XML parse error - %s' % six.u(exc))
         data = self._xml_convert(tree.getroot())
 
diff --git a/rest_framework/tests/parsers.py b/rest_framework/tests/parsers.py
index c03df08f..539c5b44 100644
--- a/rest_framework/tests/parsers.py
+++ b/rest_framework/tests/parsers.py
@@ -2,6 +2,8 @@ from __future__ import unicode_literals
 from rest_framework.compat import StringIO
 from django import forms
 from django.test import TestCase
+from django.utils import unittest
+from rest_framework.compat import etree
 from rest_framework.parsers import FormParser
 from rest_framework.parsers import XMLParser
 import datetime
@@ -69,11 +71,13 @@ class TestXMLParser(TestCase):
             ]
         }
 
+    @unittest.skipUnless(etree, 'defusedxml not installed')
     def test_parse(self):
         parser = XMLParser()
         data = parser.parse(self._input)
         self.assertEqual(data, self._data)
 
+    @unittest.skipUnless(etree, 'defusedxml not installed')
     def test_complex_data_parse(self):
         parser = XMLParser()
         data = parser.parse(self._complex_data_input)
diff --git a/rest_framework/tests/renderers.py b/rest_framework/tests/renderers.py
index 90ef1221..0f3fe3f1 100644
--- a/rest_framework/tests/renderers.py
+++ b/rest_framework/tests/renderers.py
@@ -1,23 +1,21 @@
-import pickle
-import re
-
+from decimal import Decimal
 from django.core.cache import cache
 from django.test import TestCase
 from django.test.client import RequestFactory
-
+from django.utils import unittest
 from rest_framework import status, permissions
-from rest_framework.compat import yaml, patterns, url, include
+from rest_framework.compat import yaml, etree, patterns, url, include
 from rest_framework.response import Response
 from rest_framework.views import APIView
 from rest_framework.renderers import BaseRenderer, JSONRenderer, YAMLRenderer, \
     XMLRenderer, JSONPRenderer, BrowsableAPIRenderer
 from rest_framework.parsers import YAMLParser, XMLParser
 from rest_framework.settings import api_settings
-
 from rest_framework.compat import StringIO
 from rest_framework.compat import six
 import datetime
-from decimal import Decimal
+import pickle
+import re
 
 
 DUMMYSTATUS = status.HTTP_200_OK
@@ -410,6 +408,7 @@ class XMLRendererTestCase(TestCase):
         self.assertXMLContains(content, 'first')
         self.assertXMLContains(content, 'second')
 
+    @unittest.skipUnless(etree, 'defusedxml not installed')
     def test_render_and_parse_complex_data(self):
         """
         Test XML rendering.
-- 
cgit v1.2.3