truemail 2.2.3 → 2.3.4
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/.circleci/config.yml +26 -3
 - data/.codeclimate.yml +1 -1
 - data/.reek.yml +7 -0
 - data/.rubocop.yml +30 -0
 - data/CHANGELOG.md +67 -1
 - data/Gemfile.lock +46 -33
 - data/LICENSE.txt +1 -1
 - data/README.md +49 -20
 - data/bin/console +3 -10
 - data/lib/truemail.rb +1 -1
 - data/lib/truemail/audit/base.rb +0 -1
 - data/lib/truemail/audit/dns.rb +1 -1
 - data/lib/truemail/audit/ip.rb +1 -1
 - data/lib/truemail/audit/ptr.rb +4 -3
 - data/lib/truemail/auditor.rb +1 -1
 - data/lib/truemail/configuration.rb +38 -30
 - data/lib/truemail/core.rb +11 -15
 - data/lib/truemail/dns/punycode_representer.rb +16 -0
 - data/lib/truemail/dns/resolver.rb +17 -0
 - data/lib/truemail/dns/worker.rb +52 -0
 - data/lib/truemail/log/serializer/auditor_json.rb +1 -1
 - data/lib/truemail/log/serializer/base.rb +2 -1
 - data/lib/truemail/log/serializer/validator_base.rb +1 -1
 - data/lib/truemail/log/serializer/validator_text.rb +2 -2
 - data/lib/truemail/logger.rb +1 -1
 - data/lib/truemail/validate/mx.rb +7 -9
 - data/lib/truemail/validate/smtp/request.rb +3 -3
 - data/lib/truemail/validate/smtp/response.rb +1 -1
 - data/lib/truemail/validator.rb +2 -2
 - data/lib/truemail/version.rb +1 -1
 - data/lib/truemail/wrapper.rb +3 -3
 - data/truemail.gemspec +10 -8
 - metadata +62 -25
 
    
        data/bin/console
    CHANGED
    
    | 
         @@ -3,14 +3,7 @@ 
     | 
|
| 
       3 
3 
     | 
    
         
             
            # frozen_string_literal: true
         
     | 
| 
       4 
4 
     | 
    
         | 
| 
       5 
5 
     | 
    
         
             
            require 'bundler/setup'
         
     | 
| 
       6 
     | 
    
         
            -
            require ' 
     | 
| 
      
 6 
     | 
    
         
            +
            require 'pry'
         
     | 
| 
      
 7 
     | 
    
         
            +
            require_relative '../lib/truemail'
         
     | 
| 
       7 
8 
     | 
    
         | 
| 
       8 
     | 
    
         
            -
             
     | 
| 
       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
         
     | 
    
        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)
         
     | 
    
        data/lib/truemail/audit/base.rb
    CHANGED
    
    
    
        data/lib/truemail/audit/dns.rb
    CHANGED
    
    
    
        data/lib/truemail/audit/ip.rb
    CHANGED
    
    
    
        data/lib/truemail/audit/ptr.rb
    CHANGED
    
    | 
         @@ -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 
     | 
    
         
            -
                       
     | 
| 
       24 
     | 
    
         
            -
                        current_host_reverse_lookup, 
     | 
| 
      
 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
         
     | 
    
        data/lib/truemail/auditor.rb
    CHANGED
    
    | 
         @@ -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 
     | 
    
         
            -
             
     | 
| 
       12 
     | 
    
         
            -
             
     | 
| 
       13 
     | 
    
         
            -
             
     | 
| 
      
 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 
     | 
    
         
            -
                            : 
     | 
| 
       21 
     | 
    
         
            -
                            : 
     | 
| 
       22 
     | 
    
         
            -
                             
     | 
| 
      
 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 
     | 
    
         
            -
                 
     | 
| 
      
 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__,  
     | 
| 
      
 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]\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
         
     | 
| 
         @@ -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,
         
     | 
| 
         @@ -20,8 +20,8 @@ module Truemail 
     | 
|
| 
       20 
20 
     | 
    
         
             
                      enumerable_object.inject([]) do |formatted_data, (key, value)|
         
     | 
| 
       21 
21 
     | 
    
         
             
                        data =
         
     | 
| 
       22 
22 
     | 
    
         
             
                          case
         
     | 
| 
       23 
     | 
    
         
            -
                          when value.is_a?(Hash) then "\n#{printer(value)}"
         
     | 
| 
       24 
     | 
    
         
            -
                          when value.is_a?(Array) then value.join(', ')
         
     | 
| 
      
 23 
     | 
    
         
            +
                          when value.is_a?(::Hash) then "\n#{printer(value)}"
         
     | 
| 
      
 24 
     | 
    
         
            +
                          when value.is_a?(::Array) then value.join(', ')
         
     | 
| 
       25 
25 
     | 
    
         
             
                          else value
         
     | 
| 
       26 
26 
     | 
    
         
             
                          end
         
     | 
| 
       27 
27 
     | 
    
         
             
                        formatted_data << "#{key.to_s.tr('_', ' ')}: #{data}".chomp << "\n"
         
     | 
    
        data/lib/truemail/logger.rb
    CHANGED
    
    | 
         @@ -23,7 +23,7 @@ module Truemail 
     | 
