sanitize_email 2.0.3 → 2.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/CHANGELOG.md +97 -12
  4. data/CODE_OF_CONDUCT.md +84 -0
  5. data/CONTRIBUTING.md +47 -0
  6. data/{LICENSE → LICENSE.txt} +1 -1
  7. data/README.md +415 -59
  8. data/SECURITY.md +15 -0
  9. data/lib/sanitize_email/bleach.rb +75 -68
  10. data/lib/sanitize_email/config.rb +22 -22
  11. data/lib/sanitize_email/deprecation.rb +6 -6
  12. data/lib/sanitize_email/{engine.rb → engine_v5.rb} +4 -3
  13. data/lib/sanitize_email/engine_v6.rb +15 -0
  14. data/lib/sanitize_email/mail_ext.rb +2 -0
  15. data/lib/sanitize_email/mail_header_tools.rb +24 -16
  16. data/lib/sanitize_email/overridden_addresses.rb +84 -22
  17. data/lib/sanitize_email/railtie.rb +1 -1
  18. data/lib/sanitize_email/rspec_matchers.rb +66 -31
  19. data/lib/sanitize_email/test_helpers.rb +6 -6
  20. data/lib/sanitize_email/version.rb +4 -2
  21. data/lib/sanitize_email.rb +35 -19
  22. data.tar.gz.sig +0 -0
  23. metadata +108 -93
  24. metadata.gz.sig +0 -0
  25. data/.coveralls.yml +0 -1
  26. data/.gitignore +0 -12
  27. data/.pryrc +0 -11
  28. data/.reek +0 -9
  29. data/.rspec +0 -2
  30. data/.rubocop.yml +0 -73
  31. data/.rubocop_rspec.yml +0 -35
  32. data/.rubocop_todo.yml +0 -21
  33. data/.ruby-gemset +0 -1
  34. data/.ruby-version +0 -1
  35. data/.travis.yml +0 -71
  36. data/Appraisals +0 -29
  37. data/Gemfile +0 -22
  38. data/REEK +0 -2
  39. data/Rakefile +0 -52
  40. data/gemfiles/rails_4_2.gemfile +0 -17
  41. data/gemfiles/rails_5_0.gemfile +0 -17
  42. data/gemfiles/rails_5_1.gemfile +0 -17
  43. data/gemfiles/rails_5_2.gemfile +0 -17
  44. data/init.rb +0 -3
  45. data/sanitize_email.gemspec +0 -49
  46. data/spec/sanitize_email_spec.rb +0 -944
  47. data/spec/spec_helper.rb +0 -28
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Copyright (c) 2008-16 Peter H. Boling of RailsBling.com
3
+ # Copyright (c) 2008 - 2018, 2020, 2022, 2024 Peter H. Boling of RailsBling.com
4
4
  # Released under the MIT license
5
5
 
6
6
  module SanitizeEmail
@@ -10,82 +10,89 @@ module SanitizeEmail
10
10
  extend SanitizeEmail::Deprecation
11
11
  attr_accessor :overridden_addresses # TODO: Just a stub, not implemented
12
12
 
13
- def initialize(*args)
14
- deprecation_message unless args.empty?
15
- end
13
+ class << self
14
+ # If all recipient addresses are allow-listed the field is left alone.
15
+ def delivering_email(message)
16
+ return unless sanitize_engaged?(message)
16
17
 
17
- # If all recipient addresses are white-listed the field is left alone.
18
- def self.delivering_email(message)
19
- return nil unless sanitize_engaged?(message)
20
- SanitizeEmail::MailHeaderTools.
21
- add_original_addresses_as_headers(message)
22
- SanitizeEmail::MailHeaderTools.
23
- prepend_custom_subject(message)
18
+ SanitizeEmail::MailHeaderTools
19
+ .add_original_addresses_as_headers(message)
20
+ SanitizeEmail::MailHeaderTools
21
+ .prepend_custom_subject(message)
24
22
 
25
- overridden = SanitizeEmail::OverriddenAddresses.new(message)
23
+ overridden = SanitizeEmail::OverriddenAddresses.new(message)
26
24
 
