mailshield 1.5 → 1.7
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/lib/mailshield/version.rb +1 -1
- data/lib/mailshield.rb +81 -13
- metadata +16 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 18604539d1e37e6bb2be57844fc3f4655db6aad98fd6f8d8509264b55ab9cf8a
|
|
4
|
+
data.tar.gz: 55fc76651d319aa7f5c7289270dcfb13d7fbacfa0c1ca9ca5d8fdf19c268f14d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3b2ab77713d4d23a5836419b01df0c804bcc66291f5efb259dc437e9584fc7ee6ef933401701332e4105877e56da3beea02b51c7b1f06ccc49f958cac6ba9cc2
|
|
7
|
+
data.tar.gz: 53771549f5c3c334906acbeea35df4277581431a8fbe83636ace180ab25e7ae4bb0e80ebcbab87a28cd45c813665a29b731017966ad160ab4ec89ee18c83b287
|
data/lib/mailshield/version.rb
CHANGED
data/lib/mailshield.rb
CHANGED
|
@@ -8,8 +8,10 @@ require 'csv'
|
|
|
8
8
|
require 'net/smtp'
|
|
9
9
|
|
|
10
10
|
module MailShield
|
|
11
|
+
# Regex pattern for validating email formats
|
|
11
12
|
EMAIL_REGEX = /\A[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}\z/.freeze
|
|
12
13
|
|
|
14
|
+
# Custom error classes for various validation failures
|
|
13
15
|
class ValidationError < StandardError; end
|
|
14
16
|
class InvalidFormatError < ValidationError; end
|
|
15
17
|
class DomainNotFoundError < ValidationError; end
|
|
@@ -17,39 +19,94 @@ module MailShield
|
|
|
17
19
|
class DMARCError < ValidationError; end
|
|
18
20
|
class SMTPError < ValidationError; end
|
|
19
21
|
class TemporaryDomainError < ValidationError; end
|
|
22
|
+
|
|
20
23
|
class << self
|
|
21
|
-
attr_accessor :dns_cache, :smtp_cache
|
|
24
|
+
attr_accessor :dns_cache, :smtp_cache, :whitelist, :blacklist
|
|
22
25
|
|
|
26
|
+
# Configuration method for initializing and yielding settings
|
|
27
|
+
def configure
|
|
28
|
+
initialize_lists
|
|
29
|
+
yield self if block_given?
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Validate the structure and existence of the provided email
|
|
23
33
|
def validate_email(email)
|
|
24
34
|
reset_caches
|
|
25
|
-
|
|
26
35
|
domain = extract_domain(email)
|
|
27
36
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
validate_spf!(domain)
|
|
33
|
-
validate_dmarc!(domain)
|
|
34
|
-
validate_smtp!(email)
|
|
35
|
-
rescue ValidationError => e
|
|
36
|
-
return { valid: false, reason: e.message }
|
|
37
|
+
return { valid: false, reason: 'Email domain is blacklisted.' } if blacklist && blacklist.include?(domain)
|
|
38
|
+
|
|
39
|
+
if whitelist && !whitelist.empty? && !whitelist.include?(domain)
|
|
40
|
+
return { valid: false, reason: 'Email domain is not whitelisted.' }
|
|
37
41
|
end
|
|
38
42
|
|
|
39
|
-
|
|
43
|
+
# Perform comprehensive validation checks
|
|
44
|
+
perform_validations(email, domain)
|
|
40
45
|
end
|
|
41
46
|
|
|
42
47
|
def email_exists?(email)
|
|
43
48
|
smtp_verify_email(email)
|
|
44
49
|
end
|
|
45
50
|
|
|
51
|
+
def valid_format?(email)
|
|
52
|
+
valid_email_format?(email)
|
|
53
|
+
end
|
|
54
|
+
|
|
46
55
|
private
|
|
47
56
|
|
|
57
|
+
# Initialize lists for whitelisting and blacklisting
|
|
58
|
+
def initialize_lists
|
|
59
|
+
@whitelist ||= []
|
|
60
|
+
@blacklist ||= []
|
|
61
|
+
end
|
|
62
|
+
|
|
48
63
|
def reset_caches
|
|
49
64
|
@dns_cache = {}
|
|
50
65
|
@smtp_cache = {}
|
|
51
66
|
end
|
|
52
67
|
|
|
68
|
+
def blacklisted?(domain)
|
|
69
|
+
blacklist.include?(domain)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def handle_blacklist(domain)
|
|
73
|
+
{
|
|
74
|
+
valid: false,
|
|
75
|
+
reason: 'Email domain is blacklisted.'
|
|
76
|
+
}
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def whitelisted?(domain)
|
|
80
|
+
!whitelist.empty? && whitelist.include?(domain)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def handle_whitelist(domain)
|
|
84
|
+
{
|
|
85
|
+
valid: false,
|
|
86
|
+
reason: 'Email domain is not whitelisted.'
|
|
87
|
+
}
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Perform all necessary validations for the given email
|
|
91
|
+
def perform_validations(email, domain)
|
|
92
|
+
begin
|
|
93
|
+
validate_known_disposal_domain?(domain) # Custom method to check for disposable domains
|
|
94
|
+
validate_format!(email)
|
|
95
|
+
validate_domain!(domain)
|
|
96
|
+
validate_spf!(domain)
|
|
97
|
+
validate_dmarc!(domain)
|
|
98
|
+
validate_smtp!(email)
|
|
99
|
+
rescue ValidationError => e
|
|
100
|
+
return handle_validation_error(e)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
{ valid: true }
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def handle_validation_error(error)
|
|
107
|
+
{ valid: false, reason: error.message }
|
|
108
|
+
end
|
|
109
|
+
|
|
53
110
|
def validate_format!(email)
|
|
54
111
|
raise InvalidFormatError, 'The email address format is invalid.' unless valid_email_format?(email)
|
|
55
112
|
end
|
|
@@ -78,6 +135,7 @@ module MailShield
|
|
|
78
135
|
EMAIL_REGEX.match?(email)
|
|
79
136
|
end
|
|
80
137
|
|
|
138
|
+
# Fetch MX records for the given domain
|
|
81
139
|
def fetch_mx_records(domain)
|
|
82
140
|
Resolv::DNS.open do |dns|
|
|
83
141
|
dns.getresources(domain, Resolv::DNS::Resource::IN::MX).map(&:exchange).map(&:to_s)
|
|
@@ -86,16 +144,19 @@ module MailShield
|
|
|
86
144
|
[]
|
|
87
145
|
end
|
|
88
146
|
|
|
147
|
+
# Check if SPF records are available for the domain
|
|
89
148
|
def spf_record?(domain)
|
|
90
149
|
spf_records = fetch_txt_records(domain)
|
|
91
150
|
spf_records.any? { |record| record.include?('v=spf1') }
|
|
92
151
|
end
|
|
93
152
|
|
|
153
|
+
# Check if DMARC records are available for the domain
|
|
94
154
|
def dmarc_record?(domain)
|
|
95
155
|
dmarc_records = fetch_txt_records("_dmarc.#{domain}")
|
|
96
156
|
dmarc_records.any? { |record| record.include?('v=DMARC1') }
|
|
97
157
|
end
|
|
98
158
|
|
|
159
|
+
# Fetch TXT records for the domain
|
|
99
160
|
def fetch_txt_records(domain)
|
|
100
161
|
Resolv::DNS.open do |dns|
|
|
101
162
|
records = dns.getresources(domain, Resolv::DNS::Resource::IN::TXT)
|
|
@@ -105,6 +166,7 @@ module MailShield
|
|
|
105
166
|
[]
|
|
106
167
|
end
|
|
107
168
|
|
|
169
|
+
# Verify the email address using SMTP
|
|
108
170
|
def smtp_verify_email(email)
|
|
109
171
|
domain = extract_domain(email)
|
|
110
172
|
smtp_server = get_smtp_server(domain)
|
|
@@ -116,13 +178,18 @@ module MailShield
|
|
|
116
178
|
smtp.helo('localhost')
|
|
117
179
|
smtp.mailfrom('test@example.com')
|
|
118
180
|
response = smtp.rcptto(email)
|
|
119
|
-
response
|
|
181
|
+
smtp_response_status(response)
|
|
120
182
|
end
|
|
121
183
|
rescue Net::SMTPFatalError, Net::SMTPServerBusy, Net::SMTPSyntaxError, Errno::ECONNREFUSED
|
|
122
184
|
false
|
|
123
185
|
end
|
|
124
186
|
end
|
|
125
187
|
|
|
188
|
+
def smtp_response_status(response)
|
|
189
|
+
response.status == '250' || response.status == '250 OK'
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
# Retrieve the SMTP server for the specified domain
|
|
126
193
|
def get_smtp_server(domain)
|
|
127
194
|
mx_records = fetch_mx_records(domain)
|
|
128
195
|
mx_records.first
|
|
@@ -132,6 +199,7 @@ end
|
|
|
132
199
|
|
|
133
200
|
module ActiveModel
|
|
134
201
|
module Validations
|
|
202
|
+
# Validator for secure email verification
|
|
135
203
|
class SecureEmailValidator < ::MailShield::SecureEmailValidator
|
|
136
204
|
def initialize(options = {})
|
|
137
205
|
super
|
metadata
CHANGED
|
@@ -1,15 +1,29 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: mailshield
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: '1.
|
|
4
|
+
version: '1.7'
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- jana
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2024-
|
|
11
|
+
date: 2024-10-05 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: activemodel
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - ">="
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '0'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - ">="
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '0'
|
|
13
27
|
- !ruby/object:Gem::Dependency
|
|
14
28
|
name: net-smtp
|
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|