coppertone 0.0.10 → 0.0.11
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 +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