[READ-ONLY] a fast, modern browser for the npm registry
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

fix: handle html entities in playground description (#591)

authored by

Daniel Roe and committed by
GitHub
f5ae776f 700cf225

+24 -4
+7 -2
app/components/MarkdownText.vue
··· 1 1 <script setup lang="ts"> 2 + import { decodeHtmlEntities } from '~/utils/formatters' 3 + 2 4 const props = defineProps<{ 3 5 text: string 4 6 /** When true, renders link text without the anchor tag (useful when inside another link) */ ··· 21 23 22 24 // Strip HTML tags and escape remaining HTML to prevent XSS 23 25 function stripAndEscapeHtml(text: string): string { 24 - // First strip markdown image badges 25 - let stripped = stripMarkdownImages(text) 26 + // First decode any HTML entities in the input 27 + let stripped = decodeHtmlEntities(text) 28 + 29 + // Then strip markdown image badges 30 + stripped = stripMarkdownImages(stripped) 26 31 27 32 // Then strip actual HTML tags (keep their text content) 28 33 // Only match tags that start with a letter or / (to avoid matching things like "a < b > c")
+3 -2
app/components/PackagePlaygrounds.vue
··· 1 1 <script setup lang="ts"> 2 2 import type { PlaygroundLink } from '#shared/types' 3 + import { decodeHtmlEntities } from '~/utils/formatters' 3 4 4 5 const props = defineProps<{ 5 6 links: PlaygroundLink[] ··· 126 127 :class="[getIcon(firstLink.provider), getColor(firstLink.provider), 'w-4 h-4 shrink-0']" 127 128 aria-hidden="true" 128 129 /> 129 - <span class="truncate text-fg-muted">{{ firstLink.label }}</span> 130 + <span class="truncate text-fg-muted">{{ decodeHtmlEntities(firstLink.label) }}</span> 130 131 </a> 131 132 </AppTooltip> 132 133 ··· 182 183 :class="[getIcon(link.provider), getColor(link.provider), 'w-4 h-4 shrink-0']" 183 184 aria-hidden="true" 184 185 /> 185 - <span class="truncate">{{ link.label }}</span> 186 + <span class="truncate">{{ decodeHtmlEntities(link.label) }}</span> 186 187 </a> 187 188 </AppTooltip> 188 189 </div>
+14
app/utils/formatters.ts
··· 5 5 return `${year}-${month}-${day}` 6 6 } 7 7 8 + const htmlEntities: Record<string, string> = { 9 + '&amp;': '&', 10 + '&lt;': '<', 11 + '&gt;': '>', 12 + '&quot;': '"', 13 + '&#39;': "'", 14 + '&apos;': "'", 15 + '&nbsp;': ' ', 16 + } 17 + 18 + export function decodeHtmlEntities(text: string): string { 19 + return text.replace(/&(?:amp|lt|gt|quot|apos|nbsp|#39);/g, match => htmlEntities[match] || match) 20 + } 21 + 8 22 export function formatCompactNumber( 9 23 value: number, 10 24 options?: { decimals?: number; space?: boolean },