mailshield 0.1.0 → 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/version.rb +1 -1
- data/lib/mailshield.rb +118 -15
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 75a143d6ea43f54fb93d97111b110549b11af8e4095d8633a0983d29bc24d48a
|
4
|
+
data.tar.gz: 7a7f03578febe47f990afc419a87af3b0c2eb3100adf9738c8f8188511bc779d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dcbab4e23187ea34d25a7f6a8a63083ed006d5456a40ac089b1e1dad4cab5c2dbc3d31833da47b03a6a49357d95cf0a7655805528ea1b9b14fa84748014f991b
|
7
|
+
data.tar.gz: 27ce04f2c1422ee26667c891d037c55bffc55aa1865dd66d62449469dbcdd90577c60b8eb4aa8c48dfde299b1445fb3c1f86002de9c034a3074a721db196b853
|
data/lib/mailshield/version.rb
CHANGED
data/lib/mailshield.rb
CHANGED
@@ -2,33 +2,121 @@
|
|
2
2
|
|
3
3
|
require_relative "mailshield/version"
|
4
4
|
require 'resolv'
|
5
|
+
require 'csv'
|
5
6
|
|
6
7
|
module MailShield
|
7
|
-
class DomainChecker
|
8
|
-
TEMP_DOMAINS = %w[mailinator.com tempmail.com guerrillamail.com].freeze # If any one want to contribute, please add the known more domains here.
|
9
8
|
|
10
|
-
|
9
|
+
class ValidationResult
|
10
|
+
attr_reader :emails
|
11
|
+
|
12
|
+
def initialize(emails)
|
13
|
+
@emails = emails
|
14
|
+
end
|
15
|
+
|
16
|
+
def valid_emails
|
17
|
+
emails.select { |email| email[:valid] }.map { |email| email[:email] }
|
18
|
+
end
|
19
|
+
|
20
|
+
def invalid_emails
|
21
|
+
emails.reject { |email| email[:valid] }.map { |email| email[:email] }
|
22
|
+
end
|
23
|
+
|
24
|
+
def get_emails
|
25
|
+
emails.map { |email| { email: email[:email], valid: email[:valid] } }
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class << self
|
30
|
+
TEMP_DOMAINS = %w[mailinator.com tempmail.com guerrillamail.com].freeze # Known temporary domains
|
31
|
+
|
32
|
+
|
33
|
+
# Main method to validate an email and return results in a hash
|
34
|
+
def validate_email(email)
|
35
|
+
result = {
|
36
|
+
email: email,
|
37
|
+
valid: true,
|
38
|
+
issues: []
|
39
|
+
}
|
40
|
+
|
41
|
+
unless valid_email_format?(email)
|
42
|
+
result[:valid] = false
|
43
|
+
result[:issues] << "The email address format is invalid."
|
44
|
+
return result
|
45
|
+
end
|
46
|
+
|
11
47
|
domain = extract_domain(email)
|
12
|
-
|
48
|
+
|
49
|
+
if known_temp_domain?(domain)
|
50
|
+
result[:valid] = false
|
51
|
+
result[:issues] << "The email domain is known to be temporary or disposable."
|
52
|
+
return result
|
53
|
+
end
|
13
54
|
|
14
55
|
mx_records = fetch_mx_records(domain)
|
15
|
-
|
56
|
+
if mx_records.empty?
|
57
|
+
result[:valid] = false
|
58
|
+
result[:issues] << "The email domain does not have valid mail exchange records."
|
59
|
+
return result
|
60
|
+
elsif suspicious_mx_records?(mx_records)
|
61
|
+
result[:valid] = false
|
62
|
+
result[:issues] << "The email domain's mail exchange records suggest it might be used for temporary or disposable email services."
|
63
|
+
return result
|
64
|
+
end
|
16
65
|
|
17
|
-
|
18
|
-
|
66
|
+
unless spf_record?(domain)
|
67
|
+
result[:valid] = false
|
68
|
+
result[:issues] << "The email domain is missing important records that help confirm its authenticity."
|
69
|
+
return result
|
70
|
+
end
|
71
|
+
|
72
|
+
unless dmarc_record?(domain)
|
73
|
+
result[:valid] = false
|
74
|
+
result[:issues] << "The email domain is missing records that help protect against email fraud."
|
75
|
+
return result
|
76
|
+
end
|
77
|
+
|
78
|
+
result
|
79
|
+
end
|
80
|
+
|
81
|
+
def validate_emails_from_csv(input_file_path)
|
82
|
+
email_results = []
|
83
|
+
|
84
|
+
CSV.foreach(input_file_path, headers: true) do |row|
|
85
|
+
email = row['email']
|
86
|
+
validation_result = validate_email_for_csv(email)
|
87
|
+
email_results << { email: email, valid: validation_result }
|
88
|
+
end
|
89
|
+
|
90
|
+
ValidationResult.new(email_results)
|
91
|
+
end
|
92
|
+
|
93
|
+
def validate_email_for_csv(email)
|
94
|
+
return false unless valid_email_format?(email)
|
95
|
+
domain = extract_domain(email)
|
19
96
|
|
20
|
-
false
|
97
|
+
return false if known_temp_domain?(domain)
|
98
|
+
return false unless spf_record?(domain)
|
99
|
+
return false unless dmarc_record?(domain)
|
100
|
+
|
101
|
+
true
|
21
102
|
end
|
22
103
|
|
23
|
-
|
104
|
+
private
|
105
|
+
|
106
|
+
def extract_domain(email)
|
24
107
|
email.split('@').last.downcase
|
25
108
|
end
|
26
109
|
|
27
|
-
def
|
110
|
+
def valid_email_format?(email)
|
111
|
+
email_regex = /\A[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}\z/
|
112
|
+
!!(email =~ email_regex)
|
113
|
+
end
|
114
|
+
|
115
|
+
def known_temp_domain?(domain)
|
28
116
|
TEMP_DOMAINS.include?(domain)
|
29
117
|
end
|
30
118
|
|
31
|
-
def
|
119
|
+
def fetch_mx_records(domain)
|
32
120
|
Resolv::DNS.open do |dns|
|
33
121
|
mx_records = dns.getresources(domain, Resolv::DNS::Resource::IN::MX)
|
34
122
|
mx_records.map(&:exchange).map(&:to_s)
|
@@ -37,22 +125,22 @@ module MailShield
|
|
37
125
|
[]
|
38
126
|
end
|
39
127
|
|
40
|
-
def
|
128
|
+
def suspicious_mx_records?(mx_records)
|
41
129
|
suspicious_patterns = [/mailinator/, /tempmail/, /guerrillamail/]
|
42
130
|
mx_records.any? { |mx| suspicious_patterns.any? { |pattern| mx.match?(pattern) } }
|
43
131
|
end
|
44
132
|
|
45
|
-
def
|
133
|
+
def spf_record?(domain)
|
46
134
|
spf_records = fetch_txt_records(domain)
|
47
135
|
spf_records.any? { |record| record.include?('v=spf1') }
|
48
136
|
end
|
49
137
|
|
50
|
-
def
|
138
|
+
def dmarc_record?(domain)
|
51
139
|
dmarc_records = fetch_txt_records("_dmarc.#{domain}")
|
52
140
|
dmarc_records.any? { |record| record.include?('v=DMARC1') }
|
53
141
|
end
|
54
142
|
|
55
|
-
def
|
143
|
+
def fetch_txt_records(domain)
|
56
144
|
Resolv::DNS.open do |dns|
|
57
145
|
records = dns.getresources(domain, Resolv::DNS::Resource::IN::TXT)
|
58
146
|
records.map(&:data)
|
@@ -62,3 +150,18 @@ module MailShield
|
|
62
150
|
end
|
63
151
|
end
|
64
152
|
end
|
153
|
+
|
154
|
+
|
155
|
+
# SPF Record Explanation:
|
156
|
+
#
|
157
|
+
# SPF (Sender Policy Framework): A mechanism that helps email servers verify that an email claiming to come from a specific domain is actually sent by an authorized mail server.
|
158
|
+
# This prevents spammers from sending messages with forged "From" addresses.
|
159
|
+
#
|
160
|
+
# DMARC Record Explanation:
|
161
|
+
#
|
162
|
+
# DMARC (Domain-based Message Authentication, Reporting, and Conformance): A protocol that builds on SPF and DKIM (DomainKeys Identified Mail) to help email domain owners protect their domain from being used in email spoofing.
|
163
|
+
# It provides a way for domain owners to publish policies about their email authentication practices and how receiving mail servers should enforce them.
|
164
|
+
#
|
165
|
+
# References:
|
166
|
+
# https://www.dkim.org/
|
167
|
+
# https://www.cloudflare.com/en-gb/learning/dns/dns-records/dns-spf-record/
|