diff -Naur pysaml2/setup.py pysaml2.new/setup.py --- pysaml2/setup.py 2015-12-06 00:46:33.000000000 -0600 +++ pysaml2.new/setup.py 2017-01-10 20:31:43.387413477 -0600 @@ -17,6 +17,7 @@ 'pytz', 'pyOpenSSL', 'python-dateutil', + 'defusedxml', 'six' ] diff -Naur pysaml2/src/saml2/__init__.py pysaml2.new/src/saml2/__init__.py --- pysaml2/src/saml2/__init__.py 2016-01-07 05:53:57.000000000 -0600 +++ pysaml2.new/src/saml2/__init__.py 2017-01-10 20:34:04.171641116 -0600 @@ -35,6 +35,7 @@ import cElementTree as ElementTree except ImportError: from elementtree import ElementTree +import defusedxml.ElementTree root_logger = logging.getLogger(__name__) root_logger.level = logging.NOTSET @@ -86,7 +87,7 @@ """ if not isinstance(xml_string, six.binary_type): xml_string = xml_string.encode('utf-8') - tree = ElementTree.fromstring(xml_string) + tree = defusedxml.ElementTree.fromstring(xml_string) return create_class_from_element_tree(target_class, tree) @@ -268,7 +269,7 @@ def extension_element_from_string(xml_string): - element_tree = ElementTree.fromstring(xml_string) + element_tree = defusedxml.ElementTree.fromstring(xml_string) return _extension_element_from_element_tree(element_tree) diff -Naur pysaml2/src/saml2/pack.py pysaml2.new/src/saml2/pack.py --- pysaml2/src/saml2/pack.py 2015-12-11 07:31:39.000000000 -0600 +++ pysaml2.new/src/saml2/pack.py 2017-01-10 20:35:35.382435020 -0600 @@ -37,6 +37,7 @@ import cElementTree as ElementTree except ImportError: from elementtree import ElementTree +import defusedxml.ElementTree NAMESPACE = "http://schemas.xmlsoap.org/soap/envelope/" FORM_SPEC = """
@@ -235,7 +236,7 @@ :param text: The SOAP object as XML :return: header parts and body as saml.samlbase instances """ - envelope = ElementTree.fromstring(text) + envelope = defusedxml.ElementTree.fromstring(text) assert envelope.tag == '{%s}Envelope' % NAMESPACE # print(len(envelope)) diff -Naur pysaml2/src/saml2/soap.py pysaml2.new/src/saml2/soap.py --- pysaml2/src/saml2/soap.py 2015-05-18 02:54:05.000000000 -0500 +++ pysaml2.new/src/saml2/soap.py 2017-01-10 20:36:16.163808770 -0600 @@ -19,6 +19,7 @@ except ImportError: #noinspection PyUnresolvedReferences from elementtree import ElementTree +import defusedxml.ElementTree logger = logging.getLogger(__name__) @@ -133,7 +134,7 @@ :param expected_tags: What the tag of the SAML thingy is expected to be. :return: SAML thingy as a string """ - envelope = ElementTree.fromstring(text) + envelope = defusedxml.ElementTree.fromstring(text) # Make sure it's a SOAP message assert envelope.tag == '{%s}Envelope' % soapenv.NAMESPACE @@ -183,7 +184,7 @@ :return: The body and headers as class instances """ try: - envelope = ElementTree.fromstring(text) + envelope = defusedxml.ElementTree.fromstring(text) except Exception as exc: raise XmlParseError("%s" % exc) @@ -209,7 +210,7 @@ :return: dictionary with two keys "body"/"header" """ try: - envelope = ElementTree.fromstring(text) + envelope = defusedxml.ElementTree.fromstring(text) except Exception as exc: raise XmlParseError("%s" % exc) diff -Naur pysaml2/tests/test_03_saml2.py pysaml2.new/tests/test_03_saml2.py --- pysaml2/tests/test_03_saml2.py 2015-06-06 02:15:20.000000000 -0500 +++ pysaml2.new/tests/test_03_saml2.py 2017-01-10 20:38:32.541728380 -0600 @@ -17,6 +17,7 @@ import cElementTree as ElementTree except ImportError: from elementtree import ElementTree +from defusedxml.common import EntitiesForbidden ITEMS = { NameID: [""" @@ -27,7 +28,7 @@ """, """ _1632879f09d08ea5ede2dc667cbed7e429ebc4335c """, """ """, SubjectConfirmation: """ @@ -166,6 +167,19 @@ assert kl == None +def test_create_class_from_xml_string_xxe(): + xml = """ + + + + ]> + &lol1; + """ + with raises(EntitiesForbidden) as err: + create_class_from_xml_string(NameID, xml) + + def test_ee_1(): ee = saml2.extension_element_from_string( """bar""") @@ -193,7 +207,7 @@ def test_ee_3(): ee = saml2.extension_element_from_string( """ - bar""") assert ee != None print(ee.__dict__) @@ -454,6 +468,19 @@ assert nid.text.strip() == "http://federationX.org" +def test_ee_xxe(): + xml = """ + + + + ]> + &lol1; + """ + with raises(EntitiesForbidden): + saml2.extension_element_from_string(xml) + + def test_extension_element_loadd(): ava = {'attributes': {}, 'tag': 'ExternalEntityAttributeAuthority', diff -Naur pysaml2/tests/test_43_soap.py pysaml2.new/tests/test_43_soap.py --- pysaml2/tests/test_43_soap.py 2013-04-28 09:38:07.000000000 -0500 +++ pysaml2.new/tests/test_43_soap.py 2017-01-10 20:39:53.730364008 -0600 @@ -12,16 +12,20 @@ import cElementTree as ElementTree except ImportError: from elementtree import ElementTree +from defusedxml.common import EntitiesForbidden + +from pytest import raises import saml2.samlp as samlp from saml2.samlp import NAMESPACE as SAMLP_NAMESPACE +from saml2 import soap NAMESPACE = "http://schemas.xmlsoap.org/soap/envelope/" example = """ - https://www.example.com/SAML @@ -55,7 +59,7 @@ envelope.tag = '{%s}Envelope' % NAMESPACE body = ElementTree.Element('') body.tag = '{%s}Body' % NAMESPACE - envelope.append(body) + envelope.append(body) request = samlp.AuthnRequest() request.become_child_element_of(body) @@ -66,3 +70,42 @@ assert len(body) == 1 saml_part = body[0] assert saml_part.tag == '{%s}AuthnRequest' % SAMLP_NAMESPACE + + +def test_parse_soap_enveloped_saml_thingy_xxe(): + xml = """ + + + + ]> + &lol1; + """ + with raises(EntitiesForbidden): + soap.parse_soap_enveloped_saml_thingy(xml, None) + + +def test_class_instances_from_soap_enveloped_saml_thingies_xxe(): + xml = """ + + + + ]> + &lol1; + """ + with raises(soap.XmlParseError): + soap.class_instances_from_soap_enveloped_saml_thingies(xml, None) + + +def test_open_soap_envelope_xxe(): + xml = """ + + + + ]> + &lol1; + """ + with raises(soap.XmlParseError): + soap.open_soap_envelope(xml) diff -Naur pysaml2/tests/test_51_client.py pysaml2.new/tests/test_51_client.py --- pysaml2/tests/test_51_client.py 2015-12-11 05:10:01.000000000 -0600 +++ pysaml2.new/tests/test_51_client.py 2017-01-10 20:42:12.819280442 -0600 @@ -5,6 +5,7 @@ import uuid import six from six.moves.urllib.parse import parse_qs, urlencode, urlparse +from pytest import raises from saml2.cert import OpenSSLWrapper from saml2.xmldsig import SIG_RSA_SHA256 from saml2 import BINDING_HTTP_POST @@ -21,6 +22,7 @@ from saml2.authn_context import INTERNETPROTOCOLPASSWORD from saml2.client import Saml2Client from saml2.config import SPConfig +from saml2.pack import parse_soap_enveloped_saml from saml2.response import LogoutResponse from saml2.saml import NAMEID_FORMAT_PERSISTENT, EncryptedAssertion, Advice from saml2.saml import NAMEID_FORMAT_TRANSIENT @@ -34,6 +36,8 @@ from saml2.s_utils import factory from saml2.time_util import in_a_while, a_while_ago +from defusedxml.common import EntitiesForbidden + from fakeIDP import FakeIDP from fakeIDP import unpack_form from pathutils import full_path @@ -1445,6 +1449,18 @@ 'http://www.example.com/login' assert ac.authn_context_class_ref.text == INTERNETPROTOCOLPASSWORD +def test_parse_soap_enveloped_saml_xxe(): + xml = """ + + + + ]> + &lol1; + """ + with raises(EntitiesForbidden): + parse_soap_enveloped_saml(xml, None) + # if __name__ == "__main__": # tc = TestClient()