Skip to content

Commit 542f6cc

Browse files
authored
Merge commit from fork
fix: resolve O(n^2) DoS in parse_link_text (CWE-400)
2 parents e2abd76 + b6b499d commit 542f6cc

2 files changed

Lines changed: 12 additions & 8 deletions

File tree

src/mistune/helpers.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ def parse_link_text(src: str, pos: int) -> Union[Tuple[str, int], Tuple[None, No
115115
while pos < len(src):
116116
m = _INLINE_SQUARE_BRACKET_RE.search(src, pos)
117117
if not m:
118-
pos = len(src) # ← CHANGE 1: record we scanned to end
118+
pos = len(src) # FIX: record we scanned to end
119119
break
120120

121121
pos = m.end()
@@ -129,9 +129,9 @@ def parse_link_text(src: str, pos: int) -> Union[Tuple[str, int], Tuple[None, No
129129
level += 1
130130

131131
if found:
132-
text = src[start_pos : pos - 1]
132+
text = src[start_pos: pos - 1]
133133
return text, pos
134-
return None, pos # ← CHANGE 2: return pos instead of None
134+
return None, pos # FIX: return pos instead of None
135135

136136

137137
def parse_link_label(src: str, start_pos: int) -> Union[Tuple[str, int], Tuple[None, None]]:

src/mistune/inline_parser.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -126,13 +126,17 @@ def parse_link(self, m: Match[str], state: InlineState) -> Optional[int]:
126126
text = None
127127
label, end_pos = parse_link_label(state.src, pos)
128128
if label is None:
129+
# FIX: high-water mark — if we already scanned this region
130+
# and found no closing ']', skip immediately without rescanning
131+
no_close = getattr(state, '_no_close_bracket_before', 0)
132+
if pos <= no_close:
133+
state.append_token({"type": "text", "raw": marker})
134+
return pos
129135
text, end_pos = parse_link_text(state.src, pos)
130136
if text is None:
131-
# ← CHANGE: emit skipped content as literal text
132-
# so nothing is dropped, then jump ahead to end_pos
133-
skipped = state.src[m.start():end_pos]
134-
state.append_token({"type": "text", "raw": skipped})
135-
return end_pos
137+
if end_pos is not None and end_pos > no_close:
138+
state._no_close_bracket_before = end_pos
139+
return None
136140

137141
assert end_pos is not None
138142

0 commit comments

Comments
 (0)