@@ -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 ('<' , '<' )
224+ .replace ('>' , '>' )
225+ .replace ('"' , '"' )
226+ .replace ("'" , ''' ))
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 ('&' , '&' )
269- .replace ('<' , '<' )
270- .replace ('>' , '>' )
271- .replace ('"' , '"' )
272- .replace ("'" , ''' ))
273-
274266 def _format_duration_display (self , seconds ):
275267 if seconds < 60 :
276268 return f"{ seconds :.1f} s"
0 commit comments