valid_email2 2.3.1 → 3.0.0
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/.travis.yml +5 -1
- data/CHANGELOG.md +12 -0
- data/README.md +17 -4
- data/config/blacklisted_email_domains.yml +1 -0
- data/{vendor/disposable_emails.yml → config/disposable_email_domains.yml} +238 -0
- data/gemfiles/activemodel3.gemfile +1 -1
- data/gemfiles/activemodel4.gemfile +1 -1
- data/gemfiles/activemodel5.gemfile +1 -1
- data/lib/valid_email2.rb +18 -5
- data/lib/valid_email2/address.rb +28 -8
- data/lib/valid_email2/version.rb +1 -1
- data/pull_mailchecker_emails.rb +2 -2
- data/spec/valid_email2_spec.rb +77 -70
- data/valid_email2.gemspec +1 -1
- metadata +6 -6
- data/vendor/blacklist.yml +0 -1
data/lib/valid_email2.rb
CHANGED
@@ -1,17 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "valid_email2/email_validator"
|
2
4
|
|
3
5
|
module ValidEmail2
|
6
|
+
BLACKLIST_FILE = "config/blacklisted_email_domains.yml"
|
7
|
+
WHITELIST_FILE = "config/whitelisted_email_domains.yml"
|
8
|
+
|
4
9
|
def self.disposable_emails
|
5
|
-
|
10
|
+
@disposable_emails ||= YAML.load_file(
|
11
|
+
File.expand_path('../config/disposable_email_domains.yml', __dir__)
|
12
|
+
)
|
6
13
|
end
|
7
14
|
|
8
15
|
def self.blacklist
|
9
|
-
|
10
|
-
|
16
|
+
@blacklist ||= if File.exist?(BLACKLIST_FILE)
|
17
|
+
YAML.load_file(File.expand_path(BLACKLIST_FILE))
|
18
|
+
else
|
19
|
+
[]
|
20
|
+
end
|
11
21
|
end
|
12
22
|
|
13
23
|
def self.whitelist
|
14
|
-
|
15
|
-
|
24
|
+
@whitelist ||= if File.exist?(WHITELIST_FILE)
|
25
|
+
YAML.load_file(File.expand_path(WHITELIST_FILE))
|
26
|
+
else
|
27
|
+
[]
|
28
|
+
end
|
16
29
|
end
|
17
30
|
end
|
data/lib/valid_email2/address.rb
CHANGED
@@ -45,7 +45,11 @@ module ValidEmail2
|
|
45
45
|
end
|
46
46
|
|
47
47
|
def disposable?
|
48
|
-
valid? &&
|
48
|
+
valid? &&
|
49
|
+
(
|
50
|
+
domain_is_in?(ValidEmail2.disposable_emails) ||
|
51
|
+
mx_server_is_in?(ValidEmail2.disposable_emails)
|
52
|
+
)
|
49
53
|
end
|
50
54
|
|
51
55
|
def whitelisted?
|
@@ -59,10 +63,7 @@ module ValidEmail2
|
|
59
63
|
def valid_mx?
|
60
64
|
return false unless valid?
|
61
65
|
|
62
|
-
|
63
|
-
return dns.getresources(address.domain, Resolv::DNS::Resource::IN::MX).size > 0 ||
|
64
|
-
dns.getresources(address.domain, Resolv::DNS::Resource::IN::A).size > 0
|
65
|
-
end
|
66
|
+
mx_servers.any?
|
66
67
|
end
|
67
68
|
|
68
69
|
private
|
@@ -74,10 +75,29 @@ module ValidEmail2
|
|
74
75
|
}
|
75
76
|
end
|
76
77
|
|
77
|
-
def
|
78
|
-
|
78
|
+
def mx_server_is_in?(domain_list)
|
79
|
+
mx_servers.any? { |mx_server|
|
80
|
+
return false unless mx_server.respond_to?(:exchange)
|
81
|
+
mx_server = mx_server.exchange.to_s
|
82
|
+
|
83
|
+
domain_list.any? { |domain|
|
84
|
+
mx_server.end_with?(domain) && mx_server =~ /\A(?:.+\.)*?#{domain}\z/
|
85
|
+
}
|
86
|
+
}
|
87
|
+
end
|
88
|
+
|
89
|
+
def address_contain_emoticons?(email)
|
90
|
+
return false if email.nil?
|
91
|
+
|
92
|
+
email.each_char.any? { |char| char.bytesize > 1 }
|
93
|
+
end
|
79
94
|
|
80
|
-
|
95
|
+
def mx_servers
|
96
|
+
@mx_servers ||= Resolv::DNS.open do |dns|
|
97
|
+
mx_servers = dns.getresources(address.domain, Resolv::DNS::Resource::IN::MX)
|
98
|
+
(mx_servers.any? && mx_servers) ||
|
99
|
+
dns.getresources(address.domain, Resolv::DNS::Resource::IN::A)
|
100
|
+
end
|
81
101
|
end
|
82
102
|
end
|
83
103
|
end
|
data/lib/valid_email2/version.rb
CHANGED
data/pull_mailchecker_emails.rb
CHANGED
@@ -10,7 +10,7 @@ whitelisted_emails = %w(
|
|
10
10
|
hush.ai hush.com hushmail.me naver.com qq.com example.com
|
11
11
|
)
|
12
12
|
|
13
|
-
existing_emails = YAML.load_file("
|
13
|
+
existing_emails = YAML.load_file("config/disposable_email_domains.yml")
|
14
14
|
|
15
15
|
url = "https://raw.githubusercontent.com/FGRibreau/mailchecker/master/list.json"
|
16
16
|
resp = Net::HTTP.get_response(URI.parse(url))
|
@@ -19,4 +19,4 @@ remote_emails = JSON.parse(resp.body).flatten - whitelisted_emails
|
|
19
19
|
|
20
20
|
result_emails = (existing_emails + remote_emails).map(&:strip).uniq.sort
|
21
21
|
|
22
|
-
File.open("
|
22
|
+
File.open("config/disposable_email_domains.yml", "w") {|f| f.write result_emails.to_yaml }
|
data/spec/valid_email2_spec.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "spec_helper"
|
2
4
|
|
3
5
|
class TestUser < TestModel
|
@@ -5,7 +7,7 @@ class TestUser < TestModel
|
|
5
7
|
end
|
6
8
|
|
7
9
|
class TestUserSubaddressing < TestModel
|
8
|
-
validates :email, 'valid_email_2/email': {disallow_subaddressing: true}
|
10
|
+
validates :email, 'valid_email_2/email': { disallow_subaddressing: true }
|
9
11
|
end
|
10
12
|
|
11
13
|
class TestUserMX < TestModel
|
@@ -24,99 +26,127 @@ class TestUserDisallowBlacklisted < TestModel
|
|
24
26
|
validates :email, 'valid_email_2/email': { blacklist: true }
|
25
27
|
end
|
26
28
|
|
29
|
+
class TestUserMessage < TestModel
|
30
|
+
validates :email, 'valid_email_2/email': { message: "custom message" }
|
31
|
+
end
|
32
|
+
|
27
33
|
describe ValidEmail2 do
|
28
34
|
describe "basic validation" do
|
29
35
|
subject(:user) { TestUser.new(email: "") }
|
30
36
|
|
31
|
-
it "
|
37
|
+
it "is valid" do
|
32
38
|
user = TestUser.new(email: "foo@bar123.com")
|
33
39
|
expect(user.valid?).to be_truthy
|
34
40
|
end
|
35
41
|
|
36
|
-
it "
|
42
|
+
it "is invalid if email is empty" do
|
37
43
|
expect(user.valid?).to be_truthy
|
38
44
|
end
|
39
45
|
|
40
|
-
it "
|
46
|
+
it "is invalid if domain is missing" do
|
41
47
|
user = TestUser.new(email: "foo@.com")
|
42
48
|
expect(user.valid?).to be_falsey
|
43
49
|
end
|
44
50
|
|
45
|
-
it "
|
46
|
-
%w
|
51
|
+
it "is invalid if email contains invalid characters" do
|
52
|
+
%w[+ _ !].each do |invalid_character|
|
47
53
|
user = TestUser.new(email: "foo@google#{invalid_character}yahoo.com")
|
48
54
|
expect(user.valid?).to be_falsey
|
49
55
|
end
|
50
56
|
end
|
51
57
|
|
52
|
-
it "
|
58
|
+
it "is invalid if address is malformed" do
|
53
59
|
user = TestUser.new(email: "foo@bar")
|
54
60
|
expect(user.valid?).to be_falsey
|
55
61
|
end
|
56
62
|
|
57
|
-
it "
|
63
|
+
it "is invalid if Mail::AddressListsParser raises exception" do
|
58
64
|
user = TestUser.new(email: "foo@gmail.com")
|
59
65
|
expect(Mail::Address).to receive(:new).and_raise(Mail::Field::ParseError.new(nil, nil, nil))
|
60
66
|
expect(user.valid?).to be_falsey
|
61
67
|
end
|
62
68
|
|
63
|
-
it "
|
69
|
+
it "is invalid if the domain contains consecutive dots" do
|
64
70
|
user = TestUser.new(email: "foo@bar..com")
|
65
71
|
expect(user.valid?).to be_falsey
|
66
72
|
end
|
73
|
+
|
74
|
+
it "is invalid if the domain contains emoticons" do
|
75
|
+
user = TestUser.new(email: "foo🙈@gmail.com")
|
76
|
+
expect(user.valid?).to be_falsy
|
77
|
+
end
|
67
78
|
end
|
68
79
|
|
69
|
-
describe "disposable
|
70
|
-
it "
|
80
|
+
describe "with disposable validation" do
|
81
|
+
it "is valid if it's not a disposable email" do
|
71
82
|
user = TestUserDisallowDisposable.new(email: "foo@gmail.com")
|
72
83
|
expect(user.valid?).to be_truthy
|
73
84
|
end
|
74
85
|
|
75
|
-
it "
|
86
|
+
it "is invalid if it's a disposable email" do
|
76
87
|
user = TestUserDisallowDisposable.new(email: "foo@#{ValidEmail2.disposable_emails.first}")
|
77
88
|
expect(user.valid?).to be_falsey
|
78
89
|
end
|
79
90
|
|
80
|
-
it "
|
91
|
+
it "is invalid if the domain is a subdomain of a disposable domain" do
|
81
92
|
user = TestUserDisallowDisposable.new(email: "foo@bar.#{ValidEmail2.disposable_emails.first}")
|
82
93
|
expect(user.valid?).to be_falsey
|
83
94
|
end
|
84
95
|
|
85
|
-
it "
|
96
|
+
it "allows example.com" do
|
86
97
|
user = TestUserDisallowDisposable.new(email: "foo@example.com")
|
87
98
|
expect(user.valid?).to be_truthy
|
88
99
|
end
|
89
100
|
|
101
|
+
context "with domain that is not disposable but it's mx server is disposable" do
|
102
|
+
let(:domain) { "sogetthis.com" }
|
103
|
+
|
104
|
+
around do |example|
|
105
|
+
ValidEmail2.disposable_emails.delete(domain)
|
106
|
+
example.run
|
107
|
+
ValidEmail2.disposable_emails << domain
|
108
|
+
end
|
109
|
+
|
110
|
+
it "is invalid" do
|
111
|
+
user = TestUserDisallowDisposable.new(email: "foo@sogetthis.com")
|
112
|
+
expect(user.valid?).to be_falsey
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
90
116
|
describe "with whitelisted emails" do
|
91
|
-
|
92
|
-
|
117
|
+
let(:whitelist_domain) { ValidEmail2.disposable_emails.first }
|
118
|
+
let(:whitelist_file_path) { "config/whitelisted_email_domains.yml" }
|
119
|
+
|
120
|
+
after do
|
121
|
+
FileUtils.rm(whitelist_file_path, force: true)
|
122
|
+
end
|
123
|
+
|
124
|
+
it "is invalid if the domain is disposable and not in the whitelist" do
|
125
|
+
user = TestUserDisallowDisposableWithWhitelist.new(email: "foo@#{whitelist_domain}")
|
93
126
|
expect(user.valid?).to be_falsey
|
94
127
|
end
|
95
128
|
|
96
|
-
it "
|
97
|
-
|
98
|
-
whitelist_file_path = "vendor/whitelist.yml"
|
99
|
-
File.open(whitelist_file_path, "w") {|f| f.write whitelist_domain.to_yaml }
|
129
|
+
it "is valid if the domain is disposable but in the whitelist" do
|
130
|
+
File.open(whitelist_file_path, "w") { |f| f.write [whitelist_domain].to_yaml }
|
100
131
|
user = TestUserDisallowDisposableWithWhitelist.new(email: "foo@#{whitelist_domain}")
|
101
132
|
expect(user.valid?).to be_falsey
|
102
|
-
File.delete(whitelist_file_path)
|
103
133
|
end
|
104
134
|
end
|
105
135
|
end
|
106
136
|
|
107
|
-
describe "
|
108
|
-
it "
|
137
|
+
describe "with blacklist validation" do
|
138
|
+
it "is valid if the domain is not blacklisted" do
|
109
139
|
user = TestUserDisallowBlacklisted.new(email: "foo@gmail.com")
|
110
140
|
expect(user.valid?).to be_truthy
|
111
141
|
end
|
112
142
|
|
113
|
-
it "
|
143
|
+
it "is invalid if the domain is blacklisted" do
|
114
144
|
user = TestUserDisallowBlacklisted.new(email: "foo@#{ValidEmail2.blacklist.first}")
|
115
145
|
expect(user.valid?).to be_falsey
|
116
146
|
end
|
117
147
|
end
|
118
148
|
|
119
|
-
describe "mx
|
149
|
+
describe "with mx validation" do
|
120
150
|
it "should be valid if mx records are found" do
|
121
151
|
user = TestUserMX.new(email: "foo@gmail.com")
|
122
152
|
expect(user.valid?).to be_truthy
|
@@ -133,58 +163,35 @@ describe ValidEmail2 do
|
|
133
163
|
end
|
134
164
|
end
|
135
165
|
|
136
|
-
describe "
|
137
|
-
it "
|
138
|
-
|
139
|
-
expect(
|
166
|
+
describe "with subaddress validation" do
|
167
|
+
it "is valid when address does not contain subaddress" do
|
168
|
+
user = TestUserSubaddressing.new(email: "foo@gmail.com")
|
169
|
+
expect(user.valid?).to be_truthy
|
140
170
|
end
|
141
|
-
end
|
142
171
|
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
it "should be recipient delimiter ('+')" do
|
147
|
-
expect(ValidEmail2::Address::DEFAULT_RECIPIENT_DELIMITER).to eq('+')
|
148
|
-
end
|
172
|
+
it "is invalid when address cotains subaddress" do
|
173
|
+
user = TestUserSubaddressing.new(email: "foo+1@gmail.com")
|
174
|
+
expect(user.valid?).to be_falsey
|
149
175
|
end
|
176
|
+
end
|
150
177
|
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
it "should be false when address local part contains a recipient delimiter ('+')" do
|
158
|
-
email = ValidEmail2::Address.new("foo@gmail.com")
|
159
|
-
expect(email.subaddressed?).to be_falsey
|
160
|
-
end
|
178
|
+
describe "with custom error message" do
|
179
|
+
it "supports settings a custom error message" do
|
180
|
+
user = TestUserMessage.new(email: "fakeemail")
|
181
|
+
user.valid?
|
182
|
+
expect(user.errors.full_messages).to include("Email custom message")
|
161
183
|
end
|
184
|
+
end
|
162
185
|
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
expect(user.valid?).to be_truthy
|
168
|
-
end
|
169
|
-
|
170
|
-
it "should be valid when address local part contains a recipient delimiter ('+')" do
|
171
|
-
user = TestUser.new(email: "foo+1@gmail.com")
|
172
|
-
expect(user.valid?).to be_truthy
|
173
|
-
end
|
174
|
-
end
|
175
|
-
|
176
|
-
context "subaddressing is forbidden" do
|
177
|
-
it "should be valid when address local part does not contain a recipient delimiter ('+')" do
|
178
|
-
user = TestUserSubaddressing.new(email: "foo@gmail.com")
|
179
|
-
expect(user.valid?).to be_truthy
|
180
|
-
end
|
181
|
-
|
182
|
-
it "should be invalid when address local part contains a recipient delimiter ('+')" do
|
183
|
-
user = TestUserSubaddressing.new(email: "foo+1@gmail.com")
|
184
|
-
expect(user.valid?).to be_falsey
|
185
|
-
end
|
186
|
-
end
|
186
|
+
describe "#subaddressed?" do
|
187
|
+
it "should be true when address local part contains a recipient delimiter ('+')" do
|
188
|
+
email = ValidEmail2::Address.new("foo+1@gmail.com")
|
189
|
+
expect(email.subaddressed?).to be_truthy
|
187
190
|
end
|
188
191
|
|
192
|
+
it "should be false when address local part contains a recipient delimiter ('+')" do
|
193
|
+
email = ValidEmail2::Address.new("foo@gmail.com")
|
194
|
+
expect(email.subaddressed?).to be_falsey
|
195
|
+
end
|
189
196
|
end
|
190
197
|
end
|
data/valid_email2.gemspec
CHANGED
@@ -20,7 +20,7 @@ Gem::Specification.new do |spec|
|
|
20
20
|
|
21
21
|
spec.required_ruby_version = ">= 1.9.3"
|
22
22
|
|
23
|
-
spec.add_development_dependency "bundler", "~>
|
23
|
+
spec.add_development_dependency "bundler", "~> 2.0"
|
24
24
|
spec.add_development_dependency "rake", "~> 11.3.0"
|
25
25
|
spec.add_development_dependency "rspec", "~> 3.5.0"
|
26
26
|
spec.add_runtime_dependency "mail", "~> 2.5"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: valid_email2
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 3.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Micke Lisinge
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2019-02-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
19
|
+
version: '2.0'
|
20
20
|
type: :development
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
26
|
+
version: '2.0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: rake
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -95,6 +95,8 @@ files:
|
|
95
95
|
- LICENSE.txt
|
96
96
|
- README.md
|
97
97
|
- Rakefile
|
98
|
+
- config/blacklisted_email_domains.yml
|
99
|
+
- config/disposable_email_domains.yml
|
98
100
|
- gemfiles/activemodel3.gemfile
|
99
101
|
- gemfiles/activemodel4.gemfile
|
100
102
|
- gemfiles/activemodel5.gemfile
|
@@ -106,8 +108,6 @@ files:
|
|
106
108
|
- spec/spec_helper.rb
|
107
109
|
- spec/valid_email2_spec.rb
|
108
110
|
- valid_email2.gemspec
|
109
|
-
- vendor/blacklist.yml
|
110
|
-
- vendor/disposable_emails.yml
|
111
111
|
homepage: https://github.com/micke/valid_email2
|
112
112
|
licenses:
|
113
113
|
- MIT
|
data/vendor/blacklist.yml
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
- blacklisted-test.com
|