diff --git a/kart/html_diff_writer.py b/kart/html_diff_writer.py index 064f7e8d..22cac534 100644 --- a/kart/html_diff_writer.py +++ b/kart/html_diff_writer.py @@ -57,16 +57,7 @@ def write_diff(self, diff_format=DiffFormat.FULL): } fo = resolve_output_path(self.output_path) - fo.write( - template.substitute( - { - "title": html.escape(title), - "geojson_data": json.dumps( - all_datasets_geojson, cls=ExtendedJsonEncoder - ), - } - ) - ) + fo.write(self.substitute_into_template(template, title, all_datasets_geojson)) if fo != sys.stdout: fo.close() @@ -74,6 +65,20 @@ def write_diff(self, diff_format=DiffFormat.FULL): self.write_warnings_footer() + @classmethod + def substitute_into_template(cls, template, title, all_datasets_geojson): + return template.substitute( + { + "title": html.escape(title), + "geojson_data": json.dumps( + all_datasets_geojson, cls=ExtendedJsonEncoder + ) + .replace("/", r"\x2f") + .replace("<", r"\x3c") + .replace(">", r"\x3e"), + } + ) + HtmlDiffWriter.filtered_dataset_deltas_as_geojson = ( GeojsonDiffWriter.filtered_dataset_deltas_as_geojson diff --git a/tests/test_diff.py b/tests/test_diff.py index d745734b..e1276ca8 100644 --- a/tests/test_diff.py +++ b/tests/test_diff.py @@ -1,6 +1,7 @@ import functools import json import re +import string import time from pathlib import Path @@ -10,6 +11,7 @@ import kart from kart.diff_format import DiffFormat from kart.diff_structs import Delta, DeltaDiff +from kart.html_diff_writer import HtmlDiffWriter from kart.json_diff_writers import JsonLinesDiffWriter from kart.geometry import hex_wkb_to_ogr from kart.repo import KartRepo @@ -2096,14 +2098,47 @@ def test_attached_files_patch(data_archive, cli_runner): }, } + def test_load_user_provided_html_template(data_archive, cli_runner): with data_archive("points") as repo_path: r = cli_runner.invoke( [ "diff", f"--output-format=html", - f"--html-template=" + str(Path(__file__).absolute().parent.parent / "kart" / "diff-view.html"), + f"--html-template=" + + str( + Path(__file__).absolute().parent.parent / "kart" / "diff-view.html" + ), "HEAD^...", ] ) assert r.exit_code == 0, r.stderr + + +def test_xss_protection(): + TEMPLATE = """ + + + Kart Diff: ${title} + + + ... + +""".lstrip() + html_xss = "" + json_xss = {"key": ""} + result = HtmlDiffWriter.substitute_into_template( + string.Template(TEMPLATE), html_xss, json_xss + ) + + EXPECTED_RESULT = """ + + + Kart Diff: <script>alert(1);</script> + + + ... + +""".lstrip() + + assert result == EXPECTED_RESULT