diff --git a/docs/changelog.md b/docs/changelog.md index 4311353f..6db0b5c0 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -16,6 +16,7 @@ See the [Contributing Guide](contributing.md) for details. * Ensure nested elements inside inline comments are properly unescaped (#1571). * Make the docs build successfully with mkdocstrings-python 2.0 (#1575). +* Fix infinite loop when multiple bogus or unclosed HTML comments appear in input (#1578). ## [3.10.0] - 2025-11-03 diff --git a/markdown/htmlparser.py b/markdown/htmlparser.py index 658cd37e..697f84f2 100644 --- a/markdown/htmlparser.py +++ b/markdown/htmlparser.py @@ -111,6 +111,7 @@ def __init__(self, md: Markdown, *args, **kwargs): self.lineno_start_cache = [0] self.override_comment_update = False + self.override_comment_start = 0 # This calls self.reset super().__init__(*args, **kwargs) @@ -124,6 +125,7 @@ def reset(self): self._cache: list[str] = [] self.cleandoc: list[str] = [] self.lineno_start_cache = [0] + self.override_comment_start = 0 super().reset() @@ -276,6 +278,8 @@ def handle_comment(self, data: str): i = self.line_offset + self.offset + len(data) + 4 if self.rawdata[i:i + 3] != '-->': self.handle_data('<') + pos = self.line_offset + self.offset + self.override_comment_start = pos - 1 if self.rawdata[pos - 1:pos] == '<' else pos self.override_comment_update = True return self.handle_empty_tag(''.format(data), is_block=True) @@ -283,8 +287,8 @@ def handle_comment(self, data: str): def updatepos(self, i: int, j: int) -> int: if self.override_comment_update: self.override_comment_update = False - i = 0 - j = 1 + i = self.override_comment_start + j = self.override_comment_start + 1 return super().updatepos(i, j) def handle_decl(self, data: str): diff --git a/tests/test_syntax/blocks/test_html_blocks.py b/tests/test_syntax/blocks/test_html_blocks.py index bee30366..3251d0fa 100644 --- a/tests/test_syntax/blocks/test_html_blocks.py +++ b/tests/test_syntax/blocks/test_html_blocks.py @@ -1684,3 +1684,17 @@ def test_noname_tag(self): """ ) ) + + def test_multiple_bogus_comments_no_hang(self): + """Test that multiple bogus comments (</ and </

' + ) + + def test_multiple_unclosed_comments_no_hang(self): + """Test that multiple unclosed comments don't cause infinite loop.""" + self.assertMarkdownRenders( + '