Skip to content

Commit 941d1cb

Browse files
committed
fix: Escaping HTML in log
1 parent e407056 commit 941d1cb

File tree

3 files changed

+41
-3
lines changed

3 files changed

+41
-3
lines changed

src/pytest_html/basereport.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import re
99
import warnings
1010
from collections import defaultdict
11+
from html import escape
1112
from pathlib import Path
1213

1314
import pytest
@@ -312,11 +313,11 @@ def _is_error(report):
312313
def _process_logs(report):
313314
log = []
314315
if report.longreprtext:
315-
log.append(report.longreprtext.replace("<", "&lt;").replace(">", "&gt;") + "\n")
316+
log.append(escape(report.longreprtext) + "\n")
316317
# Don't add captured output to reruns
317318
if report.outcome != "rerun":
318319
for section in report.sections:
319-
header, content = section
320+
header, content = map(escape, section)
320321
log.append(f"{' ' + header + ' ':-^80}\n{content}")
321322

322323
# weird formatting related to logs

src/pytest_html/report_data.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
44
import warnings
55
from collections import defaultdict
6+
from html import escape
67

78
from pytest_html.util import _handle_ansi
89

@@ -146,7 +147,7 @@ def append_teardown_log(self, report):
146147
# Last index is "call"
147148
test = self._data["tests"][report.nodeid][-1]
148149
for section in report.sections:
149-
header, content = section
150+
header, content = map(escape, section)
150151
if "teardown" in header:
151152
log.append(f"{' ' + header + ' ':-^80}\n{content}")
152153
test["log"] += _handle_ansi("\n".join(log))

testing/test_integration.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -772,6 +772,42 @@ def test_collect_error(self, pytester):
772772
log = get_log(page)
773773
assert_that(log).matches(rf"E\s+ImportError: {error_msg}")
774774

775+
@pytest.mark.parametrize("outcome, occurrence", [(True, 1), (False, 2)])
776+
def test_log_escaping(self, pytester, outcome, occurrence):
777+
"""
778+
Not the best test, but it does a simple verification
779+
that the string is escaped properly and not rendered as HTML
780+
"""
781+
texts = [
782+
"0 Checking object <Chopstick Container> and more",
783+
"1 Checking object < > and more",
784+
"2 Checking object <> and more",
785+
"3 Checking object < C > and more",
786+
"4 Checking object <C > and more",
787+
"5 Checking object < and more",
788+
"6 Checking object < and more",
789+
"7 Checking object < C and more",
790+
"8 Checking object <C and more",
791+
'9 Checking object "<Chopstick Container>" and more',
792+
'10 Checking object "< >" and more',
793+
'11 Checking object "<>" and more',
794+
'12 Checking object "< C >" and more',
795+
'13 Checking object "<C >" and more',
796+
]
797+
test_file = "def test_escape():\n"
798+
for t in texts:
799+
test_file += f"\tprint('{t}')\n"
800+
test_file += f"\tassert {outcome}"
801+
pytester.makepyfile(test_file)
802+
803+
page = run(pytester)
804+
assert_results(page, passed=1 if outcome else 0, failed=1 if not outcome else 0)
805+
806+
log = get_log(page)
807+
for each in texts:
808+
count = log.count(each)
809+
assert_that(count).is_equal_to(occurrence)
810+
775811

776812
class TestLogCapturing:
777813
LOG_LINE_REGEX = r"\s+this is {}"

0 commit comments

Comments
 (0)