Skip to content

Commit 18b17e1

Browse files
committed
Avoid catastrophic backtracking in hr regex
Fixes #1055.
1 parent 897c854 commit 18b17e1

File tree

3 files changed

+28
-5
lines changed

3 files changed

+28
-5
lines changed

docs/change_log/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ Under development: version 3.3.3 (a bug-fix release).
77

88
* Unify all block-level tags (#1047).
99
* Fix issue where some empty elements would have text rendered as `None` when using `md_in_html` (#1049).
10+
* Avoid catastrophic backtracking in `hr` regex (#1055).
1011

1112
Oct 19, 2020: version 3.3.2 (a bug-fix release).
1213

markdown/blockprocessors.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -496,16 +496,15 @@ def run(self, parent, blocks):
496496
class HRProcessor(BlockProcessor):
497497
""" Process Horizontal Rules. """
498498

499-
RE = r'^[ ]{0,3}((-+[ ]{0,2}){3,}|(_+[ ]{0,2}){3,}|(\*+[ ]{0,2}){3,})[ ]*$'
499+
# Python's re module doesn't officially support atomic grouping. However you can fake it.
500+
# See https://stackoverflow.com/a/13577411/866026
501+
RE = r'^[ ]{0,3}(?=(?P<atomicgroup>(-+[ ]{0,2}){3,}|(_+[ ]{0,2}){3,}|(\*+[ ]{0,2}){3,}))(?P=atomicgroup)[ ]*$'
500502
# Detect hr on any line of a block.
501503
SEARCH_RE = re.compile(RE, re.MULTILINE)
502504

503505
def test(self, parent, block):
504506
m = self.SEARCH_RE.search(block)
505-
# No atomic grouping in python so we simulate it here for performance.
506-
# The regex only matches what would be in the atomic group - the HR.
507-
# Then check if we are at end of block or if next char is a newline.
508-
if m and (m.end() == len(block) or block[m.end()] == '\n'):
507+
if m:
509508
# Save match object on class instance so we can use it later.
510509
self.match = m
511510
return True

tests/test_syntax/blocks/test_hr.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,3 +377,26 @@ def test_not_hr_2_underscores_spaces(self):
377377

378378
'<p>_ _</p>'
379379
)
380+
381+
def test_2_consecutive_hr(self):
382+
self.assertMarkdownRenders(
383+
self.dedent(
384+
"""
385+
- - -
386+
- - -
387+
"""
388+
),
389+
self.dedent(
390+
"""
391+
<hr />
392+
<hr />
393+
"""
394+
)
395+
)
396+
397+
def test_not_hr_end_in_char(self):
398+
self.assertMarkdownRenders(
399+
'--------------------------------------c',
400+
401+
'<p>--------------------------------------c</p>'
402+
)

0 commit comments

Comments
 (0)