diff --git a/htmlreport/cppcheck-htmlreport b/htmlreport/cppcheck-htmlreport index fb263c73d12..3f8d2399da1 100755 --- a/htmlreport/cppcheck-htmlreport +++ b/htmlreport/cppcheck-htmlreport @@ -620,15 +620,15 @@ class AnnotateCodeFormatter(HtmlFormatter): # from actual message if error.get('verbose') and (error['verbose'] != error['msg']): index = t.rfind('\n') - t = t[:index] + HTML_EXPANDABLE_INCONCLUSIVE % (error['msg'], html_escape(error['verbose'].replace("\\012", '\n'))) + t[index + 1:] + t = t[:index] + HTML_EXPANDABLE_INCONCLUSIVE % (html_escape(error['msg']), html_escape(error['verbose'].replace("\\012", '\n'))) + t[index + 1:] else: - t = t.replace('\n', HTML_INCONCLUSIVE % error['msg']) + t = t.replace('\n', HTML_INCONCLUSIVE % html_escape(error['msg'])) except KeyError: if error.get('verbose') and (error['verbose'] != error['msg']): index = t.rfind('\n') - t = t[:index] + HTML_EXPANDABLE_ERROR % (error['msg'], html_escape(error['verbose'].replace("\\012", '\n'))) + t[index + 1:] + t = t[:index] + HTML_EXPANDABLE_ERROR % (html_escape(error['msg']), html_escape(error['verbose'].replace("\\012", '\n'))) + t[index + 1:] else: - t = t.replace('\n', HTML_ERROR % error['msg']) + t = t.replace('\n', HTML_ERROR % html_escape(error['msg'])) line_no = line_no + 1 yield i, t @@ -824,7 +824,7 @@ def main() -> None: if is_remote: # Construct remote URL for GitHub/GitLab # tr_str() will use the actual line number, so we can just start with line 1 - remote_url = source_dir.rstrip('/') + '/' + filename + '#L1' + remote_url = source_dir.rstrip('/') + '/' + html_escape(filename) + '#L1' files[filename] = {'errors': [], 'htmlfile': remote_url} else: files[filename] = {'errors': [], 'htmlfile': str(file_no) + '.html'} @@ -900,10 +900,10 @@ def main() -> None: (options.title, htmlFormatter.get_style_defs('.highlight'), options.title, - ': ' + filename)) + ': ' + html_escape(filename))) output_file.write(HTML_HEAD_END) - output_file.write(HTML_MENU % (filename.split('/')[-1])) + output_file.write(HTML_MENU % (html_escape(filename.split('/')[-1]))) for error in sorted(errors, key=lambda k: k['line']): output_file.write(" %s %s" % (data['htmlfile'], error['line'], error['id'], error['line'])) output_file.write(HTML_MENU_END) @@ -1006,7 +1006,7 @@ def main() -> None: for filename, data in sorted(files.items()): file_error = filename in decode_errors or filename.endswith('*') is_file = filename != '' and not file_error - row_content = filename if file_error else "%s" % (data['htmlfile'], filename) + row_content = html_escape(filename) if file_error else "%s" % (data['htmlfile'], html_escape(filename)) htmlfile = data.get('htmlfile') if is_file else None output_file.write("\n ") @@ -1122,7 +1122,7 @@ def main() -> None: if it == 0: LENGTH = len(str(i[1])) # <- length of longest number, now get the difference and try to make other numbers align to it - stats_file.write(" " * 3 + str(i[1]) + " " * (1 + LENGTH - len(str(i[1]))) + " " + i[0] + "
\n") + stats_file.write(" " * 3 + str(i[1]) + " " * (1 + LENGTH - len(str(i[1]))) + " " + html_escape(i[0]) + "
\n") it += 1 if it == 10: # print only the top 10 break diff --git a/test/tools/htmlreport/test_htmlreport.py b/test/tools/htmlreport/test_htmlreport.py index f84fd279fb3..b8c73072a70 100755 --- a/test/tools/htmlreport/test_htmlreport.py +++ b/test/tools/htmlreport/test_htmlreport.py @@ -114,6 +114,32 @@ def testSeverityFilterBar(self): self.assertIn('onclick="toggleSeverity(this)"', report) output_directory.cleanup() + def testEscape(self): + with tempfile.TemporaryDirectory() as source_directory: + source_filename = os.path.join(source_directory, 'escape.c') + with open(source_filename, 'w') as source_file: + source_file.write('#error escape\n') + + with runCheck( + source_filename, + xml_version='2' + ) as (report, output_directory): + self.assertIn('<b>escape.c', report) + self.assertNotIn('', report) + + with open(os.path.join(output_directory.name, '0.html')) as input_file: + detail_contents = input_file.read() + self.assertIn('<b>escape.c', detail_contents) + self.assertIn('<--- #error <b>escape</b>', detail_contents) + self.assertNotIn('', detail_contents) + + with open(os.path.join(output_directory.name, 'stats.html')) as input_file: + stats_contents = input_file.read() + self.assertIn('<b>escape.c', stats_contents) + self.assertNotIn('', stats_contents) + + output_directory.cleanup() + @contextlib.contextmanager def runCheck(source_filename=None, xml_version='1', xml_filename=None, checkers_filename=None):