From 4eeec2031e2785f22a7004d890168f1d0e3bbc71 Mon Sep 17 00:00:00 2001 From: Claude Paroz <claude@2xlibre.net> Date: Sat, 26 Sep 2015 10:51:59 +0200 Subject: [PATCH] [1.9.x] Separated XML serialization tests Backport of d3cfdfb508 from master. --- tests/serializers/test_xml.py | 118 +++++++++++++++++++++++++++++ tests/serializers/tests.py | 98 ------------------------ tests/serializers_regress/tests.py | 17 +---- 3 files changed, 119 insertions(+), 114 deletions(-) create mode 100644 tests/serializers/test_xml.py diff --git a/tests/serializers/test_xml.py b/tests/serializers/test_xml.py new file mode 100644 index 0000000000..e516444c95 --- /dev/null +++ b/tests/serializers/test_xml.py @@ -0,0 +1,118 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from xml.dom import minidom + +from django.core import serializers +from django.core.serializers.xml_serializer import DTDForbidden +from django.test import TestCase, TransactionTestCase +from django.utils import six + +from .tests import SerializersTestBase, SerializersTransactionTestBase + + +class XmlSerializerTestCase(SerializersTestBase, TestCase): + serializer_name = "xml" + pkless_str = """<?xml version="1.0" encoding="utf-8"?> +<django-objects version="1.0"> + <object model="serializers.category"> + <field type="CharField" name="name">Reference</field> + </object> + <object model="serializers.category"> + <field type="CharField" name="name">Non-fiction</field> + </object> +</django-objects>""" + mapping_ordering_str = """<?xml version="1.0" encoding="utf-8"?> +<django-objects version="1.0"> + <object model="serializers.article" pk="%(article_pk)s"> + <field name="author" rel="ManyToOneRel" to="serializers.author">%(author_pk)s</field> + <field name="headline" type="CharField">Poker has no place on ESPN</field> + <field name="pub_date" type="DateTimeField">2006-06-16T11:00:00</field> + <field name="categories" rel="ManyToManyRel" to="serializers.category"><object pk="%(first_category_pk)s"></object><object pk="%(second_category_pk)s"></object></field> + <field name="meta_data" rel="ManyToManyRel" to="serializers.categorymetadata"></field> + </object> +</django-objects>""" # NOQA + + @staticmethod + def _comparison_value(value): + # The XML serializer handles everything as strings, so comparisons + # need to be performed on the stringified value + return six.text_type(value) + + @staticmethod + def _validate_output(serial_str): + try: + minidom.parseString(serial_str) + except Exception: + return False + else: + return True + + @staticmethod + def _get_pk_values(serial_str): + ret_list = [] + dom = minidom.parseString(serial_str) + fields = dom.getElementsByTagName("object") + for field in fields: + ret_list.append(field.getAttribute("pk")) + return ret_list + + @staticmethod + def _get_field_values(serial_str, field_name): + ret_list = [] + dom = minidom.parseString(serial_str) + fields = dom.getElementsByTagName("field") + for field in fields: + if field.getAttribute("name") == field_name: + temp = [] + for child in field.childNodes: + temp.append(child.nodeValue) + ret_list.append("".join(temp)) + return ret_list + + def test_control_char_failure(self): + """ + Serializing control characters with XML should fail as those characters + are not supported in the XML 1.0 standard (except HT, LF, CR). + """ + self.a1.headline = "This contains \u0001 control \u0011 chars" + msg = "Article.headline (pk:%s) contains unserializable characters" % self.a1.pk + with self.assertRaisesMessage(ValueError, msg): + serializers.serialize(self.serializer_name, [self.a1]) + self.a1.headline = "HT \u0009, LF \u000A, and CR \u000D are allowed" + self.assertIn( + "HT \t, LF \n, and CR \r are allowed", + serializers.serialize(self.serializer_name, [self.a1]) + ) + + def test_no_dtd(self): + """ + The XML deserializer shouldn't allow a DTD. + + This is the most straightforward way to prevent all entity definitions + and avoid both external entities and entity-expansion attacks. + """ + xml = '<?xml version="1.0" standalone="no"?><!DOCTYPE example SYSTEM "http://example.com/example.dtd">' + with self.assertRaises(DTDForbidden): + next(serializers.deserialize('xml', xml)) + + +class XmlSerializerTransactionTestCase(SerializersTransactionTestBase, TransactionTestCase): + serializer_name = "xml" + fwd_ref_str = """<?xml version="1.0" encoding="utf-8"?> +<django-objects version="1.0"> + <object pk="1" model="serializers.article"> + <field to="serializers.author" name="author" rel="ManyToOneRel">1</field> + <field type="CharField" name="headline">Forward references pose no problem</field> + <field type="DateTimeField" name="pub_date">2006-06-16T15:00:00</field> + <field to="serializers.category" name="categories" rel="ManyToManyRel"> + <object pk="1"></object> + </field> + <field to="serializers.categorymetadata" name="meta_data" rel="ManyToManyRel"></field> + </object> + <object pk="1" model="serializers.author"> + <field type="CharField" name="name">Agnes</field> + </object> + <object pk="1" model="serializers.category"> + <field type="CharField" name="name">Reference</field></object> +</django-objects>""" diff --git a/tests/serializers/tests.py b/tests/serializers/tests.py index f92f0775d6..c288bf7ab8 100644 --- a/tests/serializers/tests.py +++ b/tests/serializers/tests.py @@ -4,7 +4,6 @@ from __future__ import unicode_literals import json import re from datetime import datetime -from xml.dom import minidom from django.core import serializers from django.core.serializers.base import ProgressBar @@ -14,7 +13,6 @@ from django.test import ( skipUnlessDBFeature, ) from django.test.utils import Approximate -from django.utils import six from django.utils.six import StringIO from .models import ( @@ -324,102 +322,6 @@ class SerializersTransactionTestBase(object): self.assertEqual(art_obj.author.name, "Agnes") -class XmlSerializerTestCase(SerializersTestBase, TestCase): - serializer_name = "xml" - pkless_str = """<?xml version="1.0" encoding="utf-8"?> -<django-objects version="1.0"> - <object model="serializers.category"> - <field type="CharField" name="name">Reference</field> - </object> - <object model="serializers.category"> - <field type="CharField" name="name">Non-fiction</field> - </object> -</django-objects>""" - mapping_ordering_str = """<?xml version="1.0" encoding="utf-8"?> -<django-objects version="1.0"> - <object model="serializers.article" pk="%(article_pk)s"> - <field name="author" rel="ManyToOneRel" to="serializers.author">%(author_pk)s</field> - <field name="headline" type="CharField">Poker has no place on ESPN</field> - <field name="pub_date" type="DateTimeField">2006-06-16T11:00:00</field> - <field name="categories" rel="ManyToManyRel" to="serializers.category"><object pk="%(first_category_pk)s"></object><object pk="%(second_category_pk)s"></object></field> - <field name="meta_data" rel="ManyToManyRel" to="serializers.categorymetadata"></field> - </object> -</django-objects>""" # NOQA - - @staticmethod - def _comparison_value(value): - # The XML serializer handles everything as strings, so comparisons - # need to be performed on the stringified value - return six.text_type(value) - - @staticmethod - def _validate_output(serial_str): - try: - minidom.parseString(serial_str) - except Exception: - return False - else: - return True - - @staticmethod - def _get_pk_values(serial_str): - ret_list = [] - dom = minidom.parseString(serial_str) - fields = dom.getElementsByTagName("object") - for field in fields: - ret_list.append(field.getAttribute("pk")) - return ret_list - - @staticmethod - def _get_field_values(serial_str, field_name): - ret_list = [] - dom = minidom.parseString(serial_str) - fields = dom.getElementsByTagName("field") - for field in fields: - if field.getAttribute("name") == field_name: - temp = [] - for child in field.childNodes: - temp.append(child.nodeValue) - ret_list.append("".join(temp)) - return ret_list - - def test_control_char_failure(self): - """ - Serializing control characters with XML should fail as those characters - are not supported in the XML 1.0 standard (except HT, LF, CR). - """ - self.a1.headline = "This contains \u0001 control \u0011 chars" - msg = "Article.headline (pk:%s) contains unserializable characters" % self.a1.pk - with self.assertRaisesMessage(ValueError, msg): - serializers.serialize(self.serializer_name, [self.a1]) - self.a1.headline = "HT \u0009, LF \u000A, and CR \u000D are allowed" - self.assertIn( - "HT \t, LF \n, and CR \r are allowed", - serializers.serialize(self.serializer_name, [self.a1]) - ) - - -class XmlSerializerTransactionTestCase(SerializersTransactionTestBase, TransactionTestCase): - serializer_name = "xml" - fwd_ref_str = """<?xml version="1.0" encoding="utf-8"?> -<django-objects version="1.0"> - <object pk="1" model="serializers.article"> - <field to="serializers.author" name="author" rel="ManyToOneRel">1</field> - <field type="CharField" name="headline">Forward references pose no problem</field> - <field type="DateTimeField" name="pub_date">2006-06-16T15:00:00</field> - <field to="serializers.category" name="categories" rel="ManyToManyRel"> - <object pk="1"></object> - </field> - <field to="serializers.categorymetadata" name="meta_data" rel="ManyToManyRel"></field> - </object> - <object pk="1" model="serializers.author"> - <field type="CharField" name="name">Agnes</field> - </object> - <object pk="1" model="serializers.category"> - <field type="CharField" name="name">Reference</field></object> -</django-objects>""" - - class JsonSerializerTestCase(SerializersTestBase, TestCase): serializer_name = "json" pkless_str = """[ diff --git a/tests/serializers_regress/tests.py b/tests/serializers_regress/tests.py index 3da6ddafb4..fd05eedc43 100644 --- a/tests/serializers_regress/tests.py +++ b/tests/serializers_regress/tests.py @@ -15,10 +15,9 @@ import uuid from django.core import serializers from django.core.serializers import SerializerDoesNotExist from django.core.serializers.base import DeserializationError -from django.core.serializers.xml_serializer import DTDForbidden from django.db import connection, models from django.http import HttpResponse -from django.test import SimpleTestCase, TestCase, skipUnlessDBFeature +from django.test import TestCase, skipUnlessDBFeature from django.utils import six from django.utils.functional import curry @@ -570,17 +569,3 @@ for format in [f for f in serializers.get_serializer_formats() setattr(SerializerTests, 'test_' + format + '_serializer_natural_keys', curry(naturalKeyTest, format)) if format != 'python': setattr(SerializerTests, 'test_' + format + '_serializer_stream', curry(streamTest, format)) - - -class XmlDeserializerSecurityTests(SimpleTestCase): - - def test_no_dtd(self): - """ - The XML deserializer shouldn't allow a DTD. - - This is the most straightforward way to prevent all entity definitions - and avoid both external entities and entity-expansion attacks. - """ - xml = '<?xml version="1.0" standalone="no"?><!DOCTYPE example SYSTEM "http://example.com/example.dtd">' - with self.assertRaises(DTDForbidden): - next(serializers.deserialize('xml', xml))