mirror of
https://github.com/stablyai/orca.git
synced 2026-10-16 16:03:26 +00:00
fix: address pr-bug-scan validated finding from #6315
Protect fenced/inline code spans from the HTML stripTags pass in normalizeMobileMarkdownPreviewHtml; blocks stripTags(next) at mobile-markdown-preview-html.ts:95 from deleting <...> inside code.
This commit is contained in:
@@ -49,4 +49,16 @@ describe('parseMobileMarkdown', () => {
|
||||
normalizeMobileMarkdownPreviewHtml('<p>Use <code>&lt;button&gt;</code></p>')
|
||||
).toBe('Use `<button>`')
|
||||
})
|
||||
|
||||
it('preserves angle brackets and generics inside fenced and inline code', () => {
|
||||
expect(normalizeMobileMarkdownPreviewHtml('```html\n<div>x</div>\n```')).toBe(
|
||||
'```html\n<div>x</div>\n```'
|
||||
)
|
||||
expect(normalizeMobileMarkdownPreviewHtml('```ts\nconst x: Array<string> = []\n```')).toBe(
|
||||
'```ts\nconst x: Array<string> = []\n```'
|
||||
)
|
||||
expect(normalizeMobileMarkdownPreviewHtml('Use `Array<string>` here')).toBe(
|
||||
'Use `Array<string>` here'
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -74,8 +74,39 @@ function normalizeInlineHtml(value: string): string {
|
||||
})
|
||||
}
|
||||
|
||||
// Why: the HTML cleanup pipeline below strips every `<...>` run and decodes
|
||||
// entities. Markdown code (fenced ```...``` and inline `...`) is literal source
|
||||
// the user wants verbatim — e.g. `<div>x</div>` or `Array<string>` — so it must
|
||||
// not pass through stripTags. Protect those spans with placeholder tokens (no
|
||||
// `<`, `>`, or entity text, so the pipeline leaves them untouched) and restore
|
||||
// them after. Fenced blocks are matched before inline spans so a fence's
|
||||
// backticks can't be misread as inline code.
|
||||
const CODE_PLACEHOLDER_PREFIX = 'ORCA_MD_CODE_'
|
||||
const CODE_PLACEHOLDER_SUFFIX = ''
|
||||
|
||||
function protectMarkdownCode(content: string): { protectedText: string; codeSpans: string[] } {
|
||||
const codeSpans: string[] = []
|
||||
const store = (match: string): string => {
|
||||
const token = `${CODE_PLACEHOLDER_PREFIX}${codeSpans.length}${CODE_PLACEHOLDER_SUFFIX}`
|
||||
codeSpans.push(match)
|
||||
return token
|
||||
}
|
||||
const protectedText = content
|
||||
.replace(/```[\s\S]*?```/g, store)
|
||||
.replace(/`[^`\n]+`/g, store)
|
||||
return { protectedText, codeSpans }
|
||||
}
|
||||
|
||||
function restoreMarkdownCode(value: string, codeSpans: string[]): string {
|
||||
return value.replace(
|
||||
new RegExp(`${CODE_PLACEHOLDER_PREFIX}(\\d+)${CODE_PLACEHOLDER_SUFFIX}`, 'g'),
|
||||
(_token, index) => codeSpans[Number(index)] ?? _token
|
||||
)
|
||||
}
|
||||
|
||||
export function normalizeMobileMarkdownPreviewHtml(content: string): string {
|
||||
let next = content.replace(/\r\n?/g, '\n')
|
||||
const { protectedText, codeSpans } = protectMarkdownCode(content.replace(/\r\n?/g, '\n'))
|
||||
let next = protectedText
|
||||
|
||||
// Why: repository Markdown often uses small HTML islands for centered README
|
||||
// headers and badges. Preview mode should read like Markdown, while Source
|
||||
@@ -94,5 +125,5 @@ export function normalizeMobileMarkdownPreviewHtml(content: string): string {
|
||||
next = normalizeInlineHtml(next)
|
||||
next = stripTags(next)
|
||||
|
||||
return restoreEscapedHtmlEntities(next)
|
||||
return restoreMarkdownCode(restoreEscapedHtmlEntities(next), codeSpans)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user