coppertone 0.0.1 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.travis.yml +1 -0
- data/LICENSE +1 -1
- data/Rakefile +2 -2
- data/coppertone.gemspec +1 -0
- data/lib/coppertone/directive.rb +14 -7
- data/lib/coppertone/error.rb +24 -5
- data/lib/coppertone/ip_address_wrapper.rb +9 -6
- data/lib/coppertone/macro_context.rb +7 -13
- data/lib/coppertone/macro_string/macro_expand.rb +3 -2
- data/lib/coppertone/macro_string.rb +12 -5
- data/lib/coppertone/mechanism/a.rb +5 -1
- data/lib/coppertone/mechanism/all.rb +11 -4
- data/lib/coppertone/mechanism/cidr_parser.rb +1 -1
- data/lib/coppertone/mechanism/domain_spec_mechanism.rb +9 -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 +1 -0
- data/lib/coppertone/mechanism/exists.rb +5 -1
- data/lib/coppertone/mechanism/include.rb +17 -5
- data/lib/coppertone/mechanism/ip4.rb +5 -1
- data/lib/coppertone/mechanism/ip6.rb +5 -1
- data/lib/coppertone/mechanism/ip_mechanism.rb +9 -1
- data/lib/coppertone/mechanism/mx.rb +5 -1
- data/lib/coppertone/mechanism/ptr.rb +5 -1
- data/lib/coppertone/mechanism.rb +25 -2
- data/lib/coppertone/modifier/base.rb +1 -0
- data/lib/coppertone/modifier/exp.rb +6 -2
- data/lib/coppertone/modifier/redirect.rb +5 -1
- data/lib/coppertone/modifier/unknown.rb +6 -1
- data/lib/coppertone/modifier.rb +11 -2
- data/lib/coppertone/null_macro_context.rb +23 -0
- data/lib/coppertone/qualifier.rb +8 -0
- data/lib/coppertone/record.rb +33 -41
- data/lib/coppertone/record_finder.rb +3 -1
- data/lib/coppertone/record_term_parser.rb +30 -0
- data/lib/coppertone/request.rb +3 -1
- data/lib/coppertone/request_context.rb +1 -2
- data/lib/coppertone/result.rb +6 -2
- data/lib/coppertone/utils/validated_domain_finder.rb +6 -4
- data/lib/coppertone/version.rb +1 -1
- data/lib/coppertone.rb +3 -1
- data/spec/directive_spec.rb +13 -0
- data/spec/ip_address_wrapper_spec.rb +3 -0
- data/spec/macro_string_spec.rb +20 -0
- data/spec/mechanism/a_spec.rb +22 -7
- data/spec/mechanism/all_spec.rb +8 -0
- data/spec/mechanism/exists_spec.rb +14 -6
- data/spec/mechanism/include_spec.rb +13 -1
- data/spec/mechanism/ip4_spec.rb +15 -5
- data/spec/mechanism/ip6_spec.rb +18 -11
- data/spec/mechanism/mx_spec.rb +56 -0
- data/spec/mechanism/ptr_spec.rb +7 -0
- data/spec/modifier/exp_spec.rb +10 -0
- data/spec/modifier/redirect_spec.rb +10 -0
- data/spec/open_spf/ALL_mechanism_syntax_spec.rb +6 -6
- data/spec/open_spf/A_mechanism_syntax_spec.rb +30 -30
- data/spec/open_spf/EXISTS_mechanism_syntax_spec.rb +8 -8
- data/spec/open_spf/IP4_mechanism_syntax_spec.rb +10 -10
- data/spec/open_spf/IP6_mechanism_syntax_spec.rb +10 -10
- data/spec/open_spf/Include_mechanism_semantics_and_syntax_spec.rb +10 -10
- data/spec/open_spf/Initial_processing_spec.rb +21 -14
- data/spec/open_spf/MX_mechanism_syntax_spec.rb +22 -22
- data/spec/open_spf/Macro_expansion_rules_spec.rb +26 -30
- data/spec/open_spf/PTR_mechanism_syntax_spec.rb +7 -7
- data/spec/open_spf/Processing_limits_spec.rb +12 -12
- data/spec/open_spf/Record_evaluation_spec.rb +13 -13
- data/spec/open_spf/Record_lookup_spec.rb +8 -8
- data/spec/open_spf/Selecting_records_spec.rb +11 -11
- data/spec/open_spf/Semantics_of_exp_and_other_modifiers_spec.rb +27 -25
- data/spec/open_spf/Test_cases_from_implementation_bugs_spec.rb +2 -2
- data/spec/record_spec.rb +34 -13
- data/spec/request_context_spec.rb +1 -1
- data/spec/rfc7208-tests.yml +10 -0
- metadata +59 -48
- data/lib/coppertone/dns/error.rb +0 -9
- data/lib/coppertone/dns/mock_client.rb +0 -106
- data/lib/coppertone/dns/resolv_client.rb +0 -110
- data/lib/coppertone/dns.rb +0 -3
- data/lib/resolv/dns/resource/in/spf.rb +0 -15
- data/spec/dns/resolv_client_spec.rb +0 -307
data/lib/coppertone/qualifier.rb
CHANGED
@@ -27,6 +27,14 @@ module Coppertone
|
|
27
27
|
@result_code = result_code
|
28
28
|
end
|
29
29
|
|
30
|
+
def default?
|
31
|
+
text == DEFAULT_QUALIFIER_TEXT
|
32
|
+
end
|
33
|
+
|
34
|
+
def to_s
|
35
|
+
text
|
36
|
+
end
|
37
|
+
|
30
38
|
PASS = new(DEFAULT_QUALIFIER_TEXT, Result::PASS)
|
31
39
|
FAIL = new('-'.freeze, Result::FAIL)
|
32
40
|
SOFTFAIL = new('~'.freeze, Result::SOFTFAIL)
|
data/lib/coppertone/record.rb
CHANGED
@@ -2,51 +2,17 @@ module Coppertone
|
|
2
2
|
# Represents an SPF record. Includes class level methods for parsing
|
3
3
|
# record from a text string.
|
4
4
|
class Record
|
5
|
-
VERSION_STR = 'v=spf1'
|
6
|
-
RECORD_REGEXP = /\A#{VERSION_STR}(\s|\z)/i
|
7
|
-
ALLOWED_CHARACTERS = /\A([\x21-\x7e ]+)\z/
|
8
|
-
|
9
5
|
attr_reader :text
|
10
6
|
def initialize(raw_text)
|
11
|
-
|
12
|
-
|
13
|
-
fail RecordParsingError unless ALLOWED_CHARACTERS.match(raw_text)
|
14
|
-
@text = raw_text.dup
|
15
|
-
validate_and_parse
|
7
|
+
@terms = Coppertone::RecordTermParser.new(raw_text).terms
|
8
|
+
normalize_terms
|
16
9
|
end
|
17
10
|
|
18
11
|
def self.record?(record_text)
|
19
|
-
|
20
|
-
RECORD_REGEXP.match(record_text.strip) ? true : false
|
21
|
-
end
|
22
|
-
|
23
|
-
def self.parse(text)
|
24
|
-
return nil unless record?(text)
|
25
|
-
new(text)
|
26
|
-
end
|
27
|
-
|
28
|
-
def validate_and_parse
|
29
|
-
text_without_prefix = text[VERSION_STR.length..-1]
|
30
|
-
@term_tokens = text_without_prefix.strip.split(/ /)
|
31
|
-
parse_terms
|
32
|
-
end
|
33
|
-
|
34
|
-
def parse_terms
|
35
|
-
@terms = []
|
36
|
-
@term_tokens.each do |token|
|
37
|
-
term = Term.build_from_token(token)
|
38
|
-
fail RecordParsingError,
|
39
|
-
"Could not parse record with #{text}" unless term
|
40
|
-
@terms << term
|
41
|
-
end
|
42
|
-
normalize_terms
|
12
|
+
Coppertone::RecordTermParser.record?(record_text)
|
43
13
|
end
|
44
14
|
|
45
15
|
def normalize_terms
|
46
|
-
# Discard any redirects if there is a directive with an
|
47
|
-
# all mechanism present
|
48
|
-
# Section 6.1
|
49
|
-
# TODO: PMG
|
50
16
|
find_redirect # Checks for duplicate redirect modifiers
|
51
17
|
exp # Checks for duplicate exp modifiers
|
52
18
|
end
|
@@ -55,8 +21,21 @@ module Coppertone
|
|
55
21
|
@directives ||= @terms.select { |t| t.is_a?(Coppertone::Directive) }
|
56
22
|
end
|
57
23
|
|
24
|
+
def all_directive
|
25
|
+
@all_directive ||= directives.find(&:all?)
|
26
|
+
end
|
27
|
+
|
58
28
|
def include_all?
|
59
|
-
|
29
|
+
all_directive ? true : false
|
30
|
+
end
|
31
|
+
|
32
|
+
def default_result
|
33
|
+
return Result.neutral unless all_directive
|
34
|
+
Result.from_directive(all_directive)
|
35
|
+
end
|
36
|
+
|
37
|
+
def safe_to_include?
|
38
|
+
include_all?
|
60
39
|
end
|
61
40
|
|
62
41
|
def modifiers
|
@@ -68,8 +47,6 @@ module Coppertone
|
|
68
47
|
end
|
69
48
|
|
70
49
|
def redirect
|
71
|
-
# Ignore if an 'all' modifier is present
|
72
|
-
return nil if include_all?
|
73
50
|
@redirect ||= find_redirect
|
74
51
|
end
|
75
52
|
|
@@ -77,10 +54,25 @@ module Coppertone
|
|
77
54
|
@exp ||= find_modifier(Coppertone::Modifier::Exp)
|
78
55
|
end
|
79
56
|
|
57
|
+
KNOWN_MODS =
|
58
|
+
[Coppertone::Modifier::Exp, Coppertone::Modifier::Redirect]
|
59
|
+
def unknown_modifiers
|
60
|
+
@unknown_modifiers ||=
|
61
|
+
modifiers.select { |m| KNOWN_MODS.select { |k| m.is_a?(k) }.empty? }
|
62
|
+
end
|
63
|
+
|
80
64
|
def find_modifier(klass)
|
81
65
|
arr = modifiers.select { |m| m.is_a?(klass) }
|
82
|
-
fail
|
66
|
+
fail DuplicateModifierError if arr.size > 1
|
83
67
|
arr.first
|
84
68
|
end
|
69
|
+
|
70
|
+
def self.version_str
|
71
|
+
Coppertone::RecordTermParser::VERSION_STR
|
72
|
+
end
|
73
|
+
|
74
|
+
def to_s
|
75
|
+
"#{self.class.version_str} #{@terms.map(&:to_s).join(' ')}"
|
76
|
+
end
|
85
77
|
end
|
86
78
|
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Coppertone
|
2
|
+
class RecordTermParser
|
3
|
+
VERSION_STR = 'v=spf1'
|
4
|
+
RECORD_REGEXP = /\A#{VERSION_STR}(\s|\z)/i
|
5
|
+
ALLOWED_CHARACTERS = /\A([\x21-\x7e ]+)\z/
|
6
|
+
|
7
|
+
def self.record?(text)
|
8
|
+
return false if text.blank?
|
9
|
+
RECORD_REGEXP.match(text.strip) ? true : false
|
10
|
+
end
|
11
|
+
|
12
|
+
attr_reader :terms
|
13
|
+
def initialize(text)
|
14
|
+
fail RecordParsingError unless self.class.record?(text)
|
15
|
+
fail RecordParsingError unless ALLOWED_CHARACTERS.match(text)
|
16
|
+
@terms = term_tokens(text).map { |token| parse_token(token) }
|
17
|
+
end
|
18
|
+
|
19
|
+
def term_tokens(text)
|
20
|
+
text_without_prefix = text[VERSION_STR.length..-1]
|
21
|
+
text_without_prefix.strip.split(/ /).select { |s| !s.blank? }
|
22
|
+
end
|
23
|
+
|
24
|
+
def parse_token(token)
|
25
|
+
term = Term.build_from_token(token)
|
26
|
+
fail RecordParsingError unless term
|
27
|
+
term
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/lib/coppertone/request.rb
CHANGED
@@ -17,7 +17,7 @@ module Coppertone
|
|
17
17
|
check_spf_for_mailfrom
|
18
18
|
return mailfrom_result if mailfrom_result && !mailfrom_result.none?
|
19
19
|
|
20
|
-
no_matching_record? ? Result.none : Result.
|
20
|
+
no_matching_record? ? Result.none : Result.neutral
|
21
21
|
end
|
22
22
|
|
23
23
|
def no_matching_record?
|
@@ -35,6 +35,8 @@ module Coppertone
|
|
35
35
|
def check_spf_for_context(macro_context, identity)
|
36
36
|
record = spf_record(macro_context)
|
37
37
|
@result = spf_request(macro_context, record, identity) if record
|
38
|
+
rescue DNSAdapter::Error => e
|
39
|
+
Result.temperror(e.message)
|
38
40
|
rescue Coppertone::TemperrorError => e
|
39
41
|
Result.temperror(e.message)
|
40
42
|
rescue Coppertone::PermerrorError => e
|
@@ -1,5 +1,4 @@
|
|
1
1
|
require 'active_support/core_ext/module/delegation'
|
2
|
-
require 'coppertone/dns/resolv_client'
|
3
2
|
|
4
3
|
module Coppertone
|
5
4
|
# A container for information that should span the lifetime of
|
@@ -41,7 +40,7 @@ module Coppertone
|
|
41
40
|
elsif Coppertone.config.dns_client_class
|
42
41
|
Coppertone.config.dns_client_class.new
|
43
42
|
else
|
44
|
-
|
43
|
+
DNSAdapter::ResolvClient.new
|
45
44
|
end
|
46
45
|
end
|
47
46
|
|
data/lib/coppertone/result.rb
CHANGED
@@ -21,8 +21,8 @@ module Coppertone
|
|
21
21
|
@mechanism = mechanism
|
22
22
|
end
|
23
23
|
|
24
|
-
def self.from_directive(directive
|
25
|
-
new(
|
24
|
+
def self.from_directive(directive)
|
25
|
+
new(directive.qualifier.result_code, directive.mechanism)
|
26
26
|
end
|
27
27
|
|
28
28
|
def self.permerror(message)
|
@@ -41,6 +41,10 @@ module Coppertone
|
|
41
41
|
Result.new(:none)
|
42
42
|
end
|
43
43
|
|
44
|
+
def self.neutral
|
45
|
+
Result.new(:neutral)
|
46
|
+
end
|
47
|
+
|
44
48
|
%w(none pass fail softfail neutral temperror permerror).each do |t|
|
45
49
|
define_method("#{t}?") do
|
46
50
|
self.class.const_get(t.upcase) == send(:code)
|
@@ -3,9 +3,11 @@ module Coppertone
|
|
3
3
|
# A class used to find validated domains as defined in
|
4
4
|
# section 5.5 of the RFC.
|
5
5
|
class ValidatedDomainFinder
|
6
|
-
|
6
|
+
attr_reader :subdomain_only
|
7
|
+
def initialize(macro_context, request_context, subdomain_only = true)
|
7
8
|
@mc = macro_context
|
8
9
|
@request_context = request_context
|
10
|
+
@subdomain_only = subdomain_only
|
9
11
|
end
|
10
12
|
|
11
13
|
def find(target_name)
|
@@ -27,10 +29,10 @@ module Coppertone
|
|
27
29
|
|
28
30
|
def ptr_record_matches?(ip_checker,
|
29
31
|
target_name, ptr_name)
|
30
|
-
is_candidate =
|
31
|
-
|
32
|
+
is_candidate = !subdomain_only ||
|
33
|
+
DomainUtils.subdomain_or_same?(ptr_name, target_name)
|
32
34
|
is_candidate && ip_checker.check(ptr_name)
|
33
|
-
rescue
|
35
|
+
rescue DNSAdapter::Error
|
34
36
|
# If a DNS error occurs when looking up a domain, treat it
|
35
37
|
# as a non match
|
36
38
|
false
|
data/lib/coppertone/version.rb
CHANGED
data/lib/coppertone.rb
CHANGED
@@ -27,20 +27,22 @@ module Coppertone
|
|
27
27
|
end
|
28
28
|
end
|
29
29
|
|
30
|
+
require 'dns_adapter'
|
30
31
|
require 'coppertone/version'
|
31
32
|
require 'coppertone/utils'
|
32
33
|
require 'coppertone/error'
|
33
34
|
require 'coppertone/request_count_limiter'
|
34
35
|
require 'coppertone/sender_identity'
|
35
|
-
require 'coppertone/dns'
|
36
36
|
require 'coppertone/ip_address_wrapper'
|
37
37
|
require 'coppertone/macro_context'
|
38
|
+
require 'coppertone/null_macro_context'
|
38
39
|
require 'coppertone/macro_string'
|
39
40
|
require 'coppertone/request_context'
|
40
41
|
require 'coppertone/domain_spec'
|
41
42
|
require 'coppertone/directive'
|
42
43
|
require 'coppertone/modifier'
|
43
44
|
require 'coppertone/term'
|
45
|
+
require 'coppertone/record_term_parser'
|
44
46
|
require 'coppertone/record'
|
45
47
|
require 'coppertone/record_evaluator'
|
46
48
|
require 'coppertone/record_finder'
|
data/spec/directive_spec.rb
CHANGED
@@ -28,6 +28,19 @@ describe Coppertone::Directive do
|
|
28
28
|
end
|
29
29
|
end
|
30
30
|
|
31
|
+
context 'all?' do
|
32
|
+
it 'should be true when the directive is an all' do
|
33
|
+
expect(Coppertone::Directive.matching_term('+all')).to be_all
|
34
|
+
expect(Coppertone::Directive.matching_term('-all')).to be_all
|
35
|
+
expect(Coppertone::Directive.matching_term('~all')).to be_all
|
36
|
+
expect(Coppertone::Directive.matching_term('?all')).to be_all
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'should be false otherwise' do
|
40
|
+
expect(Coppertone::Directive.matching_term('ip4:1.2.3.4')).not_to be_all
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
31
44
|
context '.evaluate' do
|
32
45
|
Coppertone::Qualifier.qualifiers.each do |q|
|
33
46
|
it "returns a result with the expected code when it matches #{q.text}" do
|
@@ -22,6 +22,7 @@ describe Coppertone::IPAddressWrapper do
|
|
22
22
|
expect(ipw.c).to eq(ip_as_s)
|
23
23
|
expect(ipw.i).to eq(ip_as_s)
|
24
24
|
expect(ipw.v).to eq('in-addr')
|
25
|
+
expect(ipw.to_s).to eq(ip_as_s)
|
25
26
|
end
|
26
27
|
|
27
28
|
it 'should raise an ArgumentError when passed an IP with a prefix' do
|
@@ -42,6 +43,7 @@ describe Coppertone::IPAddressWrapper do
|
|
42
43
|
'F.E.8.0.0.0.0.0.0.0.0.0.0.0.0.0.0.2.0.2.B.3.F.F.F.E.1.E.8.3.2.9'
|
43
44
|
expect(ipw.i).to eq(dotted)
|
44
45
|
expect(ipw.v).to eq('ip6')
|
46
|
+
expect(ipw.to_s).to eq(ip_as_s)
|
45
47
|
end
|
46
48
|
end
|
47
49
|
|
@@ -55,6 +57,7 @@ describe Coppertone::IPAddressWrapper do
|
|
55
57
|
expect(ipw.c).to eq(ip_v4_as_s)
|
56
58
|
expect(ipw.i).to eq(ip_v4_as_s)
|
57
59
|
expect(ipw.v).to eq('in-addr')
|
60
|
+
expect(ipw.to_s).to eq(ip_as_s)
|
58
61
|
end
|
59
62
|
end
|
60
63
|
|
data/spec/macro_string_spec.rb
CHANGED
@@ -17,4 +17,24 @@ describe Coppertone::MacroString do
|
|
17
17
|
.to eql(false)
|
18
18
|
end
|
19
19
|
end
|
20
|
+
|
21
|
+
context '#context_dependent?' do
|
22
|
+
it 'should return true when the macro string contains a macro' do
|
23
|
+
strs = ['abc.%{dr-}.%%.test.com', '%{l}.test.com', '%{d}', 'test.%{d}']
|
24
|
+
strs.each do |s|
|
25
|
+
macro_string = Coppertone::MacroString.new(s)
|
26
|
+
expect(macro_string).to be_context_dependent
|
27
|
+
expect(macro_string.to_s).to eq(s)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'should return false when the macro string does not contain a macro' do
|
32
|
+
strs = ['abc.%%.test.com', 'test.example.com', '%_', 'test']
|
33
|
+
strs.each do |s|
|
34
|
+
macro_string = Coppertone::MacroString.new(s)
|
35
|
+
expect(macro_string).not_to be_context_dependent
|
36
|
+
expect(macro_string.to_s).to eq(s)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
20
40
|
end
|
data/spec/mechanism/a_spec.rb
CHANGED
@@ -8,6 +8,7 @@ describe Coppertone::Mechanism::A do
|
|
8
8
|
expect(mech.domain_spec).to be_nil
|
9
9
|
expect(mech.ip_v4_cidr_length).to eq(32)
|
10
10
|
expect(mech.ip_v6_cidr_length).to eq(128)
|
11
|
+
expect(mech.to_s).to eq('a')
|
11
12
|
end
|
12
13
|
|
13
14
|
it 'should not fail if called with a blank argument' do
|
@@ -16,6 +17,7 @@ describe Coppertone::Mechanism::A do
|
|
16
17
|
expect(mech.domain_spec).to be_nil
|
17
18
|
expect(mech.ip_v4_cidr_length).to eq(32)
|
18
19
|
expect(mech.ip_v6_cidr_length).to eq(128)
|
20
|
+
expect(mech.to_s).to eq('a')
|
19
21
|
end
|
20
22
|
|
21
23
|
it 'should fail if called with an invalid macrostring' do
|
@@ -31,6 +33,7 @@ describe Coppertone::Mechanism::A do
|
|
31
33
|
.to eq(Coppertone::DomainSpec.new('_spf.%{d}.example.com'))
|
32
34
|
expect(mech.ip_v4_cidr_length).to eq(32)
|
33
35
|
expect(mech.ip_v6_cidr_length).to eq(128)
|
36
|
+
expect(mech.to_s).to eq('a:_spf.%{d}.example.com')
|
34
37
|
end
|
35
38
|
|
36
39
|
it 'should parse a valid IP v4 CIDR length with a domain spec' do
|
@@ -38,8 +41,9 @@ describe Coppertone::Mechanism::A do
|
|
38
41
|
expect(mech).not_to be_nil
|
39
42
|
expect(mech.domain_spec)
|
40
43
|
.to eq(Coppertone::DomainSpec.new('_spf.%{d}.example.com'))
|
41
|
-
expect(mech.ip_v4_cidr_length).to eq(
|
44
|
+
expect(mech.ip_v4_cidr_length).to eq(28)
|
42
45
|
expect(mech.ip_v6_cidr_length).to eq(128)
|
46
|
+
expect(mech.to_s).to eq('a:_spf.%{d}.example.com/28')
|
43
47
|
end
|
44
48
|
|
45
49
|
it 'should fail if called with an invalid macrostring and IPv4 CIDR' do
|
@@ -52,8 +56,9 @@ describe Coppertone::Mechanism::A do
|
|
52
56
|
mech = Coppertone::Mechanism::A.new('/28')
|
53
57
|
expect(mech).not_to be_nil
|
54
58
|
expect(mech.domain_spec).to be_nil
|
55
|
-
expect(mech.ip_v4_cidr_length).to eq(
|
59
|
+
expect(mech.ip_v4_cidr_length).to eq(28)
|
56
60
|
expect(mech.ip_v6_cidr_length).to eq(128)
|
61
|
+
expect(mech.to_s).to eq('a/28')
|
57
62
|
end
|
58
63
|
|
59
64
|
it 'should not parse an invalid IP v4 CIDR length' do
|
@@ -72,7 +77,8 @@ describe Coppertone::Mechanism::A do
|
|
72
77
|
expect(mech.domain_spec)
|
73
78
|
.to eq(Coppertone::DomainSpec.new('_spf.%{d}.example.com'))
|
74
79
|
expect(mech.ip_v4_cidr_length).to eq(32)
|
75
|
-
expect(mech.ip_v6_cidr_length).to eq(
|
80
|
+
expect(mech.ip_v6_cidr_length).to eq(64)
|
81
|
+
expect(mech.to_s).to eq('a:_spf.%{d}.example.com//64')
|
76
82
|
end
|
77
83
|
|
78
84
|
it 'should fail if called with an invalid macrostring and IPv6 CIDR' do
|
@@ -86,7 +92,8 @@ describe Coppertone::Mechanism::A do
|
|
86
92
|
expect(mech).not_to be_nil
|
87
93
|
expect(mech.domain_spec).to be_nil
|
88
94
|
expect(mech.ip_v4_cidr_length).to eq(32)
|
89
|
-
expect(mech.ip_v6_cidr_length).to eq(
|
95
|
+
expect(mech.ip_v6_cidr_length).to eq(64)
|
96
|
+
expect(mech.to_s).to eq('a//64')
|
90
97
|
end
|
91
98
|
|
92
99
|
it 'should not parse an invalid IP v6 CIDR length' do
|
@@ -104,8 +111,9 @@ describe Coppertone::Mechanism::A do
|
|
104
111
|
expect(mech).not_to be_nil
|
105
112
|
expect(mech.domain_spec)
|
106
113
|
.to eq(Coppertone::DomainSpec.new('_spf.%{d}.example.com'))
|
107
|
-
expect(mech.ip_v4_cidr_length).to eq(
|
108
|
-
expect(mech.ip_v6_cidr_length).to eq(
|
114
|
+
expect(mech.ip_v4_cidr_length).to eq(28)
|
115
|
+
expect(mech.ip_v6_cidr_length).to eq(64)
|
116
|
+
expect(mech.to_s).to eq('a:_spf.%{d}.example.com/28//64')
|
109
117
|
end
|
110
118
|
|
111
119
|
it 'should not parse a invalid dual CIDR length with a domain spec' do
|
@@ -178,7 +186,7 @@ describe Coppertone::Mechanism::A do
|
|
178
186
|
end
|
179
187
|
|
180
188
|
before do
|
181
|
-
allow(
|
189
|
+
allow(DNSAdapter::ResolvClient)
|
182
190
|
.to receive(:new).and_return(gmail_dns_client)
|
183
191
|
end
|
184
192
|
|
@@ -195,4 +203,11 @@ describe Coppertone::Mechanism::A do
|
|
195
203
|
end
|
196
204
|
end
|
197
205
|
end
|
206
|
+
|
207
|
+
context 'dns_lookup_term?' do
|
208
|
+
it 'should be true' do
|
209
|
+
expect(Coppertone::Mechanism::A).to be_dns_lookup_term
|
210
|
+
expect(Coppertone::Mechanism::A.new('//64')).to be_dns_lookup_term
|
211
|
+
end
|
212
|
+
end
|
198
213
|
end
|
data/spec/mechanism/all_spec.rb
CHANGED
@@ -4,6 +4,7 @@ describe Coppertone::Mechanism::All do
|
|
4
4
|
subject { Coppertone::Mechanism::All.instance }
|
5
5
|
it 'should always return true regardless of argument' do
|
6
6
|
expect(subject.match?(double, double)).to eq(true)
|
7
|
+
expect(subject.to_s).to eq('all')
|
7
8
|
end
|
8
9
|
|
9
10
|
it 'should not allow creation of new instances' do
|
@@ -19,4 +20,11 @@ describe Coppertone::Mechanism::All do
|
|
19
20
|
end.to raise_error(Coppertone::RecordParsingError)
|
20
21
|
end
|
21
22
|
end
|
23
|
+
|
24
|
+
context 'dns_lookup_term?' do
|
25
|
+
it 'should be false' do
|
26
|
+
expect(Coppertone::Mechanism::All).not_to be_dns_lookup_term
|
27
|
+
expect(Coppertone::Mechanism::All.instance).not_to be_dns_lookup_term
|
28
|
+
end
|
29
|
+
end
|
22
30
|
end
|
@@ -19,6 +19,11 @@ describe Coppertone::Mechanism::Exists do
|
|
19
19
|
Coppertone::Mechanism::Exists.new(':abc%:def')
|
20
20
|
end.to raise_error(Coppertone::InvalidMechanismError)
|
21
21
|
end
|
22
|
+
|
23
|
+
it 'should succeed if called with a valid macrostring' do
|
24
|
+
mech = Coppertone::Mechanism::Exists.new(':%{d}.example.com')
|
25
|
+
expect(mech.to_s).to eq('exists:%{d}.example.com')
|
26
|
+
end
|
22
27
|
end
|
23
28
|
|
24
29
|
context '#create' do
|
@@ -66,12 +71,8 @@ describe Coppertone::Mechanism::Exists do
|
|
66
71
|
Coppertone::MacroContext.new(domain, '74.125.239.118', 'bob@gmail.com')
|
67
72
|
end
|
68
73
|
|
69
|
-
let(:not_matching_context) do
|
70
|
-
Coppertone::MacroContext.new(domain, '74.125.249.118', 'bob@gmail.com')
|
71
|
-
end
|
72
|
-
|
73
74
|
before do
|
74
|
-
allow(
|
75
|
+
allow(DNSAdapter::ResolvClient)
|
75
76
|
.to receive(:new).and_return(dns_client)
|
76
77
|
end
|
77
78
|
|
@@ -81,11 +82,18 @@ describe Coppertone::Mechanism::Exists do
|
|
81
82
|
.to eq(true)
|
82
83
|
end
|
83
84
|
|
84
|
-
it 'should match when the domain record for the target name
|
85
|
+
it 'should not match when the domain record for the target name does not exist' do
|
85
86
|
mech = Coppertone::Mechanism::Exists.create(bad_arg)
|
86
87
|
expect(mech.match?(matching_context, Coppertone::RequestContext.new))
|
87
88
|
.to eq(false)
|
88
89
|
end
|
89
90
|
end
|
90
91
|
end
|
92
|
+
|
93
|
+
context 'dns_lookup_term?' do
|
94
|
+
it 'should be true' do
|
95
|
+
expect(Coppertone::Mechanism::Exists).to be_dns_lookup_term
|
96
|
+
expect(Coppertone::Mechanism::Exists.new(':example.com')).to be_dns_lookup_term
|
97
|
+
end
|
98
|
+
end
|
91
99
|
end
|
@@ -16,9 +16,14 @@ describe Coppertone::Mechanism::Include do
|
|
16
16
|
|
17
17
|
it 'should fail if called with an invalid macrostring' do
|
18
18
|
expect do
|
19
|
-
Coppertone::Mechanism::Include.new('abc%:def')
|
19
|
+
Coppertone::Mechanism::Include.new(':abc%:def')
|
20
20
|
end.to raise_error(Coppertone::InvalidMechanismError)
|
21
21
|
end
|
22
|
+
|
23
|
+
it 'creates a mechanism if called with a valid macrostring' do
|
24
|
+
mech = Coppertone::Mechanism::Include.new(':_spf.example.com')
|
25
|
+
expect(mech.to_s).to eq('include:_spf.example.com')
|
26
|
+
end
|
22
27
|
end
|
23
28
|
|
24
29
|
context '#create' do
|
@@ -40,4 +45,11 @@ describe Coppertone::Mechanism::Include do
|
|
40
45
|
end.to raise_error(Coppertone::InvalidMechanismError)
|
41
46
|
end
|
42
47
|
end
|
48
|
+
|
49
|
+
context 'dns_lookup_term?' do
|
50
|
+
it 'should be true' do
|
51
|
+
expect(Coppertone::Mechanism::Include).to be_dns_lookup_term
|
52
|
+
expect(Coppertone::Mechanism::Include.new(':example.com')).to be_dns_lookup_term
|
53
|
+
end
|
54
|
+
end
|
43
55
|
end
|
data/spec/mechanism/ip4_spec.rb
CHANGED
@@ -23,16 +23,19 @@ describe Coppertone::Mechanism::IP4 do
|
|
23
23
|
it 'should not fail if called with an IP v6' do
|
24
24
|
mech = Coppertone::Mechanism::IP4.new(':fe80::202:b3ff:fe1e:8329')
|
25
25
|
expect(mech.ip_network).to eq(IPAddr.new('fe80::202:b3ff:fe1e:8329'))
|
26
|
+
expect(mech.to_s).to eq('ip4:fe80::202:b3ff:fe1e:8329')
|
26
27
|
end
|
27
28
|
|
28
29
|
it 'should work if called with an IP4' do
|
29
30
|
mech = Coppertone::Mechanism::IP4.new(':1.2.3.4')
|
30
31
|
expect(mech.ip_network).to eq(IPAddr.new('1.2.3.4'))
|
32
|
+
expect(mech.to_s).to eq('ip4:1.2.3.4')
|
31
33
|
end
|
32
34
|
|
33
35
|
it 'should work if called with an IP4 with a pfxlen' do
|
34
36
|
mech = Coppertone::Mechanism::IP4.new(':1.2.3.4/4')
|
35
37
|
expect(mech.ip_network).to eq(IPAddr.new('1.2.3.4/4'))
|
38
|
+
expect(mech.to_s).to eq('ip4:1.2.3.4/4')
|
36
39
|
end
|
37
40
|
end
|
38
41
|
|
@@ -66,13 +69,13 @@ describe Coppertone::Mechanism::IP4 do
|
|
66
69
|
end
|
67
70
|
|
68
71
|
it 'should work if called with an IP4 with a pfxlen' do
|
69
|
-
mech = Coppertone::Mechanism::IP4.create('1.2.3.4/4')
|
72
|
+
mech = Coppertone::Mechanism::IP4.create(':1.2.3.4/4')
|
70
73
|
expect(mech.ip_network).to eq(IPAddr.new('1.2.3.4/4'))
|
71
74
|
end
|
72
75
|
|
73
76
|
it 'should fail if called with an invalid pfxlen' do
|
74
77
|
expect do
|
75
|
-
Coppertone::Mechanism::IP4.new('1.2.3.4/127')
|
78
|
+
Coppertone::Mechanism::IP4.new(':1.2.3.4/127')
|
76
79
|
end.to raise_error(Coppertone::InvalidMechanismError)
|
77
80
|
end
|
78
81
|
end
|
@@ -93,18 +96,25 @@ describe Coppertone::Mechanism::IP4 do
|
|
93
96
|
end
|
94
97
|
|
95
98
|
it 'should return true if the client IP is in the network' do
|
96
|
-
mech = Coppertone::Mechanism::IP4.create('4.5.6.0/29')
|
99
|
+
mech = Coppertone::Mechanism::IP4.create(':4.5.6.0/29')
|
97
100
|
expect(mech.match?(macro_context, double)).to eq(true)
|
98
101
|
end
|
99
102
|
|
100
103
|
it 'should return false if the client IP is not in the network' do
|
101
|
-
mech = Coppertone::Mechanism::IP4.create('4.5.6.0/30')
|
104
|
+
mech = Coppertone::Mechanism::IP4.create(':4.5.6.0/30')
|
102
105
|
expect(mech.match?(macro_context, double)).to eq(false)
|
103
106
|
end
|
104
107
|
|
105
108
|
it 'should return false if the client IP is v6 only' do
|
106
|
-
mech = Coppertone::Mechanism::IP4.create('4.5.6.0/29')
|
109
|
+
mech = Coppertone::Mechanism::IP4.create(':4.5.6.0/29')
|
107
110
|
expect(mech.match?(ip_v6_macro_context, double)).to eq(false)
|
108
111
|
end
|
109
112
|
end
|
113
|
+
|
114
|
+
context 'dns_lookup_term?' do
|
115
|
+
it 'should be false' do
|
116
|
+
expect(Coppertone::Mechanism::IP4).not_to be_dns_lookup_term
|
117
|
+
expect(Coppertone::Mechanism::IP4.create(':4.5.6.7')).not_to be_dns_lookup_term
|
118
|
+
end
|
119
|
+
end
|
110
120
|
end
|