Skip to content

Commit b83b31c

Browse files
committed
Write qldocs
1 parent 61e873d commit b83b31c

1 file changed

Lines changed: 77 additions & 0 deletions

File tree

  • python/ql/src/experimental/semmle/python/frameworks

python/ql/src/experimental/semmle/python/frameworks/XML.qll

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,13 @@ private import experimental.semmle.python.Concepts
99
private import semmle.python.ApiGraphs
1010

1111
private 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

Comments
 (0)