Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## python-markdown2 2.5.6 (not yet released)

- [pull #732] Fix excessive CPU use in the inline HTML tokenizer on repeated unclosed tag fragments (#707)
- [pull #736] Clarify how to enable file variables in the Python API and command-line help (#269, #365).
- [pull #730] Fix `tables` extra splitting multi-backtick code spans at a leading pipe and merging cells after a code span containing literal backticks.
- [pull #729] Fix `tables` extra merging cells when a pipe directly follows a code span, as in compact rows like `|`-v`|verbose|`.
Expand Down
18 changes: 15 additions & 3 deletions lib/markdown2.py
Original file line number Diff line number Diff line change
Expand Up @@ -1169,7 +1169,20 @@ def _tag_is_closed(self, tag_name: str, text: str) -> bool:
return True

# check if number of open tags == number of close tags
if len(re.findall('<%s(?:.*?)>' % tag_name, text)) != text.count('</%s>' % tag_name):
# find all open tags on each line. Tags split over multiple lines are
# ignored, as with the original <tag.*?> regex. Stopping at the first
# unclosed <tag on a line keeps the scan linear.
open_tag = '<%s' % tag_name
open_count = 0
for line in text.splitlines():
pos = line.find(open_tag)
while pos != -1:
end = line.find('>', pos)
if end == -1:
break
open_count += 1
pos = line.find(open_tag, end + 1)
if open_count != text.count('</%s>' % tag_name):
return False

# check that close tag position is AFTER open tag
Expand Down Expand Up @@ -1347,8 +1360,7 @@ def _run_span_gamut(self, text: str) -> str:
(?:\w+) # tag name
(?: # attributes
\s+ # whitespace after tag
(?:[^\t<>"'=/]+:)?
[^<>"'=/]+= # attr name
[^\s<>"'=/][^<>"'=/]*= # attr name, can't start with whitespace
(?:"[^"]*?"|'[^']*?'|[^<>"'=/\s]+) # value, quoted or unquoted. If unquoted, no spaces allowed
)*
\s*/?>
Expand Down
18 changes: 18 additions & 0 deletions test/test_redos.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,21 @@ def issue_668():
return 'a_b **x***y* c_d'


def issue_707_unclosed_tags():
# https://github.com/trentm/python-markdown2/issues/707
return '<p m="1"' * 15000


def issue_707_namespaced_attrs():
# https://github.com/trentm/python-markdown2/issues/707
return 'x <p' + ' a:b=1' * 40


def issue_707_spaced_attrs():
# https://github.com/trentm/python-markdown2/issues/707
return 'x <p' + ' a=1' * 40


# whack everything in a dict for easy lookup later on
CASES = {
fn.__name__: (fn, extras)
Expand All @@ -54,6 +69,9 @@ def issue_668():
(issue493, None),
(issue_633, None),
(issue_668, ['code-friendly']),
(issue_707_unclosed_tags, None),
(issue_707_namespaced_attrs, None),
(issue_707_spaced_attrs, None),
]
}

Expand Down
Loading