27
- message.to = overridden.overridden_to
28
- message.cc = overridden.overridden_cc
29
- message.bcc = overridden.overridden_bcc
30
- end
25
+ message.to = overridden.overridden_to
26
+ message.cc = overridden.overridden_cc
27
+ message.bcc = overridden.overridden_bcc
28
+
29
+ return if message["personalizations"].nil?
31
30
 
32
- # Will be called by the Hook to determine if an override should occur
33
- # There are three ways SanitizeEmail can be turned on;
34
- # in order of precedence they are:
35
- #
36
- # 1. SanitizeEmail.force_sanitize = true # by default it is nil
37
- # Only useful for local context.
38
- # Inside a method where you will be sending an email, set
39
- #
40
- # SanitizeEmail.force_sanitize = true
41
- #
42
- # just prior to delivering it. Also useful in the console.
43
- #
44
- # 2. If SanitizeEmail seems to not be sanitizing,
45
- # you have probably not registered the interceptor.
46
- # SanitizeEmail tries to do this for you.
47
- # *Note*: If you are working in an environment that has
48
- # a Mail or Mailer class that uses the register_interceptor API,
49
- # the interceptor will already have been registered.
50
- # The gem will probably have already done this for you,
51
- # but some really old versions of Rails may need you to do this manually:
52
- #
53
- # Mail.register_interceptor(SanitizeEmail::Bleach)
54
- #
55
- # Once registered, SanitizeEmail needs to be engaged:
56
- #
57
- # # in config/initializers/sanitize_email.rb
58
- # SanitizeEmail::Config.configure {|config| config[:engage] = true }
59
- #
60
- # 3. SanitizeEmail::Config.configure do |config|
61
- # config[:activation_proc] = Proc.new { true }
62
- # end
63
- #
64
- # If you don't need to compute anything,
65
- # then don't use the Proc, go with the previous option.
66
- #
67
- # Note: Number 1 is the method used by the SanitizeEmail.sanitary block
68
- # Note: Number 2 You may need to setup your own register_interceptor
69
- #
70
- # If installed but not configured, sanitize_email DOES NOTHING.
71
- # Until configured the defaults leave it turned off.
72
- def self.sanitize_engaged?(message)
73
- # Don't sanitize the message if it will not be delivered
74
- return false unless message.perform_deliveries
31
+ message["personalizations"].value = overridden.overridden_personalizations
32
+ end
75
33
 
76
- # Has it been forced via the force_sanitize mattr?
77
- forced = SanitizeEmail.force_sanitize
78
- return forced unless forced.nil?
34
+ # Will be called by the Hook to determine if an override should occur
35
+ # There are three ways SanitizeEmail can be turned on;
36
+ # in order of precedence they are:
37
+ #
38
+ # 1. SanitizeEmail.force_sanitize = true # by default it is nil
39
+ # Only useful for local context.
40
+ # Inside a method where you will be sending an email, set
41
+ #
42
+ # SanitizeEmail.force_sanitize = true
43
+ #
44
+ # just prior to delivering it. Also useful in the console.
45
+ #
46
+ # 2. If SanitizeEmail seems to not be sanitizing,
47
+ # you have probably not registered the interceptor.
48
+ # SanitizeEmail tries to do this for you.
49
+ # *Note*: If you are working in an environment that has
50
+ # a Mail or Mailer class that uses the register_interceptor API,
51
+ # the interceptor will already have been registered.
52
+ # The gem will probably have already done this for you,
53
+ # but some really old versions of Rails may need you to do this manually:
54
+ #
55
+ # Mail.register_interceptor(SanitizeEmail::Bleach)
56
+ #
57
+ # Once registered, SanitizeEmail needs to be engaged:
58
+ #
59
+ # # in config/initializers/sanitize_email.rb
60
+ # SanitizeEmail::Config.configure {|config| config[:engage] = true }
61
+ #
62
+ # 3. SanitizeEmail::Config.configure do |config|
63
+ # config[:activation_proc] = Proc.new { true }
64
+ # end
65
+ #
66
+ # If you don't need to compute anything,
67
+ # then don't use the Proc, go with the previous option.
68
+ #
69
+ # Note: Number 1 is the method used by the SanitizeEmail.sanitary block
70
+ # Note: Number 2 You may need to setup your own register_interceptor
71
+ #
72
+ # If installed but not configured, sanitize_email DOES NOTHING.
73
+ # Until configured the defaults leave it turned off.
74
+ def sanitize_engaged?(message)
75
+ # Don't sanitize the message if it will not be delivered
76
+ return false unless message.perform_deliveries
79
77
 
