mailshield 1.6 → 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 +71 -21
- 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,57 +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
24
|
attr_accessor :dns_cache, :smtp_cache, :whitelist, :blacklist
|
22
25
|
|
26
|
+
# Configuration method for initializing and yielding settings
|
23
27
|
def configure
|
24
|
-
|
25
|
-
|
26
|
-
yield self
|
28
|
+
initialize_lists
|
29
|
+
yield self if block_given?
|
27
30
|
end
|
28
31
|
|
32
|
+
# Validate the structure and existence of the provided email
|
29
33
|
def validate_email(email)
|
30
34
|
reset_caches
|
31
|
-
|
32
35
|
domain = extract_domain(email)
|
33
36
|
|
34
|
-
return { valid: false, reason: 'Email domain is blacklisted.' } if blacklist.include?(domain)
|
37
|
+
return { valid: false, reason: 'Email domain is blacklisted.' } if blacklist && blacklist.include?(domain)
|
35
38
|
|
36
|
-
if !whitelist.empty? && !whitelist.include?(domain)
|
39
|
+
if whitelist && !whitelist.empty? && !whitelist.include?(domain)
|
37
40
|
return { valid: false, reason: 'Email domain is not whitelisted.' }
|
38
41
|
end
|
39
42
|
|
40
|
-
|
41
|
-
|
42
|
-
validate_format!(email)
|
43
|
-
validate_domain!(domain)
|
44
|
-
validate_spf!(domain)
|
45
|
-
validate_dmarc!(domain)
|
46
|
-
validate_smtp!(email)
|
47
|
-
rescue ValidationError => e
|
48
|
-
return { valid: false, reason: e.message }
|
49
|
-
end
|
50
|
-
|
51
|
-
{ valid: true }
|
43
|
+
# Perform comprehensive validation checks
|
44
|
+
perform_validations(email, domain)
|
52
45
|
end
|
53
46
|
|
54
|
-
# Added Support for verifying email existance in real world
|
55
47
|
def email_exists?(email)
|
56
48
|
smtp_verify_email(email)
|
57
49
|
end
|
58
50
|
|
59
|
-
# Added Support for Email Format Validation
|
60
51
|
def valid_format?(email)
|
61
52
|
valid_email_format?(email)
|
62
53
|
end
|
63
54
|
|
64
55
|
private
|
65
56
|
|
57
|
+
# Initialize lists for whitelisting and blacklisting
|
58
|
+
def initialize_lists
|
59
|
+
@whitelist ||= []
|
60
|
+
@blacklist ||= []
|
61
|
+
end
|
62
|
+
|
66
63
|
def reset_caches
|
67
64
|
@dns_cache = {}
|
68
65
|
@smtp_cache = {}
|
69
66
|
end
|
70
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
|
+
|
71
110
|
def validate_format!(email)
|
72
111
|
raise InvalidFormatError, 'The email address format is invalid.' unless valid_email_format?(email)
|
73
112
|
end
|
@@ -96,6 +135,7 @@ module MailShield
|
|
96
135
|
EMAIL_REGEX.match?(email)
|
97
136
|
end
|
98
137
|
|
138
|
+
# Fetch MX records for the given domain
|
99
139
|
def fetch_mx_records(domain)
|
100
140
|
Resolv::DNS.open do |dns|
|
101
141
|
dns.getresources(domain, Resolv::DNS::Resource::IN::MX).map(&:exchange).map(&:to_s)
|
@@ -104,16 +144,19 @@ module MailShield
|
|
104
144
|
[]
|
105
145
|
end
|
106
146
|
|
147
|
+
# Check if SPF records are available for the domain
|
107
148
|
def spf_record?(domain)
|
108
149
|
spf_records = fetch_txt_records(domain)
|
109
150
|
spf_records.any? { |record| record.include?('v=spf1') }
|
110
151
|
end
|
111
152
|
|
153
|
+
# Check if DMARC records are available for the domain
|
112
154
|
def dmarc_record?(domain)
|
113
155
|
dmarc_records = fetch_txt_records("_dmarc.#{domain}")
|
114
156
|
dmarc_records.any? { |record| record.include?('v=DMARC1') }
|
115
157
|
end
|
116
158
|
|
159
|
+
# Fetch TXT records for the domain
|
117
160
|
def fetch_txt_records(domain)
|
118
161
|
Resolv::DNS.open do |dns|
|
119
162
|
records = dns.getresources(domain, Resolv::DNS::Resource::IN::TXT)
|
@@ -123,6 +166,7 @@ module MailShield
|
|
123
166
|
[]
|
124
167
|
end
|
125
168
|
|
169
|
+
# Verify the email address using SMTP
|
126
170
|
def smtp_verify_email(email)
|
127
171
|
domain = extract_domain(email)
|
128
172
|
smtp_server = get_smtp_server(domain)
|
@@ -134,13 +178,18 @@ module MailShield
|
|
134
178
|
smtp.helo('localhost')
|
135
179
|
smtp.mailfrom('test@example.com')
|
136
180
|
response = smtp.rcptto(email)
|
137
|
-
response
|
181
|
+
smtp_response_status(response)
|
138
182
|
end
|
139
183
|
rescue Net::SMTPFatalError, Net::SMTPServerBusy, Net::SMTPSyntaxError, Errno::ECONNREFUSED
|
140
184
|
false
|
141
185
|
end
|
142
186
|
end
|
143
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
|
144
193
|
def get_smtp_server(domain)
|
145
194
|
mx_records = fetch_mx_records(domain)
|
146
195
|
mx_records.first
|
@@ -150,6 +199,7 @@ end
|
|
150
199
|
|
151
200
|
module ActiveModel
|
152
201
|
module Validations
|
202
|
+
# Validator for secure email verification
|
153
203
|
class SecureEmailValidator < ::MailShield::SecureEmailValidator
|
154
204
|
def initialize(options = {})
|
155
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
|