diff --git a/docs/changelog.md b/docs/changelog.md index 4f5c00729..1e2f9dbd8 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -12,6 +12,14 @@ 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 quadratic-time regex backtracking in `attr_list` when a block ends with an + unclosed attribute list followed by a long run of spaces, e.g. a line + consisting only of `{` and some text with many trailing spaces. + ## [3.11.1] - 2026-10-13 ### Fixed diff --git a/markdown/extensions/attr_list.py b/markdown/extensions/attr_list.py index 518288087..f1edc5798 100644 --- a/markdown/extensions/attr_list.py +++ b/markdown/extensions/attr_list.py @@ -90,7 +90,7 @@ def isheader(elem: Element) -> bool: class AttrListTreeprocessor(Treeprocessor): - BASE_RE = r'\{\:?[ ]*([^\}\n ][^\n]*)[ ]*\}' + BASE_RE = r'\{\:?[ ]*([^\}\n ][^\n]*)\}' HEADER_RE = re.compile(r'[ ]+{}[ ]*$'.format(BASE_RE)) BLOCK_RE = re.compile(r'\n[ ]*{}[ ]*$'.format(BASE_RE)) INLINE_RE = re.compile(r'^{}'.format(BASE_RE)) diff --git a/tests/test_syntax/extensions/test_attr_list.py b/tests/test_syntax/extensions/test_attr_list.py index 1a0ae12c2..7558d0503 100644 --- a/tests/test_syntax/extensions/test_attr_list.py +++ b/tests/test_syntax/extensions/test_attr_list.py @@ -78,6 +78,18 @@ def test_backslash_escape_value(self): '
*Foo*{text}
') + def test_table_td(self): self.assertMarkdownRenders( self.dedent(