Skip to content

Commit ea814c8

Browse files
committed
fix: resolve critical security vulnerabilities in Python test report generator.
1 parent dd7fbdd commit ea814c8

1 file changed

Lines changed: 18 additions & 26 deletions

File tree

Scripts/generate_test_report.py

Lines changed: 18 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,9 @@ def __init__(self, trx_path, coverage_path=None):
3535
self.file_coverage = []
3636

3737
def parse_trx(self):
38-
# Create a secure XML parser that disables external entity processing
39-
parser = ET.XMLParser()
40-
parser.parser.DefaultHandler = lambda data: None
41-
parser.parser.ExternalEntityRefHandler = lambda context, base, uri, notationName: False
42-
parser.parser.EntityDeclHandler = lambda entityName, is_parameter_entity, value, base, systemId, notationName, publicId: False
43-
44-
tree = ET.parse(self.trx_path, parser)
38+
# Use defusedxml for secure XML parsing
39+
from defusedxml.ElementTree import parse
40+
tree = parse(self.trx_path)
4541
root = tree.getroot()
4642
ns = {'t': 'http://microsoft.com/schemas/VisualStudio/TeamTest/2010'}
4743

@@ -121,13 +117,9 @@ def parse_coverage(self):
121117
if not self.coverage_path or not os.path.exists(self.coverage_path):
122118
return
123119
try:
124-
# Create a secure XML parser that disables external entity processing
125-
parser = ET.XMLParser()
126-
parser.parser.DefaultHandler = lambda data: None
127-
parser.parser.ExternalEntityRefHandler = lambda context, base, uri, notationName: False
128-
parser.parser.EntityDeclHandler = lambda entityName, is_parameter_entity, value, base, systemId, notationName, publicId: False
129-
130-
tree = ET.parse(self.coverage_path, parser)
120+
# Use defusedxml for secure XML parsing
121+
from defusedxml.ElementTree import parse
122+
tree = parse(self.coverage_path)
131123
root = tree.getroot()
132124
self.coverage['lines_pct'] = float(root.get('line-rate', 0)) * 100
133125
self.coverage['branches_pct'] = float(root.get('branch-rate', 0)) * 100
@@ -221,6 +213,18 @@ def _parse_condition_coverage(cond_str):
221213
return int(m.group(2)), int(m.group(3))
222214
return 0, 0
223215

216+
@staticmethod
217+
def _esc(text):
218+
if text is None:
219+
return ""
220+
text = str(text)
221+
return (text
222+
.replace('&', '&')
223+
.replace('<', '&lt;')
224+
.replace('>', '&gt;')
225+
.replace('"', '&quot;')
226+
.replace("'", '&#39;'))
227+
224228
@staticmethod
225229
def _sanitize_xml_attribute_value(value):
226230
"""Sanitize XML attribute value to prevent XPath injection."""
@@ -259,18 +263,6 @@ def _validate_output_path(output_path):
259263

260264
return normalized_path
261265

262-
@staticmethod
263-
def _esc(text):
264-
if text is None:
265-
return ""
266-
text = str(text)
267-
return (text
268-
.replace('&', '&amp;')
269-
.replace('<', '&lt;')
270-
.replace('>', '&gt;')
271-
.replace('"', '&quot;')
272-
.replace("'", '&#39;'))
273-
274266
def _format_duration_display(self, seconds):
275267
if seconds < 60:
276268
return f"{seconds:.1f}s"

0 commit comments

Comments
 (0)