80
- # Is this particular instance of Bleach engaged
81
- engaged = SanitizeEmail::Config.config[:engage]
82
- return engaged unless engaged.nil?
78
+ # Has it been forced via the force_sanitize mattr?
79
+ forced = SanitizeEmail.force_sanitize
80
+ return forced unless forced.nil?
83
81
 
84
- # Should we sanitize due to the activation_proc?
85
- SanitizeEmail.activate?(message)
82
+ # Is this particular instance of Bleach engaged
83
+ engaged = SanitizeEmail::Config.config[:engage]
84
+ return engaged unless engaged.nil?
85
+
86
+ # Should we sanitize due to the activation_proc?
87
+ SanitizeEmail.activate?(message)
88
+ end
89
+ end
90
+
91
+ def initialize(*args)
92
+ deprecation_message unless args.empty?
86
93
  end
87
94
 
88
- private
95
+ private
89
96
 
90
97
  def deprecation_message
91
98
  deprecation = <<~DEPRECATION
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Copyright (c) 2008-16 Peter H. Boling of RailsBling.com
3
+ # Copyright (c) 2008 - 2018, 2020, 2022, 2024 Peter H. Boling of RailsBling.com
4
4
  # Released under the MIT license
5
5
 
6
6
  module SanitizeEmail
@@ -16,48 +16,48 @@ module SanitizeEmail
16
16
  DEFAULTS = {
17
17
  # Specify the BCC addresses for the messages
18
18
  # that go out in "local" environments
19
- :sanitized_bcc => nil,
19
+ sanitized_bcc: nil,
20
20
 
21
21
  # Specify the CC addresses for the messages
22
22
  # that go out in "local" environments
23
- :sanitized_cc => nil,
23
+ sanitized_cc: nil,
24
24
 
25
25
  # The recipient addresses for the messages,
26
26
  # either as a string (for a single address)
27
27
  # or an array (for multiple addresses)
28
28
  # that go out in "local" environments
29
- :sanitized_to => nil,
29
+ sanitized_to: nil,
30
30
 
31
- # a white list
32
- :good_list => nil,
31
+ # an allow list
32
+ good_list: nil,
33
33
 
34
- # a black list
35
- :bad_list => nil,
34
+ # a block list
35
+ bad_list: nil,
36
36
 
37
- :environment => if defined?(Rails) && Rails.env.present?
38
- "[#{Rails.env}]"
39
- else
40
- '[UNKNOWN ENVIRONMENT]'
41
- end,
37
+ environment: if defined?(Rails) && Rails.env.present?
38
+ "[#{Rails.env}]"
39
+ else
40
+ "[UNKNOWN ENVIRONMENT]"
41
+ end,
42
42
 
43
43
  # Use the "real" email address as the username
44
44
  # for the sanitized email address
45
- # e.g. "real@example.com <sanitized@example.com>"
46
- :use_actual_email_as_sanitized_user_name => false,
45
+ # e.g. "real at example.com <sanitized@example.com>"
46
+ use_actual_email_as_sanitized_user_name: false,
47
47
 
48
48
  # Prepend the "real" email address onto the Subject line of the message
49
- # e.g. "real@example.com rest of subject"
50
- :use_actual_email_prepended_to_subject => false,
49
+ # e.g. "real at example.com rest of subject"
50
+ use_actual_email_prepended_to_subject: false,
51
51
 
52
52
  # Prepend the Rails environment onto the Subject line of the message
53
53
  # e.g. "[development] rest of subject"
54
- :use_actual_environment_prepended_to_subject => false,
54
+ use_actual_environment_prepended_to_subject: false,
55
55
 
56
56
  # True / False turns on or off sanitization,
57
57
  # while nil ignores this setting and checks activation_proc
58
- :engage => nil,
58
+ engage: nil,
59
59
 
60
- :activation_proc => proc { false },
60
+ activation_proc: proc { false },
61
61
  }.freeze
