Skip to content

Commit ce3d3cd

Browse files
DarkaMaulencukou
authored andcommitted
Fix GHSA-55rc-7hww-hf4p
1 parent b383aa6 commit ce3d3cd

2 files changed

Lines changed: 29 additions & 1 deletion

File tree

Lib/test/test_xml_etree.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import pyexpat
1717
import sys
1818
import textwrap
19+
import time
1920
import types
2021
import unittest
2122
import unittest.mock as mock
@@ -3477,6 +3478,29 @@ def test_find_xpath(self):
34773478
self.assertRaisesRegex(SyntaxError, 'XPath', e.find, './tag[last()-0]')
34783479
self.assertRaisesRegex(SyntaxError, 'XPath', e.find, './tag[last()+1]')
34793480

3481+
def test_find_xpath_index_no_quadratic_complexity(self):
3482+
root = ET.Element("root")
3483+
first_a = ET.SubElement(root, "a")
3484+
first_a.set("pos", "first")
3485+
n = 2 ** 15
3486+
for i in range(n):
3487+
ET.SubElement(root, "a")
3488+
last_a = ET.SubElement(root, "a")
3489+
last_a.set("pos", "last")
3490+
3491+
for pattern in [".//a[1]", ".//a[last()]"]:
3492+
start = time.time()
3493+
result = root.findall(pattern)
3494+
end = time.time()
3495+
3496+
# Before the fix these took 30+ seconds.
3497+
self.assertLess(end - start, 1)
3498+
3499+
self.assertIs(root.find(".//a[1]"), first_a)
3500+
self.assertEqual(root.find(".//a[1]").get("pos"), "first")
3501+
self.assertIs(root.find(".//a[last()]"), last_a)
3502+
self.assertEqual(root.find(".//a[last()]").get("pos"), "last")
3503+
34803504
def test_findall(self):
34813505
e = ET.XML(SAMPLE_XML)
34823506
e[2] = ET.XML(SAMPLE_SECTION)

Lib/xml/etree/ElementPath.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -324,11 +324,15 @@ def select_negated(context, result):
324324
index = -1
325325
def select(context, result):
326326
parent_map = get_parent_map(context)
327+
sibling_cache = {}
327328
for elem in result:
328329
try:
329330
parent = parent_map[elem]
330331
# FIXME: what if the selector is "*" ?
331-
elems = list(parent.findall(elem.tag))
332+
cache_key = (parent, elem.tag)
333+
if cache_key not in sibling_cache:
334+
sibling_cache[cache_key] = list(parent.findall(elem.tag))
335+
elems = sibling_cache[cache_key]
332336
if elems[index] is elem:
333337
yield elem
334338
except (IndexError, KeyError):

0 commit comments

Comments
 (0)