truemail 2.2.2 → 2.3.3

Sign up to get free protection for your applications and to get access to all the features.
data/bin/console CHANGED
@@ -3,14 +3,7 @@
3
3
  # frozen_string_literal: true
4
4
 
5
5
  require 'bundler/setup'
6
- require 'truemail'
6
+ require 'pry'
7
+ require_relative '../lib/truemail'
7
8
 
8
- # You can add fixtures and/or initialization code here to make experimenting
9
- # with your gem easier. You can also use a different console, if you like.
10
-
11
- # (If you use this, don't forget to add pry to your Gemfile!)
12
- # require "pry"
13
- # Pry.start
14
-
15
- require 'irb'
16
- IRB.start(__FILE__)
9
+ Pry.start
@@ -1,3 +1,5 @@
1
+ version: 2.1
2
+
1
3
  defaults: &defaults
2
4
  working_directory: ~/truemail
3
5
  docker:
@@ -5,6 +7,9 @@ defaults: &defaults
5
7
  environment:
6
8
  CC_TEST_REPORTER_ID: 693272a1328521f6f7c09d7ffd419b21c00410da26e98e94c687fdd38b26e2cb
7
9
 
10
+ orbs:
11
+ ruby: circleci/ruby@1.1.2
12
+
8
13
  references:
9
14
  restore_bundle_cache: &restore_bundle_cache
10
15
  restore_cache:
@@ -29,7 +34,6 @@ references:
29
34
  curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
30
35
  chmod +x ./cc-test-reporter
31
36
 
32
- version: 2
33
37
  jobs:
34
38
  linters:
35
39
  <<: *defaults
@@ -81,9 +85,28 @@ jobs:
81
85
  command: |
82
86
  ./cc-test-reporter sum-coverage --output - --parts $CIRCLE_NODE_TOTAL coverage/codeclimate.*.json | ./cc-test-reporter upload-coverage --debug --input -
83
87
 
88
+ compatibility-with-ruby:
89
+ parameters:
90
+ ruby-version:
91
+ type: string
92
+ docker:
93
+ - image: cimg/ruby:<< parameters.ruby-version >>
94
+ steps:
95
+ - checkout
96
+ - ruby/install-deps:
97
+ bundler-version: '1.16.6'
98
+ with-cache: false
99
+ path: './vendor/custom_bundle'
100
+ - run:
101
+ name: Running compatibility tests
102
+ command: bundle exec rspec
103
+
84
104
  workflows:
85
- version: 2
86
- build:
105
+ build_and_test:
87
106
  jobs:
88
107
  - linters
89
108
  - tests
109
+ - compatibility-with-ruby:
110
+ matrix:
111
+ parameters:
112
+ ruby-version: ["2.6", "2.7", "3.0"]
data/lib/truemail.rb CHANGED
@@ -46,7 +46,7 @@ module Truemail
46
46
  end
47
47
 
48
48
  def check_argument_type(argument)
49
- raise_unless(argument.is_a?(String), Truemail::INVALID_TYPE, Truemail::TypeError)
49
+ raise_unless(argument.is_a?(::String), Truemail::INVALID_TYPE, Truemail::TypeError)
50
50
  end
51
51
 
52
52
  def determine_configuration(custom_configuration)
@@ -5,7 +5,6 @@ module Truemail
5
5
  class Base < Truemail::Worker
6
6
  require 'net/http'
7
7
  require 'ipaddr'
8
- require 'resolv'
9
8
 
10
9
  private
11
10
 
@@ -14,7 +14,7 @@ module Truemail
14
14
 
15
15
  def a_record
16
16
  Truemail::Wrapper.call(configuration: configuration) do
17
- Resolv::DNS.new.getaddress(verifier_domain).to_s
17
+ Truemail::Dns::Resolver.a_record(verifier_domain, configuration: configuration)
18
18
  end
19
19
  end
20
20
 
@@ -15,7 +15,7 @@ module Truemail
15
15
  private
16
16
 
17
17
  def detect_ip_via_ipify
18
- Net::HTTP.get(URI(Truemail::Audit::Ip::GET_MY_IP_URL))
18
+ ::Net::HTTP.get(URI(Truemail::Audit::Ip::GET_MY_IP_URL))
19
19
  end
20
20
 
21
21
  def detect_current_host_ip
@@ -15,13 +15,14 @@ module Truemail
15
15
  private
16
16
 
17
17
  def current_host_reverse_lookup
18
- IPAddr.new(current_host_ip).reverse
18
+ ::IPAddr.new(current_host_ip).reverse
19
19
  end
