mailshield 0.1.1 → 1.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 75a143d6ea43f54fb93d97111b110549b11af8e4095d8633a0983d29bc24d48a
4
- data.tar.gz: 7a7f03578febe47f990afc419a87af3b0c2eb3100adf9738c8f8188511bc779d
3
+ metadata.gz: 47694cab766631dd24097db7e130aeac98bbc12f2ad4aef7a5f065811796ea71
4
+ data.tar.gz: 52d6019eb905e12d7d4e5d6eee0cae2c36fba7e301a5e897c3350fa26aeb632f
5
5
  SHA512:
6
- metadata.gz: dcbab4e23187ea34d25a7f6a8a63083ed006d5456a40ac089b1e1dad4cab5c2dbc3d31833da47b03a6a49357d95cf0a7655805528ea1b9b14fa84748014f991b
7
- data.tar.gz: 27ce04f2c1422ee26667c891d037c55bffc55aa1865dd66d62449469dbcdd90577c60b8eb4aa8c48dfde299b1445fb3c1f86002de9c034a3074a721db196b853
6
+ metadata.gz: 41ec5aa1ae5db31d9e0e437eac3dc85da10561cecc9c1b57fe793fccda7990f7a707695c4f794c0518ed2fb23c99767daa18a7cca8488e7792b939e5a71d432e
7
+ data.tar.gz: 887fe66ea4b30711689cea1346c9466d326cb13c927b6e957c6d6d035ef908f0a5739751f27e6af17c4dbdb3d6df9d5bca4bf89087064fbc3d2427f8ce4a717b
@@ -0,0 +1,94 @@
1
+ # frozen_string_literal: true
2
+ require 'set'
3
+
4
+ module MailShield
5
+ KNOWN_DISPOSABLE_DOMAINS = Set.new(
6
+ [
7
+ '10mail.org', '10minut.com.pl', '10minut.xyz', '10minutemail.nl', '11163.com',
8
+ '1secmail.com', '1secmail.net', '1secmail.org', '20minutemail.com', '2chmail.net',
9
+ '30wave.com', '4warding.net', '55hosting.net', '5july.org', '5ymail.com',
10
+ '672643.net', '6ip.us', '99.com', 'acentri.com', 'afrobacon.com',
11
+ 'ahk.jp', 'aiafhg.com', 'airsi.de', 'akgq701.com', 'aleeas.com',
12
+ 'alienware13.com', 'ama-trans.de', 'amiri.net', 'ano-mail.net', 'anonaddy.com',
13
+ 'anonmails.de', 'apkmd.com', 'armyspy.com', 'asorent.com', 'augmentationtechnology.com',
14
+ 'aver.com', 'averdov.com', 'axsup.net', 'b2cmail.de', 'banit.club',
15
+ 'bearsarefuzzy.com', 'belamail.org', 'bitwhites.top', 'bladesmail.net', 'bobmail.info',
16
+ 'boxformail.in', 'ccmail.uk', 'ceed.se', 'chammy.info', 'cheaphub.net',
17
+ 'choco.la', 'choicemail1.com', 'chong-mail.net', 'cocoro.uk', 'cocovpn.com',
18
+ 'crazymailing.com', 'cream.pink', 'cutradition.com', 'cuvox.de', 'd1yun.com',
19
+ 'd3p.dk', 'dab.ro', 'dandikmail.com', 'datum2.com', 'dayrep.com',
20
+ 'desoz.com', 'devnullmail.com', 'digdig.org', 'digitalsanctuary.com', 'dispo.in',
21
+ 'dispostable.com', 'dndent.com', 'domozmail.com', 'drdrb.com', 'drevo.si',
22
+ 'dropmail.me', 'dukedish.com', 'durandinterstellar.com', 'e4ward.com', 'eay.jp',
23
+ 'ee1.pl', 'efxs.ca', 'einmalmail.de', 'einrot.com', 'eintagsmail.de',
24
+ 'email.com', 'email.it', 'email60.com', 'emailinfive.com', 'emailresort.com',
25
+ 'emailxfer.com', 'emeraldwebmail.com', 'emlhub.com', 'emlpro.com', 'emltmp.com',
26
+ 'envy17.com', 'esemay.com', 'ether123.net', 'ethereum1.top', 'ethersports.org',
27
+ 'evyush.com', 'exdonuts.com', 'express.net.ua', 'eyepaste.com', 'fanclub.pm',
28
+ 'fangoh.com', 'fantasymail.de', 'fastmail.com', 'fatflap.com', 'fightallspam.com',
29
+ 'fiifke.de', 'findu.pl', 'fleckens.hu', 'foobarbot.net', 'fr33mail.info',
30
+ 'freeinbox.email', 'freeml.net', 'fukurou.ch', 'fuwamofu.com', 'fuwari.be',
31
+ 'gafy.net', 'gambling.com', 'gehensiemirnichtaufdensack.de', 'gelitik.in', 'geronra.com',
32
+ 'getmails.eu', 'getover.de', 'givememail.club', 'gmatch.org', 'go2vpn.net',
33
+ 'golemico.com', 'greenhousemail.com', 'guerillamail.biz', 'guerrillamail.com', 'gustr.com',
34
+ 'haltospam.com', 'hamham.uk', 'harakirimail.com', 'haydoo.com', 'headstrong.de',
35
+ 'heisei.be', 'herp.in', 'herpderp.nl', 'hidemyass.com', 'hidzz.com',
36
+ 'hola.org', 'honeys.be', 'honor-8.com', 'hornyalwary.top', 'hostcalls.com',
37
+ 'hotmai.com', 'hotsoup.be', 'ichigo.me', 'icx.ro', 'ieatspam.eu',
38
+ 'ige.es', 'imails.info', 'imstations.com', 'in-ulm.de', 'inboxalias.com',
39
+ 'inkomail.com', 'instaddr.ch', 'interstats.org', 'ip6.li', 'janproz.com',
40
+ 'jobposts.net', 'jourrapide.com', 'kagi.be', 'katztube.com', 'kbox.li',
41
+ 'kkmail.be', 'kksm.be', 'klipschx12.com', 'kmail.li', 'koszmail.pl',
42
+ 'kpay.be', 'kpost.be', 'kulturbetrieb.info', 'labworld.org', 'lee.mx',
43
+ 'lenovog4.com', 'lgxscreen.com', 'lifetotech.com', 'ligsb.com', 'lillemap.net',
44
+ 'locanto1.club', 'locantofuck.top', 'lukop.dk', 'macr2.com', 'macromaid.com',
45
+ 'magim.be', 'mail.darmajaya.ac.id', 'mail.ru', 'mail.tm', 'mail21.cc',
46
+ 'mailcker.com', 'maildrop.cc', 'maildx.com', 'mailfs.com', 'mailguard.me',
47
+ 'mailinater.com', 'mailinator.com', 'mailinator.net', 'mailinator.us', 'mailsac.com',
48
+ 'mailscrap.com', 'mailshell.com', 'mailtechx.com', 'mailtothis.com', 'mama3.org',
49
+ 'manybrain.com', 'mbox.re', 'meantinc.com', 'mepost.pw', 'merry.pink',
50
+ 'metalunits.com', 'midiharmonica.com', 'mirai.re', 'mobilevpn.top', 'mofu.be',
51
+ 'moimoi.re', 'monumentmail.com', 'morsin.com', 'mt2009.com', 'mt2014.com',
52
+ 'mt2015.com', 'mybitti.de', 'mymail-in.net', 'mymailoasis.com', 'mypacks.net',
53
+ 'myspamless.com', 'mystvpn.com', 'mytrashmail.com', 'na-cat.com', 'nada.email',
54
+ 'nagi.be', 'nanonym.ch', 'neko2.net', 'nekochan.fr', 'nervmich.net',
55
+ 'nextstopvalhalla.com', 'nezdiro.org', 'nezumi.be', 'niseko.be', 'nospamfor.us',
56
+ 'notmailinator.com', 'ntlhelp.net', 'nurfuerspam.de', 'nutpa.net', 'nypato.com',
57
+ 'okinawa.li', 'onemail.host', 'onetm.jp', 'online.ms', 'oopi.org',
58
+ 'ovomail.co', 'owleyes.ch', 'oxopoha.com', 'p33.org', 'pancakemail.com',
59
+ 'pavilionx2.com', 'payperex2.com', 'pecinan.net', 'pingir.com', 'placebomail10.com',
60
+ 'plw.me', 'porsh.net', 'pratikmail.com', 'pratikmail.org', 'prin.be',
61
+ 'privy-mail.de', 'pro-tag.org', 'projectcl.com', 'psh.me', 'put2.net',
62
+ 'putthisinyourspamdatabase.com', 'qtum-ico.com', 'quickemail.info', 'quicksend.ch', 'rabin.ca',
63
+ 'rapt.be', 'reallymymail.com', 'recode.me', 'redfeathercrow.com', 'reftoken.net',
64
+ 'rhyta.com', 'rootfest.net', 'royal.net', 'ruru.be', 'safetymail.info',
65
+ 'schrott-email.de', 'sendnow.win', 'sendspamhere.com', 'senseless-entertainment.com', 'sexyalwasmi.top',
66
+ 'shalar.net', 'sharklasers.com', 'shieldedmail.com', 'shieldemail.com', 'shipfromto.com',
67
+ 'shrib.com', 'sika3.com', 'simaenaga.com', 'simplelogin.co', 'sinaite.net',
68
+ 'slaskpost.se', 'sofia.re', 'sogetthis.com', 'spamdecoy.net', 'spamhereplease.com',
69
+ 'spamhole.com', 'stayhome.li', 'storiqax.top', 'storj99.com', 'storj99.top',
70
+ 'stromox.com', 'superrito.com', 'supersave.net', 'suremail.info', 'svk.jp',
71
+ 'tapi.re', 'teleworm.us', 'temp-mail.org', 'tempail.com', 'tempemail.net',
72
+ 'tempmail.dev', 'tempmail2.com', 'tempmailer.com', 'tempmailo.com', 'tensi.org',
73
+ 'testudine.com', 'thankyou2010.com', 'thisisnotmyrealemail.com', 'throwam.com', 'tilien.com',
74
+ 'tmail.ws', 'tokem.co', 'topless-new-world.com', 'topmail.com', 'torba.cc',
75
+ 'trickmail.net', 'trickmail.org', 'trixtrux1.com', 'truckeremail.net', 'tryalert.com',
76
+ 'turual.com', 'twddos.com', 'unmail.ru', 'uploadnolimit.com', 'uw5t6ds54.com',
77
+ 'ventolin.org', 'vintomaper.com', 'vmailcloud.com', 'vubby.com', 'vuiy.pw',
78
+ 'warpmail.net', 'watch-harry-potter.com', 'watchfulli.com', 'wep68.com', 'wh4f.org',
79
+ 'willhackforfood.biz', 'winbroadband.co.uk', 'workshop-liberal.com', 'wronghead.com', 'wwwmail.win',
80
+ 'x24.com', 'xn--9kq967o.com', 'xn--cg7bx4b4x1b.com', 'xsynergy.top', 'xxhamsterxx.com',
81
+ 'yepmail.net', 'yesspam.com', 'yo-site.com', 'yogotemail.com', 'yopmail.fr',
82
+ 'yopmail.gq', 'yopmail.net', 'yopmail.org', 'yourinbox.email', 'yuurok.com',
83
+ 'zainmax.net', 'ze.gy', 'zipmail.com', 'zoemail.net', 'zombie-hive.com',
84
+ 'zsero.com', 'zumrot.com'
85
+ ]
86
+ )
87
+
88
+
89
+ class << self
90
+ def disposable_domain?(domain)
91
+ KNOWN_DISPOSABLE_DOMAINS.include?(domain)
92
+ end
93
+ end
94
+ end
Binary file
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_model'
4
+
5
+ module MailShield
6
+ class SecureEmailValidator < ActiveModel::Validator
7
+ def validate(record)
8
+ email = record.email
9
+ return if email.blank?
10
+
11
+ result = MailShield.validate_email(email)
12
+
13
+ return if result[:valid]
14
+
15
+ record.errors.add(:email, result[:issue])
16
+ end
17
+ end
18
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Mailshield
4
- VERSION = "0.1.1"
4
+ VERSION = '1.2'
5
5
  end
