From f0304a9213ab28d5273ef4e872c0c6b26ef94cb6 Mon Sep 17 00:00:00 2001 From: zolaski333 Date: Sat, 17 Oct 2026 21:33:29 +0200 Subject: [PATCH] gh-159640: Escape profile data embedded in flamegraph HTML --- Lib/profiling/sampling/stack_collector.py | 4 +++ .../test_sampling_profiler/test_collectors.py | 30 +++++++++++++++++++ ...17-21-30-00.gh-issue-159640.flamegraph.rst | 2 ++ 3 files changed, 36 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2026-10-17-21-30-00.gh-issue-159640.flamegraph.rst diff --git a/Lib/profiling/sampling/stack_collector.py b/Lib/profiling/sampling/stack_collector.py index 60061b4a2ecf111..0c754c3598b57c2 100644 --- a/Lib/profiling/sampling/stack_collector.py +++ b/Lib/profiling/sampling/stack_collector.py @@ -505,6 +505,10 @@ def _create_flamegraph_html(self, data): # Python recursion limit. iterencode() uses the Python encoder. data_json = "".join(json.JSONEncoder().iterencode(data)) + # Prevent profile strings from closing the script element or starting + # an HTML comment, while preserving their values in JavaScript. + data_json = data_json.replace("<", "\\u003c") + template_dir = importlib.resources.files(__package__) vendor_dir = template_dir / "_vendor" assets_dir = template_dir / "_assets" diff --git a/Lib/test/test_profiling/test_sampling_profiler/test_collectors.py b/Lib/test/test_profiling/test_sampling_profiler/test_collectors.py index d4ba2df5044559f..a8c99fc5b29a136 100644 --- a/Lib/test/test_profiling/test_sampling_profiler/test_collectors.py +++ b/Lib/test/test_profiling/test_sampling_profiler/test_collectors.py @@ -7,6 +7,7 @@ import sys import tempfile import unittest +from unittest import mock from test.support import is_emscripten, run_with_limited_c_stack, set_recursion_limit @@ -596,6 +597,35 @@ def test_flamegraph_collector_export(self): self.assertIn('"sample_interval_usec": 10000', content) self.assertIn("samples * data.stats.sample_interval_usec / 1000", content) + def test_flamegraph_html_escapes_script_data(self): + collector = FlamegraphCollector(1000) + data = { + "strings": [ + "", + "", + "