diff --git a/src/pytest_html/basereport.py b/src/pytest_html/basereport.py index 5a896165..f3aa6455 100644 --- a/src/pytest_html/basereport.py +++ b/src/pytest_html/basereport.py @@ -8,6 +8,7 @@ import re import warnings from collections import defaultdict +from html import escape from pathlib import Path import pytest @@ -312,11 +313,11 @@ def _is_error(report): def _process_logs(report): log = [] if report.longreprtext: - log.append(report.longreprtext.replace("<", "<").replace(">", ">") + "\n") + log.append(escape(report.longreprtext) + "\n") # Don't add captured output to reruns if report.outcome != "rerun": for section in report.sections: - header, content = section + header, content = map(escape, section) log.append(f"{' ' + header + ' ':-^80}\n{content}") # weird formatting related to logs diff --git a/src/pytest_html/report_data.py b/src/pytest_html/report_data.py index 7be80531..fd002099 100644 --- a/src/pytest_html/report_data.py +++ b/src/pytest_html/report_data.py @@ -3,6 +3,7 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. import warnings from collections import defaultdict +from html import escape from pytest_html.util import _handle_ansi @@ -146,7 +147,7 @@ def append_teardown_log(self, report): # Last index is "call" test = self._data["tests"][report.nodeid][-1] for section in report.sections: - header, content = section + header, content = map(escape, section) if "teardown" in header: log.append(f"{' ' + header + ' ':-^80}\n{content}") test["log"] += _handle_ansi("\n".join(log)) diff --git a/testing/test_integration.py b/testing/test_integration.py index b3dd3064..50fecc70 100644 --- a/testing/test_integration.py +++ b/testing/test_integration.py @@ -787,6 +787,42 @@ def test_pass(utf8): log = get_log(page) assert_that(log).does_not_match(r"测试用例名称") + @pytest.mark.parametrize("outcome, occurrence", [(True, 1), (False, 2)]) + def test_log_escaping(self, pytester, outcome, occurrence): + """ + Not the best test, but it does a simple verification + that the string is escaped properly and not rendered as HTML + """ + texts = [ + "0 Checking object and more", + "1 Checking object < > and more", + "2 Checking object <> and more", + "3 Checking object < C > and more", + "4 Checking object and more", + "5 Checking object < and more", + "6 Checking object < and more", + "7 Checking object < C and more", + "8 Checking object " and more', + '10 Checking object "< >" and more', + '11 Checking object "<>" and more', + '12 Checking object "< C >" and more', + '13 Checking object "" and more', + ] + test_file = "def test_escape():\n" + for t in texts: + test_file += f"\tprint('{t}')\n" + test_file += f"\tassert {outcome}" + pytester.makepyfile(test_file) + + page = run(pytester) + assert_results(page, passed=1 if outcome else 0, failed=1 if not outcome else 0) + + log = get_log(page) + for each in texts: + count = log.count(each) + assert_that(count).is_equal_to(occurrence) + class TestLogCapturing: LOG_LINE_REGEX = r"\s+this is {}"