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.
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Mailshield
4
- VERSION = "0.1.0"
4
+ VERSION = "1.1"
5
5
  end
data/lib/mailshield.rb CHANGED
@@ -1,64 +1,131 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "mailshield/version"
3
+ require_relative 'mailshield/version'
4
4
  require 'resolv'
5
+ require 'csv'
6
+ require 'net/smtp'
5
7
 
6
8
  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
+ 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
- mx_records = fetch_mx_records(domain)
15
- return true if suspicious_mx_records?(mx_records)
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
- false
40
+ def verify_address(email)
41
+ smtp_verify_email(email)
21
42
  end
43
+ private
22
44
 
23
- def self.extract_domain(email)
24
- email.split('@').last.downcase
45
+ def reset_caches
46
+ @dns_cache = {}
47
+ @smtp_cache = {}
25
48
  end
26
49
 
27
- def self.known_temp_domain?(domain)
28
- TEMP_DOMAINS.include?(domain)
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 self.fetch_mx_records(domain)
32
- Resolv::DNS.open do |dns|
33
- mx_records = dns.getresources(domain, Resolv::DNS::Resource::IN::MX)
34
- mx_records.map(&:exchange).map(&:to_s)
35
- end
36
- rescue Resolv::ResolvError
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 self.suspicious_mx_records?(mx_records)
41
- suspicious_patterns = [/mailinator/, /tempmail/, /guerrillamail/]
42
- mx_records.any? { |mx| suspicious_patterns.any? { |pattern| mx.match?(pattern) } }
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 self.spf_record?(domain)
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 self.dmarc_record?(domain)
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 self.fetch_txt_records(domain)
56
- Resolv::DNS.open do |dns|
57
- records = dns.getresources(domain, Resolv::DNS::Resource::IN::TXT)
58
- records.map(&:data)
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
- rescue Resolv::ResolvError
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: 0.1.0
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-25 00:00:00.000000000 Z
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.11
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.