mailshield 1.5 → 1.7

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a7a2c56984d3291c6834cb7ec0dd5b8ad1217853be110281863a0aa8fe47c0ce
4
- data.tar.gz: 2b36df2860cbf4bc9b4374541cd3b0a73706c1096a87e053131e77e03ba7f3d3
3
+ metadata.gz: 18604539d1e37e6bb2be57844fc3f4655db6aad98fd6f8d8509264b55ab9cf8a
4
+ data.tar.gz: 55fc76651d319aa7f5c7289270dcfb13d7fbacfa0c1ca9ca5d8fdf19c268f14d
5
5
  SHA512:
6
- metadata.gz: 1ce12f3f8a8496c9d8e548a2f5195c00c4ffeaec64648a9ece07744d9825681224317f7e5cdf0fb02679eeca449ae3437024576f470da32ce1ba661be469bf37
7
- data.tar.gz: cb1560e4f84081e7e23788ec48f0453bef2f7435336fc51d569def04c38798f997f7815ab13396fb653573934098405752d330b623504e8114555f63d9bf6a93
6
+ metadata.gz: 3b2ab77713d4d23a5836419b01df0c804bcc66291f5efb259dc437e9584fc7ee6ef933401701332e4105877e56da3beea02b51c7b1f06ccc49f958cac6ba9cc2
7
+ data.tar.gz: 53771549f5c3c334906acbeea35df4277581431a8fbe83636ace180ab25e7ae4bb0e80ebcbab87a28cd45c813665a29b731017966ad160ab4ec89ee18c83b287
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Mailshield
4
- VERSION = '1.5'
4
+ VERSION = '1.7'
5
5
  end
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
- begin
29
- validate_known_disposal_domain?(domain)
30
- validate_format!(email)
31
- validate_domain!(domain)
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
- { valid: true }
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.status == '250' || response.status == '250 OK'
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.5'
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-09-12 00:00:00.000000000 Z
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