Skip to content

Commit fb6b27a

Browse files
authored
Fix infinite loop when text contains multiple unclosed comments
The `updatepos()` override was resetting the parser position to `0`, causing infinite loops when multiple bogus/unclosed comments appeared in the input (e.g., `</` or `<!--` followed by non-standard characters). Now tracks the position of the triggering `<` and advances past it instead of rewinding to the start of the buffer. Fixes #1578. * update changelog
1 parent 89112c2 commit fb6b27a

File tree

3 files changed

+21
-2
lines changed

3 files changed

+21
-2
lines changed

docs/changelog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ See the [Contributing Guide](contributing.md) for details.
1616

1717
* Ensure nested elements inside inline comments are properly unescaped (#1571).
1818
* Make the docs build successfully with mkdocstrings-python 2.0 (#1575).
19+
* Fix infinite loop when multiple bogus or unclosed HTML comments appear in input (#1578).
1920

2021
## [3.10.0] - 2025-11-03
2122

markdown/htmlparser.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ def __init__(self, md: Markdown, *args, **kwargs):
111111
self.lineno_start_cache = [0]
112112

113113
self.override_comment_update = False
114+
self.override_comment_start = 0
114115

115116
# This calls self.reset
116117
super().__init__(*args, **kwargs)
@@ -124,6 +125,7 @@ def reset(self):
124125
self._cache: list[str] = []
125126
self.cleandoc: list[str] = []
126127
self.lineno_start_cache = [0]
128+
self.override_comment_start = 0
127129

128130
super().reset()
129131

@@ -276,15 +278,17 @@ def handle_comment(self, data: str):
276278
i = self.line_offset + self.offset + len(data) + 4
277279
if self.rawdata[i:i + 3] != '-->':
278280
self.handle_data('<')
281+
pos = self.line_offset + self.offset
282+
self.override_comment_start = pos - 1 if self.rawdata[pos - 1:pos] == '<' else pos
279283
self.override_comment_update = True
280284
return
281285
self.handle_empty_tag('<!--{}-->'.format(data), is_block=True)
282286

283287
def updatepos(self, i: int, j: int) -> int:
284288
if self.override_comment_update:
285289
self.override_comment_update = False
286-
i = 0
287-
j = 1
290+
i = self.override_comment_start
291+
j = self.override_comment_start + 1
288292
return super().updatepos(i, j)
289293

290294
def handle_decl(self, data: str):

tests/test_syntax/blocks/test_html_blocks.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1684,3 +1684,17 @@ def test_noname_tag(self):
16841684
"""
16851685
)
16861686
)
1687+
1688+
def test_multiple_bogus_comments_no_hang(self):
1689+
"""Test that multiple bogus comments (</` patterns) don't cause infinite loop."""
1690+
self.assertMarkdownRenders(
1691+
'`</` and `</`',
1692+
'<p><code>&lt;/</code> and <code>&lt;/</code></p>'
1693+
)
1694+
1695+
def test_multiple_unclosed_comments_no_hang(self):
1696+
"""Test that multiple unclosed comments don't cause infinite loop."""
1697+
self.assertMarkdownRenders(
1698+
'<!-- and <!--',
1699+
'<p>&lt;!-- and &lt;!--</p>'
1700+
)

0 commit comments

Comments
 (0)