From bc6556890a699de09895a68a737838dd121cd244 Mon Sep 17 00:00:00 2001 From: tanu tiwari Date: Sat, 16 May 2026 22:22:43 +0530 Subject: [PATCH] fix(svg): add accessible title and desc tags MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added semantic title and desc tags to improve SVG accessibility for screen readers. Escape XML characters in user title and labels Thanks for pointing this out! I’ve added XML escaping for user-controlled strings using an escapeXML() helper and updated the affected , <desc>, and SVG text usages in both SVG generators to prevent malformed SVG/XML injection issues. Implement escapeXML function for XML escaping Add escapeXML function to sanitize XML strings fix(svg): add accessible title/desc tags and escape XML - Added semantic title and desc tags for accessibility - Implemented escapeXML helper to sanitize user strings - Escaped XML characters in user-controlled labels --- lib/svg/generator.ts | 41 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 36 insertions(+), 5 deletions(-) diff --git a/lib/svg/generator.ts b/lib/svg/generator.ts index d3a096c..653e6f8 100644 --- a/lib/svg/generator.ts +++ b/lib/svg/generator.ts @@ -15,7 +15,14 @@ function deterministicRandom(seed: string): number { } return (hash >>> 0) / 4294967296; } - +function escapeXML(str: string): string { + return str + .replace(/&/g, '&') + .replace(/</g, '<') + .replace(/>/g, '>') + .replace(/"/g, '"') + .replace(/'/g, '''); +} function generateParticles( x: number, y: number, @@ -148,6 +155,7 @@ export function generateSVG( if (params.autoTheme) { return generateAutoThemeSVG(stats, params, calendar); } + const safeUser = escapeXML(params.user || 'GitHub User'); const bg = `#${(params.bg || '0d1117').replace('#', '')}`; const accent = `#${(params.accent || '00ffaa').replace('#', '')}`; @@ -216,7 +224,18 @@ export function generateSVG( : ''; return ` -<svg xmlns="http://www.w3.org/2000/svg" width="600" height="420" viewBox="0 0 600 420" fill="none"> +<svg + xmlns="http://www.w3.org/2000/svg" + width="600" + height="420" + viewBox="0 0 600 420" + fill="none" + role="img" +> + <title>CommitPulse Stats for ${safeUser} + + ${params.user || 'This user'} has ${stats.totalContributions} total contributions and a longest streak of ${stats.longestStreak} days. + @@ -287,7 +306,7 @@ export function generateSVG( ${stats.longestStreak} - ${params.user?.toUpperCase() || ''} + ${safeUser.toUpperCase()} @@ -313,6 +332,7 @@ function generateAutoThemeSVG( ): string { const light = AUTO_LIGHT_THEME; const dark = AUTO_DARK_THEME; + const safeUser = escapeXML(params.user || 'GitHub User'); const selectedFont = params.font ? FONT_MAP[params.font.toLowerCase()] || '"JetBrains Mono", monospace' @@ -363,7 +383,18 @@ function generateAutoThemeSVG( } return ` - + + CommitPulse Stats for ${safeUser} + + ${params.user || 'This user'} has ${stats.totalContributions} total contributions and a longest streak of ${stats.longestStreak} days. + @@ -455,7 +486,7 @@ function generateAutoThemeSVG( ${stats.longestStreak} - ${params.user?.toUpperCase() || ''} + ${safeUser.toUpperCase()}