diff --git a/docs/changelog.md b/docs/changelog.md index 31cec0b2..000fe3c4 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -10,6 +10,13 @@ and this project adheres to the [Python Version Specification](https://packaging.python.org/en/latest/specifications/version-specifiers/). See the [Contributing Guide](contributing.md) for details. +## [Unreleased] + +### Fixed + +* Fix `SetextHeaderProcessor` regex to prevent mixed `=` and `-` chars in setext-style headers (#1606). + + ## [3.10.2] - 2026-02-09 ### Fixed diff --git a/markdown/blockprocessors.py b/markdown/blockprocessors.py index 3ed4cf07..c2d20ddb 100644 --- a/markdown/blockprocessors.py +++ b/markdown/blockprocessors.py @@ -494,7 +494,7 @@ class SetextHeaderProcessor(BlockProcessor): """ Process Setext-style Headers. """ # Detect Setext-style header. Must be first 2 lines of block. - RE = re.compile(r'^.*?\n[=-]+[ ]*(\n|$)', re.MULTILINE) + RE = re.compile(r'^.*?\n(?:=+|-+)[ ]*(\n|$)', re.MULTILINE) def test(self, parent: etree.Element, block: str) -> bool: return bool(self.RE.match(block)) diff --git a/tests/test_syntax/blocks/test_headers.py b/tests/test_syntax/blocks/test_headers.py index da2ca14f..b1992ad3 100644 --- a/tests/test_syntax/blocks/test_headers.py +++ b/tests/test_syntax/blocks/test_headers.py @@ -109,6 +109,58 @@ def test_setext_h2_followed_by_p(self): # TODO: fix this # see https://johnmacfarlane.net/babelmark2/?normalize=1&text=Paragraph%0AAn+H1%0A%3D%3D%3D%3D%3D + + def test_setext_mixed_chars_not_h1(self): + """Mixed = and - chars should not form a valid H1.""" + self.assertMarkdownRenders( + self.dedent( + """ + This is not an H1 + =- + """ + ), + self.dedent( + """ +

This is not an H1 + =-

+ """ + ) + ) + + def test_setext_mixed_chars_not_h2(self): + """Mixed - and = chars should not form a valid H2.""" + self.assertMarkdownRenders( + self.dedent( + """ + This is not an H2 + -= + """ + ), + self.dedent( + """ +

This is not an H2 + -=

+ """ + ) + ) + + def test_setext_mixed_multiple_chars(self): + """Multiple mixed = and - chars should not form a valid H1/H2.""" + self.assertMarkdownRenders( + self.dedent( + """ + Not a Header + =-=- + """ + ), + self.dedent( + """ +

Not a Header + =-=-

+ """ + ) + ) + @unittest.skip('This is broken in Python-Markdown') def test_p_followed_by_setext_h1(self): self.assertMarkdownRenders(