data/lib/mailshield.rb CHANGED
@@ -1,133 +1,89 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "mailshield/version"
3
+ require_relative 'mailshield/version'
4
+ require_relative 'mailshield/disposable_domains'
4
5
  require 'resolv'
5
6
  require 'csv'
7
+ require 'net/smtp'
6
8
 
7
9
  module MailShield
10
+ EMAIL_REGEX = /\A[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}\z/.freeze
8
11
 
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
12
+ class ValidationError < StandardError; end
13
+ class InvalidFormatError < ValidationError; end
14
+ class DomainNotFoundError < ValidationError; end
15
+ class SPFError < ValidationError; end
16
+ class DMARCError < ValidationError; end
17
+ class SMTPError < ValidationError; end
28
18
 
29
19
  class << self
30
- TEMP_DOMAINS = %w[mailinator.com tempmail.com guerrillamail.com].freeze # Known temporary domains
31
-
20
+ attr_accessor :dns_cache, :smtp_cache
32
21
 
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
22
+ def validate_email(email, verify_by_send: false)
23
+ reset_caches
46
24
 
47
25
  domain = extract_domain(email)
48
26
 
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
54
-
55
- mx_records = fetch_mx_records(domain)
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
65
-
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
27
+ begin
28
+ validate_format!(email)
29
+ validate_domain!(domain)
30
+ validate_spf!(domain)
31
+ validate_dmarc!(domain)
32
+ validate_smtp!(email) if verify_by_send
33
+ rescue ValidationError => e
34
+ return { valid: false, issues: [e.message] }
70
35
  end
