2929import jinja2
3030from jinja2 import select_autoescape
3131import json
32+ import markupsafe
3233import mimetypes
3334import os
3435import pwd
@@ -517,7 +518,7 @@ def suites(
517518 continue
518519 with contextlib .suppress (OSError ):
519520 data ["entries" ].append ({
520- "name" : str ( item ),
521+ "name" : item . encode (). decode ( 'UTF-8' , errors = 'replace' ),
521522 "info" : {},
522523 "last_activity_time" : (
523524 self .get_last_activity_time (user , item ))})
@@ -548,8 +549,9 @@ def suites(
548549 rosie_info = {}
549550 with contextlib .suppress (IOError ):
550551 for line in open ( # noqa: SIM115
551- rosie_suite_info , 'r '
552+ rosie_suite_info , 'rb '
552553 ).readlines ():
554+ line = line .decode ('utf-8' , errors = 'replace' )
553555 if not line .strip ().startswith ('#' ) and '=' in line :
554556 rosie_key , rosie_val = line .strip ().split ("=" , 1 )
555557 if rosie_key in ("project" , "title" ):
@@ -576,8 +578,8 @@ def get_file(self, user, suite, path, path_in_tar=None, mode=None):
576578 except KeyError :
577579 raise cherrypy .HTTPError (404 ) from None
578580 f_size = tar_info .size
579- handle = tar_f .extractfile (path_in_tar )
580- if handle . read ( 2 ) == "#!" :
581+ handle = tar_f .extractfile (path_in_tar , 'rb' )
582+ if self . _is_shebang ( handle ) :
581583 mime = self .MIME_TEXT_PLAIN
582584 else :
583585 mime = mimetypes .guess_type (
@@ -601,10 +603,10 @@ def get_file(self, user, suite, path, path_in_tar=None, mode=None):
601603 return cherrypy .lib .static .serve_file (temp_f .name , mime )
602604 finally :
603605 temp_f .close ()
604- text = handle . read ( )
606+ text = self . _get_file_text ( handle )
605607 else :
606608 f_size = os .stat (f_name ).st_size
607- if open (f_name ). read ( 2 ) == "#!" : # noqa: SIM115
609+ if self . _is_shebang (f_name ):
608610 mime = self .MIME_TEXT_PLAIN
609611 else :
610612 mime = mimetypes .guess_type (quote (f_name ))[0 ]
@@ -618,7 +620,7 @@ def get_file(self, user, suite, path, path_in_tar=None, mode=None):
618620 ):
619621 cherrypy .response .headers ["Content-Type" ] = mime
620622 return cherrypy .lib .static .serve_file (f_name , mime )
621- text = open (f_name ). read () # noqa: SIM115
623+ text = self . _get_file_text (f_name )
622624 try :
623625 text = str (text )
624626 if mode in [None , "text" ]:
@@ -627,7 +629,7 @@ def get_file(self, user, suite, path, path_in_tar=None, mode=None):
627629 # escape future modifications to this string. In order to
628630 # allow log file syntax highlighting (DEBUG, INFO, etc) we
629631 # must cast this back to a str to remove this functionality.
630- text = str (jinja2 .escape (text ))
632+ text = str (markupsafe .escape (text ))
631633 lines = text .splitlines ()
632634 except ValueError :
633635 if path_in_tar :
@@ -1097,3 +1099,28 @@ def _get_user_suite_dir(self, user, suite, *paths):
10971099 def _sort_summary_entries (suite1 ):
10981100 """Sort suites by last_activity_time."""
10991101 return suite1 .get ("last_activity_time" ) or suite1 ["name" ]
1102+
1103+ @staticmethod
1104+ def _has_shebang (file ):
1105+ """File (or handle) has shebang"""
1106+ # It's a filepath
1107+ if isinstance (file , str ):
1108+ with open (file , 'rb' ) as handle :
1109+ return handle .read (2 ) == b'!#'
1110+
1111+ # It's a file handle: Make sure that you close it later!
1112+ first_chars = file .read (2 )
1113+ return first_chars in [b'!#' , '!#' ]
1114+
1115+ @staticmethod
1116+ def _get_file_text (file ):
1117+ """File (or handle) has shebang"""
1118+ if isinstance (file , str ):
1119+ with open (file , 'rb' ) as handle :
1120+ return handle .read ().decode ('utf-8' , errors = 'replace' )
1121+
1122+ # It's a file handle: Make sure that you close it later!
1123+ filecontent = file .read ()
1124+ if isinstance (filecontent , bytes ):
1125+ return filecontent .decode ('utf-8' , errors = 'replace' )
1126+ return filecontent
0 commit comments