sanitize_email 2.0.0 → 2.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|