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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b14ffd727fa0a54fbce427772f17317f28743bdcd87ec2f204bf89fc25530f3e
4
- data.tar.gz: 6e593d1bac55e212d43d875692a2ae9d6ae10a451409e779f188531849bdac78
3
+ metadata.gz: 75a143d6ea43f54fb93d97111b110549b11af8e4095d8633a0983d29bc24d48a
4
+ data.tar.gz: 7a7f03578febe47f990afc419a87af3b0c2eb3100adf9738c8f8188511bc779d
5
5
  SHA512:
6
- metadata.gz: 7bff616725f5ee9c1ea1d8cf49777ad6a0e83915bf1e48639e771785b84df7e0cd3db9f55aa7093477cee93fdaf93c241c3b475d3c159f1dd31cfb95afbf7ce4
7
- data.tar.gz: acd38d78a78e94edf2df0e8f17b5debe32aca7ce17d2657df69323ff9909fa4c72f2f9d3023c3a937f535a12242525fc9ebf54209159fcacf5790b9ccb5e3fc7
6
+ metadata.gz: dcbab4e23187ea34d25a7f6a8a63083ed006d5456a40ac089b1e1dad4cab5c2dbc3d31833da47b03a6a49357d95cf0a7655805528ea1b9b14fa84748014f991b
7
+ data.tar.gz: 27ce04f2c1422ee26667c891d037c55bffc55aa1865dd66d62449469dbcdd90577c60b8eb4aa8c48dfde299b1445fb3c1f86002de9c034a3074a721db196b853
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Mailshield
4
- VERSION = "0.1.0"
4
+ VERSION = "0.1.1"
5
5
  end
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
- def self.temporary_email?(email)
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
- return true if known_temp_domain?(domain)
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
- return true if suspicious_mx_records?(mx_records)
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
- return true unless spf_record?(domain)
18
- return true unless dmarc_record?(domain)
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
- def self.extract_domain(email)
104
+ private
105
+
106
+ def extract_domain(email)
24
107
  email.split('@').last.downcase
25
108
  end
26
109
 
27
- def self.known_temp_domain?(domain)
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 self.fetch_mx_records(domain)
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 self.suspicious_mx_records?(mx_records)
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 self.spf_record?(domain)
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 self.dmarc_record?(domain)
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 self.fetch_txt_records(domain)
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/
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mailshield
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - jana