62
62
 
63
63
  @config ||= DEFAULTS.dup
@@ -83,9 +83,9 @@ module SanitizeEmail
83
83
  end
84
84
 
85
85
  def self.config_force_sanitize_deprecation_warning
86
- return nil if @config[:force_sanitize].nil?
86
+ return if @config[:force_sanitize].nil?
87
87
  deprecation_warning_message(
88
- <<-DEPRECATION
88
+ <<-DEPRECATION,
89
89
  SanitizeEmail::Config.config[:force_sanitize] is deprecated.
90
90
  Please use SanitizeEmail.force_sanitize or SanitizeEmail.sanitary instead.
91
91
  Refer to https://github.com/pboling/sanitize_email/wiki for examples.
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Copyright (c) 2008-16 Peter H. Boling of RailsBling.com
3
+ # Copyright (c) 2008 - 2018, 2020, 2022, 2024 Peter H. Boling of RailsBling.com
4
4
  # Released under the MIT license
5
5
 
6
6
  module SanitizeEmail
@@ -19,8 +19,8 @@ module SanitizeEmail
19
19
  def deprecated_alias(name, replacement)
20
20
  # Create a wrapped version
21
21
  define_method(name) do |*args, &block|
22
- warn "SanitizeEmail: ##{name} deprecated (please use ##{replacement})" unless SanitizeEmail::Deprecation.deprecate_in_silence
23
- send replacement, *args, &block
22
+ warn("SanitizeEmail: ##{name} deprecated (please use ##{replacement})") unless SanitizeEmail::Deprecation.deprecate_in_silence
23
+ send(replacement, *args, &block)
24
24
  end
25
25
  end
26
26
 
@@ -30,11 +30,11 @@ module SanitizeEmail
30
30
  def deprecated(name, replacement = nil)
31
31
  # Replace old method
32
32
  old_name = :"#{name}_without_deprecation"
33
- alias_method old_name, name
33
+ alias_method(old_name, name)
34
34
  # And replace it with a wrapped version
35
35
  define_method(name) do |*args, &block|
36
36
  deprecation(name, " (please use ##{replacement})")
37
- send old_name, *args, &block
37
+ send(old_name, *args, &block)
38
38
  end
39
39
  end
40
40
 
@@ -47,7 +47,7 @@ module SanitizeEmail
47
47
  end
48
48
 
49
49
  def deprecation_warning_message(message)
50
- warn message unless SanitizeEmail::Deprecation.deprecate_in_silence
50
+ warn(message) unless SanitizeEmail::Deprecation.deprecate_in_silence
51
51
  end
52
52
  end
53
53
  end
@@ -1,11 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Copyright (c) 2008-16 Peter H. Boling of RailsBling.com
3
+ # Copyright (c) 2008 - 2018, 2020, 2022, 2024 Peter H. Boling of RailsBling.com
4
4
  # Released under the MIT license
5
5
 
6
6
  module SanitizeEmail
7
- # For Rails >= 3.1
8
- class Engine < ::Rails::Engine
7
+ # For Rails >= 3.1, < 6.0
8
+ # TODO: Remove when support for Rails < 6 is dropped
9
+ class EngineV5 < ::Rails::Engine
9
10
  config.to_prepare do
10
11
  ActionMailer::Base.register_interceptor(SanitizeEmail::Bleach)
11
12
  end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (c) 2008 - 2018, 2020, 2022, 2024 Peter H. Boling of RailsBling.com
4
+ # Released under the MIT license
5
+
6
+ module SanitizeEmail
7
+ # For Rails >= 6.0
8
+ class EngineV5 < ::Rails::Engine
9
+ config.to_prepare do
10
+ # For the reasoning behind the difference between v5 and v6 engines,
11
+ # - see: https://github.com/rails/rails/issues/36546#issuecomment-850888284
12
+ config.action_mailer.register_interceptor(SanitizeEmail::Bleach)
13
+ end
14
+ end
15
+ end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "mail"
4
+
3
5
  # Cribbed from email_spec gem
4
6
  module SanitizeEmail::MailExt
5
7
  def default_part
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Copyright (c) 2008-16 Peter H. Boling of RailsBling.com
3
+ # Copyright (c) 2008 - 2018, 2020, 2022, 2024 Peter H. Boling of RailsBling.com
4
4
  # Released under the MIT license
5
5
 
6
6
  module SanitizeEmail
@@ -9,39 +9,47 @@ module SanitizeEmail
9
9
  def self.prepend_subject_array(message)
10
10
  prepend = []
11
11
  if SanitizeEmail.use_actual_email_prepended_to_subject
12
- prepend << SanitizeEmail::MailHeaderTools.
13
- prepend_email_to_subject(Array(message.to))
12
+ prepend << SanitizeEmail::MailHeaderTools
13
+ .prepend_email_to_subject(Array(message.to))
14
14
  end
15
15
  if SanitizeEmail.use_actual_environment_prepended_to_subject
16
- prepend << SanitizeEmail::MailHeaderTools.
17
- prepend_environment_to_subject
16
+ prepend << SanitizeEmail::MailHeaderTools
17
+ .prepend_environment_to_subject
18
18
  end
19
19
  # this will force later joins to add an extra space
20
- prepend << '' unless prepend.empty?
20
+ prepend << "" unless prepend.empty?
21
21
  prepend
22
22
  end
23
23
 
24
24
  def self.custom_subject(message)
25
- prepend_subject_array(message).join(' ')
25
+ prepend_subject_array(message).join(" ")
26
26
  end
27
27
 
28
28
  def self.prepend_environment_to_subject
29
- SanitizeEmail::Config.config[:environment]
29
+ if SanitizeEmail::Config.config[:environment].respond_to?(:call)
30
+ SanitizeEmail::Config.config[:environment].call.to_s
31
+ else
32
+ SanitizeEmail::Config.config[:environment].to_s
33
+ end
30
34
  end
31
35
 
32
36
  def self.prepend_email_to_subject(actual_addresses)
33
- "(#{Array(actual_addresses).uniq.join(',').gsub(/@/, ' at ').
34
- gsub(/[<>]/, '~')})"
37
+ "(#{Array(actual_addresses).uniq.join(",").gsub(/@/, " at ")
38
+ .gsub(/[<>]/, "~")})"
35
39
  end
