diff --git a/python/ql/lib/semmle/python/Concepts.qll b/python/ql/lib/semmle/python/Concepts.qll index a768f29795c..3d83ec100a5 100644 --- a/python/ql/lib/semmle/python/Concepts.qll +++ b/python/ql/lib/semmle/python/Concepts.qll @@ -550,6 +550,68 @@ module XML { abstract string getName(); } } + + /** + * A kind of XML vulnerability. + * + * See overview of kinds at https://pypi.org/project/defusedxml/#python-xml-libraries + */ + class XMLVulnerabilityKind extends string { + XMLVulnerabilityKind() { + this in ["Billion Laughs", "Quadratic Blowup", "XXE", "DTD retrieval"] + } + + /** Holds for Billion Laughs vulnerability kind. */ + predicate isBillionLaughs() { this = "Billion Laughs" } + + /** Holds for Quadratic Blowup vulnerability kind. */ + predicate isQuadraticBlowup() { this = "Quadratic Blowup" } + + /** Holds for XXE vulnerability kind. */ + predicate isXxe() { this = "XXE" } + + /** Holds for DTD retrieval vulnerability kind. */ + predicate isDtdRetrieval() { this = "DTD retrieval" } + } + + /** + * A data-flow node that parses XML. + * + * Extend this class to model new APIs. If you want to refine existing API models, + * extend `XMLParsing` instead. + */ + class XMLParsing extends DataFlow::Node instanceof XMLParsing::Range { + /** + * Gets the argument containing the content to parse. + */ + DataFlow::Node getAnInput() { result = super.getAnInput() } + + /** + * Holds if this XML parsing is vulnerable to `kind`. + */ + predicate vulnerableTo(XMLVulnerabilityKind kind) { super.vulnerableTo(kind) } + } + + /** Provides classes for modeling XML parsing APIs. */ + module XMLParsing { + /** + * A data-flow node that parses XML. + * + * Extend this class to model new APIs. If you want to refine existing API models, + * extend `XMLParsing` instead. + */ + abstract class Range extends DataFlow::Node { + /** + * Gets the argument containing the content to parse. + */ + abstract DataFlow::Node getAnInput(); + + /** + * Holds if this XML parsing is vulnerable to `kind`. + */ + abstract predicate vulnerableTo(XMLVulnerabilityKind kind); + } + } } /** Provides classes for modeling LDAP-related APIs. */ diff --git a/python/ql/src/experimental/semmle/python/Concepts.qll b/python/ql/src/experimental/semmle/python/Concepts.qll index 6fdba4d3627..09b44d95e89 100644 --- a/python/ql/src/experimental/semmle/python/Concepts.qll +++ b/python/ql/src/experimental/semmle/python/Concepts.qll @@ -14,74 +14,6 @@ private import semmle.python.dataflow.new.RemoteFlowSources private import semmle.python.dataflow.new.TaintTracking private import experimental.semmle.python.Frameworks -/** - * Since there is both XML module in normal and experimental Concepts, - * we have to rename the experimental module as this. - */ -module ExperimentalXML { - /** - * A kind of XML vulnerability. - * - * See https://pypi.org/project/defusedxml/#python-xml-libraries - */ - class XMLVulnerabilityKind extends string { - XMLVulnerabilityKind() { - this in ["Billion Laughs", "Quadratic Blowup", "XXE", "DTD retrieval"] - } - - /** Holds for Billion Laughs vulnerability kind. */ - predicate isBillionLaughs() { this = "Billion Laughs" } - - /** Holds for Quadratic Blowup vulnerability kind. */ - predicate isQuadraticBlowup() { this = "Quadratic Blowup" } - - /** Holds for XXE vulnerability kind. */ - predicate isXxe() { this = "XXE" } - - /** Holds for DTD retrieval vulnerability kind. */ - predicate isDtdRetrieval() { this = "DTD retrieval" } - } - - /** - * A data-flow node that parses XML. - * - * Extend this class to model new APIs. If you want to refine existing API models, - * extend `XMLParsing` instead. - */ - class XMLParsing extends DataFlow::Node instanceof XMLParsing::Range { - /** - * Gets the argument containing the content to parse. - */ - DataFlow::Node getAnInput() { result = super.getAnInput() } - - /** - * Holds if this XML parsing is vulnerable to `kind`. - */ - predicate vulnerableTo(XMLVulnerabilityKind kind) { super.vulnerableTo(kind) } - } - - /** Provides classes for modeling XML parsing APIs. */ - module XMLParsing { - /** - * A data-flow node that parses XML. - * - * Extend this class to model new APIs. If you want to refine existing API models, - * extend `XMLParsing` instead. - */ - abstract class Range extends DataFlow::Node { - /** - * Gets the argument containing the content to parse. - */ - abstract DataFlow::Node getAnInput(); - - /** - * Holds if this XML parsing is vulnerable to `kind`. - */ - abstract predicate vulnerableTo(XMLVulnerabilityKind kind); - } - } -} - /** Provides classes for modeling LDAP query execution-related APIs. */ module LdapQuery { /** diff --git a/python/ql/src/experimental/semmle/python/frameworks/Xml.qll b/python/ql/src/experimental/semmle/python/frameworks/Xml.qll index a2f36f66f2e..87aa236804d 100644 --- a/python/ql/src/experimental/semmle/python/frameworks/Xml.qll +++ b/python/ql/src/experimental/semmle/python/frameworks/Xml.qll @@ -5,11 +5,9 @@ private import python private import semmle.python.dataflow.new.DataFlow -private import experimental.semmle.python.Concepts +private import semmle.python.Concepts private import semmle.python.ApiGraphs -module XML = ExperimentalXML; - private module XmlEtree { /** * Provides models for `xml.etree` parsers diff --git a/python/ql/src/experimental/semmle/python/security/dataflow/XmlBombCustomizations.qll b/python/ql/src/experimental/semmle/python/security/dataflow/XmlBombCustomizations.qll index 66a16a4494a..a4cbfe61821 100644 --- a/python/ql/src/experimental/semmle/python/security/dataflow/XmlBombCustomizations.qll +++ b/python/ql/src/experimental/semmle/python/security/dataflow/XmlBombCustomizations.qll @@ -6,7 +6,8 @@ private import python private import semmle.python.dataflow.new.DataFlow -private import experimental.semmle.python.Concepts +private import semmle.python.Concepts +import experimental.semmle.python.frameworks.Xml // needed until modeling have been promoted private import semmle.python.dataflow.new.RemoteFlowSources /** @@ -40,7 +41,7 @@ module XmlBomb { */ class XmlParsingWithEntityResolution extends Sink { XmlParsingWithEntityResolution() { - exists(ExperimentalXML::XMLParsing parsing, ExperimentalXML::XMLVulnerabilityKind kind | + exists(XML::XMLParsing parsing, XML::XMLVulnerabilityKind kind | (kind.isBillionLaughs() or kind.isQuadraticBlowup()) and parsing.vulnerableTo(kind) and this = parsing.getAnInput() diff --git a/python/ql/src/experimental/semmle/python/security/dataflow/XxeCustomizations.qll b/python/ql/src/experimental/semmle/python/security/dataflow/XxeCustomizations.qll index b2992dd335f..c118e1b2ff9 100644 --- a/python/ql/src/experimental/semmle/python/security/dataflow/XxeCustomizations.qll +++ b/python/ql/src/experimental/semmle/python/security/dataflow/XxeCustomizations.qll @@ -6,7 +6,8 @@ private import python private import semmle.python.dataflow.new.DataFlow -private import experimental.semmle.python.Concepts +private import semmle.python.Concepts +import experimental.semmle.python.frameworks.Xml // needed until modeling have been promoted private import semmle.python.dataflow.new.RemoteFlowSources /** @@ -40,7 +41,7 @@ module Xxe { */ class XmlParsingWithExternalEntityResolution extends Sink { XmlParsingWithExternalEntityResolution() { - exists(ExperimentalXML::XMLParsing parsing, ExperimentalXML::XMLVulnerabilityKind kind | + exists(XML::XMLParsing parsing, XML::XMLVulnerabilityKind kind | kind.isXxe() and parsing.vulnerableTo(kind) and this = parsing.getAnInput() diff --git a/python/ql/test/experimental/library-tests/frameworks/XML/ExperimentalXmlConceptsTests.ql b/python/ql/test/experimental/library-tests/frameworks/XML/ExperimentalXmlConceptsTests.ql index 81bc391d0e5..679dbc3456c 100644 --- a/python/ql/test/experimental/library-tests/frameworks/XML/ExperimentalXmlConceptsTests.ql +++ b/python/ql/test/experimental/library-tests/frameworks/XML/ExperimentalXmlConceptsTests.ql @@ -1,5 +1,5 @@ import python -import experimental.semmle.python.Concepts +import semmle.python.Concepts import experimental.semmle.python.frameworks.Xml import semmle.python.dataflow.new.DataFlow import TestUtilities.InlineExpectationsTest