Skip to content

Commit 46da727

Browse files
authored
Merge pull request #466 from chuenchen309/fix/link-dest-backslash-non-punctuation
Only treat a backslash as an escape before punctuation in a link destination
2 parents 7da7e1b + 85701ee commit 46da727

2 files changed

Lines changed: 14 additions & 1 deletion

File tree

src/mistune/helpers.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ def parse_link_href(src: str, start_pos: int, block: bool = False) -> Union[Tupl
146146
break
147147
if c == "\x00":
148148
return None, None
149-
if c == "\\":
149+
if c == "\\" and pos + 1 < len(src) and src[pos + 1] in string.punctuation:
150150
pos = min(pos + 2, len(src))
151151
continue
152152
if not block:

tests/test_misc.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,19 @@ def test_harmful_links(self):
9191
expected = '<p><a href="#harmful-link">h</a></p>'
9292
self.assertEqual(result.strip(), expected)
9393

94+
def test_backslash_space_in_link_destination_is_not_an_escape(self):
95+
# A space is not ASCII punctuation, so "\ " is a literal backslash and a
96+
# space that ends the bare destination: the link fails to parse and is
97+
# rendered as text (as CommonMark and other parsers do). Previously the
98+
# scanner treated "\ " as an escape and swallowed the space into the URL,
99+
# producing an illegal href containing %5C%20.
100+
result = mistune.html(r"[a](foo\ bar)")
101+
self.assertEqual(result.strip(), r"<p>[a](foo\ bar)</p>")
102+
103+
# An escaped punctuation character is still consumed as an escape.
104+
result = mistune.html(r"[a](foo\*bar)")
105+
self.assertEqual(result.strip(), '<p><a href="foo*bar">a</a></p>')
106+
94107
def test_harmful_links_variants(self):
95108
# entity-decoded, alternate-scheme, and reference-link forms are all
96109
# routed to the #harmful-link sentinel.

0 commit comments

Comments
 (0)