coppertone 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
@@ -5,68 +5,68 @@ describe 'Processing limits' do
|
|
5
5
|
{ 'mail.example.com' => [{ 'A' => '1.2.3.4' }], 'e1.example.com' => [{ 'TXT' => 'v=spf1 ip4:1.1.1.1 redirect=e1.example.com' }, { 'A' => '1.2.3.6' }], 'e2.example.com' => [{ 'TXT' => 'v=spf1 include:e3.example.com' }, { 'A' => '1.2.3.7' }], 'e3.example.com' => [{ 'TXT' => 'v=spf1 include:e2.example.com' }, { 'A' => '1.2.3.8' }], 'e4.example.com' => [{ 'TXT' => 'v=spf1 mx' }, { 'MX' => [0, 'mail.example.com'] }, { 'MX' => [1, 'mail.example.com'] }, { 'MX' => [2, 'mail.example.com'] }, { 'MX' => [3, 'mail.example.com'] }, { 'MX' => [4, 'mail.example.com'] }, { 'MX' => [5, 'mail.example.com'] }, { 'MX' => [6, 'mail.example.com'] }, { 'MX' => [7, 'mail.example.com'] }, { 'MX' => [8, 'mail.example.com'] }, { 'MX' => [9, 'mail.example.com'] }, { 'MX' => [10, 'e4.example.com'] }, { 'A' => '1.2.3.5' }], 'e5.example.com' => [{ 'TXT' => 'v=spf1 ptr' }, { 'A' => '1.2.3.5' }], '5.3.2.1.in-addr.arpa' => [{ 'PTR' => 'e1.example.com.' }, { 'PTR' => 'e2.example.com.' }, { 'PTR' => 'e3.example.com.' }, { 'PTR' => 'e4.example.com.' }, { 'PTR' => 'example.com.' }, { 'PTR' => 'e6.example.com.' }, { 'PTR' => 'e7.example.com.' }, { 'PTR' => 'e8.example.com.' }, { 'PTR' => 'e9.example.com.' }, { 'PTR' => 'e10.example.com.' }, { 'PTR' => 'e5.example.com.' }], 'e6.example.com' => [{ 'TXT' => 'v=spf1 a mx a mx a mx a mx a ptr ip4:1.2.3.4 -all' }, { 'A' => '1.2.3.8' }, { 'MX' => [10, 'e6.example.com'] }], 'e7.example.com' => [{ 'TXT' => 'v=spf1 a mx a mx a mx a mx a ptr a ip4:1.2.3.4 -all' }, { 'A' => '1.2.3.20' }], 'e8.example.com' => [{ 'TXT' => 'v=spf1 a include:inc.example.com ip4:1.2.3.4 mx -all' }, { 'A' => '1.2.3.4' }], 'inc.example.com' => [{ 'TXT' => 'v=spf1 a a a a a a a a' }, { 'A' => '1.2.3.10' }], 'e9.example.com' => [{ 'TXT' => 'v=spf1 a include:inc.example.com a ip4:1.2.3.4 -all' }, { 'A' => '1.2.3.21' }], 'e10.example.com' => [{ 'TXT' => 'v=spf1 a -all' }, { 'A' => '1.2.3.1' }, { 'A' => '1.2.3.2' }, { 'A' => '1.2.3.3' }, { 'A' => '1.2.3.4' }, { 'A' => '1.2.3.5' }, { 'A' => '1.2.3.6' }, { 'A' => '1.2.3.7' }, { 'A' => '1.2.3.8' }, { 'A' => '1.2.3.9' }, { 'A' => '1.2.3.10' }, { 'A' => '1.2.3.11' }, { 'A' => '1.2.3.12' }], 'e11.example.com' => [{ 'TXT' => 'v=spf1 a:err.example.com a:err1.example.com a:err2.example.com ?all' }], 'e12.example.com' => [{ 'TXT' => 'v=spf1 a:err.example.com a:err1.example.com ?all' }] }
|
6
6
|
end
|
7
7
|
|
8
|
-
let(:dns_client) {
|
8
|
+
let(:dns_client) { DNSAdapter::MockClient.new(zonefile) }
|
9
9
|
let(:options) { { dns_client: dns_client } }
|
10
10
|
|
11
11
|
it 'SPF implementations MUST limit the number of mechanisms and modifiers that do DNS lookups to at most 10 per SPF check.' do
|
12
12
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e1.example.com', 'mail.example.com', options)
|
13
|
-
expect(
|
13
|
+
expect([:permerror]).to include(result.code)
|
14
14
|
end
|
15
15
|
|
16
16
|
it 'SPF implementations MUST limit the number of mechanisms and modifiers that do DNS lookups to at most 10 per SPF check.' do
|
17
17
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e2.example.com', 'mail.example.com', options)
|
18
|
-
expect(
|
18
|
+
expect([:permerror]).to include(result.code)
|
19
19
|
end
|
20
20
|
|
21
21
|
it 'there MUST be a limit of no more than 10 MX looked up and checked.' do
|
22
22
|
# The required result for this test was the subject of much controversy with RFC4408. For RFC7208 the ambiguity was resolved in favor of producing a permerror result.
|
23
23
|
result = Coppertone::SpfService.authenticate_email('1.2.3.5', 'foo@e4.example.com', 'mail.example.com', options)
|
24
|
-
expect(
|
24
|
+
expect([:permerror]).to include(result.code)
|
25
25
|
end
|
26
26
|
|
27
27
|
it 'there MUST be a limit of no more than 10 PTR looked up and checked.' do
|
28
28
|
# The result of this test cannot be permerror not only because the RFC does not specify it, but because the sender has no control over the PTR records of spammers. The preferred result reflects evaluating the 10 allowed PTR records in the order returned by the test data. If testing with live DNS, the PTR order may be random, and a pass result would still be compliant. The SPF result is effectively randomized.
|
29
29
|
result = Coppertone::SpfService.authenticate_email('1.2.3.5', 'foo@e5.example.com', 'mail.example.com', options)
|
30
|
-
expect(
|
30
|
+
expect([:neutral, :pass]).to include(result.code)
|
31
31
|
end
|
32
32
|
|
33
33
|
it 'unlike MX, PTR, there is no RR limit for A' do
|
34
34
|
# There seems to be a tendency for developers to want to limit A RRs in addition to MX and PTR. These are IPs, not usable for 3rd party DoS attacks, and hence need no low limit.
|
35
35
|
result = Coppertone::SpfService.authenticate_email('1.2.3.12', 'foo@e10.example.com', 'mail.example.com', options)
|
36
|
-
expect(
|
36
|
+
expect([:pass]).to include(result.code)
|
37
37
|
end
|
38
38
|
|
39
39
|
it 'SPF implementations MUST limit the number of mechanisms and modifiers that do DNS lookups to at most 10 per SPF check.' do
|
40
40
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e6.example.com', 'mail.example.com', options)
|
41
|
-
expect(
|
41
|
+
expect([:pass]).to include(result.code)
|
42
42
|
end
|
43
43
|
|
44
44
|
it 'SPF implementations MUST limit the number of mechanisms and modifiers that do DNS lookups to at most 10 per SPF check.' do
|
45
45
|
# We do not check whether an implementation counts mechanisms before or after evaluation. The RFC is not clear on this.
|
46
46
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e7.example.com', 'mail.example.com', options)
|
47
|
-
expect(
|
47
|
+
expect([:permerror]).to include(result.code)
|
48
48
|
end
|
49
49
|
|
50
50
|
it 'SPF implementations MUST limit the number of mechanisms and modifiers that do DNS lookups to at most 10 per SPF check.' do
|
51
51
|
# The part of the RFC that talks about MAY parse the entire record first (4.6) is specific to syntax errors. In RFC7208, processing limits are part of syntax checking (4.6).
|
52
52
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e8.example.com', 'mail.example.com', options)
|
53
|
-
expect(
|
53
|
+
expect([:pass]).to include(result.code)
|
54
54
|
end
|
55
55
|
|
56
56
|
it 'SPF implementations MUST limit the number of mechanisms and modifiers that do DNS lookups to at most 10 per SPF check.' do
|
57
57
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e9.example.com', 'mail.example.com', options)
|
58
|
-
expect(
|
58
|
+
expect([:permerror]).to include(result.code)
|
59
59
|
end
|
60
60
|
|
61
61
|
it 'SPF implementations SHOULD limit "void lookups" to two. An implementation MAY choose to make such a limit configurable. In this case, a default of two is RECOMMENDED.' do
|
62
62
|
# This is a new check in RFC7208, but it\'s been implemented in Mail::SPF for years with no issues.
|
63
63
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e12.example.com', 'mail.example.com', options)
|
64
|
-
expect(
|
64
|
+
expect([:neutral]).to include(result.code)
|
65
65
|
end
|
66
66
|
|
67
67
|
it 'SPF implementations SHOULD limit "void lookups" to two. An implementation MAY choose to make such a limit configurable. In this case, a default of two is RECOMMENDED.' do
|
68
68
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e11.example.com', 'mail.example.com', options)
|
69
|
-
expect(
|
69
|
+
expect([:permerror]).to include(result.code)
|
70
70
|
end
|
71
71
|
|
72
72
|
end
|
@@ -5,71 +5,71 @@ describe 'Record evaluation' do
|
|
5
5
|
{ 'mail.example.com' => [{ 'A' => '1.2.3.4' }], 't1.example.com' => [{ 'TXT' => 'v=spf1 ip4:1.2.3.4 -all moo' }], 't2.example.com' => [{ 'TXT' => 'v=spf1 moo.cow-far_out=man:dog/cat ip4:1.2.3.4 -all' }], 't3.example.com' => [{ 'TXT' => 'v=spf1 moo.cow/far_out=man:dog/cat ip4:1.2.3.4 -all' }], 't4.example.com' => [{ 'TXT' => 'v=spf1 moo.cow:far_out=man:dog/cat ip4:1.2.3.4 -all' }], 't5.example.com' => [{ 'TXT' => 'v=spf1 redirect=t5.example.com ~all' }], 't6.example.com' => [{ 'TXT' => 'v=spf1 ip4:1.2.3.4 redirect=t2.example.com' }], 't7.example.com' => [{ 'TXT' => 'v=spf1 ip4:1.2.3.4' }], 't8.example.com' => [{ 'TXT' => 'v=spf1 ip4:1.2.3.4 redirect:t2.example.com' }], 't9.example.com' => [{ 'TXT' => 'v=spf1 a:foo-bar -all' }], 't10.example.com' => [{ 'TXT' => 'v=spf1 a:mail.example...com -all' }], 't11.example.com' => [{ 'TXT' => 'v=spf1 a:a123456789012345678901234567890123456789012345678901234567890123.example.com -all' }], 't12.example.com' => [{ 'TXT' => 'v=spf1 a:%{H}.bar -all' }] }
|
6
6
|
end
|
7
7
|
|
8
|
-
let(:dns_client) {
|
8
|
+
let(:dns_client) { DNSAdapter::MockClient.new(zonefile) }
|
9
9
|
let(:options) { { dns_client: dns_client } }
|
10
10
|
|
11
11
|
it 'Any syntax errors anywhere in the record MUST be detected.' do
|
12
12
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@t1.example.com', 'mail.example.com', options)
|
13
|
-
expect(
|
13
|
+
expect([:permerror]).to include(result.code)
|
14
14
|
end
|
15
15
|
|
16
16
|
it 'name = ALPHA *( ALPHA / DIGIT / "-" / "_" / "." )' do
|
17
17
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@t2.example.com', 'mail.example.com', options)
|
18
|
-
expect(
|
18
|
+
expect([:pass]).to include(result.code)
|
19
19
|
end
|
20
20
|
|
21
21
|
it '= character immediately after the name and before any ":" or "/"' do
|
22
22
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@t3.example.com', 'mail.example.com', options)
|
23
|
-
expect(
|
23
|
+
expect([:permerror]).to include(result.code)
|
24
24
|
end
|
25
25
|
|
26
26
|
it '= character immediately after the name and before any ":" or "/"' do
|
27
27
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@t4.example.com', 'mail.example.com', options)
|
28
|
-
expect(
|
28
|
+
expect([:permerror]).to include(result.code)
|
29
29
|
end
|
30
30
|
|
31
31
|
it 'The "redirect" modifier has an effect after all the mechanisms.' do
|
32
32
|
# The redirect in this example would violate processing limits, except that it is never used because of the all mechanism.
|
33
33
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@t5.example.com', 'mail.example.com', options)
|
34
|
-
expect(
|
34
|
+
expect([:softfail]).to include(result.code)
|
35
35
|
end
|
36
36
|
|
37
37
|
it 'The "redirect" modifier has an effect after all the mechanisms.' do
|
38
38
|
result = Coppertone::SpfService.authenticate_email('1.2.3.5', 'foo@t6.example.com', 'mail.example.com', options)
|
39
|
-
expect(
|
39
|
+
expect([:fail]).to include(result.code)
|
40
40
|
end
|
41
41
|
|
42
42
|
it 'Default result is neutral.' do
|
43
43
|
result = Coppertone::SpfService.authenticate_email('1.2.3.5', 'foo@t7.example.com', 'mail.example.com', options)
|
44
|
-
expect(
|
44
|
+
expect([:neutral]).to include(result.code)
|
45
45
|
end
|
46
46
|
|
47
47
|
it 'Invalid mechanism. Redirect is a modifier.' do
|
48
48
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@t8.example.com', 'mail.example.com', options)
|
49
|
-
expect(
|
49
|
+
expect([:permerror]).to include(result.code)
|
50
50
|
end
|
51
51
|
|
52
52
|
it 'Domain-spec must end in macro-expand or valid toplabel.' do
|
53
53
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@t9.example.com', 'mail.example.com', options)
|
54
|
-
expect(
|
54
|
+
expect([:permerror]).to include(result.code)
|
55
55
|
end
|
56
56
|
|
57
57
|
it 'target-name that is a valid domain-spec per RFC 4408 and RFC 7208 but an invalid domain name per RFC 1035 (empty label) should be treated as non-existent.' do
|
58
58
|
# An empty domain label, i.e. two successive dots, in a mechanism target-name is valid domain-spec syntax (perhaps formed from a macro expansion), even though a DNS query cannot be composed from it. The spec being unclear about it, this could either be considered a syntax error, or, by analogy to 4.3/1 and 5/10/3, the mechanism could be treated as a no-match. RFC 7208 failed to agree on which result to use, and declares the situation undefined. The preferred test result is therefore a matter of opinion.
|
59
59
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@t10.example.com', 'mail.example.com', options)
|
60
|
-
expect(
|
60
|
+
expect([:fail, :permerror]).to include(result.code)
|
61
61
|
end
|
62
62
|
|
63
63
|
it 'target-name that is a valid domain-spec per RFC 4408 and RFC 7208 but an invalid domain name per RFC 1035 (long label) must be treated as non-existent.' do
|
64
64
|
# A domain label longer than 63 characters in a mechanism target-name is valid domain-spec syntax (perhaps formed from a macro expansion), even though a DNS query cannot be composed from it. The spec being unclear about it, this could either be considered a syntax error, or, by analogy to 4.3/1 and 5/10/3, the mechanism could be treated as a no-match. RFC 7208 failed to agree on which result to use, and declares the situation undefined. The preferred test result is therefore a matter of opinion.
|
65
65
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@t11.example.com', 'mail.example.com', options)
|
66
|
-
expect(
|
66
|
+
expect([:fail, :permerror]).to include(result.code)
|
67
67
|
end
|
68
68
|
|
69
69
|
it 'target-name that is a valid domain-spec per RFC 4408 and RFC 7208 but an invalid domain name per RFC 1035 (long label) must be treated as non-existent.' do
|
70
70
|
# A domain label longer than 63 characters that results from macro expansion in a mechanism target-name is valid domain-spec syntax (and is not even subject to syntax checking after macro expansion), even though a DNS query cannot be composed from it. The spec being unclear about it, this could either be considered a syntax error, or, by analogy to 4.3/1 and 5/10/3, the mechanism could be treated as a no-match. RFC 7208 failed to agree on which result to use, and declares the situation undefined. The preferred test result is therefore a matter of opinion.
|
71
71
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@t12.example.com', '%%%%%%%%%%%%%%%%%%%%%%', options)
|
72
|
-
expect(
|
72
|
+
expect([:fail, :permerror]).to include(result.code)
|
73
73
|
end
|
74
74
|
|
75
75
|
end
|
@@ -5,44 +5,44 @@ describe 'Record lookup' do
|
|
5
5
|
{ 'both.example.net' => [{ 'TXT' => 'v=spf1 -all' }, { 'SPF' => 'v=spf1 -all' }], 'txtonly.example.net' => [{ 'TXT' => 'v=spf1 -all' }], 'spfonly.example.net' => [{ 'SPF' => 'v=spf1 -all' }, { 'TXT' => 'NONE' }], 'spftimeout.example.net' => [{ 'TXT' => 'v=spf1 -all' }, 'TIMEOUT'], 'txttimeout.example.net' => [{ 'SPF' => 'v=spf1 -all' }, { 'TXT' => 'NONE' }, 'TIMEOUT'], 'nospftxttimeout.example.net' => [{ 'SPF' => 'v=spf3 !a:yahoo.com -all' }, { 'TXT' => 'NONE' }, 'TIMEOUT'], 'alltimeout.example.net' => ['TIMEOUT'] }
|
6
6
|
end
|
7
7
|
|
8
|
-
let(:dns_client) {
|
8
|
+
let(:dns_client) { DNSAdapter::MockClient.new(zonefile) }
|
9
9
|
let(:options) { { dns_client: dns_client } }
|
10
10
|
|
11
11
|
it 'both' do
|
12
12
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@both.example.net', 'mail.example.net', options)
|
13
|
-
expect(
|
13
|
+
expect([:fail]).to include(result.code)
|
14
14
|
end
|
15
15
|
|
16
16
|
it 'Result is none if checking SPF records only (which you should not be doing).' do
|
17
17
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@txtonly.example.net', 'mail.example.net', options)
|
18
|
-
expect(
|
18
|
+
expect([:fail]).to include(result.code)
|
19
19
|
end
|
20
20
|
|
21
21
|
it 'Result is none if checking TXT records only.' do
|
22
22
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@spfonly.example.net', 'mail.example.net', options)
|
23
|
-
expect(
|
23
|
+
expect([:none]).to include(result.code)
|
24
24
|
end
|
25
25
|
|
26
26
|
it 'TXT record present, but SPF lookup times out. Result is temperror if checking SPF records only. Fortunately, we dont do type SPF anymore.' do
|
27
27
|
# This actually happens for a popular braindead DNS server.
|
28
28
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@spftimeout.example.net', 'mail.example.net', options)
|
29
|
-
expect(
|
29
|
+
expect([:fail]).to include(result.code)
|
30
30
|
end
|
31
31
|
|
32
32
|
it 'SPF record present, but TXT lookup times out. If only TXT records are checked, result is temperror.' do
|
33
33
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@txttimeout.example.net', 'mail.example.net', options)
|
34
|
-
expect(
|
34
|
+
expect([:temperror]).to include(result.code)
|
35
35
|
end
|
36
36
|
|
37
37
|
it 'No SPF record present, and TXT lookup times out. If only TXT records are checked, result is temperror.' do
|
38
38
|
# Because TXT records is where v=spf1 records will likely be, returning temperror will try again later. A timeout due to a braindead server is unlikely in the case of TXT, as opposed to the newer SPF RR.
|
39
39
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@nospftxttimeout.example.net', 'mail.example.net', options)
|
40
|
-
expect(
|
40
|
+
expect([:temperror]).to include(result.code)
|
41
41
|
end
|
42
42
|
|
43
43
|
it 'Both TXT and SPF queries time out' do
|
44
44
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@alltimeout.example.net', 'mail.example.net', options)
|
45
|
-
expect(
|
45
|
+
expect([:temperror]).to include(result.code)
|
46
46
|
end
|
47
47
|
|
48
48
|
end
|
@@ -5,57 +5,57 @@ describe 'Selecting records' do
|
|
5
5
|
{ 'example3.com' => [{ 'TXT' => 'v=spf10' }, { 'TXT' => 'v=spf1 mx' }, { 'MX' => [0, 'mail.example1.com'] }], 'example1.com' => [{ 'TXT' => 'v=spf1' }], 'example2.com' => [{ 'TXT' => ['v=spf1', 'mx'] }], 'mail.example1.com' => [{ 'A' => '1.2.3.4' }], 'example4.com' => [{ 'SPF' => 'v=spf1 +all' }, { 'TXT' => 'v=spf1 -all' }], 'example5.com' => [{ 'SPF' => 'v=spf1 +all' }, { 'TXT' => 'v=spf1 -all' }, { 'TXT' => 'v=spf1 +all' }], 'example6.com' => [{ 'TXT' => 'v=spf1 -all' }, { 'TXT' => 'V=sPf1 +all' }], 'example7.com' => [{ 'TXT' => 'v=spf1 -all' }, { 'TXT' => 'v=spf1 -all' }], 'example8.com' => [{ 'SPF' => 'V=spf1 -all' }, { 'SPF' => 'v=spf1 -all' }, { 'TXT' => 'v=spf1 +all' }], 'example9.com' => [{ 'TXT' => 'v=SpF1 ~all' }] }
|
6
6
|
end
|
7
7
|
|
8
|
-
let(:dns_client) {
|
8
|
+
let(:dns_client) { DNSAdapter::MockClient.new(zonefile) }
|
9
9
|
let(:options) { { dns_client: dns_client } }
|
10
10
|
|
11
11
|
it 'Version must be terminated by space or end of record. TXT pieces are joined without intervening spaces.' do
|
12
12
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@example2.com', 'mail.example1.com', options)
|
13
|
-
expect(
|
13
|
+
expect([:none]).to include(result.code)
|
14
14
|
end
|
15
15
|
|
16
16
|
it 'Empty SPF record.' do
|
17
17
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@example1.com', 'mail1.example1.com', options)
|
18
|
-
expect(
|
18
|
+
expect([:neutral]).to include(result.code)
|
19
19
|
end
|
20
20
|
|
21
21
|
it 'nospace2' do
|
22
22
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@example3.com', 'mail.example1.com', options)
|
23
|
-
expect(
|
23
|
+
expect([:pass]).to include(result.code)
|
24
24
|
end
|
25
25
|
|
26
26
|
it 'SPF records no longer used.' do
|
27
27
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@example4.com', 'mail.example1.com', options)
|
28
|
-
expect(
|
28
|
+
expect([:fail]).to include(result.code)
|
29
29
|
end
|
30
30
|
|
31
31
|
it 'Implementations should give permerror/unknown because of the conflicting TXT records.' do
|
32
32
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@example5.com', 'mail.example1.com', options)
|
33
|
-
expect(
|
33
|
+
expect([:permerror]).to include(result.code)
|
34
34
|
end
|
35
35
|
|
36
36
|
it 'Multiple records is a permerror, v=spf1 is case insensitive' do
|
37
37
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@example6.com', 'mail.example1.com', options)
|
38
|
-
expect(
|
38
|
+
expect([:permerror]).to include(result.code)
|
39
39
|
end
|
40
40
|
|
41
41
|
it 'Multiple records is a permerror, even when they are identical. However, this situation cannot be reliably reproduced with live DNS since cache and resolvers are allowed to combine identical records.' do
|
42
42
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@example7.com', 'mail.example1.com', options)
|
43
|
-
expect(
|
43
|
+
expect([:permerror, :fail]).to include(result.code)
|
44
44
|
end
|
45
45
|
|
46
46
|
it 'Ignoring SPF-type records will give pass because there is a (single) TXT record.' do
|
47
47
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@example8.com', 'mail.example1.com', options)
|
48
|
-
expect(
|
48
|
+
expect([:pass]).to include(result.code)
|
49
49
|
end
|
50
50
|
|
51
51
|
it 'nospf' do
|
52
52
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@mail.example1.com', 'mail.example1.com', options)
|
53
|
-
expect(
|
53
|
+
expect([:none]).to include(result.code)
|
54
54
|
end
|
55
55
|
|
56
56
|
it 'v=spf1 is case insensitive' do
|
57
57
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@example9.com', 'mail.example1.com', options)
|
58
|
-
expect(
|
58
|
+
expect([:softfail]).to include(result.code)
|
59
59
|
end
|
60
60
|
|
61
61
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# -- encoding : utf-8 --
|
2
|
+
|
1
3
|
require 'spec_helper'
|
2
4
|
|
3
5
|
describe 'Semantics of exp and other modifiers' do
|
@@ -5,17 +7,17 @@ describe 'Semantics of exp and other modifiers' do
|
|
5
7
|
{ 'mail.example.com' => [{ 'A' => '1.2.3.4' }], 'e1.example.com' => [{ 'TXT' => 'v=spf1 exp=exp1.example.com redirect=e2.example.com' }], 'e2.example.com' => [{ 'TXT' => 'v=spf1 -all' }], 'e3.example.com' => [{ 'TXT' => 'v=spf1 exp=exp1.example.com redirect=e4.example.com' }], 'e4.example.com' => [{ 'TXT' => 'v=spf1 -all exp=exp2.example.com' }], 'exp1.example.com' => [{ 'TXT' => 'No-see-um' }], 'exp2.example.com' => [{ 'TXT' => 'See me.' }], 'exp3.example.com' => [{ 'TXT' => 'Correct!' }], 'exp4.example.com' => [{ 'TXT' => '%{l} in implementation' }], 'e5.example.com' => [{ 'TXT' => 'v=spf1 1up=foo' }], 'e6.example.com' => [{ 'TXT' => 'v=spf1 =all' }], 'e7.example.com' => [{ 'TXT' => 'v=spf1 include:e3.example.com -all exp=exp3.example.com' }], 'e8.example.com' => [{ 'TXT' => 'v=spf1 -all exp=exp4.example.com' }], 'e9.example.com' => [{ 'TXT' => 'v=spf1 -all foo=%abc' }], 'e10.example.com' => [{ 'TXT' => 'v=spf1 redirect=erehwon.example.com' }], 'e11.example.com' => [{ 'TXT' => 'v=spf1 -all exp=e11msg.example.com' }], 'e11msg.example.com' => [{ 'TXT' => 'Answer a fool according to his folly.' }, { 'TXT' => 'Do not answer a fool according to his folly.' }], 'e12.example.com' => [{ 'TXT' => 'v=spf1 exp= -all' }], 'e13.example.com' => [{ 'TXT' => 'v=spf1 exp=e13msg.example.com -all' }], 'e13msg.example.com' => [{ 'TXT' => 'The %{x}-files.' }], 'e14.example.com' => [{ 'TXT' => 'v=spf1 exp=e13msg.example.com -all exp=e11msg.example.com' }], 'e15.example.com' => [{ 'TXT' => 'v=spf1 redirect=e12.example.com -all redirect=e12.example.com' }], 'e16.example.com' => [{ 'TXT' => 'v=spf1 exp=-all' }], 'e17.example.com' => [{ 'TXT' => 'v=spf1 redirect=-all ?all' }], 'e18.example.com' => [{ 'TXT' => 'v=spf1 ?all redirect=' }], 'e19.example.com' => [{ 'TXT' => 'v=spf1 default=pass' }], 'e20.example.com' => [{ 'TXT' => 'v=spf1 default=+' }], 'e21.example.com' => [{ 'TXT' => 'v=spf1 exp=e21msg.example.com -all' }], 'e21msg.example.com' => ['TIMEOUT'], 'e22.example.com' => [{ 'TXT' => 'v=spf1 exp=mail.example.com -all' }], 'nonascii.example.com' => [{ 'TXT' => 'v=spf1 exp=badexp.example.com -all' }], 'badexp.example.com' => [{ 'TXT' => 'Explanation' }], 'tworecs.example.com' => [{ 'TXT' => 'v=spf1 exp=twoexp.example.com -all' }], 'twoexp.example.com' => [{ 'TXT' => 'one' }, { 'TXT' => 'two' }], 'e23.example.com' => [{ 'TXT' => 'v=spf1 a:erehwon.example.com a:foobar.com exp=nxdomain.com -all' }], 'e24.example.com' => [{ 'TXT' => 'v=spf1 redirect=testimplicit.example.com' }, { 'A' => '192.0.2.1' }], 'testimplicit.example.com' => [{ 'TXT' => 'v=spf1 a -all' }, { 'A' => '192.0.2.2' }] }
|
6
8
|
end
|
7
9
|
|
8
|
-
let(:dns_client) {
|
10
|
+
let(:dns_client) { DNSAdapter::MockClient.new(zonefile) }
|
9
11
|
let(:options) { { dns_client: dns_client } }
|
10
12
|
|
11
13
|
it 'If no SPF record is found, or if the target-name is malformed, the result is a "PermError" rather than "None".' do
|
12
14
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e10.example.com', 'mail.example.com', options)
|
13
|
-
expect(
|
15
|
+
expect([:permerror]).to include(result.code)
|
14
16
|
end
|
15
17
|
|
16
18
|
it 'when executing "redirect", exp= from the original domain MUST NOT be used.' do
|
17
19
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e1.example.com', 'mail.example.com', options)
|
18
|
-
expect(
|
20
|
+
expect([:fail]).to include(result.code)
|
19
21
|
expect(result.explanation).to eq('DEFAULT')
|
20
22
|
end
|
21
23
|
|
@@ -26,71 +28,71 @@ describe 'Semantics of exp and other modifiers' do
|
|
26
28
|
#
|
27
29
|
# However, it is generally agreed, with precedent in other RFCs, that unknown-modifier should not be "greedy", and should not match known modifier names. There should have been explicit prose to this effect, and some has been proposed as an erratum.
|
28
30
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e17.example.com', 'mail.example.com', options)
|
29
|
-
expect(
|
31
|
+
expect([:permerror]).to include(result.code)
|
30
32
|
end
|
31
33
|
|
32
34
|
it 'when executing "include", exp= from the target domain MUST NOT be used.' do
|
33
35
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e7.example.com', 'mail.example.com', options)
|
34
|
-
expect(
|
36
|
+
expect([:fail]).to include(result.code)
|
35
37
|
expect(result.explanation).to eq('Correct!')
|
36
38
|
end
|
37
39
|
|
38
40
|
it 'when executing "redirect", exp= from the original domain MUST NOT be used.' do
|
39
41
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e3.example.com', 'mail.example.com', options)
|
40
|
-
expect(
|
42
|
+
expect([:fail]).to include(result.code)
|
41
43
|
expect(result.explanation).to eq('See me.')
|
42
44
|
end
|
43
45
|
|
44
46
|
it 'unknown-modifier = name "=" macro-string name = ALPHA *( ALPHA / DIGIT / "-" / "_" / "." ) ' do
|
45
47
|
# Unknown modifier name must begin with alpha.
|
46
48
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e5.example.com', 'mail.example.com', options)
|
47
|
-
expect(
|
49
|
+
expect([:permerror]).to include(result.code)
|
48
50
|
end
|
49
51
|
|
50
52
|
it 'name = ALPHA *( ALPHA / DIGIT / "-" / "_" / "." ) ' do
|
51
53
|
# Unknown modifier name must not be empty.
|
52
54
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e6.example.com', 'mail.example.com', options)
|
53
|
-
expect(
|
55
|
+
expect([:permerror]).to include(result.code)
|
54
56
|
end
|
55
57
|
|
56
58
|
it 'An implementation that uses a legal expansion as a sentinel. We cannot check them all, but we can check this one.' do
|
57
59
|
# Spaces are allowed in local-part.
|
58
60
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'Macro Error@e8.example.com', 'mail.example.com', options)
|
59
|
-
expect(
|
61
|
+
expect([:fail]).to include(result.code)
|
60
62
|
expect(result.explanation).to eq('Macro Error in implementation')
|
61
63
|
end
|
62
64
|
|
63
65
|
it 'Ignore exp if multiple TXT records. ' do
|
64
66
|
# If domain-spec is empty, or there are any DNS processing errors (any RCODE other than 0), or if no records are returned, or if more than one record is returned, or if there are syntax errors in the explanation string, then proceed as if no exp modifier was given.
|
65
67
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e11.example.com', 'mail.example.com', options)
|
66
|
-
expect(
|
68
|
+
expect([:fail]).to include(result.code)
|
67
69
|
expect(result.explanation).to eq('DEFAULT')
|
68
70
|
end
|
69
71
|
|
70
72
|
it 'Ignore exp if no TXT records. ' do
|
71
73
|
# If domain-spec is empty, or there are any DNS processing errors (any RCODE other than 0), or if no records are returned, or if more than one record is returned, or if there are syntax errors in the explanation string, then proceed as if no exp modifier was given.
|
72
74
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e22.example.com', 'mail.example.com', options)
|
73
|
-
expect(
|
75
|
+
expect([:fail]).to include(result.code)
|
74
76
|
expect(result.explanation).to eq('DEFAULT')
|
75
77
|
end
|
76
78
|
|
77
79
|
it 'Ignore exp if DNS error. ' do
|
78
80
|
# If domain-spec is empty, or there are any DNS processing errors (any RCODE other than 0), or if no records are returned, or if more than one record is returned, or if there are syntax errors in the explanation string, then proceed as if no exp modifier was given.
|
79
81
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e21.example.com', 'mail.example.com', options)
|
80
|
-
expect(
|
82
|
+
expect([:fail]).to include(result.code)
|
81
83
|
expect(result.explanation).to eq('DEFAULT')
|
82
84
|
end
|
83
85
|
|
84
86
|
it 'PermError if exp= domain-spec is empty. ' do
|
85
87
|
# Section 6.2/4 says, "If domain-spec is empty, or there are any DNS processing errors (any RCODE other than 0), or if no records are returned, or if more than one record is returned, or if there are syntax errors in the explanation string, then proceed as if no exp modifier was given." However, "if domain-spec is empty" conflicts with the grammar given for the exp modifier. This was reported as an erratum, and the solution chosen was to report explicit "exp=" as PermError, but ignore problems due to macro expansion, DNS, or invalid explanation string.
|
86
88
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e12.example.com', 'mail.example.com', options)
|
87
|
-
expect(
|
89
|
+
expect([:permerror]).to include(result.code)
|
88
90
|
end
|
89
91
|
|
90
92
|
it 'Ignore exp if the explanation string has a syntax error. ' do
|
91
93
|
# If domain-spec is empty, or there are any DNS processing errors (any RCODE other than 0), or if no records are returned, or if more than one record is returned, or if there are syntax errors in the explanation string, then proceed as if no exp modifier was given.
|
92
94
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e13.example.com', 'mail.example.com', options)
|
93
|
-
expect(
|
95
|
+
expect([:fail]).to include(result.code)
|
94
96
|
expect(result.explanation).to eq('DEFAULT')
|
95
97
|
end
|
96
98
|
|
@@ -101,67 +103,67 @@ describe 'Semantics of exp and other modifiers' do
|
|
101
103
|
#
|
102
104
|
# However, it is generally agreed, with precedent in other RFCs, that unknown-modifier should not be "greedy", and should not match known modifier names. There should have been explicit prose to this effect, and some has been proposed as an erratum.
|
103
105
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e16.example.com', 'mail.example.com', options)
|
104
|
-
expect(
|
106
|
+
expect([:permerror]).to include(result.code)
|
105
107
|
end
|
106
108
|
|
107
109
|
it 'exp= appears twice. ' do
|
108
110
|
# These two modifiers (exp,redirect) MUST NOT appear in a record more than once each. If they do, then check_host() exits with a result of "PermError".
|
109
111
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e14.example.com', 'mail.example.com', options)
|
110
|
-
expect(
|
112
|
+
expect([:permerror]).to include(result.code)
|
111
113
|
end
|
112
114
|
|
113
115
|
it 'redirect = "redirect" "=" domain-spec ' do
|
114
116
|
# Unlike for exp, there is no instruction to override the permerror for an empty domain-spec (which is invalid syntax).
|
115
117
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e18.example.com', 'mail.example.com', options)
|
116
|
-
expect(
|
118
|
+
expect([:permerror]).to include(result.code)
|
117
119
|
end
|
118
120
|
|
119
121
|
it 'redirect= appears twice. ' do
|
120
122
|
# These two modifiers (exp,redirect) MUST NOT appear in a record more than once each. If they do, then check_host() exits with a result of "PermError".
|
121
123
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e15.example.com', 'mail.example.com', options)
|
122
|
-
expect(
|
124
|
+
expect([:permerror]).to include(result.code)
|
123
125
|
end
|
124
126
|
|
125
127
|
it 'unknown-modifier = name "=" macro-string ' do
|
126
128
|
# Unknown modifiers must have valid macro syntax.
|
127
129
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e9.example.com', 'mail.example.com', options)
|
128
|
-
expect(
|
130
|
+
expect([:permerror]).to include(result.code)
|
129
131
|
end
|
130
132
|
|
131
133
|
it 'Unknown modifiers do not modify the RFC SPF result. ' do
|
132
134
|
# Some implementations may have a leftover default= modifier from earlier drafts.
|
133
135
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e19.example.com', 'mail.example.com', options)
|
134
|
-
expect(
|
136
|
+
expect([:neutral]).to include(result.code)
|
135
137
|
end
|
136
138
|
|
137
139
|
it 'Unknown modifiers do not modify the RFC SPF result. ' do
|
138
140
|
# Some implementations may have a leftover default= modifier from earlier drafts.
|
139
141
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e20.example.com', 'mail.example.com', options)
|
140
|
-
expect(
|
142
|
+
expect([:neutral]).to include(result.code)
|
141
143
|
end
|
142
144
|
|
143
145
|
it 'SPF explanation text is restricted to 7-bit ascii.' do
|
144
146
|
# Checking a possibly different code path for non-ascii chars.
|
145
147
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foobar@nonascii.example.com', 'hosed', options)
|
146
|
-
expect(
|
148
|
+
expect([:fail]).to include(result.code)
|
147
149
|
expect(result.explanation).to eq('DEFAULT')
|
148
150
|
end
|
149
151
|
|
150
152
|
it 'Must ignore exp= if DNS returns more than one TXT record.' do
|
151
153
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foobar@tworecs.example.com', 'hosed', options)
|
152
|
-
expect(
|
154
|
+
expect([:fail]).to include(result.code)
|
153
155
|
expect(result.explanation).to eq('DEFAULT')
|
154
156
|
end
|
155
157
|
|
156
158
|
it 'exp=nxdomain.tld ' do
|
157
159
|
# Non-existent exp= domains MUST NOT count against the void lookup limit. Implementations should lookup any exp record at most once after computing the result.
|
158
160
|
result = Coppertone::SpfService.authenticate_email('1.2.3.4', 'foo@e23.example.com', 'mail.example.com', options)
|
159
|
-
expect(
|
161
|
+
expect([:fail]).to include(result.code)
|
160
162
|
end
|
161
163
|
|
162
164
|
it 'redirect changes implicit domain ' do
|
163
165
|
result = Coppertone::SpfService.authenticate_email('192.0.2.2', 'bar@e24.example.com', 'e24.example.com', options)
|
164
|
-
expect(
|
166
|
+
expect([:pass]).to include(result.code)
|
165
167
|
end
|
166
168
|
|
167
169
|
end
|
@@ -5,13 +5,13 @@ describe 'Test cases from implementation bugs' do
|
|
5
5
|
{ 'example.org' => [{ 'TXT' => 'v=spf1 mx redirect=_spf.example.com' }, { 'MX' => [10, 'smtp.example.org'] }, { 'MX' => [10, 'smtp1.example.com'] }], 'smtp.example.org' => [{ 'A' => '198.51.100.2' }, { 'AAAA' => '2001:db8:ff0:100::3' }], 'smtp1.example.com' => [{ 'A' => '192.0.2.26' }, { 'AAAA' => '2001:db8:ff0:200::2' }], '2.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.1.0.0.F.F.0.8.B.D.0.1.0.0.2.ip6.arpa' => [{ 'PTR' => 'smtp6-v.fe.example.org' }], 'smtp6-v.fe.example.org' => [{ 'AAAA' => '2001:db8:ff0:100::2' }], '_spf.example.com' => [{ 'TXT' => 'v=spf1 ptr:fe.example.org ptr:sgp.example.com exp=_expspf.example.org -all' }], '_expspf.example.org' => [{ 'TXT' => 'Sender domain not allowed from this host. Please see http://www.openspf.org/Why?s=mfrom&id=%{S}&ip=%{C}&r=%{R}' }] }
|
6
6
|
end
|
7
7
|
|
8
|
-
let(:dns_client) {
|
8
|
+
let(:dns_client) { DNSAdapter::MockClient.new(zonefile) }
|
9
9
|
let(:options) { { dns_client: dns_client } }
|
10
10
|
|
11
11
|
it 'Bytes vs str bug from pyspf.' do
|
12
12
|
# Pyspf failed with strict=2 only. Other implementations may ignore the strict parameter.
|
13
13
|
result = Coppertone::SpfService.authenticate_email('2001:db8:ff0:100::2', 'test@example.org', 'example.org', options)
|
14
|
-
expect(
|
14
|
+
expect([:pass]).to include(result.code)
|
15
15
|
end
|
16
16
|
|
17
17
|
end
|
data/spec/record_spec.rb
CHANGED
@@ -16,18 +16,24 @@ describe Coppertone::Record do
|
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
19
|
-
context '
|
20
|
-
it 'should
|
21
|
-
expect
|
19
|
+
context 'creation' do
|
20
|
+
it 'should raise an error for nil' do
|
21
|
+
expect do
|
22
|
+
Coppertone::Record.new(nil)
|
23
|
+
end.to raise_error(Coppertone::RecordParsingError)
|
22
24
|
end
|
23
25
|
|
24
|
-
it 'should
|
25
|
-
expect
|
26
|
-
|
26
|
+
it 'should raise an error for text without the prefix' do
|
27
|
+
expect do
|
28
|
+
Coppertone::Record.new('not a record')
|
29
|
+
end.to raise_error(Coppertone::RecordParsingError)
|
30
|
+
expect do
|
31
|
+
Coppertone::Record.new('v=spf ~all')
|
32
|
+
end.to raise_error(Coppertone::RecordParsingError)
|
27
33
|
end
|
28
34
|
|
29
35
|
it 'parse simple mechanism records' do
|
30
|
-
record = Coppertone::Record.
|
36
|
+
record = Coppertone::Record.new('v=spf1 ~all')
|
31
37
|
expect(record).not_to be_nil
|
32
38
|
expect(record.directives.size).to eq(1)
|
33
39
|
directive = record.directives.first
|
@@ -37,7 +43,7 @@ describe Coppertone::Record do
|
|
37
43
|
end
|
38
44
|
|
39
45
|
it 'be case insensitive when parsing the version string' do
|
40
|
-
record = Coppertone::Record.
|
46
|
+
record = Coppertone::Record.new('V=sPf1 ~all')
|
41
47
|
expect(record).not_to be_nil
|
42
48
|
expect(record.directives.size).to eq(1)
|
43
49
|
directive = record.directives.first
|
@@ -47,7 +53,7 @@ describe Coppertone::Record do
|
|
47
53
|
end
|
48
54
|
|
49
55
|
it 'should parse more complex records' do
|
50
|
-
record = Coppertone::Record.
|
56
|
+
record = Coppertone::Record.new('v=spf1 mx -all exp=explain._spf.%{d}')
|
51
57
|
expect(record).not_to be_nil
|
52
58
|
expect(record.directives.size).to eq(2)
|
53
59
|
directive = record.directives.first
|
@@ -74,27 +80,42 @@ describe Coppertone::Record do
|
|
74
80
|
]
|
75
81
|
bad_records.each do |rec|
|
76
82
|
expect do
|
77
|
-
Coppertone::Record.
|
83
|
+
Coppertone::Record.new(rec)
|
78
84
|
end.to raise_error(Coppertone::RecordParsingError)
|
79
85
|
end
|
80
86
|
end
|
81
87
|
|
82
88
|
it 'should fail when mechanisms are separated by ctrl characters' do
|
83
89
|
expect do
|
84
|
-
Coppertone::Record.
|
90
|
+
Coppertone::Record.new("v=spf1 a:ctrl.example.com\x0dptr -all")
|
85
91
|
end.to raise_error(Coppertone::RecordParsingError)
|
86
92
|
end
|
87
93
|
|
88
94
|
it 'should fail when it contains spurious terms' do
|
89
95
|
expect do
|
90
|
-
Coppertone::Record.
|
96
|
+
Coppertone::Record.new('v=spf1 ip4:1.2.3.4 -all moo')
|
91
97
|
end.to raise_error(Coppertone::RecordParsingError)
|
92
98
|
end
|
93
99
|
|
94
100
|
it 'should fail the domain-spec is not syntactically valid' do
|
95
101
|
expect do
|
96
|
-
Coppertone::Record.
|
102
|
+
Coppertone::Record.new('v=spf1 a:foo-bar')
|
97
103
|
end.to raise_error(Coppertone::RecordParsingError)
|
98
104
|
end
|
99
105
|
end
|
106
|
+
|
107
|
+
context '#unknown_modifiers' do
|
108
|
+
it 'should return a non-empty array when there are unknown modifiers' do
|
109
|
+
expect(Coppertone::Record.new('v=spf1 moose=www.example.com').unknown_modifiers.size).to eq(1)
|
110
|
+
expect(Coppertone::Record.new('v=spf1 moose=www.example.com squirrel=xxx').unknown_modifiers.size).to eq(2)
|
111
|
+
expect(Coppertone::Record.new('v=spf1 moose=www.example.com moose=xxx').unknown_modifiers.size).to eq(2)
|
112
|
+
end
|
113
|
+
|
114
|
+
it 'should return an empty array when there are no unknown modifiers' do
|
115
|
+
expect(Coppertone::Record.new('v=spf1 ~all').unknown_modifiers).to be_empty
|
116
|
+
expect(Coppertone::Record.new('v=spf1 ip4:1.2.3.4 a:test.example.com -all').unknown_modifiers).to be_empty
|
117
|
+
expect(Coppertone::Record.new('v=spf1 mx -all exp=explain._spf.%{d}').unknown_modifiers).to be_empty
|
118
|
+
expect(Coppertone::Record.new('v=spf1 mx -all redirect=explain._spf.%{d}').unknown_modifiers).to be_empty
|
119
|
+
end
|
120
|
+
end
|
100
121
|
end
|
@@ -4,7 +4,7 @@ describe Coppertone::RequestContext do
|
|
4
4
|
it 'should have reasonable values by default' do
|
5
5
|
ctx = Coppertone::RequestContext.new
|
6
6
|
expect(ctx.dns_client).to_not be_nil
|
7
|
-
expect(ctx.dns_client.class).to eq(
|
7
|
+
expect(ctx.dns_client.class).to eq(DNSAdapter::ResolvClient)
|
8
8
|
expect(ctx.message_locale).to eq('en')
|
9
9
|
expect(ctx.dns_lookups_per_mx_mechanism_limit).to eq(10)
|
10
10
|
expect(ctx.dns_lookups_per_ptr_mechanism_limit).to eq(10)
|