36
40
 
37
41
  def self.add_original_addresses_as_headers(message)
38
42
  # Add headers by string concat.
39
43
  # Setting hash values on message.headers does nothing, strangely.
40
44
  # See: http://goo.gl/v46GY
45
+ to_addrs = message[:to]&.addrs
46
+ cc_addrs = message[:cc]&.addrs
47
+ to_decoded = Array(to_addrs&.map(&:decoded))
48
+ cc_decoded = Array(cc_addrs&.map(&:decoded))
41
49
  {
42
50
  # can be an arrays, so casting it as arrays
43
- 'X-Sanitize-Email-To' => Array(message.to).uniq,
44
- 'X-Sanitize-Email-Cc' => Array(message.cc).uniq
51
+ "X-Sanitize-Email-To" => to_decoded,
52
+ "X-Sanitize-Email-Cc" => cc_decoded,
45
53
  # Don't write out the BCC, as those addresses should not be visible
46
54
  # in message headers for obvious reasons
47
55
  }.each do |header_key, header_value|
@@ -49,13 +57,13 @@ module SanitizeEmail
49
57
  SanitizeEmail::MailHeaderTools.update_header(
50
58
  header_key,
51
59
  header_value,
52
- message
60
+ message,
53
61
  )
54
62
  end
55
63
  end
56
64
 
57
65
  def self.prepend_custom_subject(message)
58
- message.subject = '' unless message.subject
66
+ message.subject = "" unless message.subject
59
67
  custom_subject = SanitizeEmail::MailHeaderTools.custom_subject(message)
60
68
  message.subject = custom_subject + message.subject
61
69
  end
@@ -63,11 +71,11 @@ module SanitizeEmail
63
71
  # According to https://github.com/mikel/mail
64
72
  # this is the correct way to update headers.
65
73
  def self.update_header(header_key, header_value, message)
66
- return nil unless header_value
74
+ return unless header_value
67
75
  # For each address, as header_value can be an array of addresses
