From 23c26569a45d98a72c1c8032ff357ab4beed545f Mon Sep 17 00:00:00 2001 From: Hamed Niroomand Date: Sat, 15 Aug 2026 15:55:24 +0400 Subject: [PATCH] fix(ui): strip truncated html from package descriptions --- app/composables/useMarkdown.ts | 7 +++++++ shared/utils/html.ts | 3 ++- test/nuxt/composables/use-markdown.spec.ts | 14 +++++++++++++- test/unit/shared/utils/html.spec.ts | 16 ++++++++++++++++ 4 files changed, 38 insertions(+), 2 deletions(-) diff --git a/app/composables/useMarkdown.ts b/app/composables/useMarkdown.ts index bcf8678096..6d948bef25 100644 --- a/app/composables/useMarkdown.ts +++ b/app/composables/useMarkdown.ts @@ -56,6 +56,12 @@ function stripAndEscapeHtml(text: string): string { (match, codeSpan: string | undefined) => codeSpan ?? '', ) + // Strip unclosed HTML tags left by registry truncation (no closing '>') + stripped = stripped.replace( + /(`[^`]*`)|<\/?[a-z][^>]*$/gi, + (match, codeSpan: string | undefined) => codeSpan ?? '', + ) + // Strip HTML comments: (including unclosed comments from truncation) stripped = stripped.replace( /(`[^`]*`)||$)/g, @@ -69,6 +75,7 @@ function stripAndEscapeHtml(text: string): string { .replace(/>/g, '>') .replace(/"/g, '"') .replace(/'/g, ''') + .trim() } // Parse simple inline markdown to HTML diff --git a/shared/utils/html.ts b/shared/utils/html.ts index 1f9de45c67..ad054a1002 100644 --- a/shared/utils/html.ts +++ b/shared/utils/html.ts @@ -25,7 +25,8 @@ export function stripHtmlTags(text: string): string { previous = result result = result.replace(tagPattern, '') } while (result !== previous) - return result + // Strip unclosed HTML tags left by registry truncation (no closing '>') + return result.replace(/<\/?[a-z][^>]*$/gi, '').trim() } /** * Generate a GitHub-style slug from heading text. diff --git a/test/nuxt/composables/use-markdown.spec.ts b/test/nuxt/composables/use-markdown.spec.ts index 2002a5ad30..fc226c9b5b 100644 --- a/test/nuxt/composables/use-markdown.spec.ts +++ b/test/nuxt/composables/use-markdown.spec.ts @@ -256,6 +256,18 @@ describe('useMarkdown', () => { const processed = useMarkdown({ text: 'bold and **also bold**' }) expect(processed.value).toBe('bold and also bold') }) + + it('strips unclosed HTML tags (truncated)', () => { + const processed = useMarkdown({ + text: '

NPM version { @@ -286,7 +298,7 @@ describe('useMarkdown', () => { it('strips unclosed HTML comments (truncated)', () => { const processed = useMarkdown({ text: 'A library