sanitize_email 2.0.0 → 2.0.1
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.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.rubocop.yml +73 -0
- data/.rubocop_rspec.yml +35 -0
- data/.rubocop_todo.yml +21 -0
- data/Appraisals +48 -46
- data/Gemfile +14 -1
- data/Rakefile +24 -22
- data/gemfiles/rails_4_2.gemfile +8 -6
- data/gemfiles/rails_5_0.gemfile +8 -6
- data/gemfiles/rails_5_1.gemfile +8 -6
- data/gemfiles/rails_5_2.gemfile +8 -6
- data/init.rb +2 -1
- data/lib/sanitize_email.rb +23 -22
- data/lib/sanitize_email/bleach.rb +24 -24
- data/lib/sanitize_email/config.rb +23 -15
- data/lib/sanitize_email/deprecation.rb +3 -2
- data/lib/sanitize_email/engine.rb +2 -2
- data/lib/sanitize_email/mail_ext.rb +2 -0
- data/lib/sanitize_email/mail_header_tools.rb +16 -17
- data/lib/sanitize_email/overridden_addresses.rb +52 -52
- data/lib/sanitize_email/railtie.rb +2 -2
- data/lib/sanitize_email/rspec_matchers.rb +17 -16
- data/lib/sanitize_email/test_helpers.rb +10 -12
- data/lib/sanitize_email/version.rb +3 -1
- data/sanitize_email.gemspec +34 -32
- data/spec/sanitize_email_spec.rb +476 -480
- data/spec/spec_helper.rb +11 -10
- metadata +42 -38
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# Copyright (c) 2008-16 Peter H. Boling of RailsBling.com
|
2
4
|
# Released under the MIT license
|
3
5
|
|
@@ -9,30 +11,22 @@ module SanitizeEmail
|
|
9
11
|
attr_accessor :overridden_addresses # TODO: Just a stub, not implemented
|
10
12
|
|
11
13
|
def initialize(*args)
|
12
|
-
deprecation_message
|
13
|
-
SanitizeEmail:
|
14
|
-
Passing arguments to SanitizeEmail::Bleach.new is deprecated.
|
15
|
-
SanitizeEmail::Bleach.new now takes no arguments.
|
16
|
-
EOS
|
17
|
-
if !args.empty?
|
18
|
-
self.class.deprecation_warning_message(deprecation_message)
|
19
|
-
end
|
14
|
+
deprecation_message unless args.empty?
|
20
15
|
end
|
21
16
|
|
22
17
|
# If all recipient addresses are white-listed the field is left alone.
|
23
18
|
def self.delivering_email(message)
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
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)
|
29
24
|
|
30
|
-
|
25
|
+
overridden = SanitizeEmail::OverriddenAddresses.new(message)
|
31
26
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
end
|
27
|
+
message.to = overridden.overridden_to
|
28
|
+
message.cc = overridden.overridden_cc
|
29
|
+
message.bcc = overridden.overridden_bcc
|
36
30
|
end
|
37
31
|
|
38
32
|
# Will be called by the Hook to determine if an override should occur
|
@@ -76,7 +70,6 @@ EOS
|
|
76
70
|
# If installed but not configured, sanitize_email DOES NOTHING.
|
77
71
|
# Until configured the defaults leave it turned off.
|
78
72
|
def self.sanitize_engaged?(message)
|
79
|
-
|
80
73
|
# Don't sanitize the message if it will not be delivered
|
81
74
|
return false unless message.perform_deliveries
|
82
75
|
|
@@ -89,11 +82,18 @@ EOS
|
|
89
82
|
return engaged unless engaged.nil?
|
90
83
|
|
91
84
|
# Should we sanitize due to the activation_proc?
|
92
|
-
|
93
|
-
|
85
|
+
SanitizeEmail.activate?(message)
|
94
86
|
end
|
95
87
|
|
96
|
-
|
97
|
-
end # end Module SanitizeEmail
|
98
|
-
|
88
|
+
private
|
99
89
|
|
90
|
+
def deprecation_message
|
91
|
+
deprecation = <<~DEPRECATION
|
92
|
+
SanitizeEmail:
|
93
|
+
Passing arguments to SanitizeEmail::Bleach.new is deprecated.
|
94
|
+
SanitizeEmail::Bleach.new now takes no arguments.
|
95
|
+
DEPRECATION
|
96
|
+
self.class.deprecation_warning_message(deprecation)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# Copyright (c) 2008-16 Peter H. Boling of RailsBling.com
|
2
4
|
# Released under the MIT license
|
3
5
|
|
@@ -5,7 +7,6 @@ module SanitizeEmail
|
|
5
7
|
# The API for configuring SanitizeEmail is via `SanitizeEmail.config`
|
6
8
|
# Available configuration options are listed in the `DEFAULTS` constant.
|
7
9
|
class Config
|
8
|
-
|
9
10
|
extend SanitizeEmail::Deprecation
|
10
11
|
|
11
12
|
class << self
|
@@ -34,9 +35,9 @@ module SanitizeEmail
|
|
34
35
|
:bad_list => nil,
|
35
36
|
|
36
37
|
:environment => if defined?(Rails) && Rails.env.present?
|
37
|
-
|
38
|
+
"[#{Rails.env}]"
|
38
39
|
else
|
39
|
-
|
40
|
+
'[UNKNOWN ENVIRONMENT]'
|
40
41
|
end,
|
41
42
|
|
42
43
|
# Use the "real" email address as the username
|
@@ -56,20 +57,20 @@ module SanitizeEmail
|
|
56
57
|
# while nil ignores this setting and checks activation_proc
|
57
58
|
:engage => nil,
|
58
59
|
|
59
|
-
:activation_proc =>
|
60
|
-
}
|
60
|
+
:activation_proc => proc { false },
|
61
|
+
}.freeze
|
61
62
|
|
62
63
|
@config ||= DEFAULTS.dup
|
63
|
-
def self.configure
|
64
|
+
def self.configure
|
64
65
|
yield @config
|
65
66
|
|
66
67
|
# Gracefully handle deprecated config values.
|
67
68
|
# Actual deprecation warnings are thrown in the top SanitizeEmail module
|
68
69
|
# thanks to our use of dynamic methods.
|
69
70
|
if @config[:local_environments] && defined?(Rails)
|
70
|
-
@config[:activation_proc] =
|
71
|
+
@config[:activation_proc] = proc do
|
71
72
|
SanitizeEmail.local_environments.include?(Rails.env)
|
72
|
-
|
73
|
+
end
|
73
74
|
end
|
74
75
|
if @config[:sanitized_recipients]
|
75
76
|
# calling it to trigger the deprecation warning.
|
@@ -78,17 +79,24 @@ module SanitizeEmail
|
|
78
79
|
SanitizeEmail.sanitized_recipients
|
79
80
|
@config[:sanitized_to] = @config[:sanitized_recipients]
|
80
81
|
end
|
81
|
-
|
82
|
-
|
83
|
-
|
82
|
+
config_force_sanitize_deprecation_warning
|
83
|
+
end
|
84
|
+
|
85
|
+
def self.config_force_sanitize_deprecation_warning
|
86
|
+
return nil if @config[:force_sanitize].nil?
|
87
|
+
deprecation_warning_message(
|
88
|
+
<<-DEPRECATION
|
84
89
|
SanitizeEmail::Config.config[:force_sanitize] is deprecated.
|
85
90
|
Please use SanitizeEmail.force_sanitize or SanitizeEmail.sanitary instead.
|
86
91
|
Refer to https://github.com/pboling/sanitize_email/wiki for examples.
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
end
|
92
|
+
DEPRECATION
|
93
|
+
)
|
94
|
+
SanitizeEmail.force_sanitize = @config[:force_sanitize]
|
91
95
|
end
|
92
96
|
|
97
|
+
INIT_KEYS = [:sanitized_to, :sanitized_cc, :sanitized_bcc, :good_list, :bad_list].freeze
|
98
|
+
def self.to_init
|
99
|
+
@config.select { |key, _value| INIT_KEYS.include?(key) }
|
100
|
+
end
|
93
101
|
end
|
94
102
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# Copyright (c) 2008-16 Peter H. Boling of RailsBling.com
|
2
4
|
# Released under the MIT license
|
3
5
|
|
@@ -5,7 +7,6 @@ module SanitizeEmail
|
|
5
7
|
# Provides tools that allow methods to be deprecated with new releases of the gem.
|
6
8
|
# See http://www.seejohncode.com/2012/01/09/deprecating-methods-in-ruby/
|
7
9
|
module Deprecation
|
8
|
-
|
9
10
|
class << self
|
10
11
|
attr_accessor :deprecate_in_silence
|
11
12
|
end
|
@@ -32,7 +33,7 @@ module SanitizeEmail
|
|
32
33
|
alias_method old_name, name
|
33
34
|
# And replace it with a wrapped version
|
34
35
|
define_method(name) do |*args, &block|
|
35
|
-
|
36
|
+
deprecation(name, " (please use ##{replacement})")
|
36
37
|
send old_name, *args, &block
|
37
38
|
end
|
38
39
|
end
|
@@ -1,13 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# Copyright (c) 2008-16 Peter H. Boling of RailsBling.com
|
2
4
|
# Released under the MIT license
|
3
5
|
|
4
6
|
module SanitizeEmail
|
5
7
|
# For Rails >= 3.1
|
6
8
|
class Engine < ::Rails::Engine
|
7
|
-
|
8
9
|
config.to_prepare do
|
9
10
|
ActionMailer::Base.register_interceptor(SanitizeEmail::Bleach)
|
10
11
|
end
|
11
|
-
|
12
12
|
end
|
13
13
|
end
|
@@ -1,27 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# Copyright (c) 2008-16 Peter H. Boling of RailsBling.com
|
2
4
|
# Released under the MIT license
|
3
5
|
|
4
6
|
module SanitizeEmail
|
5
7
|
# Tools for modifying the header of an email
|
6
8
|
module MailHeaderTools
|
7
|
-
|
8
9
|
def self.prepend_subject_array(message)
|
9
10
|
prepend = []
|
10
11
|
if SanitizeEmail.use_actual_email_prepended_to_subject
|
11
12
|
prepend << SanitizeEmail::MailHeaderTools.
|
12
|
-
|
13
|
+
prepend_email_to_subject(Array(message.to))
|
13
14
|
end
|
14
15
|
if SanitizeEmail.use_actual_environment_prepended_to_subject
|
15
16
|
prepend << SanitizeEmail::MailHeaderTools.
|
16
|
-
|
17
|
+
prepend_environment_to_subject
|
17
18
|
end
|
18
19
|
# this will force later joins to add an extra space
|
19
|
-
prepend <<
|
20
|
+
prepend << '' unless prepend.empty?
|
20
21
|
prepend
|
21
22
|
end
|
22
23
|
|
23
24
|
def self.custom_subject(message)
|
24
|
-
prepend_subject_array(message).join(
|
25
|
+
prepend_subject_array(message).join(' ')
|
25
26
|
end
|
26
27
|
|
27
28
|
def self.prepend_environment_to_subject
|
@@ -39,38 +40,36 @@ module SanitizeEmail
|
|
39
40
|
# See: http://goo.gl/v46GY
|
40
41
|
{
|
41
42
|
# can be an arrays, so casting it as arrays
|
42
|
-
|
43
|
-
|
43
|
+
'X-Sanitize-Email-To' => Array(message.to).uniq,
|
44
|
+
'X-Sanitize-Email-Cc' => Array(message.cc).uniq
|
44
45
|
# Don't write out the BCC, as those addresses should not be visible
|
45
46
|
# in message headers for obvious reasons
|
46
|
-
}.each
|
47
|
+
}.each do |header_key, header_value|
|
47
48
|
# For each type of address line
|
48
49
|
SanitizeEmail::MailHeaderTools.update_header(
|
49
50
|
header_key,
|
50
51
|
header_value,
|
51
52
|
message
|
52
53
|
)
|
53
|
-
|
54
|
+
end
|
54
55
|
end
|
55
56
|
|
56
57
|
def self.prepend_custom_subject(message)
|
57
|
-
message.subject =
|
58
|
+
message.subject = '' unless message.subject
|
58
59
|
custom_subject = SanitizeEmail::MailHeaderTools.custom_subject(message)
|
59
|
-
+
|
60
|
+
+message.subject.prepend(custom_subject)
|
60
61
|
end
|
61
62
|
|
62
63
|
# According to https://github.com/mikel/mail
|
63
64
|
# this is the correct way to update headers.
|
64
65
|
def self.update_header(header_key, header_value, message)
|
66
|
+
return nil unless header_value
|
65
67
|
# For each address, as header_value can be an array of addresses
|
66
|
-
Array(header_value).each_with_index
|
68
|
+
Array(header_value).each_with_index do |elem, index|
|
67
69
|
num = index + 1
|
68
|
-
new_header_key = num > 1 ?
|
69
|
-
"#{header_key}-#{num}" :
|
70
|
-
header_key
|
70
|
+
new_header_key = num > 1 ? "#{header_key}-#{num}" : header_key
|
71
71
|
message.header[new_header_key] = elem.to_s
|
72
|
-
|
72
|
+
end
|
73
73
|
end
|
74
|
-
|
75
74
|
end
|
76
75
|
end
|
@@ -1,10 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# Copyright (c) 2008-16 Peter H. Boling of RailsBling.com
|
2
4
|
# Released under the MIT license
|
3
5
|
|
4
6
|
module SanitizeEmail
|
5
7
|
# Tools for overriding addresses
|
6
8
|
class OverriddenAddresses
|
7
|
-
|
8
9
|
# Raised when after applying all sanitization rules there are no addresses to send the email to.
|
9
10
|
class MissingTo < StandardError; end
|
10
11
|
|
@@ -13,126 +14,125 @@ module SanitizeEmail
|
|
13
14
|
# recognized recipient types are: TO, CC, and BCC
|
14
15
|
class UnknownOverride < StandardError; end
|
15
16
|
|
16
|
-
REPLACE_AT = [/@/,
|
17
|
-
REPLACE_ALLIGATOR = [/[<>]/,
|
17
|
+
REPLACE_AT = [/@/, ' at '].freeze
|
18
|
+
REPLACE_ALLIGATOR = [/[<>]/, '~'].freeze
|
18
19
|
attr_accessor :overridden_to, :overridden_cc, :overridden_bcc,
|
19
20
|
:good_list, # White-listed addresses will not be molested as to, cc, or bcc
|
20
21
|
:bad_list, # Black-listed addresses will be removed from to, cc and bcc when sanitization is engaged
|
21
22
|
:sanitized_to, :sanitized_cc, :sanitized_bcc # Replace non-white-listed addresses with these sanitized addresses.
|
22
23
|
|
23
|
-
def initialize(message, args
|
24
|
+
def initialize(message, **args)
|
24
25
|
# Not using extract_options! because non-rails compatibility is a goal
|
25
|
-
|
26
|
-
@
|
27
|
-
@
|
28
|
-
@
|
29
|
-
@
|
30
|
-
@
|
31
|
-
@
|
32
|
-
@
|
26
|
+
args = SanitizeEmail::Config.to_init.merge(args)
|
27
|
+
@sanitized_to = args[:sanitized_to]
|
28
|
+
@sanitized_cc = args[:sanitized_cc]
|
29
|
+
@sanitized_bcc = args[:sanitized_bcc]
|
30
|
+
@good_list = args[:good_list] || []
|
31
|
+
@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)
|
33
35
|
end
|
34
36
|
|
35
37
|
# Allow good listed email addresses, and then remove the bad listed addresses
|
36
38
|
def good_listize(real_addresses)
|
37
|
-
good_listed =
|
38
|
-
good_listed =
|
39
|
+
good_listed = clean_addresses(real_addresses, :good_list)
|
40
|
+
good_listed = clean_addresses(good_listed, :bad_list) unless good_listed.empty?
|
39
41
|
good_listed
|
40
42
|
end
|
41
43
|
|
42
44
|
def to_override(actual_addresses)
|
43
45
|
to = override_email(:to, actual_addresses)
|
44
46
|
raise MissingTo, "after overriding :to (#{actual_addresses}) there are no addresses to send in To: header." if to.empty?
|
45
|
-
to.join(
|
47
|
+
to.join(',')
|
46
48
|
end
|
47
49
|
|
48
50
|
def cc_override(actual_addresses)
|
49
|
-
override_email(:cc, actual_addresses).join(
|
51
|
+
override_email(:cc, actual_addresses).join(',')
|
50
52
|
end
|
51
53
|
|
52
54
|
def bcc_override(actual_addresses)
|
53
|
-
override_email(:bcc, actual_addresses).join(
|
55
|
+
override_email(:bcc, actual_addresses).join(',')
|
54
56
|
end
|
55
57
|
|
56
58
|
def override_email(type, actual_addresses)
|
57
59
|
# Normalized to an arrays
|
58
|
-
#puts "override_email 1: #{type} - #{actual_addresses}"
|
60
|
+
# puts "override_email 1: #{type} - #{actual_addresses}"
|
59
61
|
real_addresses = Array(actual_addresses)
|
60
62
|
|
61
|
-
#puts "override_email 2: #{type} - #{real_addresses}"
|
63
|
+
# puts "override_email 2: #{type} - #{real_addresses}"
|
62
64
|
# If there were no original recipients, then we DO NOT override the nil with the sanitized recipients
|
63
65
|
return [] if real_addresses.empty?
|
64
66
|
|
65
67
|
good_listed = good_listize(real_addresses)
|
66
|
-
#puts "override_email 3: #{type} - #{good_listed}"
|
68
|
+
# puts "override_email 3: #{type} - #{good_listed}"
|
67
69
|
# If there are good_list addresses to send to then use them as is, no mods needed
|
68
70
|
return good_listed unless good_listed.empty?
|
69
71
|
|
70
72
|
# TODO: Allow overriding if an addressed email is on the good list?
|
71
73
|
# If there are no sanitized addresses we can't override!
|
72
74
|
sanitized_addresses = sanitize_addresses(type)
|
73
|
-
#puts "override_email 4: #{type} - #{sanitized_addresses}"
|
75
|
+
# puts "override_email 4: #{type} - #{sanitized_addresses}"
|
74
76
|
return [] if sanitized_addresses.empty?
|
75
77
|
|
76
78
|
# At this point it is assured that the address list will need to be sanitized
|
77
79
|
# One more check to ensure none of the configured sanitized email addresses are on the bad_list
|
78
|
-
sanitized_addresses =
|
79
|
-
#puts "override_email 5: #{type} - #{sanitized_addresses}"
|
80
|
+
sanitized_addresses = clean_addresses(sanitized_addresses, :bad_list)
|
81
|
+
# puts "override_email 5: #{type} - #{sanitized_addresses}"
|
80
82
|
|
81
83
|
# If we don't want to inject the "email" in the "user name" section of the sanitized recipients,
|
82
84
|
# then just return the default sanitized recipients
|
83
85
|
return sanitized_addresses unless SanitizeEmail.use_actual_email_as_sanitized_user_name
|
84
86
|
|
85
|
-
with_user_names =
|
86
|
-
#puts "real_addresses 2: #{real_addresses}"
|
87
|
-
#puts "override_email 6: #{type} - #{with_user_names}"
|
87
|
+
with_user_names = inject_user_names(real_addresses, sanitized_addresses)
|
88
|
+
# puts "real_addresses 2: #{real_addresses}"
|
89
|
+
# puts "override_email 6: #{type} - #{with_user_names}"
|
88
90
|
# Otherwise inject the email as the "user name"
|
89
|
-
|
91
|
+
with_user_names
|
90
92
|
end
|
91
93
|
|
92
94
|
def address_list_filter(list_type, address)
|
93
95
|
# TODO: How does this handle email addresses with user names like "Foo Example <foo@example.org>"
|
94
|
-
has_address =
|
96
|
+
has_address = send(list_type).include?(address)
|
95
97
|
case list_type
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
98
|
+
when :good_list then
|
99
|
+
has_address ? address : nil
|
100
|
+
when :bad_list then
|
101
|
+
has_address ? nil : address
|
100
102
|
end
|
101
103
|
end
|
102
104
|
|
103
105
|
def inject_user_names(real_addresses, sanitized_addresses)
|
104
|
-
real_addresses.
|
105
|
-
if real_recipient.nil?
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
106
|
+
real_addresses.each_with_object([]) do |real_recipient, result|
|
107
|
+
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
|
111
113
|
result << new_recipient
|
112
|
-
result
|
113
114
|
end.flatten
|
114
115
|
end
|
115
116
|
|
116
117
|
def clean_addresses(addresses, list_type)
|
117
118
|
# Normalize addresses just in case it isn't an array yet
|
118
|
-
addresses.map
|
119
|
+
addresses.map do |address|
|
119
120
|
# If this address is on the good list then let it pass
|
120
|
-
|
121
|
-
|
121
|
+
address_list_filter(list_type, address)
|
122
|
+
end.compact.uniq
|
122
123
|
end
|
123
124
|
|
124
125
|
def sanitize_addresses(type)
|
125
126
|
case type
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
127
|
+
when :to then
|
128
|
+
Array(sanitized_to)
|
129
|
+
when :cc then
|
130
|
+
Array(sanitized_cc)
|
131
|
+
when :bcc then
|
132
|
+
Array(sanitized_bcc)
|
133
|
+
else
|
134
|
+
raise UnknownOverride, 'unknown email override'
|
134
135
|
end
|
135
136
|
end
|
136
|
-
|
137
137
|
end
|
138
138
|
end
|