68
76
  Array(header_value).each_with_index do |elem, index|
69
77
  num = index + 1
70
- new_header_key = num > 1 ? "#{header_key}-#{num}" : header_key
78
+ new_header_key = (num > 1) ? "#{header_key}-#{num}" : header_key
71
79
  message.header[new_header_key] = elem.to_s
72
80
  end
73
81
  end
@@ -1,25 +1,37 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Copyright (c) 2008-16 Peter H. Boling of RailsBling.com
3
+ # Copyright (c) 2008 - 2018, 2020, 2022, 2024 Peter H. Boling of RailsBling.com
4
4
  # Released under the MIT license
5
5
 
6
+ # External Libraries
7
+ require "mail"
8
+
6
9
  module SanitizeEmail
7
10
  # Tools for overriding addresses
8
11
  class OverriddenAddresses
9
- # Raised when after applying all sanitization rules there are no addresses to send the email to.
10
- class MissingTo < StandardError; end
12
+ # Raised when there are no recipients after sanitization
13
+ class MissingRecipients < StandardError; end
14
+
15
+ # MissingTo is Deprecated
16
+ class MissingTo < MissingRecipients; end
11
17
 
12
18
  # Raised if there is a recipient type that sanitize_email doesn't recognize.
13
19
  # If you get this error please report it.
14
20
  # recognized recipient types are: TO, CC, and BCC
15
21
  class UnknownOverride < StandardError; end
16
22
 
17
- REPLACE_AT = [/@/, ' at '].freeze
18
- REPLACE_ALLIGATOR = [/[<>]/, '~'].freeze
19
- attr_accessor :overridden_to, :overridden_cc, :overridden_bcc,
20
- :good_list, # White-listed addresses will not be molested as to, cc, or bcc
21
- :bad_list, # Black-listed addresses will be removed from to, cc and bcc when sanitization is engaged
22
- :sanitized_to, :sanitized_cc, :sanitized_bcc # Replace non-white-listed addresses with these sanitized addresses.
23
+ REPLACE_AT = [/@/, " at "].freeze
24
+ REPLACE_ALLIGATOR = [/[<>]/, "~"].freeze
25
+ attr_accessor :tempmail,
26
+ :overridden_to,
27
+ :overridden_cc,
28
+ :overridden_bcc,
29
+ :overridden_personalizations,
30
+ :good_list, # Allow-listed addresses will not be molested as to, cc, or bcc
31
+ :bad_list, # Block-listed addresses will be removed from to, cc and bcc when sanitization is engaged
32
+ :sanitized_to,
33
+ :sanitized_cc,
34
+ :sanitized_bcc # Replace non-allow-listed addresses with these sanitized addresses.
23
35
 
24
36
  def initialize(message, **args)
25
37
  # Not using extract_options! because non-rails compatibility is a goal
@@ -29,9 +41,25 @@ module SanitizeEmail
29
41
  @sanitized_bcc = args[:sanitized_bcc]
30
42
  @good_list = args[:good_list] || []
31
43
  @bad_list = args[:bad_list] || []
32
- @overridden_to = to_override(message.to)
33
- @overridden_cc = cc_override(message.cc)
34
- @overridden_bcc = bcc_override(message.bcc)
44
+ # Mail will do the username parsing for us.
45
+ @tempmail = Mail.new
46
+
47
+ tempmail.to = to_override(message.to)
48
+ tempmail.cc = cc_override(message.cc)
49
+ tempmail.bcc = bcc_override(message.bcc)
50
+
51
+ # remove addresses from :cc / :bcc that are also in :to
52
+ remove_duplicates
53
+
54
+ @overridden_to = tempmail[:to].decoded
55
+ @overridden_cc = tempmail[:cc].decoded
56
+ @overridden_bcc = tempmail[:bcc].decoded
57
+
58
+ if message["personalizations"].nil?
59
+ raise MissingRecipients, "No recipients left post-sanitization" if (tempmail.to + tempmail.cc + tempmail.bcc).empty?
60
+ else
61
+ @overridden_personalizations = personalizations_override(message["personalizations"])
62
+ end
35
63
  end
36
64
 
37
65
  # Allow good listed email addresses, and then remove the bad listed addresses
