fix: Normalize URLs with spaces in WhatsApp template parameters (#12594)

This PR fixes URL parsing errors when WhatsApp template parameters
contain URLs with spaces or special characters. The solution adds proper
URL normalization using Addressable::URI before validation, which
automatically handles space encoding and special character
normalization.

Related with https://github.com/chatwoot/chatwoot/pull/12462

## Type of change

- [x] Bug fix (non-breaking change which fixes an issue)

## Checklist:

- [x] My code follows the style guidelines of this project
- [x] I have performed a self-review of my code
- [x] I have commented on my code, particularly in hard-to-understand
areas
- [ ] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [x] I have added tests that prove my fix is effective or that my
feature works
- [x] New and existing unit tests pass locally with my changes
- [x] Any dependent changes have been merged and published in downstream
modules

---------

Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
Aguinaldo Tupy
2025-10-08 07:03:06 -03:00
committed by GitHub
parent 978f4c431a
commit 78ebdbbbd8
2 changed files with 84 additions and 2 deletions

View File

@@ -34,8 +34,9 @@ class Whatsapp::PopulateTemplateParametersService
return nil if url.blank?
sanitized_url = sanitize_parameter(url)
validate_url(sanitized_url)
build_media_type_parameter(sanitized_url, media_type.downcase, media_name)
normalized_url = normalize_url(sanitized_url)
validate_url(normalized_url)
build_media_type_parameter(normalized_url, media_type.downcase, media_name)
end
def build_named_parameter(parameter_name, value)
@@ -138,9 +139,20 @@ class Whatsapp::PopulateTemplateParametersService
sanitized[0...1000] # Limit length to prevent DoS
end
def normalize_url(url)
# Use Addressable::URI for better URL normalization
# It handles spaces, special characters, and encoding automatically
Addressable::URI.parse(url).normalize.to_s
rescue Addressable::URI::InvalidURIError
# Fallback: simple space encoding if Addressable fails
url.gsub(' ', '%20')
end
def validate_url(url)
return if url.blank?
# url is already normalized by the caller
uri = URI.parse(url)
raise ArgumentError, "Invalid URL scheme: #{uri.scheme}. Only http and https are allowed" unless %w[http https].include?(uri.scheme)
raise ArgumentError, 'URL too long (max 2000 characters)' if url.length > 2000