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 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