coppertone 0.0.10 → 0.0.11

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +5 -5
  2. data/.circleci/config.yml +91 -0
  3. data/.rubocop.yml +6 -2
  4. data/.travis.yml +2 -9
  5. data/Gemfile +10 -2
  6. data/Rakefile +6 -1
  7. data/coppertone.gemspec +5 -7
  8. data/lib/coppertone/class_builder.rb +2 -0
  9. data/lib/coppertone/directive.rb +5 -1
  10. data/lib/coppertone/domain_spec.rb +4 -0
  11. data/lib/coppertone/ip_address_wrapper.rb +4 -1
  12. data/lib/coppertone/macro_string.rb +1 -0
  13. data/lib/coppertone/macro_string/macro_expand.rb +5 -1
  14. data/lib/coppertone/macro_string/macro_literal.rb +1 -0
  15. data/lib/coppertone/macro_string/macro_parser.rb +3 -0
  16. data/lib/coppertone/macro_string/macro_static_expand.rb +2 -0
  17. data/lib/coppertone/mechanism.rb +1 -0
  18. data/lib/coppertone/mechanism/all.rb +1 -0
  19. data/lib/coppertone/mechanism/cidr_parser.rb +3 -3
  20. data/lib/coppertone/mechanism/domain_spec_mechanism.rb +5 -0
  21. data/lib/coppertone/mechanism/domain_spec_optional.rb +1 -0
  22. data/lib/coppertone/mechanism/domain_spec_required.rb +1 -0
  23. data/lib/coppertone/mechanism/domain_spec_with_dual_cidr.rb +8 -1
  24. data/lib/coppertone/mechanism/include_matcher.rb +2 -0
  25. data/lib/coppertone/mechanism/ip_mechanism.rb +6 -2
  26. data/lib/coppertone/mechanism/mx.rb +1 -0
  27. data/lib/coppertone/modifier.rb +2 -1
  28. data/lib/coppertone/modifier/base.rb +2 -0
  29. data/lib/coppertone/modifier/exp.rb +6 -2
  30. data/lib/coppertone/modifier/redirect.rb +1 -0
  31. data/lib/coppertone/record.rb +5 -2
  32. data/lib/coppertone/record_evaluator.rb +4 -0
  33. data/lib/coppertone/record_finder.rb +1 -0
  34. data/lib/coppertone/record_term_parser.rb +6 -4
  35. data/lib/coppertone/redirect_record_finder.rb +2 -1
  36. data/lib/coppertone/request.rb +26 -21
  37. data/lib/coppertone/request_context.rb +1 -0
  38. data/lib/coppertone/request_count_limiter.rb +1 -0
  39. data/lib/coppertone/sender_identity.rb +1 -1
  40. data/lib/coppertone/term.rb +1 -0
  41. data/lib/coppertone/terms_parser.rb +1 -0
  42. data/lib/coppertone/utils/domain_utils.rb +20 -7
  43. data/lib/coppertone/version.rb +1 -1
  44. data/spec/macro_string/macro_static_expand_spec.rb +2 -2
  45. data/spec/open_spf/Initial_processing_spec.rb +0 -2
  46. data/spec/open_spf/Semantics_of_exp_and_other_modifiers_spec.rb +0 -2
  47. data/spec/spec_helper.rb +5 -0
  48. data/spec/utils/domain_utils_spec.rb +5 -2
  49. metadata +25 -26
  50. data/circle.yml +0 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 6ef116ba53f031efce0470d0e7b2650161fa44d4
4
- data.tar.gz: f8080851318443517abd3ab0f7f84e26997c6f07
2
+ SHA256:
3
+ metadata.gz: a8802307a322cafa013376ddb5f67bfd134996b4f51b9753f04c4c024158c6f5
4
+ data.tar.gz: 4a426ab5a166b4e896417bd004f2ea597d77074929f389f7f152698b65258c64
5
5
  SHA512:
6
- metadata.gz: a2a5c2716e42eb0922f84e5d7cfd9d4612ac39d11398a2e6f64bd533fa5a33dbc8e95212eedba1d8156149f9fd2829ae9267581c78bd9a16f84f89b2f980283d
7
- data.tar.gz: bf1c8f0e057f065d5f74de705685fc3634d3955cefbd8409e67ca8dde22dff31555a3aa0b21440a9ee7696414fb883622027751cd76b215dbd14e73418d15ffb
6
+ metadata.gz: 260ff1c6805f0f74cc9c8939b66b99ab903f83f548b22e3a05a8ed4fec45867549e4d8b01ea66b06fdf17096f1e08fb094c292070209684c5531ad9e481d37c4
7
+ data.tar.gz: c4753800b5357e24aed9a964d00ea0f7ae5a91056407edd38b325a5b6dee1c0fdc33edfa5c79644b6dee00b4104cca2497d7933fd18e89eabc21216888281308
@@ -0,0 +1,91 @@
1
+ version: 2
2
+ aliases:
3
+ - &current_ruby_image
4
+ circleci/ruby:2.5.1
5
+
6
+ - &previous_ruby_image
7
+ circleci/ruby:2.4.4
8
+
9
+ - &defaults
10
+ working_directory: ~/repo
11
+ docker:
12
+ - image: *current_ruby_image
13
+
14
+ - &step_install_root_dependencies
15
+ run:
16
+ name: install root dependencies
17
+ command: |
18
+ gem install bundler && bundle install --jobs=4 --retry=3 --path vendor/bundle
19
+
20
+ - &step_make_test_output_directory
21
+ run:
22
+ name: create test metadata directory
23
+ command: mkdir /tmp/test-results
24
+
25
+ - &step_run_rspec_tests
26
+ run:
27
+ name: run rspec with simplecov
28
+ command: |
29
+ TEST_FILES="$(circleci tests glob "spec/**/*_spec.rb" | circleci tests split --split-by=timings)"
30
+
31
+ COVERAGE=true bundle exec rspec \
32
+ --format progress \
33
+ --format RspecJunitFormatter \
34
+ --out /tmp/test-results/rspec.xml \
35
+ ${TEST_FILES}
36
+
37
+ - &step_store_test_results
38
+ store_test_results:
39
+ path: /tmp/test-results
40
+
41
+ - &step_store_test_artifacts
42
+ store_artifacts:
43
+ path: /tmp/test-results
44
+ destination: test-results
45
+
46
+ - &step_store_coverage_artifacts
47
+ store_artifacts:
48
+ path: coverage
49
+ destination: coverage
50
+
51
+ jobs:
52
+ code-quality-job:
53
+ <<: *defaults
54
+ steps:
55
+ - checkout
56
+ - *step_install_root_dependencies
57
+ - run:
58
+ name: Run Rubocop
59
+ command: |
60
+ bundle exec rubocop
61
+
62
+ current_ruby-job:
63
+ working_directory: ~/repo
64
+ docker:
65
+ - image: *current_ruby_image
66
+ steps: &test_run_steps
67
+ - checkout
68
+ - *step_install_root_dependencies
69
+ - *step_make_test_output_directory
70
+ - *step_run_rspec_tests
71
+ - *step_store_test_results
72
+ - *step_store_test_artifacts
73
+ - *step_store_coverage_artifacts
74
+
75
+ previous_ruby-job:
76
+ working_directory: ~/repo
77
+ docker:
78
+ - image: *previous_ruby_image
79
+ steps: *test_run_steps
80
+
81
+ workflows:
82
+ version: 2
83
+ build-test_current-ruby:
84
+ jobs:
85
+ - current_ruby-job
86
+ - code-quality-job:
87
+ requires:
88
+ - current_ruby-job # let primary job fetch all dependencies first
89
+ build-test_previous-ruby:
90
+ jobs:
91
+ - previous_ruby-job
@@ -1,5 +1,5 @@
1
1
  AllCops:
