diff --git a/docs/site/src/components/docs/search-excerpt.mjs b/docs/site/src/components/docs/search-excerpt.mjs index edf449093b9..a00a05465e2 100644 --- a/docs/site/src/components/docs/search-excerpt.mjs +++ b/docs/site/src/components/docs/search-excerpt.mjs @@ -27,8 +27,8 @@ export function stripSearchExcerptMarkdown(value) { }) const linkedText = protectedCode.includes(']') ? protectedCode - .replace(/!\[([^\]]*)\]\((?:\\.|[^)])*\)/g, '$1') - .replace(/\[([^\]]+)\]\((?:\\.|[^)])*\)/g, '$1') + .replace(/!\[([^\]]*)\]\((?:\\[\s\S]|[^)\\])*\\?\)/g, '$1') + .replace(/\[([^\]]+)\]\((?:\\[\s\S]|[^)\\])*\\?\)/g, '$1') .replace(/\[([^\]]+)\]\s*\[[^\]]*\]/g, '$1') .replace(/\[\[([^|\]]+)\|([^\]]+)\]\]/g, '$2') .replace(/\[\[([^\]]+)\]\]/g, '$1') diff --git a/docs/site/tests/search-excerpt.test.mjs b/docs/site/tests/search-excerpt.test.mjs index cef393bb21f..8d32de2e761 100644 --- a/docs/site/tests/search-excerpt.test.mjs +++ b/docs/site/tests/search-excerpt.test.mjs @@ -1,7 +1,32 @@ import assert from 'node:assert/strict' import test from 'node:test' +import { runInNewContext } from 'node:vm' import { stripSearchExcerptMarkdown } from '../src/components/docs/search-excerpt.mjs' +test('unterminated escaped link destinations do not backtrack exponentially', () => { + for (const prefix of ['![](', '[label](']) { + const input = prefix + '\\('.repeat(32_768) + const result = runInNewContext( + 'strip(input)', + { strip: stripSearchExcerptMarkdown, input }, + { + timeout: 250 + } + ) + assert.equal(result, input.replace(/\\\(/g, '(')) + } +}) + +test('link destinations retain escaped closing parentheses and literal escapes', () => { + for (const prefix of ['![label](', '[label](']) { + assert.equal(stripSearchExcerptMarkdown(prefix + 'https://example.com/a\\)b)'), 'label') + assert.equal(stripSearchExcerptMarkdown(prefix + 'https://example.com/a\\(b)'), 'label') + assert.equal(stripSearchExcerptMarkdown(prefix + 'https://example.com/a\\)'), 'label') + assert.equal(stripSearchExcerptMarkdown(prefix + 'https://example.com/a\\\\))'), 'label)') + assert.equal(stripSearchExcerptMarkdown(prefix + 'https://example.com/a\\\nb)'), 'label') + } +}) + test('search excerpts omit inline Markdown syntax', () => { assert.equal( stripSearchExcerptMarkdown(