mailshield 0.1.0 → 1.1
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/docs/mailshield.png +0 -0
- data/lib/mailshield/secure_email_validator.rb +16 -0
- data/lib/mailshield/temp_domains.yml +3520 -0
- data/lib/mailshield/version.rb +1 -1
- data/lib/mailshield.rb +99 -32
- metadata +6 -3
data/lib/mailshield/version.rb
CHANGED
data/lib/mailshield.rb
CHANGED
@@ -1,64 +1,131 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative
|
3
|
+
require_relative 'mailshield/version'
|
4
4
|
require 'resolv'
|
5
|
+
require 'csv'
|
6
|
+
require 'net/smtp'
|
5
7
|
|
6
8
|
module MailShield
|
7
|
-
|
8
|
-
|
9
|
+
EMAIL_REGEX = /\A[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}\z/.freeze
|
10
|
+
|
11
|
+
class ValidationError < StandardError; end
|
12
|
+
class InvalidFormatError < ValidationError; end
|
13
|
+
class DomainNotFoundError < ValidationError; end
|
14
|
+
class SPFError < ValidationError; end
|
15
|
+
class DMARCError < ValidationError; end
|
16
|
+
class SMTPError < ValidationError; end
|
17
|
+
|
18
|
+
class << self
|
19
|
+
attr_accessor :dns_cache, :smtp_cache
|
20
|
+
|
21
|
+
def validate_email(email, verify_by_send: false)
|
22
|
+
reset_caches
|
9
23
|
|
10
|
-
def self.temporary_email?(email)
|
11
24
|
domain = extract_domain(email)
|
12
|
-
return true if known_temp_domain?(domain)
|
13
25
|
|
14
|
-
|
15
|
-
|
26
|
+
begin
|
27
|
+
validate_format!(email)
|
28
|
+
validate_domain!(domain)
|
29
|
+
validate_spf!(domain)
|
30
|
+
validate_dmarc!(domain)
|
31
|
+
validate_smtp!(email) if verify_by_send
|
32
|
+
rescue ValidationError => e
|
33
|
+
return { valid: false, issues: [e.message] }
|
34
|
+
end
|
35
|
+
|
36
|
+
{ valid: true, issues: [] }
|
37
|
+
end
|
16
38
|
|
17
|
-
return true unless spf_record?(domain)
|
18
|
-
return true unless dmarc_record?(domain)
|
19
39
|
|
20
|
-
|
40
|
+
def verify_address(email)
|
41
|
+
smtp_verify_email(email)
|
21
42
|
end
|
43
|
+
private
|
22
44
|
|
23
|
-
def
|
24
|
-
|
45
|
+
def reset_caches
|
46
|
+
@dns_cache = {}
|
47
|
+
@smtp_cache = {}
|
25
48
|
end
|
26
49
|
|
27
|
-
def
|
28
|
-
|
50
|
+
def validate_format!(email)
|
51
|
+
raise InvalidFormatError, 'The email address format is invalid.' unless valid_email_format?(email)
|
29
52
|
end
|
30
53
|
|
31
|
-
def
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
54
|
+
def validate_domain!(domain)
|
55
|
+
raise DomainNotFoundError, 'Email Not Found.' if fetch_mx_records(domain).empty?
|
56
|
+
end
|
57
|
+
|
58
|
+
def validate_spf!(domain)
|
59
|
+
raise SPFError, 'Temporary / Disposable Email' unless spf_record?(domain)
|
60
|
+
end
|
61
|
+
|
62
|
+
def validate_dmarc!(domain)
|
63
|
+
raise DMARCError, 'Temporary / Disposable Email' unless dmarc_record?(domain)
|
64
|
+
end
|
65
|
+
|
66
|
+
def validate_smtp!(email)
|
67
|
+
raise SMTPError, 'Email Address Not Found' unless smtp_verify_email(email)
|
38
68
|
end
|
39
69
|
|
40
|
-
def
|
41
|
-
|
42
|
-
|
70
|
+
def extract_domain(email)
|
71
|
+
email.split('@').last.downcase
|
72
|
+
end
|
73
|
+
|
74
|
+
def valid_email_format?(email)
|
75
|
+
EMAIL_REGEX.match?(email)
|
76
|
+
end
|
77
|
+
|
78
|
+
def fetch_mx_records(domain)
|
79
|
+
dns_cache[domain] ||= begin
|
80
|
+
Resolv::DNS.open do |dns|
|
81
|
+
dns.getresources(domain, Resolv::DNS::Resource::IN::MX).map(&:exchange).map(&:to_s)
|
82
|
+
end
|
83
|
+
rescue Resolv::ResolvError
|
84
|
+
[]
|
85
|
+
end
|
43
86
|
end
|
44
87
|
|
45
|
-
def
|
88
|
+
def spf_record?(domain)
|
46
89
|
spf_records = fetch_txt_records(domain)
|
47
90
|
spf_records.any? { |record| record.include?('v=spf1') }
|
48
91
|
end
|
49
92
|
|
50
|
-
def
|
93
|
+
def dmarc_record?(domain)
|
51
94
|
dmarc_records = fetch_txt_records("_dmarc.#{domain}")
|
52
95
|
dmarc_records.any? { |record| record.include?('v=DMARC1') }
|
53
96
|
end
|
54
97
|
|
55
|
-
def
|
56
|
-
|
57
|
-
|
58
|
-
|
98
|
+
def fetch_txt_records(domain)
|
99
|
+
dns_cache[domain] ||= begin
|
100
|
+
Resolv::DNS.open do |dns|
|
101
|
+
dns.getresources(domain, Resolv::DNS::Resource::IN::TXT).map(&:data)
|
102
|
+
end
|
103
|
+
rescue Resolv::ResolvError
|
104
|
+
[]
|
59
105
|
end
|
60
|
-
|
61
|
-
|
106
|
+
end
|
107
|
+
|
108
|
+
def smtp_verify_email(email)
|
109
|
+
domain = extract_domain(email)
|
110
|
+
smtp_server = smtp_cache[domain] ||= get_smtp_server(domain)
|
111
|
+
|
112
|
+
return false unless smtp_server
|
113
|
+
|
114
|
+
begin
|
115
|
+
Net::SMTP.start(smtp_server, 25, 'localhost') do |smtp|
|
116
|
+
smtp.helo('localhost')
|
117
|
+
smtp.mailfrom('test@example.com')
|
118
|
+
response = smtp.rcptto(email)
|
119
|
+
response == '250 OK'
|
120
|
+
end
|
121
|
+
rescue Net::SMTPFatalError, Net::SMTPServerBusy, Net::SMTPSyntaxError, Errno::ECONNREFUSED => e
|
122
|
+
false
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def get_smtp_server(domain)
|
127
|
+
mx_records = fetch_mx_records(domain)
|
128
|
+
mx_records.first
|
62
129
|
end
|
63
130
|
end
|
64
131
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mailshield
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: '1.1'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- jana
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-08-
|
11
|
+
date: 2024-08-29 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: MailShield helps secure your application by identifying temporary or
|
14
14
|
disposable email domains.
|
@@ -19,6 +19,9 @@ extensions: []
|
|
19
19
|
extra_rdoc_files: []
|
20
20
|
files:
|
21
21
|
- lib/mailshield.rb
|
22
|
+
- lib/mailshield/docs/mailshield.png
|
23
|
+
- lib/mailshield/secure_email_validator.rb
|
24
|
+
- lib/mailshield/temp_domains.yml
|
22
25
|
- lib/mailshield/version.rb
|
23
26
|
homepage: https://github.com/janarthanan-shanmugam/mailshield
|
24
27
|
licenses:
|
@@ -43,7 +46,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
43
46
|
- !ruby/object:Gem::Version
|
44
47
|
version: '0'
|
45
48
|
requirements: []
|
46
|
-
rubygems_version: 3.5.
|
49
|
+
rubygems_version: 3.5.18
|
47
50
|
signing_key:
|
48
51
|
specification_version: 4
|
49
52
|
summary: A gem to detect temporary or disposable email domains.
|