20
20
 
21
21
  def ptr_records
22
22
  @ptr_records ||= Truemail::Wrapper.call(configuration: configuration) do
23
- Resolv::DNS.new.getresources(
24
- current_host_reverse_lookup, Resolv::DNS::Resource::IN::PTR
23
+ Truemail::Dns::Resolver.ptr_records(
24
+ current_host_reverse_lookup,
25
+ configuration: configuration
25
26
  ).map { |ptr_record| ptr_record.name.to_s }
26
27
  end || []
27
28
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Truemail
4
4
  class Auditor < Truemail::Executor
5
- Result = Struct.new(:current_host_ip, :warnings, :configuration, keyword_init: true) do
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
@@ -7,19 +7,23 @@ module Truemail
7
7
  DEFAULT_CONNECTION_ATTEMPTS = 2
8
8
  DEFAULT_VALIDATION_TYPE = :smtp
9
9
  DEFAULT_LOGGER_OPTIONS = { tracking_event: :error, stdout: false, log_absolute_path: nil }.freeze
10
-
11
- attr_reader :email_pattern,
12
- :smtp_error_body_pattern,
13
- :verifier_email,
10
+ SETTERS = %i[
11
+ email_pattern
12
+ smtp_error_body_pattern
13
+ connection_timeout
14
+ response_timeout
15
+ connection_attempts
16
+ whitelisted_domains
17
+ blacklisted_domains
18
+ ].freeze
19
+
20
+ attr_reader :verifier_email,
14
21
  :verifier_domain,
15
- :connection_timeout,
16
- :response_timeout,
17
- :connection_attempts,
18
22
  :default_validation_type,
19
23
  :validation_type_by_domain,
20
- :whitelisted_domains,
21
- :blacklisted_domains,
22
- :logger
24
+ :dns,
25
+ :logger,
26
+ *Truemail::Configuration::SETTERS
23
27
 
24
28
  attr_accessor :whitelist_validation, :not_rfc_mx_lookup_flow, :smtp_fail_fast, :smtp_safe_check
25
29
 
@@ -30,13 +34,6 @@ module Truemail
30
34
  tap(&block) if block
31
35
  end
32
36
 
33
- %i[email_pattern smtp_error_body_pattern].each do |method|
34
- define_method("#{method}=") do |argument|
35
- raise_unless(argument, __method__, argument.is_a?(Regexp))
36
- instance_variable_set(:"@#{method}", argument)
37
- end
38
- end
39
-
40
37
  def verifier_email=(email)
41
38
  validate_arguments(email, __method__)
42
39
  @verifier_email = email.downcase
@@ -48,15 +45,8 @@ module Truemail
48
45
  @verifier_domain = domain.downcase
49
46
  end
50
47
 
51
- %i[connection_timeout response_timeout connection_attempts].each do |method|
52
- define_method("#{method}=") do |argument|
53
- raise_unless(argument, __method__, argument.is_a?(Integer) && argument.positive?)
54
- instance_variable_set(:"@#{method}", argument)
55
- end
56
- end
57
-
58
48
  def default_validation_type=(argument)
59
- raise_unless(argument, __method__, argument.is_a?(Symbol) && Truemail::Validator::VALIDATION_TYPES.include?(argument))
49
+ raise_unless(argument, __method__, argument.is_a?(::Symbol) && Truemail::Validator::VALIDATION_TYPES.include?(argument))
60
50
  @default_validation_type = argument
61
51
  end
62
52
 
@@ -65,18 +55,31 @@ module Truemail
65
55
  validation_type_by_domain.merge!(settings)
66
56
  end
67
57
 
68
- %i[whitelisted_domains blacklisted_domains].each do |method|
58
+ def argument_consistent?(argument)
59
+ case argument
60
+ when ::Array then check_domain_list(argument)
61
+ when ::Integer then argument.positive?
62
+ when ::Regexp then true
63
+ end
64
+ end
65
+
66
+ Truemail::Configuration::SETTERS.each do |method|
69
67
  define_method("#{method}=") do |argument|
70
- raise_unless(argument, __method__, argument.is_a?(Array) && check_domain_list(argument))
68
+ raise_unless(argument, __method__, argument_consistent?(argument))
71
69
  instance_variable_set(:"@#{method}", argument)
72
70
  end
73
71
  end
74
72
 
73
+ def dns=(argument)
74
+ raise_unless(argument, __method__, argument.is_a?(::Array) && check_dns_settings(argument))
75
+ @dns = argument
76
+ end
77
+
75
78
  def logger=(options)
76
79
  tracking_event, stdout, log_absolute_path = logger_options(options)
77
80
  valid_event = Truemail::Log::Event::TRACKING_EVENTS.key?(tracking_event)
78
81
  stdout_only = stdout && log_absolute_path.nil?
79
- file_only = log_absolute_path.is_a?(String)
82
+ file_only = log_absolute_path.is_a?(::String)
80
83
  both_types = stdout && file_only
81
84
  argument_info = valid_event ? log_absolute_path : tracking_event
82
85
  raise_unless(argument_info, __method__, valid_event && (stdout_only || file_only || both_types))
@@ -89,7 +92,7 @@ module Truemail
89
92
 
90
93
  private
91
94
 
92
- def instance_initializer
95
+ def instance_initializer # rubocop:disable Metrics/MethodLength
93
96
  {
94
97
  email_pattern: Truemail::RegexConstant::REGEX_EMAIL_PATTERN,
95
98
  smtp_error_body_pattern: Truemail::RegexConstant::REGEX_SMTP_ERROR_BODY_PATTERN,
@@ -101,6 +104,7 @@ module Truemail
101
104
  whitelisted_domains: [],
102
105
  whitelist_validation: false,
103
106
  blacklisted_domains: [],
107
+ dns: [],
104
108
  not_rfc_mx_lookup_flow: false,
105
109
  smtp_fail_fast: false,
106
110
  smtp_safe_check: false
@@ -137,13 +141,17 @@ module Truemail
137
141
  end
138
142
 
139
143
  def validate_validation_type(settings)
140
- raise_unless(settings, 'hash with settings', settings.is_a?(Hash))
144
+ raise_unless(settings, 'hash with settings', settings.is_a?(::Hash))
141
145
  settings.each do |domain, validation_type|
142
146
  check_domain(domain)
143
147
  check_validation_type(validation_type)
144
148
  end
145
149
  end
146
150
 
151
+ def check_dns_settings(dns_servers)
152
+ dns_servers.all? { |dns_server| Truemail::RegexConstant::REGEX_DNS_SERVER_ADDRESS_PATTERN.match?(dns_server.to_s) }
153
+ end
154
+
147
155
  def logger_options(current_options)
148
156
  Truemail::Configuration::DEFAULT_LOGGER_OPTIONS.merge(current_options).values
149
157
  end
data/lib/truemail/core.rb CHANGED
@@ -10,32 +10,28 @@ module Truemail
10
10
  require_relative '../truemail/validator'
11
11
  require_relative '../truemail/logger'
12
12
 
13
- ConfigurationError = Class.new(StandardError)
14
- TypeError = Class.new(StandardError)
15
-
16
- ArgumentError = Class.new(StandardError) do
13
+ ConfigurationError = ::Class.new(::StandardError)
14
+ TypeError = ::Class.new(::StandardError)
15
+ ArgumentError = ::Class.new(::StandardError) do
17
16
  def initialize(arg_value, arg_name)
18
17
  super("#{arg_value} is not a valid #{arg_name}")
19
18
  end
20
19
  end
21
20
 
22
- PunycodeRepresenter = Class.new do
23
- require 'simpleidn'
24
-
25
- def self.call(email)
26
- return unless email.is_a?(String)
27
- return email if email.ascii_only?
28
- user, domain = email.split('@')
29
- "#{user}@#{SimpleIDN.to_ascii(domain.downcase)}"
30
- end
31
- end
32
-
33
21
  module RegexConstant
34
22
  REGEX_DOMAIN = /[\p{L}0-9]+([\-.]{1}[\p{L}0-9]+)*\.\p{L}{2,63}/i.freeze
35
23
  REGEX_EMAIL_PATTERN = /(?=\A.{6,255}\z)(\A([\p{L}0-9]+[\w|\-.+]*)@(#{REGEX_DOMAIN})\z)/.freeze
36
24
  REGEX_DOMAIN_PATTERN = /(?=\A.{4,255}\z)(\A#{REGEX_DOMAIN}\z)/.freeze
37
25
  REGEX_DOMAIN_FROM_EMAIL = /\A.+@(.+)\z/.freeze
38
26
  REGEX_SMTP_ERROR_BODY_PATTERN = /(?=.*550)(?=.*(user|account|customer|mailbox)).*/i.freeze
27
+ REGEX_PORT_NUMBER = /(6553[0-5]|655[0-2][0-9]\d|65[0-4](\d){2}|6[0-4](\d){3}|[1-5](\d){4}|[1-9](\d){0,3})/.freeze
28
+ REGEX_DNS_SERVER_ADDRESS_PATTERN = /\A((1\d|[1-9]|2[0-4])?\d|25[0-5])(\.\g<1>){3}(:#{REGEX_PORT_NUMBER})?\z/.freeze
29
+ end
30
+
31
+ module Dns
32
+ require_relative '../truemail/dns/punycode_representer'
33
+ require_relative '../truemail/dns/worker'
34
+ require_relative '../truemail/dns/resolver'
39
35
  end
40
36
 
41
37
  module Audit
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Truemail
4
+ module Dns
5
+ PunycodeRepresenter = Class.new do
6
+ require 'simpleidn'
7
+
8
+ def self.call(email)
9
+ return unless email.is_a?(::String)
10
+ return email if email.ascii_only?
11
+ user, domain = email.split('@')
12
+ "#{user}@#{SimpleIDN.to_ascii(domain.downcase)}"
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Truemail
4
+ module Dns
5
+ class Resolver
6
+ WORKER_ACTIONS = %i[dns_lookup a_record a_records cname_records mx_records ptr_records].freeze
7
+
8
+ class << self
9
+ Truemail::Dns::Resolver::WORKER_ACTIONS.each do |worker_action|
10
+ define_method(worker_action) do |argument, configuration:|
11
+ Truemail::Dns::Worker.new(configuration.dns).public_send(worker_action, argument)
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Truemail
4
+ module Dns
5
+ require 'resolv'
6
+
7
+ class Worker < ::Resolv::DNS
8
+ DEFAULT_DNS_PORT = 53
9
+
10
+ attr_reader :dns_gateway
11
+
12
+ def initialize(dns_servers)
13
+ super(dns_servers.empty? ? nil : config_info(dns_servers))
14
+ end
15
+
16
+ def dns_lookup(host_address)
17
+ getname(host_address).to_s
18
+ end
19
+
20
+ def a_record(host_name)
21
+ getaddress(host_name).to_s
22
+ end
23
+
24
+ def a_records(host_name)
25
+ getaddresses(host_name).map(&:to_s)
26
+ end
27
+
28
+ def cname_records(host_name)
29
+ getresources(host_name, ::Resolv::DNS::Resource::IN::CNAME)
30
+ end
31
+
32
+ def mx_records(host_name)
33
+ getresources(host_name, ::Resolv::DNS::Resource::IN::MX)
34
+ end
35
+
36
+ def ptr_records(host_address)
37
+ getresources(host_address, ::Resolv::DNS::Resource::IN::PTR)
38
+ end
39
+
40
+ private
41
+
42
+ def nameserver_port(server)
43
+ server_address, server_port = server.split(':')
44
+ [server_address, server_port ? server_port.to_i : Truemail::Dns::Worker::DEFAULT_DNS_PORT]
45
+ end
46
+
47
+ def config_info(dns_servers)
48
+ @dns_gateway = { nameserver_port: dns_servers.map { |server| nameserver_port(server) } }
49
+ end
50
+ end
51
+ end
52
+ end
@@ -13,7 +13,7 @@ module Truemail
13
13
  def result
14
14
  @result ||=
15
15
  {
16
- date: Time.now,
16
+ date: ::Time.now,
17
17
  current_host_ip: executor_result.current_host_ip,
18
18
  warnings: warnings(executor_result.warnings),
19
19
  configuration: configuration
@@ -30,7 +30,7 @@ module Truemail
30
30
 
31
31
  alias warnings errors
32
32
 
33
- %i[validation_type_by_domain whitelisted_domains blacklisted_domains].each do |method|
33
+ %i[validation_type_by_domain whitelisted_domains blacklisted_domains dns].each do |method|
34
34
  define_method(method) do
35
35
  value = executor_configuration.public_send(method)
36
36
  return if value.empty?
@@ -55,6 +55,7 @@ module Truemail
55
55
  whitelist_validation: executor_configuration.whitelist_validation,
56
56
  whitelisted_domains: whitelisted_domains,
57
57
  blacklisted_domains: blacklisted_domains,
58
+ dns: dns,
58
59
  not_rfc_mx_lookup_flow: executor_configuration.not_rfc_mx_lookup_flow,
59
60
  smtp_fail_fast: executor_configuration.smtp_fail_fast,
60
61
  smtp_safe_check: executor_configuration.smtp_safe_check,
@@ -34,7 +34,7 @@ module Truemail
34
34
  def result
35
35
  @result ||=
36
36
  {
37
- date: Time.now,
37
+ date: ::Time.now,
38
38
  email: executor_result.email,
39
39
  validation_type: validation_type,
40
40
  success: executor_result.success,