From 7d42edd17f87d783e5e404f10b57dd35c7636657 Mon Sep 17 00:00:00 2001 From: Tanmay Deep Sharma <32020192+tds-1@users.noreply.github.com> Date: Tue, 21 Apr 2026 13:00:20 +0700 Subject: [PATCH] fix: sanitize parentheses from email From header to prevent SMTP 553 errors (#14075) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Description When an inbox name or business name contains parentheses — e.g. `Giro Crédito - Soporte (Email` — the resulting From header becomes unparseable by SMTP servers. The `(` is interpreted as an RFC 5322 comment start, swallowing the actual email address and causing a `553 Invalid email address` rejection. Closes [CW-6323](https://linear.app/chatwoot/issue/CW-6323) ## Type of change - [ ] Bug fix (non-breaking change which fixes an issue) ## How to reproduce? 1. Set an inbox's name or business name to include a parenthesis, e.g. `Support (Email` 2. Send an outgoing email reply from that inbox 3. Observe `Net::SMTPFatalError: 553 ... Invalid email address` ## Checklist: - [ ] My code follows the style guidelines of this project - [ ] I have performed a self-review of my code - [ ] I have commented on my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [ ] My changes generate no new warnings - [ ] I have added tests that prove my fix is effective or that my feature works - [ ] New and existing unit tests pass locally with my changes - [ ] Any dependent changes have been merged and published in downstream modules --- app/builders/email/base_builder.rb | 2 +- app/mailers/conversation_reply_mailer.rb | 2 +- app/models/inbox.rb | 15 +++++++++++++-- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/app/builders/email/base_builder.rb b/app/builders/email/base_builder.rb index 6f79d6018..a6da58792 100644 --- a/app/builders/email/base_builder.rb +++ b/app/builders/email/base_builder.rb @@ -41,7 +41,7 @@ class Email::BaseBuilder end def business_name - inbox.business_name || inbox.sanitized_name + inbox.sanitized_business_name end def account_support_email diff --git a/app/mailers/conversation_reply_mailer.rb b/app/mailers/conversation_reply_mailer.rb index d9e6ec8e0..20e194fed 100644 --- a/app/mailers/conversation_reply_mailer.rb +++ b/app/mailers/conversation_reply_mailer.rb @@ -105,7 +105,7 @@ class ConversationReplyMailer < ApplicationMailer end def business_name - @inbox.business_name || @inbox.sanitized_name + @inbox.sanitized_business_name end def from_email diff --git a/app/models/inbox.rb b/app/models/inbox.rb index 0a26462ad..82b250560 100644 --- a/app/models/inbox.rb +++ b/app/models/inbox.rb @@ -102,7 +102,7 @@ class Inbox < ApplicationRecord # Sanitizes inbox name for balanced email provider compatibility # ALLOWS: /'._- and Unicode letters/numbers/emojis - # REMOVES: Forbidden chars (\<>@") + spam-trigger symbols (!#$%&*+=?^`{|}~) + # REMOVES: Forbidden chars (\<>@"()) + spam-trigger symbols (!#$%&*+=?^`{|}~) def sanitized_name return default_name_for_blank_name if name.blank? @@ -110,6 +110,10 @@ class Inbox < ApplicationRecord sanitized.blank? && email? ? display_name_from_email : sanitized end + def sanitized_business_name + sanitize_raw_name(business_name) || sanitized_name + end + def sms? channel_type == 'Channel::Sms' end @@ -209,8 +213,15 @@ class Inbox < ApplicationRecord email? ? display_name_from_email : '' end + def sanitize_raw_name(raw) + return nil if raw.blank? + + result = apply_sanitization_rules(raw) + result.presence + end + def apply_sanitization_rules(name) - name.gsub(/[\\<>@"!#$%&*+=?^`{|}~:;]/, '') # Remove forbidden chars + name.gsub(/[\\<>@"!#$%&*+=?^`{|}~:;()]/, '') # Remove forbidden chars .gsub(/[\x00-\x1F\x7F]/, ' ') # Replace control chars with spaces .gsub(/\A[[:punct:]]+|[[:punct:]]+\z/, '') # Remove leading/trailing punctuation .gsub(/\s+/, ' ') # Normalize spaces