Skip to content

Commit b310b9c

Browse files
fix: 修复 markdownToPlainText 中代码块正则的 ReDoS 风险
用非正则的线性扫描替代 \`\`\`[\s\S]*?\n([\s\S]*?)\`\`\` 匹配, 避免在含有大量重复 \`\`\` 序列的输入上触发多项式回溯。 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent db547d4 commit b310b9c

1 file changed

Lines changed: 38 additions & 2 deletions

File tree

packages/weixin/src/send.ts

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,45 @@ import { sendMessage } from './api.js'
44
import { guessMediaType, uploadFile } from './media.js'
55
import { MessageItemType, MessageState, MessageType } from './types.js'
66

7+
function stripCodeBlocks(text: string): string {
8+
// Non-regex approach to avoid ReDoS on inputs with many ``` sequences.
9+
let result = ''
10+
let i = 0
11+
while (i < text.length) {
12+
if (text.startsWith('```', i)) {
13+
// Skip the opening fence (including optional language tag on same line)
14+
let j = i + 3
15+
// skip to end of first line (the fence line itself)
16+
while (j < text.length && text[j] !== '\n') j++
17+
if (j < text.length) j++ // skip the \n
18+
// Collect content until closing ```
19+
const contentStart = j
20+
while (j < text.length) {
21+
if (text.startsWith('```', j)) {
22+
result += text.slice(contentStart, j)
23+
// skip closing fence and its trailing newline
24+
j += 3
25+
while (j < text.length && text[j] !== '\n') j++
26+
if (j < text.length) j++ // skip \n
27+
break
28+
}
29+
j++
30+
}
31+
// If no closing fence found, include rest as-is
32+
if (j >= text.length && !text.startsWith('```', j - 3)) {
33+
result += text.slice(i)
34+
}
35+
i = j
36+
} else {
37+
result += text[i]
38+
i++
39+
}
40+
}
41+
return result
42+
}
43+
744
export function markdownToPlainText(text: string): string {
8-
return text
9-
.replace(/```[\s\S]*?\n([\s\S]*?)```/g, '$1')
45+
return stripCodeBlocks(text)
1046
.replace(/`([^`]+)`/g, '$1')
1147
.replace(/\*\*\*(.+?)\*\*\*/g, '$1')
1248
.replace(/\*\*(.+?)\*\*/g, '$1')

0 commit comments

Comments
 (0)