71
36
 
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
37
+ { valid: true }
38
+ end
77
39
 
78
- result
40
+ def verify_address(email)
41
+ smtp_verify_email(email)
79
42
  end
80
43
 
81
- def validate_emails_from_csv(input_file_path)
82
- email_results = []
44
+ private
83
45
 
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
46
+ def reset_caches
47
+ @dns_cache = {}
48
+ @smtp_cache = {}
49
+ end
89
50
 
90
- ValidationResult.new(email_results)
51
+ def validate_format!(email)
52
+ raise InvalidFormatError, 'The email address format is invalid.' unless valid_email_format?(email)
91
53
  end
92
54
 
93
- def validate_email_for_csv(email)
94
- return false unless valid_email_format?(email)
95
- domain = extract_domain(email)
55
+ def validate_domain!(domain)
56
+ raise DomainNotFoundError, 'Email Not Found.' if fetch_mx_records(domain).empty?
57
+ end
96
58
 
97
- return false if known_temp_domain?(domain)
98
- return false unless spf_record?(domain)
99
- return false unless dmarc_record?(domain)
59
+ def validate_spf!(domain)
60
+ raise SPFError, 'Temporary / Disposable Email' unless spf_record?(domain)
61
+ end
100
62
 
101
- true
63
+ def validate_dmarc!(domain)
64
+ raise DMARCError, 'Temporary / Disposable Email' unless dmarc_record?(domain)
102
65
  end
