truemail 1.6.0 → 1.9.0
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/.codeclimate.yml +1 -1
- data/.reek.yml +4 -0
- data/.rubocop.yml +138 -6
- data/CHANGELOG.md +245 -1
- data/Gemfile.lock +60 -74
- data/README.md +236 -82
- data/lib/truemail.rb +3 -4
- data/lib/truemail/audit/base.rb +8 -0
- data/lib/truemail/audit/dns.rb +26 -0
- data/lib/truemail/audit/ip.rb +28 -0
- data/lib/truemail/audit/ptr.rb +8 -36
- data/lib/truemail/auditor.rb +7 -5
- data/lib/truemail/configuration.rb +3 -4
- data/lib/truemail/core.rb +29 -24
- data/lib/truemail/executor.rb +11 -0
- data/lib/truemail/log/serializer/auditor_json.rb +25 -0
- data/lib/truemail/log/serializer/base.rb +17 -41
- data/lib/truemail/log/serializer/validator_base.rb +45 -0
- data/lib/truemail/log/serializer/{json.rb → validator_json.rb} +1 -1
- data/lib/truemail/log/serializer/{text.rb → validator_text.rb} +2 -2
- data/lib/truemail/logger.rb +1 -1
- data/lib/truemail/validate/mx.rb +1 -0
- data/lib/truemail/validator.rb +3 -3
- data/lib/truemail/version.rb +1 -1
- data/truemail.gemspec +18 -10
- metadata +50 -34
data/lib/truemail.rb
CHANGED
@@ -1,17 +1,16 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
3
|
+
require_relative 'truemail/core'
|
4
4
|
|
5
5
|
module Truemail
|
6
6
|
INCOMPLETE_CONFIG = 'verifier_email is required parameter'
|
7
7
|
NOT_CONFIGURED = 'use Truemail.configure before or pass custom configuration'
|
8
8
|
|
9
9
|
class << self
|
10
|
-
def configuration
|
10
|
+
def configuration(&block)
|
11
11
|
@configuration ||= begin
|
12
12
|
return unless block_given?
|
13
|
-
configuration = Truemail::Configuration.new
|
14
|
-
yield(configuration)
|
13
|
+
configuration = Truemail::Configuration.new(&block)
|
15
14
|
raise_unless(configuration.complete?, Truemail::INCOMPLETE_CONFIG)
|
16
15
|
configuration
|
17
16
|
end
|
data/lib/truemail/audit/base.rb
CHANGED
@@ -3,12 +3,20 @@
|
|
3
3
|
module Truemail
|
4
4
|
module Audit
|
5
5
|
class Base < Truemail::Worker
|
6
|
+
require 'net/http'
|
7
|
+
require 'ipaddr'
|
8
|
+
require 'resolv'
|
9
|
+
|
6
10
|
private
|
7
11
|
|
8
12
|
def add_warning(message)
|
9
13
|
result.warnings[self.class.name.split('::').last.downcase.to_sym] = message
|
10
14
|
end
|
11
15
|
|
16
|
+
def current_host_ip
|
17
|
+
result.current_host_ip
|
18
|
+
end
|
19
|
+
|
12
20
|
def configuration
|
13
21
|
result.configuration
|
14
22
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Truemail
|
4
|
+
module Audit
|
5
|
+
class Dns < Truemail::Audit::Base
|
6
|
+
VERIFIER_DOMAIN_NOT_REFER = 'A-record of verifier domain not refers to current host ip address'
|
7
|
+
|
8
|
+
def run
|
9
|
+
return if verifier_domain_refer_to_current_host_ip?
|
10
|
+
add_warning(Truemail::Audit::Dns::VERIFIER_DOMAIN_NOT_REFER)
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def a_record
|
16
|
+
Truemail::Wrapper.call(configuration: configuration) do
|
17
|
+
Resolv::DNS.new.getaddress(verifier_domain).to_s
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def verifier_domain_refer_to_current_host_ip?
|
22
|
+
a_record.eql?(current_host_ip)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Truemail
|
4
|
+
module Audit
|
5
|
+
class Ip < Truemail::Audit::Base
|
6
|
+
GET_MY_IP_URL = 'https://api.ipify.org'
|
7
|
+
IPIFY_ERROR = 'impossible to detect current host address via third party service'
|
8
|
+
|
9
|
+
def run
|
10
|
+
return add_warning(Truemail::Audit::Ip::IPIFY_ERROR) unless detect_current_host_ip
|
11
|
+
Truemail::Audit::Dns.check(result)
|
12
|
+
Truemail::Audit::Ptr.check(result)
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def detect_ip_via_ipify
|
18
|
+
Net::HTTP.get(URI(Truemail::Audit::Ip::GET_MY_IP_URL))
|
19
|
+
end
|
20
|
+
|
21
|
+
def detect_current_host_ip
|
22
|
+
result.current_host_ip = Truemail::Wrapper.call(configuration: configuration) do
|
23
|
+
detect_ip_via_ipify
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/lib/truemail/audit/ptr.rb
CHANGED
@@ -3,37 +3,19 @@
|
|
3
3
|
module Truemail
|
4
4
|
module Audit
|
5
5
|
class Ptr < Truemail::Audit::Base
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
GET_MY_IP_URL = 'https://api.ipify.org'
|
10
|
-
IPIFY_ERROR = 'impossible to detect current host address via third party service'
|
11
|
-
PTR_NOT_FOUND = 'ptr record for current host address was not found'
|
12
|
-
PTR_NOT_REFER = 'ptr record does not reference to current verifier domain'
|
13
|
-
VERIFIER_DOMAIN_NOT_REFER = 'a record of verifier domain not refers to current host address'
|
6
|
+
PTR_NOT_FOUND = 'PTR-record for current host ip address was not found'
|
7
|
+
PTR_NOT_REFER = 'PTR-record does not reference to current verifier domain'
|
14
8
|
|
15
9
|
def run
|
16
|
-
return
|
17
|
-
return if
|
18
|
-
|
19
|
-
return if verifier_domain_refer_to_current_host_address?
|
20
|
-
add_warning(Truemail::Audit::Ptr::VERIFIER_DOMAIN_NOT_REFER)
|
10
|
+
return add_warning(Truemail::Audit::Ptr::PTR_NOT_FOUND) if ptr_records.empty?
|
11
|
+
return if ptr_refer_to_verifier_domain?
|
12
|
+
add_warning(Truemail::Audit::Ptr::PTR_NOT_REFER)
|
21
13
|
end
|
22
14
|
|
23
15
|
private
|
24
16
|
|
25
|
-
def detect_ip_via_ipify
|
26
|
-
Net::HTTP.get(URI(Truemail::Audit::Ptr::GET_MY_IP_URL))
|
27
|
-
end
|
28
|
-
|
29
|
-
def current_host_address
|
30
|
-
@current_host_address ||= Truemail::Wrapper.call(configuration: configuration) do
|
31
|
-
IPAddr.new(detect_ip_via_ipify)
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
17
|
def current_host_reverse_lookup
|
36
|
-
|
18
|
+
IPAddr.new(current_host_ip).reverse
|
37
19
|
end
|
38
20
|
|
39
21
|
def ptr_records
|
@@ -44,18 +26,8 @@ module Truemail
|
|
44
26
|
end || []
|
45
27
|
end
|
46
28
|
|
47
|
-
def
|
48
|
-
|
49
|
-
end
|
50
|
-
|
51
|
-
def a_record
|
52
|
-
Truemail::Wrapper.call(configuration: configuration) do
|
53
|
-
Resolv::DNS.new.getaddress(verifier_domain).to_s
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
def verifier_domain_refer_to_current_host_address?
|
58
|
-
a_record.eql?(current_host_address.to_s)
|
29
|
+
def ptr_refer_to_verifier_domain?
|
30
|
+
ptr_records.include?(verifier_domain)
|
59
31
|
end
|
60
32
|
end
|
61
33
|
end
|
data/lib/truemail/auditor.rb
CHANGED
@@ -1,22 +1,24 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Truemail
|
4
|
-
class Auditor
|
5
|
-
Result = Struct.new(:warnings, :configuration, keyword_init: true) do
|
4
|
+
class Auditor < Truemail::Executor
|
5
|
+
Result = Struct.new(:current_host_ip, :warnings, :configuration, keyword_init: true) do
|
6
6
|
def initialize(warnings: {}, **args)
|
7
7
|
super
|
8
8
|
end
|
9
9
|
end
|
10
10
|
|
11
|
-
attr_reader :result
|
12
|
-
|
13
11
|
def initialize(configuration:)
|
14
12
|
@result = Truemail::Auditor::Result.new(configuration: configuration)
|
15
13
|
end
|
16
14
|
|
17
15
|
def run
|
18
|
-
Truemail::Audit::
|
16
|
+
Truemail::Audit::Ip.check(result)
|
19
17
|
self
|
20
18
|
end
|
19
|
+
|
20
|
+
def as_json
|
21
|
+
Truemail::Log::Serializer::AuditorJson.call(self)
|
22
|
+
end
|
21
23
|
end
|
22
24
|
end
|
@@ -20,9 +20,7 @@ module Truemail
|
|
20
20
|
:blacklisted_domains,
|
21
21
|
:logger
|
22
22
|
|
23
|
-
attr_accessor :whitelist_validation, :smtp_safe_check
|
24
|
-
|
25
|
-
alias retry_count connection_attempts
|
23
|
+
attr_accessor :whitelist_validation, :not_rfc_mx_lookup_flow, :smtp_safe_check
|
26
24
|
|
27
25
|
def initialize(&block)
|
28
26
|
instance_initializer.each do |instace_variable, value|
|
@@ -101,6 +99,7 @@ module Truemail
|
|
101
99
|
whitelisted_domains: [],
|
102
100
|
whitelist_validation: false,
|
103
101
|
blacklisted_domains: [],
|
102
|
+
not_rfc_mx_lookup_flow: false,
|
104
103
|
smtp_safe_check: false
|
105
104
|
}
|
106
105
|
end
|
@@ -110,7 +109,7 @@ module Truemail
|
|
110
109
|
end
|
111
110
|
|
112
111
|
def validate_arguments(argument, method)
|
113
|
-
constant = Truemail::RegexConstant.const_get("regex_#{method[/\A.+_(.+)
|
112
|
+
constant = Truemail::RegexConstant.const_get("regex_#{method[/\A.+_(.+)=\z/, 1]}_pattern".upcase)
|
114
113
|
raise_unless(argument, method, constant.match?(argument.to_s))
|
115
114
|
end
|
116
115
|
|
data/lib/truemail/core.rb
CHANGED
@@ -1,19 +1,20 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Truemail
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
4
|
+
require_relative '../truemail/version'
|
5
|
+
require_relative '../truemail/configuration'
|
6
|
+
require_relative '../truemail/worker'
|
7
|
+
require_relative '../truemail/executor'
|
8
|
+
require_relative '../truemail/wrapper'
|
9
|
+
require_relative '../truemail/auditor'
|
10
|
+
require_relative '../truemail/validator'
|
11
|
+
require_relative '../truemail/logger'
|
11
12
|
|
12
13
|
ConfigurationError = Class.new(StandardError)
|
13
14
|
|
14
15
|
ArgumentError = Class.new(StandardError) do
|
15
|
-
def initialize(
|
16
|
-
super("#{
|
16
|
+
def initialize(arg_value, arg_name)
|
17
|
+
super("#{arg_value} is not a valid #{arg_name}")
|
17
18
|
end
|
18
19
|
end
|
19
20
|
|
@@ -29,32 +30,36 @@ module Truemail
|
|
29
30
|
end
|
30
31
|
|
31
32
|
module RegexConstant
|
32
|
-
REGEX_DOMAIN = /[\p{L}0-9]+([
|
33
|
-
REGEX_EMAIL_PATTERN = /(?=\A.{6,255}\z)(\A([\p{L}0-9]+[\w
|
33
|
+
REGEX_DOMAIN = /[\p{L}0-9]+([\-.]{1}[\p{L}0-9]+)*\.\p{L}{2,63}/i.freeze
|
34
|
+
REGEX_EMAIL_PATTERN = /(?=\A.{6,255}\z)(\A([\p{L}0-9]+[\w|\-|.|+]*)@(#{REGEX_DOMAIN})\z)/.freeze
|
34
35
|
REGEX_DOMAIN_PATTERN = /(?=\A.{4,255}\z)(\A#{REGEX_DOMAIN}\z)/.freeze
|
35
36
|
REGEX_DOMAIN_FROM_EMAIL = /\A.+@(.+)\z/.freeze
|
36
37
|
REGEX_SMTP_ERROR_BODY_PATTERN = /(?=.*550)(?=.*(user|account|customer|mailbox)).*/i.freeze
|
37
38
|
end
|
38
39
|
|
39
40
|
module Audit
|
40
|
-
|
41
|
-
|
41
|
+
require_relative '../truemail/audit/base'
|
42
|
+
require_relative '../truemail/audit/ip'
|
43
|
+
require_relative '../truemail/audit/dns'
|
44
|
+
require_relative '../truemail/audit/ptr'
|
42
45
|
end
|
43
46
|
|
44
47
|
module Validate
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
48
|
+
require_relative '../truemail/validate/base'
|
49
|
+
require_relative '../truemail/validate/domain_list_match'
|
50
|
+
require_relative '../truemail/validate/regex'
|
51
|
+
require_relative '../truemail/validate/mx'
|
52
|
+
require_relative '../truemail/validate/smtp'
|
53
|
+
require_relative '../truemail/validate/smtp/response'
|
54
|
+
require_relative '../truemail/validate/smtp/request'
|
52
55
|
end
|
53
56
|
|
54
57
|
module Log
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
58
|
+
require_relative '../truemail/log/event'
|
59
|
+
require_relative '../truemail/log/serializer/base'
|
60
|
+
require_relative '../truemail/log/serializer/auditor_json'
|
61
|
+
require_relative '../truemail/log/serializer/validator_base'
|
62
|
+
require_relative '../truemail/log/serializer/validator_text'
|
63
|
+
require_relative '../truemail/log/serializer/validator_json'
|
59
64
|
end
|
60
65
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Truemail
|
4
|
+
module Log
|
5
|
+
module Serializer
|
6
|
+
class AuditorJson < Truemail::Log::Serializer::Base
|
7
|
+
def serialize
|
8
|
+
result.to_json
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def result
|
14
|
+
@result ||=
|
15
|
+
{
|
16
|
+
date: Time.now,
|
17
|
+
current_host_ip: executor_result.current_host_ip,
|
18
|
+
warnings: warnings(executor_result.warnings),
|
19
|
+
configuration: configuration
|
20
|
+
}
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -4,47 +4,35 @@ module Truemail
|
|
4
4
|
module Log
|
5
5
|
module Serializer
|
6
6
|
class Base
|
7
|
+
require 'json'
|
8
|
+
|
7
9
|
DEFAULT_GEM_VALUE = 'default gem value'
|
8
10
|
|
9
|
-
def self.call(
|
10
|
-
new(
|
11
|
+
def self.call(executor_instance)
|
12
|
+
new(executor_instance).serialize
|
11
13
|
end
|
12
14
|
|
13
|
-
def initialize(
|
14
|
-
@
|
15
|
-
@
|
16
|
-
@validation_configuration = validation_result.configuration
|
15
|
+
def initialize(executor_instance)
|
16
|
+
@executor_result = executor_instance.result
|
17
|
+
@executor_configuration = executor_result.configuration
|
17
18
|
end
|
18
19
|
|
19
20
|
def serialize; end
|
20
21
|
|
21
22
|
private
|
22
23
|
|
23
|
-
attr_reader :
|
24
|
+
attr_reader :executor_result, :executor_configuration
|
24
25
|
|
25
|
-
def errors
|
26
|
-
|
27
|
-
|
28
|
-
validation_errors
|
26
|
+
def errors(executor_result_target)
|
27
|
+
return if executor_result_target.empty?
|
28
|
+
executor_result_target
|
29
29
|
end
|
30
30
|
|
31
|
-
|
32
|
-
validation_smtp_debug = validation_result.smtp_debug
|
33
|
-
return unless validation_smtp_debug
|
34
|
-
validation_smtp_debug.map do |smtp_request|
|
35
|
-
smtp_response = smtp_request.response
|
36
|
-
{
|
37
|
-
mail_host: smtp_request.host,
|
38
|
-
port_opened: smtp_response.port_opened,
|
39
|
-
connection: smtp_response.connection,
|
40
|
-
errors: smtp_response.errors
|
41
|
-
}
|
42
|
-
end
|
43
|
-
end
|
31
|
+
alias warnings errors
|
44
32
|
|
45
33
|
%i[validation_type_by_domain whitelisted_domains blacklisted_domains].each do |method|
|
46
34
|
define_method(method) do
|
47
|
-
value =
|
35
|
+
value = executor_configuration.public_send(method)
|
48
36
|
return if value.empty?
|
49
37
|
value
|
50
38
|
end
|
@@ -52,7 +40,7 @@ module Truemail
|
|
52
40
|
|
53
41
|
%i[email_pattern smtp_error_body_pattern].each do |method|
|
54
42
|
define_method(method) do
|
55
|
-
value =
|
43
|
+
value = executor_configuration.public_send(method)
|
56
44
|
default_pattern = Truemail::RegexConstant.const_get(
|
57
45
|
(method.eql?(:email_pattern) ? :regex_email_pattern : :regex_smtp_error_body_pattern).upcase
|
58
46
|
)
|
@@ -64,27 +52,15 @@ module Truemail
|
|
64
52
|
def configuration
|
65
53
|
{
|
66
54
|
validation_type_by_domain: validation_type_by_domain,
|
67
|
-
whitelist_validation:
|
55
|
+
whitelist_validation: executor_configuration.whitelist_validation,
|
68
56
|
whitelisted_domains: whitelisted_domains,
|
69
57
|
blacklisted_domains: blacklisted_domains,
|
70
|
-
|
58
|
+
not_rfc_mx_lookup_flow: executor_configuration.not_rfc_mx_lookup_flow,
|
59
|
+
smtp_safe_check: executor_configuration.smtp_safe_check,
|
71
60
|
email_pattern: email_pattern,
|
72
61
|
smtp_error_body_pattern: smtp_error_body_pattern
|
73
62
|
}
|
74
63
|
end
|
75
|
-
|
76
|
-
def result
|
77
|
-
@result ||=
|
78
|
-
{
|
79
|
-
date: Time.now,
|
80
|
-
email: validation_result.email,
|
81
|
-
validation_type: validation_type,
|
82
|
-
success: validation_result.success,
|
83
|
-
errors: errors,
|
84
|
-
smtp_debug: smtp_debug,
|
85
|
-
configuration: configuration
|
86
|
-
}
|
87
|
-
end
|
88
64
|
end
|
89
65
|
end
|
90
66
|
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Truemail
|
4
|
+
module Log
|
5
|
+
module Serializer
|
6
|
+
class ValidatorBase < Truemail::Log::Serializer::Base
|
7
|
+
def initialize(executor_instance)
|
8
|
+
@validation_type = executor_instance.validation_type
|
9
|
+
super
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
attr_reader :validation_type
|
15
|
+
|
16
|
+
def smtp_debug
|
17
|
+
validation_smtp_debug = executor_result.smtp_debug
|
18
|
+
return unless validation_smtp_debug
|
19
|
+
validation_smtp_debug.map do |smtp_request|
|
20
|
+
smtp_response = smtp_request.response
|
21
|
+
{
|
22
|
+
mail_host: smtp_request.host,
|
23
|
+
port_opened: smtp_response.port_opened,
|
24
|
+
connection: smtp_response.connection,
|
25
|
+
errors: smtp_response.errors
|
26
|
+
}
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def result
|
31
|
+
@result ||=
|
32
|
+
{
|
33
|
+
date: Time.now,
|
34
|
+
email: executor_result.email,
|
35
|
+
validation_type: validation_type,
|
36
|
+
success: executor_result.success,
|
37
|
+
errors: errors(executor_result.errors),
|
38
|
+
smtp_debug: smtp_debug,
|
39
|
+
configuration: configuration
|
40
|
+
}
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|