From 57715582523fc402b2d07e601493f61c56cbd10b Mon Sep 17 00:00:00 2001 From: LuisFigueroaG Date: Tue, 13 Oct 2026 21:28:35 -0300 Subject: [PATCH 1/2] Fix sanitized HTML leaking into header ids in safe mode --- CHANGES.md | 1 + lib/markdown2.py | 5 ++++- test/tm-cases/header_ids_safe_mode.html | 7 +++++++ test/tm-cases/header_ids_safe_mode.opts | 1 + test/tm-cases/header_ids_safe_mode.text | 8 ++++++++ test/tm-cases/header_ids_safe_mode_replace.html | 7 +++++++ test/tm-cases/header_ids_safe_mode_replace.opts | 1 + test/tm-cases/header_ids_safe_mode_replace.text | 8 ++++++++ 8 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 test/tm-cases/header_ids_safe_mode.html create mode 100644 test/tm-cases/header_ids_safe_mode.opts create mode 100644 test/tm-cases/header_ids_safe_mode.text create mode 100644 test/tm-cases/header_ids_safe_mode_replace.html create mode 100644 test/tm-cases/header_ids_safe_mode_replace.opts create mode 100644 test/tm-cases/header_ids_safe_mode_replace.text diff --git a/CHANGES.md b/CHANGES.md index cabaeb1f..58e817ec 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,6 +2,7 @@ ## python-markdown2 2.5.6 (not yet released) +- Fix `header-ids` extra putting sanitized HTML into header ids in safe mode, which allowed attribute injection (#649) - [pull #732] Fix excessive CPU use in the inline HTML tokenizer on repeated unclosed tag fragments (#707) - [pull #736] Clarify how to enable file variables in the Python API and command-line help (#269, #365). - [pull #730] Fix `tables` extra splitting multi-backtick code spans at a leading pipe and merging cells after a code span containing literal backticks. diff --git a/lib/markdown2.py b/lib/markdown2.py index 18419ef0..f97e01da 100755 --- a/lib/markdown2.py +++ b/lib/markdown2.py @@ -1743,8 +1743,11 @@ def _h_sub(self, match: re.Match[str]) -> str: n = min(n + demote_headers, 6) header_id_attr = "" if "header-ids" in self.extras: + # in safe mode, inline HTML is hashed by now. Unhash it so the + # sanitized HTML doesn't get swapped back into the id attribute + header_text = self._unhash_html_spans(header_group) header_id = self.header_id_from_text( - self._unescape_special_chars(header_group), + self._unescape_special_chars(header_text), self.extras["header-ids"].get('prefix'), n) if header_id: header_id_attr = ' id="%s"' % header_id diff --git a/test/tm-cases/header_ids_safe_mode.html b/test/tm-cases/header_ids_safe_mode.html new file mode 100644 index 00000000..91299c59 --- /dev/null +++ b/test/tm-cases/header_ids_safe_mode.html @@ -0,0 +1,7 @@ +

Hello <b>world</b>

+ +

<sometext>

+ +

Setext <em>header</em>

+ +

Hi <b x="a" onmouseover="alert(1)">there</b>

diff --git a/test/tm-cases/header_ids_safe_mode.opts b/test/tm-cases/header_ids_safe_mode.opts new file mode 100644 index 00000000..1d4e5d13 --- /dev/null +++ b/test/tm-cases/header_ids_safe_mode.opts @@ -0,0 +1 @@ +{'safe_mode': 'escape', 'extras': ['header-ids']} \ No newline at end of file diff --git a/test/tm-cases/header_ids_safe_mode.text b/test/tm-cases/header_ids_safe_mode.text new file mode 100644 index 00000000..cdf8d539 --- /dev/null +++ b/test/tm-cases/header_ids_safe_mode.text @@ -0,0 +1,8 @@ +# Hello world + +# + +Setext header +====================== + +# Hi there diff --git a/test/tm-cases/header_ids_safe_mode_replace.html b/test/tm-cases/header_ids_safe_mode_replace.html new file mode 100644 index 00000000..a3c350b9 --- /dev/null +++ b/test/tm-cases/header_ids_safe_mode_replace.html @@ -0,0 +1,7 @@ +

Hello [HTML_REMOVED]world[HTML_REMOVED]

+ +

[HTML_REMOVED]

+ +

Setext [HTML_REMOVED]header[HTML_REMOVED]

+ +

Hi [HTML_REMOVED]there[HTML_REMOVED]

diff --git a/test/tm-cases/header_ids_safe_mode_replace.opts b/test/tm-cases/header_ids_safe_mode_replace.opts new file mode 100644 index 00000000..d6f038f2 --- /dev/null +++ b/test/tm-cases/header_ids_safe_mode_replace.opts @@ -0,0 +1 @@ +{'safe_mode': 'replace', 'extras': ['header-ids']} \ No newline at end of file diff --git a/test/tm-cases/header_ids_safe_mode_replace.text b/test/tm-cases/header_ids_safe_mode_replace.text new file mode 100644 index 00000000..cdf8d539 --- /dev/null +++ b/test/tm-cases/header_ids_safe_mode_replace.text @@ -0,0 +1,8 @@ +# Hello world + +# + +Setext header +====================== + +# Hi there From abceed427d5c3842cd667a6d654c2eec715380cf Mon Sep 17 00:00:00 2001 From: LuisFigueroaG Date: Tue, 13 Oct 2026 21:30:27 -0300 Subject: [PATCH 2/2] Add the PR number to the change note --- CHANGES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 58e817ec..90f815ec 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,7 +2,7 @@ ## python-markdown2 2.5.6 (not yet released) -- Fix `header-ids` extra putting sanitized HTML into header ids in safe mode, which allowed attribute injection (#649) +- [pull #739] Fix `header-ids` extra putting sanitized HTML into header ids in safe mode, which allowed attribute injection (#649) - [pull #732] Fix excessive CPU use in the inline HTML tokenizer on repeated unclosed tag fragments (#707) - [pull #736] Clarify how to enable file variables in the Python API and command-line help (#269, #365). - [pull #730] Fix `tables` extra splitting multi-backtick code spans at a leading pipe and merging cells after a code span containing literal backticks.