VYPR
Moderate severityNVD Advisory· Published Apr 3, 2013· Updated Apr 29, 2026

CVE-2013-1665

CVE-2013-1665

Description

The XML libraries for Python 3.4, 3.3, 3.2, 3.1, 2.7, and 2.6, as used in OpenStack Keystone Essex and Folsom, Django, and possibly other products allow remote attackers to read arbitrary files via an XML external entity declaration in conjunction with an entity reference, aka an XML External Entity (XXE) attack.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
DjangoPyPI
>= 1.3.0, < 1.3.61.3.6
DjangoPyPI
>= 1.4.0, < 1.4.41.4.4

Affected products

2

Patches

2
d19a27066b22

[1.3.x] Restrict the XML deserializer to prevent network and entity-expansion DoS attacks.

https://github.com/django/djangoCarl MeyerFeb 12, 2013via ghsa
2 files changed · +108 1
  • django/core/serializers/xml_serializer.py+93 1 modified
    @@ -8,6 +8,8 @@
     from django.utils.xmlutils import SimplerXMLGenerator
     from django.utils.encoding import smart_unicode
     from xml.dom import pulldom
    +from xml.sax import handler
    +from xml.sax.expatreader import ExpatParser as _ExpatParser
     
     class Serializer(base.Serializer):
         """
    @@ -154,9 +156,13 @@ class Deserializer(base.Deserializer):
     
         def __init__(self, stream_or_string, **options):
             super(Deserializer, self).__init__(stream_or_string, **options)
    -        self.event_stream = pulldom.parse(self.stream)
    +        self.event_stream = pulldom.parse(self.stream, self._make_parser())
             self.db = options.pop('using', DEFAULT_DB_ALIAS)
     
    +    def _make_parser(self):
    +        """Create a hardened XML parser (no custom/external entities)."""
    +        return DefusedExpatParser()
    +
         def next(self):
             for event, node in self.event_stream:
                 if event == "START_ELEMENT" and node.nodeName == "object":
    @@ -295,3 +301,89 @@ def getInnerText(node):
             else:
                pass
         return u"".join(inner_text)
    +
    +
    +# Below code based on Christian Heimes' defusedxml
    +
    +
    +class DefusedExpatParser(_ExpatParser):
    +    """
    +    An expat parser hardened against XML bomb attacks.
    +
    +    Forbids DTDs, external entity references
    +
    +    """
    +    def __init__(self, *args, **kwargs):
    +        _ExpatParser.__init__(self, *args, **kwargs)
    +        self.setFeature(handler.feature_external_ges, False)
    +        self.setFeature(handler.feature_external_pes, False)
    +
    +    def start_doctype_decl(self, name, sysid, pubid, has_internal_subset):
    +        raise DTDForbidden(name, sysid, pubid)
    +
    +    def entity_decl(self, name, is_parameter_entity, value, base,
    +                    sysid, pubid, notation_name):
    +        raise EntitiesForbidden(name, value, base, sysid, pubid, notation_name)
    +
    +    def unparsed_entity_decl(self, name, base, sysid, pubid, notation_name):
    +        # expat 1.2
    +        raise EntitiesForbidden(name, None, base, sysid, pubid, notation_name)
    +
    +    def external_entity_ref_handler(self, context, base, sysid, pubid):
    +        raise ExternalReferenceForbidden(context, base, sysid, pubid)
    +
    +    def reset(self):
    +        _ExpatParser.reset(self)
    +        parser = self._parser
    +        parser.StartDoctypeDeclHandler = self.start_doctype_decl
    +        parser.EntityDeclHandler = self.entity_decl
    +        parser.UnparsedEntityDeclHandler = self.unparsed_entity_decl
    +        parser.ExternalEntityRefHandler = self.external_entity_ref_handler
    +
    +
    +class DefusedXmlException(ValueError):
    +    """Base exception."""
    +    def __repr__(self):
    +        return str(self)
    +
    +
    +class DTDForbidden(DefusedXmlException):
    +    """Document type definition is forbidden."""
    +    def __init__(self, name, sysid, pubid):
    +        self.name = name
    +        self.sysid = sysid
    +        self.pubid = pubid
    +
    +    def __str__(self):
    +        tpl = "DTDForbidden(name='{}', system_id={!r}, public_id={!r})"
    +        return tpl.format(self.name, self.sysid, self.pubid)
    +
    +
    +class EntitiesForbidden(DefusedXmlException):
    +    """Entity definition is forbidden."""
    +    def __init__(self, name, value, base, sysid, pubid, notation_name):
    +        super(EntitiesForbidden, self).__init__()
    +        self.name = name
    +        self.value = value
    +        self.base = base
    +        self.sysid = sysid
    +        self.pubid = pubid
    +        self.notation_name = notation_name
    +
    +    def __str__(self):
    +        tpl = "EntitiesForbidden(name='{}', system_id={!r}, public_id={!r})"
    +        return tpl.format(self.name, self.sysid, self.pubid)
    +
    +
    +class ExternalReferenceForbidden(DefusedXmlException):
    +    """Resolving an external reference is forbidden."""
    +    def __init__(self, context, base, sysid, pubid):
    +        super(ExternalReferenceForbidden, self).__init__()
    +        self.context = context
    +        self.base = base
    +        self.sysid = sysid
    +        self.pubid = pubid
    +
    +    def __str__(self):
    +        tpl = "ExternalReferenceForbidden(system_id='{}', public_id={})"
    +        return tpl.format(self.sysid, self.pubid)
    
  • tests/regressiontests/serializers_regress/tests.py+15 0 modified
    @@ -14,6 +14,7 @@
         from cStringIO import StringIO
     except ImportError:
         from StringIO import StringIO
    +from django.core.serializers.xml_serializer import DTDForbidden
     
     from django.conf import settings
     from django.core import serializers, management
    @@ -416,3 +417,17 @@ def streamTest(format, self):
         setattr(SerializerTests, 'test_' + format + '_serializer_fields', curry(fieldsTest, format))
         if format != 'python':
             setattr(SerializerTests, 'test_' + format + '_serializer_stream', curry(streamTest, format))
    +
    +
    +class XmlDeserializerSecurityTests(TestCase):
    +
    +    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">'
    +        self.assertRaises(DTDForbidden, serializers.deserialize('xml', xml).next)
    
1c60d07ba23e

[1.4.x] Restrict the XML deserializer to prevent network and entity-expansion DoS attacks.

https://github.com/django/djangoCarl MeyerFeb 12, 2013via ghsa
2 files changed · +108 1
  • django/core/serializers/xml_serializer.py+94 1 modified
    @@ -8,6 +8,8 @@
     from django.utils.xmlutils import SimplerXMLGenerator
     from django.utils.encoding import smart_unicode
     from xml.dom import pulldom
    +from xml.sax import handler
    +from xml.sax.expatreader import ExpatParser as _ExpatParser
     
     class Serializer(base.Serializer):
         """
    @@ -149,9 +151,13 @@ class Deserializer(base.Deserializer):
     
         def __init__(self, stream_or_string, **options):
             super(Deserializer, self).__init__(stream_or_string, **options)
    -        self.event_stream = pulldom.parse(self.stream)
    +        self.event_stream = pulldom.parse(self.stream, self._make_parser())
             self.db = options.pop('using', DEFAULT_DB_ALIAS)
     
    +    def _make_parser(self):
    +        """Create a hardened XML parser (no custom/external entities)."""
    +        return DefusedExpatParser()
    +
         def next(self):
             for event, node in self.event_stream:
                 if event == "START_ELEMENT" and node.nodeName == "object":
    @@ -290,3 +296,90 @@ def getInnerText(node):
             else:
                pass
         return u"".join(inner_text)
    +
    +
    +# Below code based on Christian Heimes' defusedxml
    +
    +
    +class DefusedExpatParser(_ExpatParser):
    +    """
    +    An expat parser hardened against XML bomb attacks.
    +
    +    Forbids DTDs, external entity references
    +
    +    """
    +    def __init__(self, *args, **kwargs):
    +        _ExpatParser.__init__(self, *args, **kwargs)
    +        self.setFeature(handler.feature_external_ges, False)
    +        self.setFeature(handler.feature_external_pes, False)
    +
    +    def start_doctype_decl(self, name, sysid, pubid, has_internal_subset):
    +        raise DTDForbidden(name, sysid, pubid)
    +
    +    def entity_decl(self, name, is_parameter_entity, value, base,
    +                    sysid, pubid, notation_name):
    +        raise EntitiesForbidden(name, value, base, sysid, pubid, notation_name)
    +
    +    def unparsed_entity_decl(self, name, base, sysid, pubid, notation_name):
    +        # expat 1.2
    +        raise EntitiesForbidden(name, None, base, sysid, pubid, notation_name)
    +
    +    def external_entity_ref_handler(self, context, base, sysid, pubid):
    +        raise ExternalReferenceForbidden(context, base, sysid, pubid)
    +
    +    def reset(self):
    +        _ExpatParser.reset(self)
    +        parser = self._parser
    +        parser.StartDoctypeDeclHandler = self.start_doctype_decl
    +        parser.EntityDeclHandler = self.entity_decl
    +        parser.UnparsedEntityDeclHandler = self.unparsed_entity_decl
    +        parser.ExternalEntityRefHandler = self.external_entity_ref_handler
    +
    +
    +class DefusedXmlException(ValueError):
    +    """Base exception."""
    +    def __repr__(self):
    +        return str(self)
    +
    +
    +class DTDForbidden(DefusedXmlException):
    +    """Document type definition is forbidden."""
    +    def __init__(self, name, sysid, pubid):
    +        super(DTDForbidden, self).__init__()
    +        self.name = name
    +        self.sysid = sysid
    +        self.pubid = pubid
    +
    +    def __str__(self):
    +        tpl = "DTDForbidden(name='{}', system_id={!r}, public_id={!r})"
    +        return tpl.format(self.name, self.sysid, self.pubid)
    +
    +
    +class EntitiesForbidden(DefusedXmlException):
    +    """Entity definition is forbidden."""
    +    def __init__(self, name, value, base, sysid, pubid, notation_name):
    +        super(EntitiesForbidden, self).__init__()
    +        self.name = name
    +        self.value = value
    +        self.base = base
    +        self.sysid = sysid
    +        self.pubid = pubid
    +        self.notation_name = notation_name
    +
    +    def __str__(self):
    +        tpl = "EntitiesForbidden(name='{}', system_id={!r}, public_id={!r})"
    +        return tpl.format(self.name, self.sysid, self.pubid)
    +
    +
    +class ExternalReferenceForbidden(DefusedXmlException):
    +    """Resolving an external reference is forbidden."""
    +    def __init__(self, context, base, sysid, pubid):
    +        super(ExternalReferenceForbidden, self).__init__()
    +        self.context = context
    +        self.base = base
    +        self.sysid = sysid
    +        self.pubid = pubid
    +
    +    def __str__(self):
    +        tpl = "ExternalReferenceForbidden(system_id='{}', public_id={})"
    +        return tpl.format(self.sysid, self.pubid)
    
  • tests/regressiontests/serializers_regress/tests.py+14 0 modified
    @@ -16,6 +16,7 @@
         from cStringIO import StringIO
     except ImportError:
         from StringIO import StringIO
    +from django.core.serializers.xml_serializer import DTDForbidden
     
     try:
         import yaml
    @@ -523,3 +524,16 @@ def streamTest(format, self):
         if format != 'python':
             setattr(SerializerTests, 'test_' + format + '_serializer_stream', curry(streamTest, format))
     
    +
    +class XmlDeserializerSecurityTests(TestCase):
    +
    +    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">'
    +        self.assertRaises(DTDForbidden, serializers.deserialize('xml', xml).next)
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

15

News mentions

0

No linked articles in our index yet.