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 {}"