Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 18 additions & 26 deletions Scripts/generate_test_report.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,9 @@ def __init__(self, trx_path, coverage_path=None):
self.file_coverage = []

def parse_trx(self):
# Create a secure XML parser that disables external entity processing
parser = ET.XMLParser()
parser.parser.DefaultHandler = lambda data: None
parser.parser.ExternalEntityRefHandler = lambda context, base, uri, notationName: False
parser.parser.EntityDeclHandler = lambda entityName, is_parameter_entity, value, base, systemId, notationName, publicId: False

tree = ET.parse(self.trx_path, parser)
# Use defusedxml for secure XML parsing
from defusedxml.ElementTree import parse
tree = parse(self.trx_path)
root = tree.getroot()
ns = {'t': 'http://microsoft.com/schemas/VisualStudio/TeamTest/2010'}

Expand Down Expand Up @@ -121,13 +117,9 @@ def parse_coverage(self):
if not self.coverage_path or not os.path.exists(self.coverage_path):
return
try:
# Create a secure XML parser that disables external entity processing
parser = ET.XMLParser()
parser.parser.DefaultHandler = lambda data: None
parser.parser.ExternalEntityRefHandler = lambda context, base, uri, notationName: False
parser.parser.EntityDeclHandler = lambda entityName, is_parameter_entity, value, base, systemId, notationName, publicId: False

tree = ET.parse(self.coverage_path, parser)
# Use defusedxml for secure XML parsing
from defusedxml.ElementTree import parse
tree = parse(self.coverage_path)
root = tree.getroot()
self.coverage['lines_pct'] = float(root.get('line-rate', 0)) * 100
self.coverage['branches_pct'] = float(root.get('branch-rate', 0)) * 100
Expand Down Expand Up @@ -221,6 +213,18 @@ def _parse_condition_coverage(cond_str):
return int(m.group(2)), int(m.group(3))
return 0, 0

@staticmethod
def _esc(text):
if text is None:
return ""
text = str(text)
return (text
.replace('&', '&')
.replace('<', '&lt;')
.replace('>', '&gt;')
.replace('"', '&quot;')
.replace("'", '&#39;'))

@staticmethod
def _sanitize_xml_attribute_value(value):
"""Sanitize XML attribute value to prevent XPath injection."""
Expand Down Expand Up @@ -259,18 +263,6 @@ def _validate_output_path(output_path):

return normalized_path

@staticmethod
def _esc(text):
if text is None:
return ""
text = str(text)
return (text
.replace('&', '&amp;')
.replace('<', '&lt;')
.replace('>', '&gt;')
.replace('"', '&quot;')
.replace("'", '&#39;'))

def _format_duration_display(self, seconds):
if seconds < 60:
return f"{seconds:.1f}s"
Expand Down
Loading