diff --git a/python/ql/lib/semmle/python/Frameworks.qll b/python/ql/lib/semmle/python/Frameworks.qll index 8a374a9ca7d..da829fe352b 100644 --- a/python/ql/lib/semmle/python/Frameworks.qll +++ b/python/ql/lib/semmle/python/Frameworks.qll @@ -45,6 +45,7 @@ private import semmle.python.frameworks.Multidict private import semmle.python.frameworks.Mysql private import semmle.python.frameworks.MySQLdb private import semmle.python.frameworks.Numpy +private import semmle.python.frameworks.Opml private import semmle.python.frameworks.Oracledb private import semmle.python.frameworks.Pandas private import semmle.python.frameworks.Peewee diff --git a/python/ql/lib/semmle/python/frameworks/Opml.qll b/python/ql/lib/semmle/python/frameworks/Opml.qll new file mode 100644 index 00000000000..e365f54f9ef --- /dev/null +++ b/python/ql/lib/semmle/python/frameworks/Opml.qll @@ -0,0 +1,64 @@ +/** + * Provides classes modeling security-relevant aspects of the `opml` PyPI package. + * + * See + * - https://pypi.org/project/opml/ + */ + +private import python +private import semmle.python.dataflow.new.DataFlow +private import semmle.python.Concepts +private import semmle.python.ApiGraphs + +/** + * Provides classes modeling security-relevant aspects of the `opml` PyPI package + * + * See + * - https://pypi.org/project/opml/ + */ +private module Opml { + /** + * A call to the `xpath` method of a parsed document. + * + * import opml + * root = opml.from_string(file(XML_DB).read()) + * find_text = root.xpath("`sink`") + */ + private class XPathCall extends XML::XPathExecution::Range, DataFlow::CallCfgNode { + XPathCall() { + exists(API::Node parseResult | + parseResult = API::moduleImport("opml").getMember(["parse", "from_string"]).getReturn() + | + this = parseResult.getMember("xpath").getACall() + ) + } + + override DataFlow::Node getXPath() { result = this.getArg(0) } + + override string getName() { result = "opml" } + } + + /** + * A call to either of: + * - `opml.parse` + * - `opml.from_string` + */ + private class OpmlParsing extends DataFlow::CallCfgNode, XML::XmlParsing::Range { + OpmlParsing() { + this = API::moduleImport("opml").getMember(["parse", "from_string"]).getACall() + } + + override DataFlow::Node getAnInput() { result = this.getArg(0) } + + DataFlow::Node getParserArg() { none() } + + /** + * The same as `Lxml::LxmlParsing::vulnerableTo`, because `opml` uses `lxml` for parsing. + */ + override predicate vulnerableTo(XML::XmlParsingVulnerabilityKind kind) { kind.isXxe() } + + override predicate mayExecuteInput() { none() } + + override DataFlow::Node getOutput() { result = this } + } +} diff --git a/python/ql/src/change-notes/2024-05-27-opml-models.md b/python/ql/src/change-notes/2024-05-27-opml-models.md new file mode 100644 index 00000000000..1569dacd2d4 --- /dev/null +++ b/python/ql/src/change-notes/2024-05-27-opml-models.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* Added models for `opml` library. \ No newline at end of file