103
66
 
104
- private
67
+ def validate_smtp!(email)
68
+ raise SMTPError, 'Email Address Not Found' unless smtp_verify_email(email)
69
+ end
105
70
 
106
71
  def extract_domain(email)
107
72
  email.split('@').last.downcase
108
73
  end
109
74
 
110
75
  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)
116
- TEMP_DOMAINS.include?(domain)
76
+ EMAIL_REGEX.match?(email)
117
77
  end
118
78
 
119
79
  def fetch_mx_records(domain)
120
- Resolv::DNS.open do |dns|
121
- mx_records = dns.getresources(domain, Resolv::DNS::Resource::IN::MX)
122
- mx_records.map(&:exchange).map(&:to_s)
80
+ dns_cache[domain] ||= begin
81
+ Resolv::DNS.open do |dns|
82
+ dns.getresources(domain, Resolv::DNS::Resource::IN::MX).map(&:exchange).map(&:to_s)
83
+ end
84
+ rescue Resolv::ResolvError
85
+ []
123
86
  end
124
- rescue Resolv::ResolvError
125
- []
126
- end
127
-
128
- def suspicious_mx_records?(mx_records)
129
- suspicious_patterns = [/mailinator/, /tempmail/, /guerrillamail/]
130
- mx_records.any? { |mx| suspicious_patterns.any? { |pattern| mx.match?(pattern) } }
131
87
  end
132
88
 
133
89
  def spf_record?(domain)
@@ -141,27 +97,36 @@ module MailShield
141
97
  end
142
98
 
143
99
  def fetch_txt_records(domain)
144
- Resolv::DNS.open do |dns|
145
- records = dns.getresources(domain, Resolv::DNS::Resource::IN::TXT)
146
- records.map(&:data)
100
+ dns_cache[domain] ||= begin
101
+ Resolv::DNS.open do |dns|
102
+ dns.getresources(domain, Resolv::DNS::Resource::IN::TXT).map(&:data)
103
+ end
104
+ rescue Resolv::ResolvError
105
+ []
147
106
  end
148
- rescue Resolv::ResolvError
149
- []
150
107
  end
151
- end
152
- end
153
108
 
109
+ def smtp_verify_email(email)
110
+ domain = extract_domain(email)
111
+ smtp_server = smtp_cache[domain] ||= get_smtp_server(domain)
112
+
113
+ return false unless smtp_server
114
+
115
+ begin
116
+ Net::SMTP.start(smtp_server, 25, 'localhost') do |smtp|
117
+ smtp.helo('localhost')
118
+ smtp.mailfrom('test@example.com')
119
+ response = smtp.rcptto(email)
120
+ response == '250 OK'
121
+ end
122
+ rescue Net::SMTPFatalError, Net::SMTPServerBusy, Net::SMTPSyntaxError, Errno::ECONNREFUSED
123
+ false
124
+ end
125
+ end
154
126
 
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/
127
+ def get_smtp_server(domain)
128
+ mx_records = fetch_mx_records(domain)
129
+ mx_records.first
130
+ end
131
+ end
132
+ 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.1
4
+ version: '1.2'
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-09-03 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/disposable_domains.rb
23
+ - lib/mailshield/docs/mailshield.png
24
+ - lib/mailshield/secure_email_validator.rb
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.