2
- TargetRubyVersion: 2.2
2
+ TargetRubyVersion: 2.5
3
3
 
4
4
  Metrics/AbcSize:
5
5
  Exclude:
@@ -18,7 +18,7 @@ Rails/HttpPositionalArguments:
18
18
  Enabled: false
19
19
  Style/FrozenStringLiteralComment:
20
20
  Enabled: false
21
- Style/FileName:
21
+ Naming/FileName:
22
22
  Exclude:
23
23
  - 'spec/open_spf/**/*'
24
24
  Style/FormatStringToken:
@@ -26,3 +26,7 @@ Style/FormatStringToken:
26
26
  Style/SymbolArray:
27
27
  Exclude:
28
28
  - 'spec/open_spf/**/*'
29
+ Naming/UncommunicativeMethodParamName:
30
+ Enabled: false
31
+ Lint/UriEscapeUnescape:
32
+ Enabled: false
@@ -1,12 +1,5 @@
1
1
  sudo: false
2
2
  language: ruby
3
3
  rvm:
4
- - 2.2.6
5
- - 2.3.3
6
- - 2.4.0
7
- - jruby-9.1.5.0
8
- addons:
9
- code_climate:
10
- repo_token: 437199ed4348853fa1dd6561b2e17a5668a7a6e78485d7ac3a21d12670fb6f2f
11
- after_success:
12
- - bundle exec codeclimate-test-reporter
4
+ - 2.4.4
5
+ - 2.5.1
data/Gemfile CHANGED
@@ -3,5 +3,13 @@ source 'https://rubygems.org'
3
3
  # Specify your gem's dependencies in coppertone.gemspec
4
4
  gemspec
5
5
 
6
- gem 'codeclimate-test-reporter', group: :test, require: nil
7
- gem 'simplecov', group: :test, require: false
6
+ group :development, :test do
7
+ gem 'brakeman', require: false
8
+ gem 'rubocop', require: false
9
+ end
10
+
11
+ group :test do
12
+ gem 'codecov', require: false
13
+ gem 'rspec_junit_formatter'
14
+ gem 'simplecov'
15
+ end
data/Rakefile CHANGED
@@ -57,21 +57,25 @@ end
57
57
 
58
58
  def escape_quote(val)
59
59
  return unless val
