mirror of https://github.com/django/django.git
Separated XML serialization tests
This commit is contained in:
parent
febd5aeec6
commit
d3cfdfb508
|
@ -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>"""
|
|
@ -4,7 +4,6 @@ from __future__ import unicode_literals
|
||||||
import json
|
import json
|
||||||
import re
|
import re
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from xml.dom import minidom
|
|
||||||
|
|
||||||
from django.core import serializers
|
from django.core import serializers
|
||||||
from django.core.serializers.base import ProgressBar
|
from django.core.serializers.base import ProgressBar
|
||||||
|
@ -14,7 +13,6 @@ from django.test import (
|
||||||
skipUnlessDBFeature,
|
skipUnlessDBFeature,
|
||||||
)
|
)
|
||||||
from django.test.utils import Approximate
|
from django.test.utils import Approximate
|
||||||
from django.utils import six
|
|
||||||
from django.utils.six import StringIO
|
from django.utils.six import StringIO
|
||||||
|
|
||||||
from .models import (
|
from .models import (
|
||||||
|
@ -324,102 +322,6 @@ class SerializersTransactionTestBase(object):
|
||||||
self.assertEqual(art_obj.author.name, "Agnes")
|
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):
|
class JsonSerializerTestCase(SerializersTestBase, TestCase):
|
||||||
serializer_name = "json"
|
serializer_name = "json"
|
||||||
pkless_str = """[
|
pkless_str = """[
|
||||||
|
|
|
@ -15,10 +15,9 @@ import uuid
|
||||||
from django.core import serializers
|
from django.core import serializers
|
||||||
from django.core.serializers import SerializerDoesNotExist
|
from django.core.serializers import SerializerDoesNotExist
|
||||||
from django.core.serializers.base import DeserializationError
|
from django.core.serializers.base import DeserializationError
|
||||||
from django.core.serializers.xml_serializer import DTDForbidden
|
|
||||||
from django.db import connection, models
|
from django.db import connection, models
|
||||||
from django.http import HttpResponse
|
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 import six
|
||||||
from django.utils.functional import curry
|
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))
|
setattr(SerializerTests, 'test_' + format + '_serializer_natural_keys', curry(naturalKeyTest, format))
|
||||||
if format != 'python':
|
if format != 'python':
|
||||||
setattr(SerializerTests, 'test_' + format + '_serializer_stream', curry(streamTest, format))
|
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))
|
|
||||||
|
|
Loading…
Reference in New Issue