coppertone 0.0.10 → 0.0.11
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.circleci/config.yml +91 -0
- data/.rubocop.yml +6 -2
- data/.travis.yml +2 -9
- data/Gemfile +10 -2
- data/Rakefile +6 -1
- data/coppertone.gemspec +5 -7
- data/lib/coppertone/class_builder.rb +2 -0
- data/lib/coppertone/directive.rb +5 -1
- data/lib/coppertone/domain_spec.rb +4 -0
- data/lib/coppertone/ip_address_wrapper.rb +4 -1
- data/lib/coppertone/macro_string.rb +1 -0
- data/lib/coppertone/macro_string/macro_expand.rb +5 -1
- data/lib/coppertone/macro_string/macro_literal.rb +1 -0
- data/lib/coppertone/macro_string/macro_parser.rb +3 -0
- data/lib/coppertone/macro_string/macro_static_expand.rb +2 -0
- data/lib/coppertone/mechanism.rb +1 -0
- data/lib/coppertone/mechanism/all.rb +1 -0
- data/lib/coppertone/mechanism/cidr_parser.rb +3 -3
- data/lib/coppertone/mechanism/domain_spec_mechanism.rb +5 -0
- data/lib/coppertone/mechanism/domain_spec_optional.rb +1 -0
- data/lib/coppertone/mechanism/domain_spec_required.rb +1 -0
- data/lib/coppertone/mechanism/domain_spec_with_dual_cidr.rb +8 -1
- data/lib/coppertone/mechanism/include_matcher.rb +2 -0
- data/lib/coppertone/mechanism/ip_mechanism.rb +6 -2
- data/lib/coppertone/mechanism/mx.rb +1 -0
- data/lib/coppertone/modifier.rb +2 -1
- data/lib/coppertone/modifier/base.rb +2 -0
- data/lib/coppertone/modifier/exp.rb +6 -2
- data/lib/coppertone/modifier/redirect.rb +1 -0
- data/lib/coppertone/record.rb +5 -2
- data/lib/coppertone/record_evaluator.rb +4 -0
- data/lib/coppertone/record_finder.rb +1 -0
- data/lib/coppertone/record_term_parser.rb +6 -4
- data/lib/coppertone/redirect_record_finder.rb +2 -1
- data/lib/coppertone/request.rb +26 -21
- data/lib/coppertone/request_context.rb +1 -0
- data/lib/coppertone/request_count_limiter.rb +1 -0
- data/lib/coppertone/sender_identity.rb +1 -1
- data/lib/coppertone/term.rb +1 -0
- data/lib/coppertone/terms_parser.rb +1 -0
- data/lib/coppertone/utils/domain_utils.rb +20 -7
- data/lib/coppertone/version.rb +1 -1
- data/spec/macro_string/macro_static_expand_spec.rb +2 -2
- data/spec/open_spf/Initial_processing_spec.rb +0 -2
- data/spec/open_spf/Semantics_of_exp_and_other_modifiers_spec.rb +0 -2
- data/spec/spec_helper.rb +5 -0
- data/spec/utils/domain_utils_spec.rb +5 -2
- metadata +25 -26
- data/circle.yml +0 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: a8802307a322cafa013376ddb5f67bfd134996b4f51b9753f04c4c024158c6f5
|
4
|
+
data.tar.gz: 4a426ab5a166b4e896417bd004f2ea597d77074929f389f7f152698b65258c64
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 260ff1c6805f0f74cc9c8939b66b99ab903f83f548b22e3a05a8ed4fec45867549e4d8b01ea66b06fdf17096f1e08fb094c292070209684c5531ad9e481d37c4
|
7
|
+
data.tar.gz: c4753800b5357e24aed9a964d00ea0f7ae5a91056407edd38b325a5b6dee1c0fdc33edfa5c79644b6dee00b4104cca2497d7933fd18e89eabc21216888281308
|
@@ -0,0 +1,91 @@
|
|
1
|
+
version: 2
|
2
|
+
aliases:
|
3
|
+
- ¤t_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
|
data/.rubocop.yml
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
AllCops:
|
2
|
-
TargetRubyVersion: 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
|
-
|
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
|
data/.travis.yml
CHANGED
@@ -1,12 +1,5 @@
|
|
1
1
|
sudo: false
|
2
2
|
language: ruby
|
3
3
|
rvm:
|
4
|
-
- 2.
|
5
|
-
- 2.
|
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
|
-
|
7
|
-
gem '
|
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']
|
data/coppertone.gemspec
CHANGED
@@ -1,6 +1,4 @@
|
|
1
|
-
|
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 '
|
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
|
data/lib/coppertone/directive.rb
CHANGED
@@ -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
|
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
|
|
@@ -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
|
|
@@ -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
|
data/lib/coppertone/mechanism.rb
CHANGED
@@ -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
|
9
|
-
|
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? ==
|
@@ -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
|
data/lib/coppertone/modifier.rb
CHANGED
@@ -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
|
|
data/lib/coppertone/record.rb
CHANGED
@@ -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
|
-
|
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
|
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
|
@@ -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
|
-
|
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
|
-
@
|
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
|
data/lib/coppertone/request.rb
CHANGED
@@ -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
|
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
|
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
|
@@ -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)
|
data/lib/coppertone/term.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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
|
data/lib/coppertone/version.rb
CHANGED
@@ -2,7 +2,7 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe Coppertone::MacroString::MacroStaticExpand do
|
4
4
|
context '#exists_for?' do
|
5
|
-
|
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
|
-
|
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
|
data/spec/spec_helper.rb
CHANGED
@@ -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.
|
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:
|
11
|
+
date: 2019-07-31 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
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:
|
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:
|
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:
|
56
|
+
name: i18n
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
59
|
- - ">="
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version: '
|
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: '
|
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:
|
84
|
+
name: flay
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
86
86
|
requirements:
|
87
|
-
- - "
|
87
|
+
- - ">="
|
88
88
|
- !ruby/object:Gem::Version
|
89
|
-
version: '
|
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: '
|
96
|
+
version: '0'
|
97
97
|
- !ruby/object:Gem::Dependency
|
98
|
-
name:
|
98
|
+
name: rake
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
100
100
|
requirements:
|
101
|
-
- - "
|
101
|
+
- - "~>"
|
102
102
|
- !ruby/object:Gem::Version
|
103
|
-
version: '3
|
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
|
110
|
+
version: '12.3'
|
111
111
|
- !ruby/object:Gem::Dependency
|
112
|
-
name:
|
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
|
-
|
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