@@ -9,10 +9,13 @@ private import experimental.semmle.python.Concepts
99private import semmle.python.ApiGraphs
1010
1111private module XML {
12+ /** Gets a reference to the `xml` module. */
1213 private API:: Node xml ( ) { result = API:: moduleImport ( "xml" ) }
1314
15+ /** Gets a reference to `xml.etree.ElementTree`. */
1416 private API:: Node xmlEtree ( ) { result = xml ( ) .getMember ( "etree" ) .getMember ( "ElementTree" ) }
1517
18+ /** Gets a call to `xml.etree.ElementTree.XMLParser`. */
1619 private class XMLEtreeParser extends DataFlow:: CallCfgNode , XMLParser:: Range {
1720 XMLEtreeParser ( ) { this = xmlEtree ( ) .getMember ( "XMLParser" ) .getACall ( ) }
1821
@@ -21,6 +24,20 @@ private module XML {
2124 override predicate mayBeDangerous ( ) { any ( ) }
2225 }
2326
27+ /**
28+ * Gets a call to `xml.etree.ElementTree.fromstring`, `xml.etree.ElementTree.fromstringlist`,
29+ * `xml.etree.ElementTree.XML` or `xml.etree.ElementTree.parse`.
30+ *
31+ * Given the following example:
32+ *
33+ * ```py
34+ * parser = lxml.etree.XMLParser()
35+ * parsed_xml = xml.etree.ElementTree.fromstring(xml_content, parser=parser).text
36+ * ```
37+ *
38+ * `this` would be `xml.etree.ElementTree.fromstring(xml_content, parser=parser)`
39+ * and `xml_content` would be the result of `getAnInput()`.
40+ */
2441 private class XMLEtreeParsing extends DataFlow:: CallCfgNode , XMLParsing:: Range {
2542 XMLEtreeParsing ( ) {
2643 this = xmlEtree ( ) .getMember ( [ "fromstring" , "fromstringlist" , "XML" , "parse" ] ) .getACall ( )
@@ -35,8 +52,27 @@ private module XML {
3552 }
3653 }
3754
55+ /** Gets a reference to `xml.sax`. */
3856 private API:: Node xmlSax ( ) { result = xml ( ) .getMember ( "sax" ) }
3957
58+ /**
59+ * Gets a call to `xml.sax.make_parser` and following calls.
60+ *
61+ * Given the following example:
62+ *
63+ * ```py
64+ * BadHandler = MainHandler()
65+ * parser = xml.sax.make_parser()
66+ * parser.setContentHandler(BadHandler)
67+ * parser.setFeature(xml.sax.handler.feature_external_ges, False)
68+ * parser.parse(StringIO(xml_content))
69+ * parsed_xml = BadHandler._result
70+ * ```
71+ *
72+ * `this` would be `xml.sax.make_parser()`, `getAnInput()` would return `StringIO(xml_content)`
73+ * and `mayBeDangerous()` would succeed since `xml.sax.handler.feature_external_ges` is set to
74+ * `False` and so it's vulnerable.
75+ */
4076 private class XMLSaxParser extends DataFlow:: CallCfgNode , XMLParser:: Range {
4177 DataFlow:: CallCfgNode attrCall ;
4278
@@ -57,10 +93,17 @@ private module XML {
5793 }
5894 }
5995
96+ /** Gets a reference to `lxml`. */
6097 private API:: Node lxml ( ) { result = API:: moduleImport ( "lxml" ) }
6198
99+ /** Gets a reference to `lxml.etree`. */
62100 private API:: Node lxmlEtree ( ) { result = lxml ( ) .getMember ( "etree" ) }
63101
102+ /**
103+ * Gets a call to `lxml.etree.XMLParser` or `lxml.etree.get_default_parser` and `mayBeDangerous()`
104+ * identifies whether the argument `no_network` is set to `False` or the arguments `huge_tree`
105+ * or `resolve_entities` are set to True.
106+ */
64107 private class LXMLParser extends DataFlow:: CallCfgNode , XMLParser:: Range {
65108 LXMLParser ( ) { this = lxmlEtree ( ) .getMember ( [ "XMLParser" , "get_default_parser" ] ) .getACall ( ) }
66109
@@ -74,6 +117,20 @@ private module XML {
74117 }
75118 }
76119
120+ /**
121+ * Gets a call to `lxml.etree.fromstring`, `xml.etree.fromstringlist`,
122+ * `xml.etree.XML` or `xml.etree.parse`.
123+ *
124+ * Given the following example:
125+ *
126+ * ```py
127+ * parser = lxml.etree.XMLParser()
128+ * parsed_xml = lxml.etree.fromstring(xml_content, parser=parser).text
129+ * ```
130+ *
131+ * `this` would be `lxml.etree.fromstring(xml_content, parser=parser)`
132+ * and `xml_content` would be the result of `getAnInput()`.
133+ */
77134 private class LXMLParsing extends DataFlow:: CallCfgNode , XMLParsing:: Range {
78135 LXMLParsing ( ) {
79136 this = lxmlEtree ( ) .getMember ( [ "fromstring" , "fromstringlist" , "XML" , "parse" ] ) .getACall ( )
@@ -88,8 +145,13 @@ private module XML {
88145 }
89146 }
90147
148+ /** Gets a reference to the `xmltodict` module. */
91149 private API:: Node xmltodict ( ) { result = API:: moduleImport ( "xmltodict" ) }
92150
151+ /**
152+ * Gets a call to `xmltodict.parse` and `mayBeDangerous()` identifies
153+ * whether the argument `disable_entities` is set to `False`.
154+ */
93155 private class XMLtoDictParsing extends DataFlow:: CallCfgNode , XMLParsing:: Range {
94156 XMLtoDictParsing ( ) { this = xmltodict ( ) .getMember ( "parse" ) .getACall ( ) }
95157
@@ -101,8 +163,23 @@ private module XML {
101163 }
102164 }
103165
166+ /** Gets a reference to `xml.dom.minidom` or `xml.dom.pulldom`. */
104167 private API:: Node xmlDom ( ) { result = xml ( ) .getMember ( "dom" ) .getMember ( [ "mini" , "pull" ] + "dom" ) }
105168
169+ /**
170+ * Gets a call to `xml.dom.minidom.parse` or `xml.dom.pulldom.parse`.
171+ *
172+ * Given the following example:
173+ *
174+ * ```py
175+ * parser = xml.sax.make_parser()
176+ * parser.setFeature(xml.sax.handler.feature_external_ges, True)
177+ * parsed_xml = xml.dom.minidom.parse(StringIO(xml_content), parser=parser).documentElement.childNod
178+ * ```
179+ *
180+ * `this` would be `xml.dom.minidom.parse(StringIO(xml_content), parser=parser)`
181+ * and `StringIO(xml_content)` would be the result of `getAnInput()`.
182+ */
106183 private class XMLDomParsing extends DataFlow:: CallCfgNode , XMLParsing:: Range {
107184 XMLDomParsing ( ) { this = xmlDom ( ) .getMember ( "parse" ) .getACall ( ) }
108185
0 commit comments