From fa4e3680038e094f81e6efb79bc55531e66cd9c7 Mon Sep 17 00:00:00 2001 From: Eugene Burmakin Date: Mon, 24 Nov 2025 19:44:21 +0100 Subject: [PATCH] Fix potential xss --- app/javascript/maps/places.js | 40 +++++++++++++++++++++------ app/javascript/maps/places_control.js | 26 ++++++++++++++--- config/initializers/01_constants.rb | 2 ++ 3 files changed, 55 insertions(+), 13 deletions(-) diff --git a/app/javascript/maps/places.js b/app/javascript/maps/places.js index eaf0498c..31b67438 100644 --- a/app/javascript/maps/places.js +++ b/app/javascript/maps/places.js @@ -177,8 +177,10 @@ export class PlacesManager { } createPlaceIcon(place) { - const emoji = place.icon || place.tags[0]?.icon || '📍'; - const color = place.color || place.tags[0]?.color || '#4CAF50'; + const rawEmoji = place.icon || place.tags[0]?.icon || '📍'; + const emoji = this.escapeHtml(rawEmoji); + const rawColor = place.color || place.tags[0]?.color || '#4CAF50'; + const color = this.sanitizeColor(rawColor); const iconHtml = `
- ${tag.icon} #${tag.name} - ` - ).join(' '); + const tags = place.tags.map(tag => { + const safeIcon = this.escapeHtml(tag.icon || ''); + const safeName = this.escapeHtml(tag.name || ''); + const safeColor = this.sanitizeColor(tag.color); + return ` + ${safeIcon} #${safeName} + `; + }).join(' '); + + const safeName = this.escapeHtml(place.name || ''); + const safeVisitsCount = place.visits_count ? parseInt(place.visits_count, 10) : 0; return `
-

${place.name}

+

${safeName}

${tags ? `
${tags}
` : ''} ${place.note ? `

${this.escapeHtml(place.note)}

` : ''} - ${place.visits_count ? `

Visits: ${place.visits_count}

` : ''} + ${safeVisitsCount > 0 ? `

Visits: ${safeVisitsCount}

` : ''}
- ${this.tags.map(tag => ` + ${this.tags.map(tag => { + const safeIcon = tag.icon ? this.escapeHtml(tag.icon) : '📍'; + const safeColor = this.sanitizeColor(tag.color); + return ` - `).join('')} + `; + }).join('')}
` : '
No tags created yet
'} @@ -209,6 +213,20 @@ export function createPlacesControl(placesManager, tags, userTheme = 'dark') { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; + }, + + sanitizeColor: function(color) { + // Validate hex color format (#RGB or #RRGGBB) + if (!color || typeof color !== 'string') { + return '#4CAF50'; // Default green + } + + const hexColorRegex = /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/; + if (hexColorRegex.test(color)) { + return color; + } + + return '#4CAF50'; // Default green for invalid colors } }); } diff --git a/config/initializers/01_constants.rb b/config/initializers/01_constants.rb index a360d0d5..53644b1e 100644 --- a/config/initializers/01_constants.rb +++ b/config/initializers/01_constants.rb @@ -50,6 +50,8 @@ OMNIAUTH_PROVIDERS = providers << :github if ENV['GITHUB_OAUTH_CLIENT_ID'].present? && ENV['GITHUB_OAUTH_CLIENT_SECRET'].present? providers << :google_oauth2 if ENV['GOOGLE_OAUTH_CLIENT_ID'].present? && ENV['GOOGLE_OAUTH_CLIENT_SECRET'].present? + + providers end # Custom OIDC provider display name