Skip to content

Commit 80be767

Browse files
committed
python: implement stdlib xpath support
1 parent 06e0f14 commit 80be767

File tree

2 files changed

+70
-6
lines changed

2 files changed

+70
-6
lines changed

python/ql/lib/semmle/python/frameworks/Stdlib.qll

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2809,6 +2809,70 @@ private module StdlibPrivate {
28092809
override string getKind() { result = Escaping::getRegexKind() }
28102810
}
28112811

2812+
// ---------------------------------------------------------------------------
2813+
// xml.etree.ElementTree
2814+
// ---------------------------------------------------------------------------
2815+
/**
2816+
* An instance of `xml.etree.ElementTree.ElementTree`.
2817+
*
2818+
* See https://docs.python.org/3.10/library/xml.etree.elementtree.html#xml.etree.ElementTree.ElementTree
2819+
*/
2820+
private API::Node elementTreeInstance() {
2821+
//parse to a tree
2822+
result =
2823+
API::moduleImport("xml")
2824+
.getMember("etree")
2825+
.getMember("ElementTree")
2826+
.getMember("parse")
2827+
.getReturn()
2828+
or
2829+
// construct a tree without parsing
2830+
result =
2831+
API::moduleImport("xml")
2832+
.getMember("etree")
2833+
.getMember("ElementTree")
2834+
.getMember("ElementTree")
2835+
.getReturn()
2836+
}
2837+
2838+
/**
2839+
* An instance of `xml.etree.ElementTree.Element`.
2840+
*
2841+
* See https://docs.python.org/3.10/library/xml.etree.elementtree.html#xml.etree.ElementTree.Element
2842+
*/
2843+
private API::Node elementInstance() {
2844+
// parse or go to the root of a tree
2845+
result = elementTreeInstance().getMember(["parse", "getroot"]).getReturn()
2846+
or
2847+
// parse directly to an element
2848+
result =
2849+
API::moduleImport("xml")
2850+
.getMember("etree")
2851+
.getMember("ElementTree")
2852+
.getMember(["fromstring", "fromstringlist", "XML"])
2853+
.getReturn()
2854+
}
2855+
2856+
/**
2857+
* A call to a find method on a tree or an element will execute an XPath expression.
2858+
*/
2859+
private class ElementTreeFindCall extends XPathExecution::Range, DataFlow::CallCfgNode {
2860+
string methodName;
2861+
2862+
ElementTreeFindCall() {
2863+
methodName in ["find", "findall", "findtext"] and
2864+
(
2865+
this = elementTreeInstance().getMember(methodName).getACall()
2866+
or
2867+
this = elementInstance().getMember(methodName).getACall()
2868+
)
2869+
}
2870+
2871+
override DataFlow::Node getXPath() { result in [this.getArg(0), this.getArgByName("match")] }
2872+
2873+
override string getName() { result = "xml.etree" }
2874+
}
2875+
28122876
// ---------------------------------------------------------------------------
28132877
// urllib
28142878
// ---------------------------------------------------------------------------

python/ql/test/library-tests/frameworks/stdlib/XPathExecution.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,14 @@
55
tree = ET.parse('country_data.xml')
66
root = tree.getroot()
77

8-
root.find(match, namespaces=ns) # $ MISSING: getXPath=match
9-
root.findall(match, namespaces=ns) # $ MISSING: getXPath=match
10-
root.findtext(match, default=None, namespaces=ns) # $ MISSING: getXPath=match
8+
root.find(match, namespaces=ns) # $ getXPath=match
9+
root.findall(match, namespaces=ns) # $ getXPath=match
10+
root.findtext(match, default=None, namespaces=ns) # $ getXPath=match
1111

1212
from xml.etree.ElementTree import ElementTree
1313
tree = ElementTree()
1414
tree.parse("index.xhtml")
1515

16-
tree.find(match, namespaces=ns) # $ MISSING: getXPath=match
17-
tree.findall(match, namespaces=ns) # $ MISSING: getXPath=match
18-
tree.findtext(match, default=None, namespaces=ns) # $ MISSING: getXPath=match
16+
tree.find(match, namespaces=ns) # $ getXPath=match
17+
tree.findall(match, namespaces=ns) # $ getXPath=match
18+
tree.findtext(match, default=None, namespaces=ns) # $ getXPath=match

0 commit comments

Comments
 (0)