diff --git a/authentik/lib/xml.py b/authentik/lib/xml.py new file mode 100644 index 000000000..6ce5f0cde --- /dev/null +++ b/authentik/lib/xml.py @@ -0,0 +1,12 @@ +"""XML Utilities""" +from lxml.etree import XMLParser, fromstring # nosec + + +def get_lxml_parser(): + """Get XML parser""" + return XMLParser(resolve_entities=False) + + +def lxml_from_string(text: str): + """Wrapper around fromstring""" + return fromstring(text, parser=get_lxml_parser()) diff --git a/authentik/providers/saml/processors/request_parser.py b/authentik/providers/saml/processors/request_parser.py index 2e8875795..1dc596412 100644 --- a/authentik/providers/saml/processors/request_parser.py +++ b/authentik/providers/saml/processors/request_parser.py @@ -7,9 +7,9 @@ from xml.etree.ElementTree import ParseError # nosec import xmlsec from defusedxml import ElementTree -from lxml import etree # nosec from structlog.stdlib import get_logger +from authentik.lib.xml import lxml_from_string from authentik.providers.saml.exceptions import CannotHandleAssertion from authentik.providers.saml.models import SAMLProvider from authentik.providers.saml.utils.encoding import decode_base64_and_inflate @@ -95,7 +95,7 @@ class AuthNRequestParser: verifier = self.provider.verification_kp - root = etree.fromstring(decoded_xml) # nosec + root = lxml_from_string(decoded_xml) xmlsec.tree.add_ids(root, ["ID"]) signature_nodes = root.xpath("/samlp:AuthnRequest/ds:Signature", namespaces=NS_MAP) # No signatures, no verifier configured -> decode xml directly diff --git a/authentik/providers/saml/tests/test_schema.py b/authentik/providers/saml/tests/test_schema.py index 9fbe7f869..ecdec2c6e 100644 --- a/authentik/providers/saml/tests/test_schema.py +++ b/authentik/providers/saml/tests/test_schema.py @@ -6,6 +6,7 @@ from lxml import etree # nosec from authentik.core.tests.utils import create_test_cert, create_test_flow from authentik.lib.tests.utils import get_request +from authentik.lib.xml import lxml_from_string from authentik.managed.manager import ObjectManager from authentik.providers.saml.models import SAMLPropertyMapping, SAMLProvider from authentik.providers.saml.processors.assertion import AssertionProcessor @@ -44,7 +45,7 @@ class TestSchema(TestCase): request_proc = RequestProcessor(self.source, http_request, "test_state") request = request_proc.build_auth_n() - metadata = etree.fromstring(request) # nosec + metadata = lxml_from_string(request) schema = etree.XMLSchema(etree.parse("xml/saml-schema-protocol-2.0.xsd")) # nosec self.assertTrue(schema.validate(metadata)) @@ -65,7 +66,7 @@ class TestSchema(TestCase): response_proc = AssertionProcessor(self.provider, http_request, parsed_request) response = response_proc.build_response() - metadata = etree.fromstring(response) # nosec + metadata = lxml_from_string(response) schema = etree.XMLSchema(etree.parse("xml/saml-schema-protocol-2.0.xsd")) self.assertTrue(schema.validate(metadata)) diff --git a/authentik/sources/saml/tests/test_metadata.py b/authentik/sources/saml/tests/test_metadata.py index 1f2034f40..9876aaa0d 100644 --- a/authentik/sources/saml/tests/test_metadata.py +++ b/authentik/sources/saml/tests/test_metadata.py @@ -4,6 +4,7 @@ from django.test import RequestFactory, TestCase from lxml import etree # nosec from authentik.core.tests.utils import create_test_cert, create_test_flow +from authentik.lib.xml import lxml_from_string from authentik.sources.saml.models import SAMLSource from authentik.sources.saml.processors.metadata import MetadataProcessor @@ -24,7 +25,7 @@ class TestMetadataProcessor(TestCase): ) request = self.factory.get("/") xml = MetadataProcessor(source, request).build_entity_descriptor() - metadata = etree.fromstring(xml) # nosec + metadata = lxml_from_string(xml) schema = etree.XMLSchema(etree.parse("xml/saml-schema-metadata-2.0.xsd")) # nosec self.assertTrue(schema.validate(metadata)) diff --git a/pyproject.toml b/pyproject.toml index dccde20f7..39ea52ea3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -55,13 +55,11 @@ show_missing = true [tool.pylint.master] disable = [ "arguments-differ", - "no-self-use", "fixme", "locally-disabled", "too-many-ancestors", "too-few-public-methods", "import-outside-toplevel", - "bad-continuation", "signature-differs", "similarities", "cyclic-import",