sanitize_email 1.0.0.alpha2 → 1.0.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +14 -2
- data/VERSION.yml +1 -1
- data/lib/sanitize_email.rb +20 -6
- data/lib/sanitize_email/bleach.rb +156 -0
- data/lib/sanitize_email/config.rb +17 -2
- data/lib/sanitize_email/deprecation.rb +36 -0
- data/lib/sanitize_email/engine.rb +1 -1
- data/lib/sanitize_email/railtie.rb +1 -1
- data/lib/sanitize_email/version.rb +1 -1
- data/sanitize_email.gemspec +5 -6
- data/spec/sanitize_email_spec.rb +112 -44
- data/spec/spec_helper.rb +37 -6
- metadata +5 -6
- data/lib/sanitize_email/hook.rb +0 -30
- data/lib/sanitize_email/sanitizer.rb +0 -73
- data/spec/tmp/mail_dump/1343461037_3f3edd7/plain.html +0 -76
data/CHANGELOG
CHANGED
@@ -1,6 +1,18 @@
|
|
1
|
-
Version 1.0.0.
|
2
|
-
-
|
1
|
+
Version 1.0.0.rc1
|
2
|
+
- Added a good_list and a bad_list (whitelist and blacklist)
|
3
|
+
- Added Deprecation library
|
4
|
+
- Refactored Sanitization module into Hook class
|
5
|
+
- Renamed Hook Class to Bleach Class
|
6
|
+
- Improve support for non-rails implementations
|
7
|
+
- Deprecated local_environments in favor of local_environment_proc
|
8
|
+
- Deprecated sanitized_recipients in favor of sanitized_to
|
9
|
+
- More specs
|
10
|
+
|
11
|
+
Version 1.0.0.alpha2
|
12
|
+
- Complete refactor! Implementing initial support for Rails >= 3.0 (new ActionMailer API)
|
3
13
|
- Support for Rails <= 2.X remains in version 0.X.X releases.
|
14
|
+
- NinthBit namespace is now SanitizeEmail namespace
|
15
|
+
- Now has a first class Config class
|
4
16
|
|
5
17
|
XXXXXXXXXXXXXXXXXXXXXXX Rail 3.0+ Only Form here on up! XXXXXXXXXXXXXXXXXXXXXXX
|
6
18
|
|
data/VERSION.yml
CHANGED
data/lib/sanitize_email.rb
CHANGED
@@ -4,18 +4,19 @@ require 'rails'
|
|
4
4
|
require 'action_mailer'
|
5
5
|
|
6
6
|
module SanitizeEmail
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
require 'sanitize_email/hook'
|
7
|
+
require 'sanitize_email/version'
|
8
|
+
require 'sanitize_email/config'
|
9
|
+
require 'sanitize_email/bleach'
|
10
|
+
require 'sanitize_email/deprecation'
|
12
11
|
|
12
|
+
# Allow non-rails implementations to use this gem
|
13
|
+
if @rails = defined?(Rails) && ::Rails::VERSION::MAJOR >= 3
|
13
14
|
if ::Rails::VERSION::MINOR >= 1
|
14
15
|
require 'sanitize_email/engine'
|
15
16
|
elsif ::Rails::VERSION::MINOR == 0
|
16
17
|
require 'sanitize_email/railtie'
|
17
18
|
end
|
18
|
-
|
19
|
+
elsif @rails = defined?(Rails)
|
19
20
|
raise "Please use the 0.X.X versions of sanitize_email for Rails 2.X and below."
|
20
21
|
end
|
21
22
|
|
@@ -28,4 +29,17 @@ module SanitizeEmail
|
|
28
29
|
SanitizeEmail[name]
|
29
30
|
end
|
30
31
|
|
32
|
+
def self.sanitized_recipients
|
33
|
+
SanitizeEmail[:sanitized_recipients]
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.local_environments
|
37
|
+
SanitizeEmail[:local_environments]
|
38
|
+
end
|
39
|
+
|
40
|
+
class << self
|
41
|
+
extend SanitizeEmail::Deprecation
|
42
|
+
deprecated_alias :sanitized_recipients, :sanitized_to
|
43
|
+
deprecated :local_environments, :local_environment_proc
|
44
|
+
end
|
31
45
|
end
|
@@ -0,0 +1,156 @@
|
|
1
|
+
#Copyright (c) 2008-12 Peter H. Boling of 9thBit LLC
|
2
|
+
#Released under the MIT license
|
3
|
+
|
4
|
+
module SanitizeEmail
|
5
|
+
class Bleach
|
6
|
+
|
7
|
+
class MissingTo < StandardError; end
|
8
|
+
class UnknownOverride < StandardError; end
|
9
|
+
|
10
|
+
# Can override global configs at the instance level.
|
11
|
+
attr_accessor :engage, # Turn sanitization on or off just for this instance
|
12
|
+
:sanitized_to, :sanitized_cc, :sanitized_bcc, # Replace non-white-listed addresses with these sanitized addresses.
|
13
|
+
:good_list, # White-listed addresses will not be molested as to, cc, or bcc
|
14
|
+
:bad_list, # Black-listed addresses will be removed from to, cc and bcc when sanitization is engaged
|
15
|
+
:injected # Track whether or not the subject has been injected with usernames
|
16
|
+
|
17
|
+
def initialize(args = {})
|
18
|
+
# Not using extract_options! because no-rails compatibility is a goal
|
19
|
+
@sanitized_to = args[:sanitized_to] || SanitizeEmail[:sanitized_to]
|
20
|
+
@sanitized_cc = args[:sanitized_cc] || SanitizeEmail[:sanitized_cc]
|
21
|
+
@sanitized_bcc = args[:sanitized_bcc] || SanitizeEmail[:sanitized_bcc]
|
22
|
+
@good_list = args[:good_list] || SanitizeEmail[:good_list] || []
|
23
|
+
@bad_list = args[:bad_list] || SanitizeEmail[:bad_list] || []
|
24
|
+
@engage = args[:engage] || SanitizeEmail[:engage]
|
25
|
+
@injected = false
|
26
|
+
end
|
27
|
+
|
28
|
+
# If all recipient addresses are white-listed the field is left alone.
|
29
|
+
def delivering_email(message)
|
30
|
+
if self.sanitize_engaged?
|
31
|
+
message.subject = self.subject_override(message.subject, message.to) if SanitizeEmail.use_actual_email_prepended_to_subject
|
32
|
+
message.to = self.to_override(message.to)
|
33
|
+
message.cc = self.cc_override(message.cc)
|
34
|
+
message.bcc = self.bcc_override(message.bcc)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Only relevant
|
39
|
+
def consider_local?
|
40
|
+
SanitizeEmail.local_environment_proc.call if SanitizeEmail.local_environment_proc.respond_to?(:call)
|
41
|
+
end
|
42
|
+
|
43
|
+
# This method will be called by the Hook to determine if an override should occur
|
44
|
+
def sanitize_engaged?
|
45
|
+
!SanitizeEmail.force_sanitize.nil? ? SanitizeEmail.force_sanitize : self.consider_local?
|
46
|
+
end
|
47
|
+
|
48
|
+
def subject_override(real_subject, actual_addresses)
|
49
|
+
if !actual_addresses.respond_to?(:join)
|
50
|
+
real_subject
|
51
|
+
else
|
52
|
+
"(#{actual_addresses.join(',').gsub(/@/,' at ').gsub(/[<>]/,'~')}) #{real_subject}"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def to_override(actual_addresses)
|
57
|
+
to = override_email(:to, actual_addresses)
|
58
|
+
raise MissingTo, 'after overriding :to there are no addresses to send in To: header.' if to.empty?
|
59
|
+
to.join(',')
|
60
|
+
end
|
61
|
+
|
62
|
+
def cc_override(actual_addresses)
|
63
|
+
override_email(:cc, actual_addresses).join(',')
|
64
|
+
end
|
65
|
+
|
66
|
+
def bcc_override(actual_addresses)
|
67
|
+
override_email(:bcc, actual_addresses).join(',')
|
68
|
+
end
|
69
|
+
|
70
|
+
#######
|
71
|
+
protected
|
72
|
+
#######
|
73
|
+
|
74
|
+
def address_list_filter(list_type, address)
|
75
|
+
# TODO: How does this handle email addresses with user names like "Foo Example <foo@example.org>"
|
76
|
+
has_address = self.send(list_type).include?(address)
|
77
|
+
case list_type
|
78
|
+
when :good_list then has_address ? address : nil
|
79
|
+
when :bad_list then has_address ? nil : address
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def inject_user_names(real_addresses, sanitized_addresses)
|
84
|
+
real_addresses.inject([]) do |result, real_recipient|
|
85
|
+
if real_recipient.nil?
|
86
|
+
new_recipient = sanitized_addresses
|
87
|
+
else
|
88
|
+
new_recipient = sanitized_addresses.map{|sanitized| "#{real_recipient.gsub(/@/,' at ').gsub(/[<>]/,'~')} <#{sanitized}>"}
|
89
|
+
end
|
90
|
+
result << new_recipient
|
91
|
+
result
|
92
|
+
end.flatten
|
93
|
+
end
|
94
|
+
|
95
|
+
def clean_addresses(addresses, list_type)
|
96
|
+
# Normalize addresses just in case it isn't an array yet
|
97
|
+
addresses.map { |address|
|
98
|
+
# If this address is on the good list then let it pass
|
99
|
+
self.address_list_filter(list_type, address)
|
100
|
+
}.compact
|
101
|
+
end
|
102
|
+
|
103
|
+
def sanitize_addresses(type)
|
104
|
+
case type
|
105
|
+
when :to then Array(self.sanitized_to)
|
106
|
+
when :cc then Array(self.sanitized_cc)
|
107
|
+
when :bcc then Array(self.sanitized_bcc)
|
108
|
+
else raise UnknownOverride, "unknown email override"
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
# Allow good listed email addresses, and then remove the bad listed addresses
|
113
|
+
def good_listize(real_addresses)
|
114
|
+
good_listed = self.clean_addresses(real_addresses, :good_list)
|
115
|
+
good_listed = self.clean_addresses(good_listed, :bad_list) unless good_listed.empty?
|
116
|
+
good_listed
|
117
|
+
end
|
118
|
+
|
119
|
+
def override_email(type, actual_addresses)
|
120
|
+
# Normalized to an arrays
|
121
|
+
#puts "override_email 1: #{type} - #{actual_addresses}"
|
122
|
+
real_addresses = Array(actual_addresses)
|
123
|
+
|
124
|
+
#puts "override_email 2: #{type} - #{real_addresses}"
|
125
|
+
# If there were no original recipients, then we DO NOT override the nil with the sanitized recipients
|
126
|
+
return [] if real_addresses.empty?
|
127
|
+
|
128
|
+
good_listed = good_listize(real_addresses)
|
129
|
+
#puts "override_email 3: #{type} - #{good_listed}"
|
130
|
+
# If there are good_list addresses to send to then use them as is, no mods needed
|
131
|
+
return good_listed unless good_listed.empty?
|
132
|
+
|
133
|
+
# If there are no sanitized addresses we can't override!
|
134
|
+
sanitized_addresses = sanitize_addresses(type)
|
135
|
+
#puts "override_email 3: #{type} - #{sanitized_addresses}"
|
136
|
+
return [] if sanitized_addresses.empty?
|
137
|
+
|
138
|
+
# At this point it is assured that the address list will need to be sanitized
|
139
|
+
# One more check to ensure none of the configured sanitized email addresses are on the bad_list
|
140
|
+
sanitized_addresses = self.clean_addresses(sanitized_addresses, :bad_list)
|
141
|
+
#puts "override_email 4: #{type} - #{sanitized_addresses}"
|
142
|
+
|
143
|
+
# If we don't want to inject the 'email' in the 'user name' section of the sanitized recipients,
|
144
|
+
# then just return the default sanitized recipients
|
145
|
+
return sanitized_addresses unless SanitizeEmail.use_actual_email_as_sanitized_user_name
|
146
|
+
|
147
|
+
with_user_names = self.inject_user_names(real_addresses, sanitized_addresses)
|
148
|
+
#puts "override_email 5: #{type} - #{with_user_names}"
|
149
|
+
# Otherwise inject the email as the 'user name'
|
150
|
+
return with_user_names
|
151
|
+
end
|
152
|
+
|
153
|
+
end # end Class Hook
|
154
|
+
end # end Module SanitizeEmail
|
155
|
+
|
156
|
+
|
@@ -13,7 +13,13 @@ module SanitizeEmail
|
|
13
13
|
|
14
14
|
# The recipient addresses for the messages, either as a string (for a single
|
15
15
|
# address) or an array (for multiple addresses) that go out in 'local' environments
|
16
|
-
:
|
16
|
+
:sanitized_to => nil,
|
17
|
+
|
18
|
+
# a white list
|
19
|
+
:good_list => nil,
|
20
|
+
|
21
|
+
# a black list
|
22
|
+
:bad_list => nil,
|
17
23
|
|
18
24
|
# Use the 'real' email address as the username for the sanitized email address
|
19
25
|
# e.g. "real@example.com <sanitized@example.com>"
|
@@ -23,11 +29,20 @@ module SanitizeEmail
|
|
23
29
|
# e.g. "real@example.com rest of subject"
|
24
30
|
:use_actual_email_prepended_to_subject => false,
|
25
31
|
|
26
|
-
:
|
32
|
+
:local_environment_proc => Proc.new { true }
|
27
33
|
}
|
28
34
|
|
29
35
|
def self.configure &block
|
30
36
|
yield self.config
|
37
|
+
|
38
|
+
# Gracefully handle deprecated config values.
|
39
|
+
# Actual deprecation warnings are thrown in the top SanitizeEmail module thanks to our use of dynamic methods.
|
40
|
+
if config[:local_environments] && defined?(Rails)
|
41
|
+
config[:local_environment_proc] = Proc.new { SanitizeEmail.local_environments.include?(Rails.env) }
|
42
|
+
end
|
43
|
+
if config[:sanitize_recipients]
|
44
|
+
config[:sanitize_to] = SanitizeEmail.sanitized_recipients
|
45
|
+
end
|
31
46
|
end
|
32
47
|
|
33
48
|
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# See http://www.seejohncode.com/2012/01/09/deprecating-methods-in-ruby/
|
2
|
+
|
3
|
+
module SanitizeEmail
|
4
|
+
module Deprecation
|
5
|
+
|
6
|
+
# Define a deprecated alias for a method
|
7
|
+
# @param [Symbol] name - name of method to define
|
8
|
+
# @param [Symbol] replacement - name of method to (alias)
|
9
|
+
def deprecated_alias(name, replacement)
|
10
|
+
# Create a wrapped version
|
11
|
+
define_method(name) do |*args, &block|
|
12
|
+
warn "SanitizeEmail: ##{name} deprecated (please use ##{replacement})"
|
13
|
+
send replacement, *args, &block
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# Deprecate a defined method
|
18
|
+
# @param [Symbol] name - name of deprecated method
|
19
|
+
# @param [Symbol] replacement - name of the desired replacement
|
20
|
+
def deprecated(name, replacement = nil)
|
21
|
+
# Replace old method
|
22
|
+
old_name = :"#{name}_without_deprecation"
|
23
|
+
alias_method old_name, name
|
24
|
+
# And replace it with a wrapped version
|
25
|
+
define_method(name) do |*args, &block|
|
26
|
+
if replacement
|
27
|
+
warn "SanitizeEmail: ##{name} deprecated (please use ##{replacement})"
|
28
|
+
else
|
29
|
+
warn "SanitizeEmail: ##{name} deprecated"
|
30
|
+
end
|
31
|
+
send old_name, *args, &block
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
data/sanitize_email.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = "sanitize_email"
|
8
|
-
s.version = "1.0.0.
|
8
|
+
s.version = "1.0.0.rc1"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new("> 1.3.1") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Peter Boling", "John Trupiano", "George Anderson"]
|
12
|
-
s.date = "2012-
|
12
|
+
s.date = "2012-08-04"
|
13
13
|
s.description = "Tool to aid in development, testing, qa, and production troubleshooting of email issues without worrying that emails will get sent to actual live addresses."
|
14
14
|
s.email = ["peter.boling@gmail.com", "jtrupiano@gmail.com", "george@benevolentcode.com"]
|
15
15
|
s.extra_rdoc_files = [
|
@@ -26,16 +26,15 @@ Gem::Specification.new do |s|
|
|
26
26
|
"VERSION.yml",
|
27
27
|
"init.rb",
|
28
28
|
"lib/sanitize_email.rb",
|
29
|
+
"lib/sanitize_email/bleach.rb",
|
29
30
|
"lib/sanitize_email/config.rb",
|
31
|
+
"lib/sanitize_email/deprecation.rb",
|
30
32
|
"lib/sanitize_email/engine.rb",
|
31
|
-
"lib/sanitize_email/hook.rb",
|
32
33
|
"lib/sanitize_email/railtie.rb",
|
33
|
-
"lib/sanitize_email/sanitizer.rb",
|
34
34
|
"lib/sanitize_email/version.rb",
|
35
35
|
"sanitize_email.gemspec",
|
36
36
|
"spec/sanitize_email_spec.rb",
|
37
|
-
"spec/spec_helper.rb"
|
38
|
-
"spec/tmp/mail_dump/1343461037_3f3edd7/plain.html"
|
37
|
+
"spec/spec_helper.rb"
|
39
38
|
]
|
40
39
|
s.homepage = "http://github.com/pboling/sanitize_email"
|
41
40
|
s.licenses = ["MIT"]
|
data/spec/sanitize_email_spec.rb
CHANGED
@@ -1,68 +1,136 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
+
#
|
4
|
+
# TODO: Letter Opener should *not* be required, but setting the delivery method to :file was causing connection errors... WTF?
|
5
|
+
#
|
6
|
+
# Letter Opener won't let us test the to when the to has a 'user name'
|
7
|
+
# @email_file.should have_to("~to at example.org~ <to@sanitize_email.org>")
|
8
|
+
# TODO: Also not sure how to test the user name part of the To: header via the mail object
|
9
|
+
#
|
10
|
+
# Letter Opener won't let us test the bcc
|
11
|
+
# @email_file.should have_cc("cc@sanitize_email.org")
|
12
|
+
# Fortunately we can still test the mail object returned by the deliver call
|
13
|
+
#
|
14
|
+
|
3
15
|
describe SanitizeEmail do
|
4
16
|
|
5
|
-
|
6
|
-
config
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
17
|
+
after(:each) do
|
18
|
+
SanitizeEmail::Config.config = {}
|
19
|
+
end
|
20
|
+
|
21
|
+
def sanitize_spec_dryer(rails_env = 'test')
|
22
|
+
Launchy.stub(:open)
|
23
|
+
location = File.expand_path('../tmp/mail_dump', __FILE__)
|
24
|
+
FileUtils.rm_rf(location)
|
25
|
+
Mail.defaults do
|
26
|
+
delivery_method LetterOpener::DeliveryMethod, :location => location
|
27
|
+
end
|
28
|
+
Rails.stub(:env).and_return(rails_env)
|
29
|
+
@location = location
|
30
|
+
end
|
31
|
+
|
32
|
+
def configure_sanitize_email(sanitize_hash = {})
|
33
|
+
sanitize_hash.reverse_merge!({
|
34
|
+
:sanitized_to => 'to@sanitize_email.org',
|
35
|
+
:sanitized_cc => 'cc@sanitize_email.org',
|
36
|
+
:sanitized_bcc => 'bcc@sanitize_email.org',
|
37
|
+
:use_actual_email_prepended_to_subject => true,
|
38
|
+
:use_actual_email_as_sanitized_user_name => true
|
39
|
+
})
|
40
|
+
SanitizeEmail::Config.configure do |config|
|
41
|
+
config[:sanitized_to] = sanitize_hash[:sanitized_to]
|
42
|
+
config[:sanitized_cc] = sanitize_hash[:sanitized_cc]
|
43
|
+
config[:sanitized_bcc] = sanitize_hash[:sanitized_bcc]
|
44
|
+
config[:use_actual_email_prepended_to_subject] = sanitize_hash[:use_actual_email_prepended_to_subject]
|
45
|
+
config[:use_actual_email_as_sanitized_user_name] = sanitize_hash[:use_actual_email_as_sanitized_user_name]
|
46
|
+
# For testing deprecated configuration options:
|
47
|
+
config[:local_environments] = sanitize_hash[:local_environments] if sanitize_hash[:local_environments]
|
48
|
+
config[:sanitized_to] ||= sanitize_hash[:sanitized_recipients] if sanitize_hash[:sanitized_recipients]
|
49
|
+
end
|
50
|
+
Mail.register_interceptor(SanitizeEmail::Bleach.new)
|
12
51
|
end
|
13
52
|
|
14
|
-
def
|
15
|
-
# Ensure that localish? will
|
53
|
+
def sanitized_mail_delivery(sanitization_switch = false)
|
54
|
+
# Ensure that localish? will return sanitization_switch if true or false, and use proc when nil
|
16
55
|
SanitizeEmail::Config.config[:force_sanitize] = sanitization_switch
|
17
56
|
Launchy.should_receive(:open)
|
18
|
-
|
57
|
+
@mail_message = Mail.deliver do
|
19
58
|
from 'from@example.org'
|
20
59
|
to 'to@example.org'
|
21
60
|
cc 'cc@example.org'
|
61
|
+
bcc 'bcc@example.org'
|
22
62
|
reply_to 'reply_to@example.org'
|
23
63
|
subject 'original subject'
|
24
64
|
end
|
65
|
+
# All the email gets dumped to file once for each type of recipient (:to, :cc, :bcc)
|
66
|
+
# Each file is identical, so we only need to check one of them:
|
67
|
+
@email_file = File.read(Dir["#{@location}/*/plain.html"].first)
|
25
68
|
end
|
26
69
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
Mail.defaults do
|
32
|
-
delivery_method LetterOpener::DeliveryMethod, :location => location
|
70
|
+
context "localish?" do
|
71
|
+
before(:each) do
|
72
|
+
sanitize_spec_dryer
|
73
|
+
configure_sanitize_email
|
33
74
|
end
|
34
|
-
Mail.register_interceptor(SanitizeEmail::Hook)
|
35
|
-
@location = location
|
36
|
-
end
|
37
75
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
76
|
+
context "false" do
|
77
|
+
it "alters nothing" do
|
78
|
+
sanitized_mail_delivery(false)
|
79
|
+
@email_file.should have_from("from@example.org")
|
80
|
+
@email_file.should have_to("to@example.org")
|
81
|
+
@mail_message.header.fields[3].value.should_not have_to("to at example.org")
|
82
|
+
@mail_message.should have_cc("cc@example.org")
|
83
|
+
@mail_message.should have_bcc("bcc@example.org")
|
84
|
+
@email_file.should have_subject("original subject")
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
context "true" do
|
89
|
+
it "should override" do
|
90
|
+
sanitized_mail_delivery(true)
|
91
|
+
@email_file.should have_from("from@example.org")
|
92
|
+
#puts "@mail_message.header.fields[3]: #{@mail_message.header.fields[3]}"
|
93
|
+
#@mail_message.header.fields[3].value.should have_to("to at example.org")
|
94
|
+
@mail_message.should have_cc("cc@sanitize_email.org")
|
95
|
+
@mail_message.should have_bcc("bcc@sanitize_email.org")
|
96
|
+
@email_file.should have_subject("(to at example.org) original subject")
|
97
|
+
end
|
51
98
|
end
|
52
99
|
end
|
53
100
|
|
54
|
-
context "
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
101
|
+
context "deprecated config options" do
|
102
|
+
context "local_environments" do
|
103
|
+
it "should use local_environment_proc for matching environment" do
|
104
|
+
sanitize_spec_dryer('test')
|
105
|
+
configure_sanitize_email({:local_environments => ['test']})
|
106
|
+
SanitizeEmail[:local_environment_proc].call.should == true
|
107
|
+
sanitized_mail_delivery(nil)
|
108
|
+
@email_file.should have_to("to@sanitize_email.org")
|
109
|
+
@email_file.should have_subject("(to at example.org) original subject")
|
110
|
+
end
|
111
|
+
it "should use local_environment_proc for non-matching environment" do
|
112
|
+
sanitize_spec_dryer('production')
|
113
|
+
configure_sanitize_email({:local_environments => ['development']}) # Won't match!
|
114
|
+
SanitizeEmail[:local_environment_proc].call.should == false
|
115
|
+
sanitized_mail_delivery(nil)
|
116
|
+
@mail_message.should_not have_subject("to at example.org")
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
context "sanitized_recipients is set" do
|
121
|
+
before(:each) do
|
122
|
+
sanitize_spec_dryer
|
123
|
+
configure_sanitize_email({:sanitized_recipients => 'barney@sanitize_email.org'})
|
124
|
+
end
|
125
|
+
it "used as sanitized_to" do
|
126
|
+
sanitized_mail_delivery(true)
|
127
|
+
@email_file.should have_from("from@example.org")
|
128
|
+
@mail_message.should have_to("to@sanitize_email.org")
|
129
|
+
@email_file.should have_subject("(to at example.org) original subject")
|
130
|
+
end
|
66
131
|
end
|
67
132
|
end
|
133
|
+
|
68
134
|
end
|
135
|
+
|
136
|
+
#TODO: test good_list
|
data/spec/spec_helper.rb
CHANGED
@@ -11,30 +11,61 @@ RSpec.configure do |config|
|
|
11
11
|
#config.filter_run :focus
|
12
12
|
end
|
13
13
|
|
14
|
+
module EmailMatcherHelpers
|
15
|
+
class UnexpectedMailType < StandardError; end
|
16
|
+
|
17
|
+
# Sweet, nourishing recursion
|
18
|
+
def email_matching(matcher, part, mail_or_part)
|
19
|
+
if mail_or_part.respond_to?(part)
|
20
|
+
email_matching(matcher, part, mail_or_part.send(part))
|
21
|
+
elsif mail_or_part.respond_to?(:join)
|
22
|
+
email_matching(matcher, part, mail_or_part.join(', '))
|
23
|
+
elsif mail_or_part.respond_to?(:=~) # Can we match a regex against it?
|
24
|
+
mail_or_part =~ Regexp.new(Regexp.escape(matcher))
|
25
|
+
else
|
26
|
+
raise UnexpectedMailType, "Cannot match #{matcher} for #{part}"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
14
32
|
RSpec::Matchers.define :have_from do |from|
|
33
|
+
include EmailMatcherHelpers
|
15
34
|
match do |container|
|
16
|
-
|
35
|
+
email_matching(from, :from, container)
|
17
36
|
end
|
18
37
|
end
|
19
38
|
RSpec::Matchers.define :have_to do |to|
|
39
|
+
include EmailMatcherHelpers
|
20
40
|
match do |container|
|
21
|
-
|
41
|
+
email_matching(to, :to, container)
|
22
42
|
end
|
23
43
|
end
|
24
44
|
RSpec::Matchers.define :have_cc do |cc|
|
45
|
+
include EmailMatcherHelpers
|
25
46
|
match do |container|
|
26
|
-
|
47
|
+
email_matching(cc, :cc, container)
|
27
48
|
end
|
28
49
|
end
|
29
50
|
# The ActionMailer :file delivery method never prints bcc recipients...
|
30
|
-
#
|
51
|
+
# Neither does LetterOpener :(
|
52
|
+
# But the mail object itself can be tested!
|
31
53
|
RSpec::Matchers.define :have_bcc do |bcc|
|
54
|
+
include EmailMatcherHelpers
|
32
55
|
match do |container|
|
33
|
-
|
56
|
+
email_matching(bcc, :bcc, container)
|
34
57
|
end
|
35
58
|
end
|
36
59
|
RSpec::Matchers.define :have_subject do |subject|
|
60
|
+
include EmailMatcherHelpers
|
37
61
|
match do |container|
|
38
|
-
|
62
|
+
email_matching(subject, :subject, container)
|
39
63
|
end
|
40
64
|
end
|
65
|
+
|
66
|
+
#RSpec::Matchers.define :have_to_username do |to|
|
67
|
+
# include EmailMatcherHelpers
|
68
|
+
# match do |container|
|
69
|
+
# email_matching(to, :, container)
|
70
|
+
# end
|
71
|
+
#end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sanitize_email
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.0.
|
4
|
+
version: 1.0.0.rc1
|
5
5
|
prerelease: 6
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -11,7 +11,7 @@ authors:
|
|
11
11
|
autorequire:
|
12
12
|
bindir: bin
|
13
13
|
cert_chain: []
|
14
|
-
date: 2012-
|
14
|
+
date: 2012-08-04 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: rails
|
@@ -194,16 +194,15 @@ files:
|
|
194
194
|
- VERSION.yml
|
195
195
|
- init.rb
|
196
196
|
- lib/sanitize_email.rb
|
197
|
+
- lib/sanitize_email/bleach.rb
|
197
198
|
- lib/sanitize_email/config.rb
|
199
|
+
- lib/sanitize_email/deprecation.rb
|
198
200
|
- lib/sanitize_email/engine.rb
|
199
|
-
- lib/sanitize_email/hook.rb
|
200
201
|
- lib/sanitize_email/railtie.rb
|
201
|
-
- lib/sanitize_email/sanitizer.rb
|
202
202
|
- lib/sanitize_email/version.rb
|
203
203
|
- sanitize_email.gemspec
|
204
204
|
- spec/sanitize_email_spec.rb
|
205
205
|
- spec/spec_helper.rb
|
206
|
-
- spec/tmp/mail_dump/1343461037_3f3edd7/plain.html
|
207
206
|
homepage: http://github.com/pboling/sanitize_email
|
208
207
|
licenses:
|
209
208
|
- MIT
|
@@ -219,7 +218,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
219
218
|
version: '0'
|
220
219
|
segments:
|
221
220
|
- 0
|
222
|
-
hash: -
|
221
|
+
hash: -2863798588423167662
|
223
222
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
224
223
|
none: false
|
225
224
|
requirements:
|
data/lib/sanitize_email/hook.rb
DELETED
@@ -1,30 +0,0 @@
|
|
1
|
-
#Copyright (c) 2008-12 Peter H. Boling of 9thBit LLC
|
2
|
-
#Released under the MIT license
|
3
|
-
|
4
|
-
module SanitizeEmail
|
5
|
-
class Hook
|
6
|
-
|
7
|
-
include SanitizeEmail::Sanitizer
|
8
|
-
|
9
|
-
def self.delivering_email(message)
|
10
|
-
if self.localish?
|
11
|
-
message.subject = self.subject_override(message.subject, message.to)
|
12
|
-
message.to = self.recipients_override(message.to)
|
13
|
-
message.cc = self.cc_override(message.cc)
|
14
|
-
message.bcc = self.bcc_override(message.bcc)
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
def self.consider_local?
|
19
|
-
SanitizeEmail.local_environments.include?(Rails.env) if defined?(Rails)
|
20
|
-
end
|
21
|
-
|
22
|
-
# This method will be called by the Hook to determine if an override should occur
|
23
|
-
def self.localish?
|
24
|
-
!SanitizeEmail.force_sanitize.nil? ? SanitizeEmail.force_sanitize : self.consider_local?
|
25
|
-
end
|
26
|
-
|
27
|
-
end # end Class Hook
|
28
|
-
end # end Module SanitizeEmail
|
29
|
-
|
30
|
-
|
@@ -1,73 +0,0 @@
|
|
1
|
-
#Copyright (c) 2008-12 Peter H. Boling of 9thBit LLC
|
2
|
-
#Released under the MIT license
|
3
|
-
module SanitizeEmail
|
4
|
-
module Sanitizer
|
5
|
-
def self.included(base)
|
6
|
-
base.extend SanitizeEmail::Sanitizer::ClassMethods
|
7
|
-
end
|
8
|
-
|
9
|
-
module ClassMethods
|
10
|
-
|
11
|
-
def subject_override(real_subject, actual_addresses)
|
12
|
-
if actual_addresses.nil? || !SanitizeEmail.use_actual_email_prepended_to_subject
|
13
|
-
real_subject
|
14
|
-
else
|
15
|
-
"(#{actual_addresses.join(',').gsub(/@/,' at ').gsub(/[<>]/,'~')}) #{real_subject}"
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
def recipients_override(actual_addresses)
|
20
|
-
override_email(:recipients, actual_addresses)
|
21
|
-
end
|
22
|
-
|
23
|
-
def cc_override(actual_addresses)
|
24
|
-
override_email(:cc, actual_addresses)
|
25
|
-
end
|
26
|
-
|
27
|
-
def bcc_override(actual_addresses)
|
28
|
-
override_email(:bcc, actual_addresses)
|
29
|
-
end
|
30
|
-
|
31
|
-
#######
|
32
|
-
private
|
33
|
-
#######
|
34
|
-
|
35
|
-
def override_email(type, actual_addresses)
|
36
|
-
real_addresses, sanitized_addresses = case type
|
37
|
-
when :recipients
|
38
|
-
[actual_addresses, SanitizeEmail.sanitized_recipients]
|
39
|
-
when :cc
|
40
|
-
[actual_addresses, SanitizeEmail.sanitized_cc]
|
41
|
-
when :bcc
|
42
|
-
[actual_addresses, SanitizeEmail.sanitized_bcc]
|
43
|
-
else raise "sanitize_email error: unknown email override"
|
44
|
-
end
|
45
|
-
# Normalize to an array
|
46
|
-
real_addresses = [real_addresses] unless real_addresses.is_a?(Array)
|
47
|
-
# Normalize to an array
|
48
|
-
sanitized_addresses = [sanitized_addresses] unless sanitized_addresses.is_a?(Array)
|
49
|
-
|
50
|
-
# If there were no original recipients, then we DO NOT override the nil with the sanitized recipients
|
51
|
-
return nil if real_addresses.blank?
|
52
|
-
# If there are no sanitized addresses we can't override!
|
53
|
-
return nil if sanitized_addresses.blank?
|
54
|
-
# If we don't want to inject the actual email in the 'user name' section of the sanitized recipients,
|
55
|
-
# then just return the default sanitized recipients
|
56
|
-
return sanitized_addresses unless SanitizeEmail.use_actual_email_as_sanitized_user_name
|
57
|
-
|
58
|
-
out = real_addresses.inject([]) do |result, real_recipient|
|
59
|
-
if real_recipient.nil?
|
60
|
-
new_recipient = sanitized_addresses
|
61
|
-
else
|
62
|
-
new_recipient = sanitized_addresses.map{|sanitized| "#{real_recipient.gsub(/@/,' at ').gsub(/[<>]/,'~')} <#{sanitized}>"}
|
63
|
-
end
|
64
|
-
result << new_recipient
|
65
|
-
result
|
66
|
-
end.flatten
|
67
|
-
return out
|
68
|
-
end
|
69
|
-
|
70
|
-
end
|
71
|
-
|
72
|
-
end
|
73
|
-
end
|
@@ -1,76 +0,0 @@
|
|
1
|
-
<style type="text/css">
|
2
|
-
#message_headers {
|
3
|
-
position: absolute;
|
4
|
-
top: 0px;
|
5
|
-
left: 0;
|
6
|
-
width: 100%;
|
7
|
-
height: 85px;
|
8
|
-
padding: 10px 0 0 0;
|
9
|
-
margin: 0;
|
10
|
-
background: #fff;
|
11
|
-
font-size: 12px;
|
12
|
-
font-family: "Lucida Grande";
|
13
|
-
border-bottom: 1px solid #dedede;
|
14
|
-
overflow: hidden;
|
15
|
-
}
|
16
|
-
|
17
|
-
#message_headers dl {
|
18
|
-
margin: 0;
|
19
|
-
padding: 0;
|
20
|
-
}
|
21
|
-
|
22
|
-
#message_headers dt {
|
23
|
-
width: 60px;
|
24
|
-
padding: 1px;
|
25
|
-
float: left;
|
26
|
-
text-align: right;
|
27
|
-
font-weight: bold;
|
28
|
-
color: #7f7f7f;
|
29
|
-
}
|
30
|
-
|
31
|
-
#message_headers dd {
|
32
|
-
margin-left: 70px;
|
33
|
-
padding: 1px;
|
34
|
-
}
|
35
|
-
|
36
|
-
#message_headers p.alternate {
|
37
|
-
position: absolute;
|
38
|
-
top: 0;
|
39
|
-
right: 15px;
|
40
|
-
}
|
41
|
-
|
42
|
-
#message_headers p.alternate a {
|
43
|
-
color: #09c;
|
44
|
-
}
|
45
|
-
|
46
|
-
pre#message_body {
|
47
|
-
padding: 10px;
|
48
|
-
word-wrap: break-word;
|
49
|
-
}
|
50
|
-
|
51
|
-
body {
|
52
|
-
margin-top: 96px;
|
53
|
-
}
|
54
|
-
</style>
|
55
|
-
|
56
|
-
<div id="message_headers">
|
57
|
-
<dl>
|
58
|
-
<dt>From:</dt>
|
59
|
-
<dd>from@example.org</dd>
|
60
|
-
|
61
|
-
<dt>Subject:</dt>
|
62
|
-
<dd><strong>(to at example.org) original subject</strong></dd>
|
63
|
-
|
64
|
-
<dt>Date:</dt>
|
65
|
-
<dd>Jul 28, 2012 03:37:17 AM EDT</dd>
|
66
|
-
|
67
|
-
<dt>To:</dt>
|
68
|
-
<dd>to@sanitize_email.org</dd>
|
69
|
-
</dl>
|
70
|
-
|
71
|
-
|
72
|
-
</div>
|
73
|
-
|
74
|
-
|
75
|
-
<pre id="message_body"></pre>
|
76
|
-
|