|
| 
       23 
23 
     | 
    
         
             
                def init_log_file
         
     | 
| 
       24 
24 
     | 
    
         
             
                  output_file = Pathname(file)
         
     | 
| 
       25 
25 
     | 
    
         
             
                  return output_file if output_file.exist?
         
     | 
| 
       26 
     | 
    
         
            -
                  output_file.parent.mkpath && FileUtils.touch(output_file)
         
     | 
| 
      
 26 
     | 
    
         
            +
                  output_file.parent.mkpath && ::FileUtils.touch(output_file)
         
     | 
| 
       27 
27 
     | 
    
         
             
                  output_file
         
     | 
| 
       28 
28 
     | 
    
         
             
                end
         
     | 
| 
       29 
29 
     | 
    
         | 
    
        data/lib/truemail/validate/mx.rb
    CHANGED
    
    | 
         @@ -3,8 +3,6 @@ 
     | 
|
| 
       3 
3 
     | 
    
         
             
            module Truemail
         
     | 
| 
       4 
4 
     | 
    
         
             
              module Validate
         
     | 
| 
       5 
5 
     | 
    
         
             
                class Mx < Truemail::Validate::Base
         
     | 
| 
       6 
     | 
    
         
            -
                  require 'resolv'
         
     | 
| 
       7 
     | 
    
         
            -
             
     | 
| 
       8 
6 
     | 
    
         
             
                  ERROR = 'target host(s) not found'
         
     | 
| 
       9 
7 
     | 
    
         
             
                  NULL_MX_RECORD = 'null_mx_record'
         
     | 
| 
       10 
8 
     | 
    
         | 
| 
         @@ -43,11 +41,11 @@ module Truemail 
     | 
|
| 
       43 
41 
     | 
    
         
             
                  end
         
     | 
| 
       44 
42 
     | 
    
         | 
| 
       45 
43 
     | 
    
         
             
                  def mx_records(hostname)
         
     | 
| 
       46 
     | 
    
         
            -
                    domain_mx_records =  
     | 
| 
      
 44 
     | 
    
         
            +
                    domain_mx_records = Truemail::Dns::Resolver.mx_records(hostname, configuration: configuration)
         
     | 
| 
       47 
45 
     | 
    
         
             
                    return [Truemail::Validate::Mx::NULL_MX_RECORD] if null_mx?(domain_mx_records)
         
     | 
| 
       48 
     | 
    
         
            -
                    domain_mx_records.sort_by(&:preference). 
     | 
| 
       49 
     | 
    
         
            -
                       
     | 
| 
       50 
     | 
    
         
            -
                    end 
     | 
| 
      
 46 
     | 
    
         
            +
                    domain_mx_records.sort_by(&:preference).flat_map do |mx_record|
         
     | 
| 
      
 47 
     | 
    
         
            +
                      Truemail::Dns::Resolver.a_records(mx_record.exchange.to_s, configuration: configuration)
         
     | 
| 
      
 48 
     | 
    
         
            +
                    end
         
     | 
| 
       51 
49 
     | 
    
         
             
                  end
         
     | 
| 
       52 
50 
     | 
    
         | 
| 
       53 
51 
     | 
    
         
             
                  def mail_servers_found?
         
     | 
| 
         @@ -64,15 +62,15 @@ module Truemail 
     | 
|
| 
       64 
62 
     | 
    
         
             
                  end
         
     | 
| 
       65 
63 
     | 
    
         | 
| 
       66 
64 
     | 
    
         
             
                  def a_record(hostname)
         
     | 
| 
       67 
     | 
    
         
            -
                     
     | 
| 
      
 65 
     | 
    
         
            +
                    Truemail::Dns::Resolver.a_record(hostname, configuration: configuration)
         
     | 
| 
       68 
66 
     | 
    
         
             
                  end
         
     | 
| 
       69 
67 
     | 
    
         | 
| 
       70 
68 
     | 
    
         
             
                  def hosts_from_cname_records?
         
     | 
| 
       71 
     | 
    
         
            -
                    cname_records =  
     | 
| 
      
 69 
     | 
    
         
            +
                    cname_records = Truemail::Dns::Resolver.cname_records(domain, configuration: configuration)
         
     | 
| 
       72 
70 
     | 
    
         
             
                    return if cname_records.empty?
         
     | 
| 
       73 
71 
     | 
    
         
             
                    cname_records.each do |cname_record|
         
     | 
| 
       74 
72 
     | 
    
         
             
                      host = a_record(cname_record.name.to_s)
         
     | 
| 
       75 
     | 
    
         
            -
                      hostname =  
     | 
| 
      
 73 
     | 
    
         
            +
                      hostname = Truemail::Dns::Resolver.dns_lookup(host, configuration: configuration)
         
     | 
| 
       76 
74 
     | 
    
         
             
                      found_hosts = mx_records(hostname)
         
     | 
| 
       77 
75 
     | 
    
         
             
                      fetch_target_hosts(found_hosts.empty? ? [host] : found_hosts)
         
     | 
| 
       78 
76 
     | 
    
         
             
                    end
         
     |