60
+
60
61
  val.gsub(/'/) { |_s| "\\'" }
61
62
  end
62
63
 
63
64
  def clean_description(description)
64
65
  return unless description
66
+
65
67
  description.tr("\n", ' ').delete("'")
66
68
  end
67
69
 
68
70
  def as_array(val)
69
71
  return [] unless val
72
+
70
73
  val.is_a?(Array) ? val : [val]
71
74
  end
72
75
 
73
76
  def write_comment(f, comment, indent)
74
77
  return unless comment
78
+
75
79
  comment.lines do |comment_line|
76
80
  puts_prefixed_string(f, "# #{comment_line}", indent)
77
81
  end
@@ -89,6 +93,7 @@ def write_expects(f, results, explanation, indent)
89
93
  code_expect = "expect(#{results_array}).to include(result.code)"
90
94
  puts_prefixed_string(f, code_expect, indent)
91
95
  return unless explanation
96
+
92
97
  exp_expect = "expect(result.explanation).to eq('#{explanation}')"
93
98
  puts_prefixed_string(f, exp_expect, indent)
94
99
  end
@@ -116,7 +121,7 @@ def write_spec(f, spec, indent = 1)
116
121
  end
117
122
 
118
123
  def write_doc(doc, output_path)
119
- open(spec_file_name(doc, output_path), 'w') do |f|
124
+ File.open(spec_file_name(doc, output_path), 'w') do |f|
120
125
  puts_prefixed_string(f, "require 'spec_helper'")
121
126
  empty_line(f)
122
127
  description = doc['description']
@@ -1,6 +1,4 @@
1
- # coding: utf-8
2
-
3
- lib = File.expand_path('../lib', __FILE__)
1
+ lib = File.expand_path('lib', __dir__)
4
2
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
3
  require 'coppertone/version'
6
4
 
@@ -18,12 +16,12 @@ Gem::Specification.new do |spec|
18
16
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
17
  spec.require_paths = ['lib']
20
18
 
19
+ spec.add_runtime_dependency 'activesupport', '>= 3.0'
20
+ spec.add_runtime_dependency 'addressable'
21
21
  spec.add_runtime_dependency 'dns_adapter'
22
22
  spec.add_runtime_dependency 'i18n'
23
- spec.add_runtime_dependency 'addressable'
24
- spec.add_runtime_dependency 'activesupport', '>= 3.0'
25
23
  spec.add_development_dependency 'bundler'
26
- spec.add_development_dependency 'rake', '~> 10.0'
24
+ spec.add_development_dependency 'flay'
25
+ spec.add_development_dependency 'rake', '~> 12.3'
27
26
  spec.add_development_dependency 'rspec', '>= 3.0'
28
- spec.add_development_dependency 'rubocop'
29
27
  end
@@ -12,8 +12,10 @@ module Coppertone
12
12
 
13
13
  def build(type, attributes)
14
14
  return nil unless type
15
+
15
16
  klass = map[type]
16
17
  return nil unless klass
18
+
17
19
  klass.create(attributes)
18
20
  end
19
21
  end
@@ -25,6 +25,7 @@ module Coppertone
25
25
 
26
26
  def target_domain
27
27
  raise NeedsContextError unless dns_lookup_term?
28
+
28
29
  mechanism.target_domain
29
30
  end
30
31
 
@@ -37,17 +38,20 @@ module Coppertone
37
38
  qualifier.default? ? mechanism_s : "#{qualifier}#{mechanism_s}"
38
39
  end
39
40
 
40
- DIRECTIVE_REGEXP = /\A([\+\-\~\?]?)([a-zA-Z0-9]*)((:?)\S*)\z/
41
+ DIRECTIVE_REGEXP = /\A([\+\-\~\?]?)([a-zA-Z0-9]*)((:?)\S*)\z/.freeze
41
42
  def self.matching_term(text)
42
43
  return nil if text.include?('=')
44
+
43
45
  matches = DIRECTIVE_REGEXP.match(text)
44
46
  return nil unless matches
47
+
45
48
  qualifier_txt = matches[1]
46
49
  mechanism_type = matches[2].downcase
47
50
  attributes = matches[3]
48
51
  qualifier = Qualifier.find_by_text(qualifier_txt)
49
52
  mechanism = Mechanism.build(mechanism_type, attributes)
50
53
  return nil unless qualifier && mechanism
54
+
51
55
  new(qualifier, mechanism)
52
56
  end
53
57
  end
@@ -15,6 +15,7 @@ module Coppertone
15
15
 
16
16
  def validate_domain_spec_restrictions
17
17
  return if only_allowed_macros? && ends_in_allowed_term?
18
+
18
19
  raise Coppertone::DomainSpecParsingError
19
20
  end
20
21
 
@@ -29,6 +30,7 @@ module Coppertone
29
30
  return true unless lm
30
31
  return false if lm.is_a?(Coppertone::MacroString::MacroStaticExpand)
31
32
  return true if lm.is_a?(Coppertone::MacroString::MacroExpand)
33
+
32
34
  ends_with_top_label?
33
35
  end
34
36
 
@@ -37,8 +39,10 @@ module Coppertone
37
39
  ends_with = ends_with[0..-2] if ends_with[-1] == '.'
38
40
  _, match, tail = ends_with.rpartition('.')
39
41
  return false if match.blank?
42
+
40
43
  hostname = Coppertone::Utils::DomainUtils.valid_tld?(tail)
41
44
  return false unless hostname
45
+
42
46
  true
43
47
  end
44
48
  end
@@ -13,6 +13,7 @@ module Coppertone
13
13
  def initialize(s)
14
14
  @ip = self.class.parse(s)
15
15
  raise ArgumentError unless @ip
16
+
16
17
  @string_representation = s
17
18
  end
18
19
 
@@ -26,6 +27,7 @@ module Coppertone
26
27
  def self.parse(s)
27
28
  return nil unless s
28
29
  return nil if s.index('/')
30
+
29
31
  ip_addr = IPAddr.new(s)
30
32
  normalize_ip(ip_addr)
31
33
  rescue IP_PARSE_ERROR
@@ -33,7 +35,8 @@ module Coppertone
33
35
  end
34
36
 
35
37
  def self.normalize_ip(parsed_ip)
36
- return parsed_ip unless parsed_ip && parsed_ip.ipv6?
38
+ return parsed_ip unless parsed_ip&.ipv6?
39
+
37
40
  parsed_ip.ipv4_mapped? ? parsed_ip.native : parsed_ip
38
41
  end
39
42
 
@@ -39,6 +39,7 @@ module Coppertone
39
39
 
40
40
  def ==(other)
41
41
  return false unless other.instance_of? self.class
42
+
42
43
  macro_text == other.macro_text
43
44
  end
44
45
  alias eql? ==
@@ -12,7 +12,7 @@ module Coppertone
12
12
  PTR_MACRO_CHAR_SET = %w[p P].freeze
13
13
  DELIMITER_CHAR_SET = '[\.\-\+\,\/\_\=]'.freeze
14
14
  VALID_BODY_REGEXP =
15
- /\A(#{MACRO_LETTER_CHAR_SET})(\d*)(r?)(#{DELIMITER_CHAR_SET}*)\z/
15
+ /\A(#{MACRO_LETTER_CHAR_SET})(\d*)(r?)(#{DELIMITER_CHAR_SET}*)\z/.freeze
16
16
 
17
17
  attr_reader :macro_letter, :digit_transformers, :reverse,
18
18
  :delimiter_regexp
@@ -20,6 +20,7 @@ module Coppertone
20
20
  def initialize(s)
21
21
  matches = VALID_BODY_REGEXP.match(s)
22
22
  raise Coppertone::MacroStringParsingError if matches.nil?
23
+
23
24
  @macro_letter = matches[1]
24
25
  initialize_digit_transformers(matches[2])
25
26
  @reverse = (matches[3] == 'r')
@@ -29,6 +30,7 @@ module Coppertone
29
30
 
30
31
  def initialize_digit_transformers(raw_value)
31
32
  return unless raw_value
33
+
32
34
  @digit_transformers = raw_value.to_i unless raw_value.empty?
33
35
  return unless @digit_transformers
34
36
  raise Coppertone::MacroStringParsingError if @digit_transformers.zero?
@@ -44,6 +46,7 @@ module Coppertone
44
46
  Coppertone::Utils::ValidatedDomainFinder
45
47
  .new(context, request, false).find(context.d)
46
48
  return 'unknown' unless ptr
49
+
47
50
  @macro_letter == 'P' ? ::Addressable::URI.encode_component(ptr) : ptr
48
51
  end
49
52
 
@@ -64,6 +67,7 @@ module Coppertone
64
67
 
65
68
  def ==(other)
66
69
  return false unless other.instance_of? self.class
70
+
67
71
  to_s == other.to_s
68
72
  end
69
73
 
@@ -17,6 +17,7 @@ module Coppertone
17
17
 
18
18
  def ==(other)
19
19
  return false unless other.instance_of? self.class
20
+
20
21
  to_s == other.to_s
21
22
  end
22
23
  end
@@ -31,8 +31,10 @@ module Coppertone
31
31
 
32
32
  def parse_contextual_interpolated_macro
33
33
  raise MacroStringParsingError unless @s[1] == '{'
34
+
34
35
  closing_index = @s.index('}')
35
36
  raise MacroStringParsingError unless closing_index
37
+
36
38
  interpolated_body = @s[2, closing_index - 2]
37
39
  @macros << MacroExpand.new(interpolated_body)
38
40
  @s = @s[(closing_index + 1)..-1]
@@ -41,6 +43,7 @@ module Coppertone
41
43
  SIMPLE_MACRO_LETTERS = %w[% _ -].freeze
42
44
  def parse_interpolated_macro
43
45
  raise MacroStringParsingError if @s.length == 1
46
+
44
47
  macro_code = @s[0, 2]
45
48
  if MacroStaticExpand.exists_for?(macro_code)
46
49
  @macros << MacroStaticExpand.macro_for(macro_code)
@@ -31,11 +31,13 @@ module Coppertone
31
31
  SIMPLE_INTERPOLATED_MACRO_LETTERS = %w[% _ -].freeze
32
32
  def self.exists_for?(x)
33
33
  return false unless x && (x.length == 2) && (x[0] == '%')
34
+
34
35
  SIMPLE_INTERPOLATED_MACRO_LETTERS.include?(x[1])
35
36
  end
36
37
 
37
38
  def self.macro_for(x)
38
39
  raise Coppertone::MacroStringParsingError unless exists_for?(x)
40
+
39
41
  case x[1]
40
42
  when '%'
41
43
  PERCENT_MACRO
@@ -18,6 +18,7 @@ module Coppertone
18
18
 
19
19
  def self.register(klass)
20
20
  raise ArgumentError unless klass < self
21
+
21
22
  class_builder.register(klass.label, klass)
22
23
  end
23
24
 
@@ -5,6 +5,7 @@ module Coppertone
5
5
  class All < Mechanism
6
6
  def self.create(attributes)
7
7
  raise InvalidMechanismError unless attributes.blank?
8
+
8
9
  SINGLETON
9
10
  end
10
11
 
@@ -4,10 +4,10 @@ module Coppertone
4
4
  class CidrParser
5
5
  def self.parse(raw_length, max_val)
6
6
  return if raw_length.blank?
7
+
7
8
  length_as_i = raw_length.to_i
8
- if length_as_i < 0 || length_as_i > max_val
9
- raise Coppertone::InvalidMechanismError
10
- end
9
+ raise Coppertone::InvalidMechanismError if length_as_i.negative? || length_as_i > max_val
10
+
11
11
  length_as_i
12
12
  end
13
13
  end
@@ -14,6 +14,7 @@ module Coppertone
14
14
 
15
15
  def trim_domain_spec(raw_domain_spec)
16
16
  return nil if raw_domain_spec.blank?
17
+
17
18
  raw_domain_spec[1..-1]
18
19
  end
19
20
 
@@ -23,21 +24,25 @@ module Coppertone
23
24
 
24
25
  def context_dependent?
25
26
  return true unless domain_spec
27
+
26
28
  domain_spec.context_dependent?
27
29
  end
28
30
 
29
31
  def includes_ptr?
30
32
  return false unless domain_spec
33
+
31
34
  domain_spec.includes_ptr?
32
35
  end
33
36
 
34
37
  def target_domain
35
38
  raise Coppertone::NeedsContextError if context_dependent?
39
+
36
40
  domain_spec.to_s
37
41
  end
38
42
 
39
43
  def ==(other)
40
44
  return false unless other.instance_of? self.class
45
+
41
46
  domain_spec == other.domain_spec
42
47
  end
43
48
  alias eql? ==
@@ -11,6 +11,7 @@ module Coppertone
11
11
  def initialize(attributes)
12
12
  super(attributes)
13
13
  return if attributes.blank?
14
+
14
15
  raw_domain_spec = trim_domain_spec(attributes)
15
16
  @domain_spec = Coppertone::DomainSpec.new(raw_domain_spec)
16
17
  rescue Coppertone::MacroStringParsingError
@@ -10,6 +10,7 @@ module Coppertone
10
10
  super(attributes)
11
11
  raw_domain_spec = trim_domain_spec(attributes)
12
12
  raise InvalidMechanismError if raw_domain_spec.blank?
13
+
13
14
  @domain_spec = Coppertone::DomainSpec.new(raw_domain_spec)
14
15
  rescue Coppertone::MacroStringParsingError
15
16
  raise Coppertone::InvalidMechanismError
@@ -14,6 +14,7 @@ module Coppertone
14
14
  def initialize(attributes)
15
15
  super(attributes)
16
16
  return if attributes.blank?
17
+
17
18
  parse_argument(attributes)
18
19
  rescue Coppertone::MacroStringParsingError
19
20
  raise Coppertone::InvalidMechanismError
@@ -37,11 +38,13 @@ module Coppertone
37
38
  end
38
39
  end
39
40
 
40
- CIDR_REGEXP = %r{(/(\d*))?(//(\d*))?\z}
41
+ CIDR_REGEXP = %r{(/(\d*))?(//(\d*))?\z}.freeze
41
42
  def parse_argument(attributes)
42
43
  raise InvalidMechanismError if attributes.blank?
44
+
43
45
  cidr_matches = CIDR_REGEXP.match(attributes)
44
46
  raise InvalidMechanismError unless cidr_matches
47
+
45
48
  macro_string, raw_ip_v4_cidr_length, raw_ip_v6_cidr_length =
46
49
  clean_matches(attributes, cidr_matches)
47
50
  process_matches(macro_string, raw_ip_v4_cidr_length,
@@ -50,12 +53,15 @@ module Coppertone
50
53
 
51
54
  def parse_domain_spec(attributes, domain_spec_end)
52
55
  return nil if attributes.blank?
56
+
53
57
  cand = attributes[0..domain_spec_end]
54
58
  return nil if cand.blank?
59
+
55
60
  cand = trim_domain_spec(cand)
56
61
  # At this point we've ascertained that there is
57
62
  # a body to the domain spec
58
63
  raise InvalidMechanismError if cand.blank?
64
+
59
65
  cand
60
66
  end
61
67
 
@@ -97,6 +103,7 @@ module Coppertone
97
103
 
98
104
  def ==(other)
99
105
  return false unless other.instance_of? self.class
106
+
100
107
  domain_spec == other.domain_spec &&
101
108
  ip_v4_cidr_length == other.ip_v4_cidr_length &&
102
109
  ip_v6_cidr_length == other.ip_v6_cidr_length
@@ -13,6 +13,7 @@ module Coppertone
13
13
  def evaluate_none_result(result, m, r)
14
14
  new_result = super
15
15
  return new_result unless new_result.none?
16
+
16
17
  raise Coppertone::NoneIncludeResultError
17
18
  end
18
19
  end
@@ -24,6 +25,7 @@ module Coppertone
24
25
 
25
26
  def match?(macro_context, request_context)
26
27
  raise Coppertone::NoneIncludeResultError if record.nil?
28
+
27
29
  record_result =
28
30
  IncludeRecordEvaluator.new(record)
29
31
  .evaluate(macro_context, request_context)
@@ -16,9 +16,10 @@ module Coppertone
16
16
  raise Coppertone::InvalidMechanismError if @netblock.nil?
17
17
  end
18
18
 
19
- LEADING_ZEROES_IN_CIDR_REGEXP = %r{\/0\d}
19
+ LEADING_ZEROES_IN_CIDR_REGEXP = %r{\/0\d}.freeze
20
20
  def validate_no_leading_zeroes_in_cidr(ip_as_s)
21
- return unless LEADING_ZEROES_IN_CIDR_REGEXP.match(ip_as_s)
21
+ return unless LEADING_ZEROES_IN_CIDR_REGEXP.match?(ip_as_s)
22
+
22
23
  raise Coppertone::InvalidMechanismError
23
24
  end
24
25
 
@@ -33,6 +34,7 @@ module Coppertone
33
34
  validate_no_leading_zeroes_in_cidr(ip_as_s)
34
35
  addr, cidr_length_as_s, dual = ip_as_s.split('/')
35
36
  return [nil, nil] if dual
37
+
36
38
  network = IPAddr.new(addr)
37
39
  network = network.mask(cidr_length_as_s.to_i) unless cidr_length_as_s.blank?
38
40
  cidr_length = cidr_length_as_s.blank? ? default_cidr(network) : cidr_length_as_s.to_i
@@ -49,11 +51,13 @@ module Coppertone
49
51
  ip = ip_for_match(macro_context)
50
52
  return false unless ip
51
53
  return false unless ip.ipv4? == @netblock.ipv4?
54
+
52
55
  @netblock.include?(ip)
53
56
  end
54
57
 
55
58
  def ==(other)
56
59
  return false unless other.instance_of? self.class
60
+
57
61
  netblock == other.netblock
58
62
  end
59
63
  end
@@ -32,6 +32,7 @@ module Coppertone
32
32
  def check_a_record_limit(request_context, count)
33
33
  limit = request_context.dns_lookups_per_mx_mechanism_limit
34
34
  return unless limit && count > limit
35
+
35
36
  raise Coppertone::MXLimitExceededError
36
37
  end
37
38
 
@@ -14,10 +14,11 @@ module Coppertone
14
14
  class_builder.register(klass.label, klass)
15
15
  end
16
16
 
17
- MODIFIER_REGEXP = /\A([a-zA-Z]+[a-zA-Z0-9\-\_\.]*)=(\S*)\z/
17
+ MODIFIER_REGEXP = /\A([a-zA-Z]+[a-zA-Z0-9\-\_\.]*)=(\S*)\z/.freeze
18
18
  def self.matching_term(text)
19
19
  matches = MODIFIER_REGEXP.match(text)
20
20
  return nil unless matches
21
+
21
22
  type = matches[1]
22
23
  attributes = matches[2]
23
24
  build(type, attributes) || build_unknown(type, attributes)
@@ -6,6 +6,7 @@ module Coppertone
6
6
  def initialize(attributes)
7
7
  super(attributes)
8
8
  raise InvalidModifierError if attributes.blank?
9
+
9
10
  @domain_spec = Coppertone::DomainSpec.new(attributes)
10
11
  rescue Coppertone::MacroStringParsingError
11
12
  raise Coppertone::InvalidModifierError
@@ -29,6 +30,7 @@ module Coppertone
29
30
 
30
31
  def ==(other)
31
32
  return false unless other.instance_of? self.class
33
+
32
34
  domain_spec == other.domain_spec
33
35
  end
34
36
  end
@@ -8,15 +8,18 @@ module Coppertone
8
8
  new(attributes)
9
9
  end
10
10
 
11
- ASCII_REGEXP = /\A[[:ascii:]]*\z/
11
+ ASCII_REGEXP = /\A[[:ascii:]]*\z/.freeze
12
12
  def evaluate(macro_context, request_context)
13
13
  target_name =
14
14
  target_name_from_domain_spec(macro_context, request_context)
15
15
  return nil unless target_name
16
+
16
17
  macro_string = lookup_macro_string(target_name, request_context)
17
18
  return nil unless macro_string
19
+
18
20
  expanded = macro_string.expand(macro_context, request_context)
19
- return nil unless ASCII_REGEXP.match(expanded)
21
+ return nil unless ASCII_REGEXP.match?(expanded)
22
+
20
23
  expanded
21
24
  rescue DNSAdapter::Error
22
25
  nil
@@ -27,6 +30,7 @@ module Coppertone
27
30
  request_context.dns_client.fetch_txt_records(target_name)
28
31
  return nil if records.empty?
29
32
  return nil if records.size > 1
33
+
30
34
  MacroString.new(records.first[:text])
31
35
  end
32
36
 
@@ -19,6 +19,7 @@ module Coppertone
19
19
 
20
20
  def target_domain
21
21
  raise NeedsContextError if context_dependent?
22
+
22
23
  arguments
23
24
  end
24
25
 
@@ -67,6 +67,7 @@ module Coppertone
67
67
 
68
68
  def netblocks_only?
69
69
  return false if redirect
70
+
70
71
  directives.reject(&:all?).reject do |d|
71
72
  d.mechanism.is_a?(Coppertone::Mechanism::IPMechanism)
72
73
  end.empty?
@@ -74,7 +75,8 @@ module Coppertone
74
75
 
75
76
  def context_dependent_evaluation?
76
77
  return true if directives.any?(&:context_dependent?)
77
- redirect && redirect.context_dependent?
78
+
79
+ redirect&.context_dependent?
78
80
  end
79
81
 
80
82
  def exp
@@ -82,7 +84,7 @@ module Coppertone
82
84
  end
83
85
 
84
86
  def context_dependent_explanation?
85
- exp && exp.context_dependent?
87
+ exp&.context_dependent?
86
88
  end
87
89
 
88
90
  KNOWN_MODS =
@@ -95,6 +97,7 @@ module Coppertone
95
97
  def find_modifier(klass)
96
98
  arr = modifiers.select { |m| m.is_a?(klass) }
97
99
  raise DuplicateModifierError if arr.size > 1
100
+
98
101
  arr.first
99
102
  end
100
103
 
@@ -9,6 +9,7 @@ module Coppertone
9
9
  def evaluate(macro_context, request_context)
10
10
  result = directive_result(macro_context, request_context)
11
11
  return result unless result.none? || result.fail?
12
+
12
13
  if result.fail?
13
14
  evaluate_fail_result(result, macro_context, request_context)
14
15
  else
@@ -30,6 +31,7 @@ module Coppertone
30
31
  def add_exp_to_result(result, macro_context, request_context)
31
32
  result = add_default_exp(result)
32
33
  return result unless record.exp
34
+
33
35
  computed_exp = record.exp.evaluate(macro_context, request_context)
34
36
  result.explanation = computed_exp if computed_exp
35
37
  result
@@ -50,10 +52,12 @@ module Coppertone
50
52
 
51
53
  def evaluate_none_result(result, macro_context, request_context)
52
54
  return result unless follow_redirect?
55
+
53
56
  finder =
54
57
  Coppertone::RedirectRecordFinder.new(record.redirect, macro_context,
55
58
  request_context)
56
59
  raise InvalidRedirectError unless finder.target && finder.record
60
+
57
61
  rc = macro_context.with_domain(finder.target)
58
62
  RecordEvaluator.new(finder.record).evaluate(rc, request_context)
59
63
  end
@@ -13,6 +13,7 @@ module Coppertone
13
13
  validate_txt_records
14
14
  spf_dns_record = txt_records.first
15
15
  return nil unless spf_dns_record
16
+
16
17
  Record.new(spf_dns_record)
17
18
  end
18
19
  end
@@ -2,18 +2,20 @@ module Coppertone
2
2
  # Parses a record into terms
3
3
  class RecordTermParser
4
4
  VERSION_STR = 'v=spf1'.freeze
5
- RECORD_REGEXP = /\A#{VERSION_STR}(\s|\z)/i
6
- ALLOWED_CHARACTERS = /\A([\x21-\x7e ]+)\z/
5
+ RECORD_REGEXP = /\A#{VERSION_STR}(\s|\z)/i.freeze
6
+ ALLOWED_CHARACTERS = /\A([\x21-\x7e ]+)\z/.freeze
7
7
 
8
8
  def self.record?(text)
9
9
  return false if text.blank?
10
- RECORD_REGEXP.match(text.strip) ? true : false
10
+
11
+ RECORD_REGEXP.match?(text.strip) ? true : false
11
12
  end
12
13
 
13
14
  attr_reader :text, :terms
14
15
  def initialize(text)
15
16
  raise RecordParsingError unless self.class.record?(text)
16
- raise RecordParsingError unless ALLOWED_CHARACTERS.match(text)
17
+ raise RecordParsingError unless ALLOWED_CHARACTERS.match?(text)
18
+
17
19
  @text = text
18
20
  @terms = Coppertone::TermsParser.new(terms_segment).terms
19
21
  end
@@ -9,11 +9,12 @@ module Coppertone
9
9
  end
10
10
 
11
11
  def target
12
- @redirect_target ||= redirect.evaluate(macro_context, request_context)
12
+ @target ||= redirect.evaluate(macro_context, request_context)
13
13
  end
14
14
 
15
15
  def record
16
16
  return unless target
17
+
17
18
  @record ||= RecordFinder.new(request_context.dns_client, target).record
18
19
  end
19
20
  end
@@ -16,13 +16,15 @@ module Coppertone
16
16
 
17
17
  def process_helo
18
18
  check_spf_for_helo
19
- return nil if helo_result && helo_result.none?
19
+ return nil if helo_result&.none?
20
+
20
21
  helo_result
21
22
  end
22
23
 
23
24
  def process_mailfrom
24
25
  check_spf_for_mailfrom
25
- return nil if mailfrom_result && mailfrom_result.none?
26
+ return nil if mailfrom_result&.none?
27
+
26
28
  mailfrom_result
27
29
  end
28
30
 
@@ -34,25 +36,6 @@ module Coppertone
34
36
  helo_result.nil? && mailfrom_result.nil?
35
37
  end
36
38
 
37
- def check_spf_for_helo
38
- @helo_result ||= check_spf_for_context(helo_context, 'helo')
39
- end
40
-
41
- def check_spf_for_mailfrom
42
- @mailfrom_result ||= check_spf_for_context(mailfrom_context, 'mailfrom')
43
- end
44
-
45
- def check_spf_for_context(macro_context, identity)
46
- record = spf_record(macro_context)
47
- @result = spf_request(macro_context, record, identity) if record
48
- rescue DNSAdapter::Error => e
49
- Result.temperror(e.message)
50
- rescue Coppertone::TemperrorError => e
51
- Result.temperror(e.message)
52
- rescue Coppertone::PermerrorError => e
53
- Result.permerror(e.message)
54
- end
55
-
56
39
  def request_context
57
40
  @request_context ||= RequestContext.new(options)
58
41
  end
@@ -72,9 +55,31 @@ module Coppertone
72
55
 
73
56
  def spf_request(macro_context, record, identity)
74
57
  return Result.new(:none) if record.nil?
58
+
75
59
  r = RecordEvaluator.new(record).evaluate(macro_context, request_context)
76
60
  r.identity = identity
77
61
  r
78
62
  end
63
+
64
+ private
65
+
66
+ def check_spf_for_helo
67
+ @helo_result = check_spf_for_context(helo_context, 'helo')
68
+ end
69
+
70
+ def check_spf_for_mailfrom
71
+ @mailfrom_result = check_spf_for_context(mailfrom_context, 'mailfrom')
72
+ end
73
+
74
+ def check_spf_for_context(macro_context, identity)
75
+ record = spf_record(macro_context)
76
+ @result = spf_request(macro_context, record, identity) if record
77
+ rescue DNSAdapter::Error => e
78
+ Result.temperror(e.message)
79
+ rescue Coppertone::TemperrorError => e
80
+ Result.temperror(e.message)
81
+ rescue Coppertone::PermerrorError => e
82
+ Result.permerror(e.message)
83
+ end
79
84
  end
80
85
  end
@@ -60,6 +60,7 @@ module Coppertone
60
60
 
61
61
  def config_value(k)
62
62
  return @options[k] if @options.key?(k)
63
+
63
64
  Coppertone.config.send(k)
64
65
  end
65
66
  end
@@ -26,6 +26,7 @@ module Coppertone
26
26
 
27
27
  def exceeded?
28
28
  return false unless limited?
29
+
29
30
  count > limit
30
31
  end
31
32
 
@@ -4,7 +4,7 @@ module Coppertone
4
4
  # for the macro letters.
5
5
  class SenderIdentity
6
6
  DEFAULT_LOCALPART = 'postmaster'.freeze
7
- EMAIL_ADDRESS_SPLIT_REGEXP = /^(.*)@(.*?)$/
7
+ EMAIL_ADDRESS_SPLIT_REGEXP = /^(.*)@(.*?)$/.freeze
8
8
 
9
9
  attr_reader :sender, :localpart, :domain
10
10
  def initialize(sender)
@@ -7,6 +7,7 @@ module Coppertone
7
7
  class Term
8
8
  def self.build_from_token(token)
9
9
  return nil unless token
10
+
10
11
  Directive.matching_term(token) || Modifier.matching_term(token)
11
12
  end
12
13
  end
@@ -17,6 +17,7 @@ module Coppertone
17
17
  def parse_token(token)
18
18
  term = Term.build_from_token(token)
19
19
  raise RecordParsingError unless term
20
+
20
21
  term
21
22
  end
22
23
  end
@@ -7,12 +7,17 @@ module Coppertone
7
7
  class DomainUtils
8
8
  def self.valid?(domain)
9
9
  return false if domain.blank?
10
- labels = to_ascii_labels(domain)
11
- return false if labels.length <= 1
12
10
  return false if domain.length > 253
13
- return false if labels.any? { |l| !valid_label?(l) }
11
+
12
+ labels_valid?(to_ascii_labels(domain))
13
+ end
14
+
15
+ def self.labels_valid?(labels)
16
+ return false if labels.length <= 1
17
+ return false if labels.first == '*'
14
18
  return false unless valid_tld?(labels.last)
15
- true
19
+
20
+ labels.all? { |l| valid_label?(l) }
16
21
  end
17
22
 
18
23
  def self.to_labels(domain)
@@ -22,6 +27,7 @@ module Coppertone
22
27
  def self.parent_domain(domain)
23
28
  labels = to_labels(domain)
24
29
  return '.' if labels.size == 1
30
+
25
31
  labels.shift
26
32
  labels.join('.')
27
33
  end
@@ -34,24 +40,28 @@ module Coppertone
34
40
  to_ascii_labels(domain).join('.')
35
41
  end
36
42
 
37
- NO_DASH_NONNUMERIC_REGEXP = /\A[a-zA-Z0-9]*[a-zA-Z]+[a-zA-Z0-9]*\z/
38
- NO_DASH_REGEXP = /\A[a-zA-Z0-9]+\z/
39
- DASH_REGEXP = /\A[a-zA-Z0-9]+\-[a-zA-Z0-9\-]*[a-zA-Z0-9]+\z/
43
+ NO_DASH_NONNUMERIC_REGEXP = /\A[a-zA-Z0-9]*[a-zA-Z]+[a-zA-Z0-9]*\z/.freeze
44
+ NO_DASH_REGEXP = /\A[a-zA-Z0-9]+\z/.freeze
45
+ DASH_REGEXP = /\A[a-zA-Z0-9]+\-[a-zA-Z0-9\-]*[a-zA-Z0-9]+\z/.freeze
40
46
 
41
47
  def self.valid_hostname_label?(l)
42
48
  return false unless valid_label?(l)
49
+
43
50
  NO_DASH_REGEXP.match(l) || DASH_REGEXP.match(l)
44
51
  end
45
52
 
46
53
  def self.valid_tld?(l)
47
54
  return false unless valid_label?(l)
55
+
48
56
  NO_DASH_NONNUMERIC_REGEXP.match(l) || DASH_REGEXP.match(l)
49
57
  end
50
58
 
51
59
  def self.valid_ldh_domain?(domain)
52
60
  return false unless valid?(domain)
61
+
53
62
  labels = to_ascii_labels(domain)
54
63
  return false unless valid_tld?(labels.last)
64
+
55
65
  labels.all? { |l| valid_hostname_label?(l) }
56
66
  end
57
67
 
@@ -61,6 +71,7 @@ module Coppertone
61
71
 
62
72
  def self.macro_expanded_domain(domain)
63
73
  return nil if domain.blank?
74
+
64
75
  labels = to_ascii_labels(domain)
65
76
  domain = labels.join('.')
66
77
  while domain.length > 253
@@ -75,12 +86,14 @@ module Coppertone
75
86
  domain_labels = to_ascii_labels(domain)
76
87
  num_labels_in_domain = domain_labels.length
77
88
  return false if subdomain_labels.length <= domain_labels.length
89
+
78
90
  subdomain_labels.last(num_labels_in_domain) == domain_labels
79
91
  end
80
92
 
81
93
  def self.subdomain_or_same?(candidate, domain)
82
94
  return false unless valid?(domain) && valid?(candidate)
83
95
  return true if normalized_domain(domain) == normalized_domain(candidate)
96
+
84
97
  subdomain_of?(candidate, domain)
85
98
  end
86
99
  end
@@ -1,3 +1,3 @@
1
1
  module Coppertone
2
- VERSION = '0.0.10'.freeze
2
+ VERSION = '0.0.11'.freeze
3
3
  end
@@ -2,7 +2,7 @@ require 'spec_helper'
2
2
 
3
3
  describe Coppertone::MacroString::MacroStaticExpand do
4
4
  context '#exists_for?' do
5
- %w[%% %_ %-].each do |x|
5
+ ['%%', '%_', '%-'].each do |x|
6
6
  it "should resolve a macro for the key #{x}" do
7
7
  expect(Coppertone::MacroString::MacroStaticExpand.exists_for?(x))
8
8
  .to eq(true)
@@ -16,7 +16,7 @@ describe Coppertone::MacroString::MacroStaticExpand do
16
16
  end
17
17
 
18
18
  context 'macro_for' do
19
- %w[%% %_ %-].each do |x|
19
+ ['%%', '%_', '%-'].each do |x|
20
20
  it "should resolve a macro for the key #{x}" do
21
21
  expect(Coppertone::MacroString::MacroStaticExpand.macro_for(x))
22
22
  .not_to be_nil
@@ -1,5 +1,3 @@
1
- # -- encoding : utf-8 --
2
-
3
1
  require 'spec_helper'
4
2
 
5
3
  describe 'Initial processing' do
@@ -1,5 +1,3 @@
1
- # -- encoding : utf-8 --
2
-
3
1
  require 'spec_helper'
4
2
 
5
3
  describe 'Semantics of exp and other modifiers' do
@@ -1,5 +1,10 @@
1
1
  require 'simplecov'
2
2
 
3
+ if ENV['CI'] == 'true'
4
+ require 'codecov'
5
+ SimpleCov.formatter = SimpleCov::Formatter::Codecov
6
+ end
7
+
3
8
  # save to CircleCI's artifacts directory if we're on CircleCI
4
9
  if ENV['CIRCLE_ARTIFACTS']
5
10
  dir = File.join(ENV['CIRCLE_ARTIFACTS'], 'coverage')
@@ -1,5 +1,3 @@
1
- # -- encoding : utf-8 --
2
-
3
1
  require 'spec_helper'
4
2
 
5
3
  describe Coppertone::Utils::DomainUtils do
@@ -46,6 +44,11 @@ describe Coppertone::Utils::DomainUtils do
46
44
  expect(subject.valid?('abcd._domainkey.gmail.com')).to eq(true)
47
45
  end
48
46
 
47
+ it 'should reject wildcard domains while allowing interstitial asterisks' do
48
+ expect(subject.valid?('*.axb.longshot.com')).to eq(false)
49
+ expect(subject.valid?('rst.*.example.com')).to eq(true)
50
+ end
51
+
49
52
  it 'should reject IP addresses' do
50
53
  expect(subject.valid?('192.38.7.14')).to eq(false)
51
54
  end
metadata CHANGED
@@ -1,31 +1,31 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: coppertone
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.10
4
+ version: 0.0.11
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter M. Goldstein
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-10-19 00:00:00.000000000 Z
11
+ date: 2019-07-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: dns_adapter
14
+ name: activesupport
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '0'
19
+ version: '3.0'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: '0'
26
+ version: '3.0'
27
27
  - !ruby/object:Gem::Dependency
28
- name: i18n
28
+ name: addressable
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - ">="
@@ -39,7 +39,7 @@ dependencies:
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
41
  - !ruby/object:Gem::Dependency
42
- name: addressable
42
+ name: dns_adapter
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - ">="
@@ -53,19 +53,19 @@ dependencies:
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
55
  - !ruby/object:Gem::Dependency
56
- name: activesupport
56
+ name: i18n
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - ">="
60
60
  - !ruby/object:Gem::Version
61
- version: '3.0'
61
+ version: '0'
62
62
  type: :runtime
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - ">="
67
67
  - !ruby/object:Gem::Version
68
- version: '3.0'
68
+ version: '0'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: bundler
71
71
  requirement: !ruby/object:Gem::Requirement
@@ -81,47 +81,47 @@ dependencies:
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
83
  - !ruby/object:Gem::Dependency
84
- name: rake
84
+ name: flay
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
- - - "~>"
87
+ - - ">="
88
88
  - !ruby/object:Gem::Version
89
- version: '10.0'
89
+ version: '0'
90
90
  type: :development
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
- - - "~>"
94
+ - - ">="
95
95
  - !ruby/object:Gem::Version
96
- version: '10.0'
96
+ version: '0'
97
97
  - !ruby/object:Gem::Dependency
98
- name: rspec
98
+ name: rake
99
99
  requirement: !ruby/object:Gem::Requirement
100
100
  requirements:
101
- - - ">="
101
+ - - "~>"
102
102
  - !ruby/object:Gem::Version
103
- version: '3.0'
103
+ version: '12.3'
104
104
  type: :development
105
105
  prerelease: false
106
106
  version_requirements: !ruby/object:Gem::Requirement
107
107
  requirements:
108
- - - ">="
108
+ - - "~>"
109
109
  - !ruby/object:Gem::Version
110
- version: '3.0'
110
+ version: '12.3'
111
111
  - !ruby/object:Gem::Dependency
112
- name: rubocop
112
+ name: rspec
113
113
  requirement: !ruby/object:Gem::Requirement
114
114
  requirements:
115
115
  - - ">="
116
116
  - !ruby/object:Gem::Version
117
- version: '0'
117
+ version: '3.0'
118
118
  type: :development
119
119
  prerelease: false
120
120
  version_requirements: !ruby/object:Gem::Requirement
121
121
  requirements:
122
122
  - - ">="
123
123
  - !ruby/object:Gem::Version
124
- version: '0'
124
+ version: '3.0'
125
125
  description: Coppertone includes tools for parsing SPF DNS records, evaluating the
126
126
  result of SPF checks for received emails, and creating appropriate email headers
127
127
  from SPF results.
@@ -131,6 +131,7 @@ executables: []
131
131
  extensions: []
132
132
  extra_rdoc_files: []
133
133
  files:
134
+ - ".circleci/config.yml"
134
135
  - ".codeclimate.yml"
135
136
  - ".gitignore"
136
137
  - ".rubocop.yml"
@@ -139,7 +140,6 @@ files:
139
140
  - LICENSE
140
141
  - README.md
141
142
  - Rakefile
142
- - circle.yml
143
143
  - coppertone.gemspec
144
144
  - lib/coppertone.rb
145
145
  - lib/coppertone/class_builder.rb
@@ -268,8 +268,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
268
268
  - !ruby/object:Gem::Version
269
269
  version: '0'
270
270
  requirements: []
271
- rubyforge_project:
272
- rubygems_version: 2.6.13
271
+ rubygems_version: 3.0.4
273
272
  signing_key:
274
273
  specification_version: 4
275
274
  summary: A Sender Policy Framework (SPF) toolkit
data/circle.yml DELETED
@@ -1,3 +0,0 @@
1
- machine:
2
- ruby:
3
- version: 2.4.0