@@ -43,16 +71,32 @@ module SanitizeEmail
43
71
 
44
72
  def to_override(actual_addresses)
45
73
  to = override_email(:to, actual_addresses)
46
- raise MissingTo, "after overriding :to (#{actual_addresses}) there are no addresses to send in To: header." if to.empty?
47
- to.join(',')
74
+ to.join(",")
48
75
  end
49
76
 
50
77
  def cc_override(actual_addresses)
51
- override_email(:cc, actual_addresses).join(',')
78
+ override_email(:cc, actual_addresses).join(",")
52
79
  end
53
80
 
54
81
  def bcc_override(actual_addresses)
55
- override_email(:bcc, actual_addresses).join(',')
82
+ override_email(:bcc, actual_addresses).join(",")
83
+ end
84
+
85
+ # Intended to result in compatibility with https://github.com/eddiezane/sendgrid-actionmailer
86
+ def personalizations_override(actual_personalizations)
87
+ actual_personalizations.unparsed_value.map do |actual_personalization|
88
+ actual_personalization.merge(
89
+ to: actual_personalization[:to]&.map do |to|
90
+ to.merge(email: override_email(:to, to[:email]).join(","))
91
+ end,
92
+ cc: actual_personalization[:cc]&.map do |cc|
93
+ cc.merge(email: override_email(:cc, cc[:email]).join(","))
94
+ end,
95
+ bcc: actual_personalization[:bcc]&.map do |bcc|
96
+ bcc.merge(email: override_email(:bcc, bcc[:email]).join(","))
97
+ end,
98
+ )
99
+ end
56
100
  end
57
101
 
58
102
  def override_email(type, actual_addresses)
@@ -99,17 +143,19 @@ module SanitizeEmail
99
143
  has_address ? address : nil
100
144
  when :bad_list then
101
145
  has_address ? nil : address
146
+ else
147
+ raise ArgumentError, "address_list_filter got unknown list_type: #{list_type}"
102
148
  end
103
149
  end
104
150
 
105
151
  def inject_user_names(real_addresses, sanitized_addresses)
106
152
  real_addresses.each_with_object([]) do |real_recipient, result|
107
153
  new_recipient = if real_recipient.nil?
108
- sanitized_addresses
109
- else
110
- # puts "SANITIZED: #{sanitized_addresses}"
111
- sanitized_addresses.map { |sanitized| "#{real_recipient.gsub(REPLACE_AT[0], REPLACE_AT[1]).gsub(/[<>]/, '~')} <#{sanitized}>" }
112
- end
154
+ sanitized_addresses
155
+ else
156
+ # puts "SANITIZED: #{sanitized_addresses}"
157
+ sanitized_addresses.map { |sanitized| "#{real_recipient.gsub(REPLACE_AT[0], REPLACE_AT[1]).gsub(/[<>]/, "~")} <#{sanitized}>" }
158
+ end
113
159
  result << new_recipient
114
160
  end.flatten
115
161
  end
@@ -131,7 +177,23 @@ module SanitizeEmail
131
177
  when :bcc then
132
178
  Array(sanitized_bcc)
133
179
  else
134
- raise UnknownOverride, 'unknown email override'
180
+ raise UnknownOverride, "unknown email override"
181
+ end
182
+ end
183
+
184
+ private
185
+
186
+ def remove_duplicates
187
+ dedup_addresses = tempmail[:to].addresses
188
+
189
+ tempmail[:cc].addrs.reject! do |addr|
190
+ # If this email address is already in the :to list, then remove
191
+ dedup_addresses.include?(addr.address)
192
+ end
193
+ dedup_addresses += tempmail[:cc].addresses
194
+ tempmail[:bcc].addrs.reject! do |addr|
195
+ # If this email address is already in the :to list, then remove
196
+ dedup_addresses.include?(addr.address)
135
197
  end
136
198
  end
137
199
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Copyright (c) 2008-16 Peter H. Boling of RailsBling.com
3
+ # Copyright (c) 2008 - 2018, 2020, 2022, 2024 Peter H. Boling of RailsBling.com
4
4
  # Released under the MIT license
5
5
 
6
6
  module SanitizeEmail