coppertone 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (81) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +1 -0
  3. data/LICENSE +1 -1
  4. data/Rakefile +2 -2
  5. data/coppertone.gemspec +1 -0
  6. data/lib/coppertone/directive.rb +14 -7
  7. data/lib/coppertone/error.rb +24 -5
  8. data/lib/coppertone/ip_address_wrapper.rb +9 -6
  9. data/lib/coppertone/macro_context.rb +7 -13
  10. data/lib/coppertone/macro_string/macro_expand.rb +3 -2
  11. data/lib/coppertone/macro_string.rb +12 -5
  12. data/lib/coppertone/mechanism/a.rb +5 -1
  13. data/lib/coppertone/mechanism/all.rb +11 -4
  14. data/lib/coppertone/mechanism/cidr_parser.rb +1 -1
  15. data/lib/coppertone/mechanism/domain_spec_mechanism.rb +9 -0
  16. data/lib/coppertone/mechanism/domain_spec_optional.rb +1 -0
  17. data/lib/coppertone/mechanism/domain_spec_required.rb +1 -0
  18. data/lib/coppertone/mechanism/domain_spec_with_dual_cidr.rb +1 -0
  19. data/lib/coppertone/mechanism/exists.rb +5 -1
  20. data/lib/coppertone/mechanism/include.rb +17 -5
  21. data/lib/coppertone/mechanism/ip4.rb +5 -1
  22. data/lib/coppertone/mechanism/ip6.rb +5 -1
  23. data/lib/coppertone/mechanism/ip_mechanism.rb +9 -1
  24. data/lib/coppertone/mechanism/mx.rb +5 -1
  25. data/lib/coppertone/mechanism/ptr.rb +5 -1
  26. data/lib/coppertone/mechanism.rb +25 -2
  27. data/lib/coppertone/modifier/base.rb +1 -0
  28. data/lib/coppertone/modifier/exp.rb +6 -2
  29. data/lib/coppertone/modifier/redirect.rb +5 -1
  30. data/lib/coppertone/modifier/unknown.rb +6 -1
  31. data/lib/coppertone/modifier.rb +11 -2
  32. data/lib/coppertone/null_macro_context.rb +23 -0
  33. data/lib/coppertone/qualifier.rb +8 -0
  34. data/lib/coppertone/record.rb +33 -41
  35. data/lib/coppertone/record_finder.rb +3 -1
  36. data/lib/coppertone/record_term_parser.rb +30 -0
  37. data/lib/coppertone/request.rb +3 -1
  38. data/lib/coppertone/request_context.rb +1 -2
  39. data/lib/coppertone/result.rb +6 -2
  40. data/lib/coppertone/utils/validated_domain_finder.rb +6 -4
  41. data/lib/coppertone/version.rb +1 -1
  42. data/lib/coppertone.rb +3 -1
  43. data/spec/directive_spec.rb +13 -0
  44. data/spec/ip_address_wrapper_spec.rb +3 -0
  45. data/spec/macro_string_spec.rb +20 -0
  46. data/spec/mechanism/a_spec.rb +22 -7
  47. data/spec/mechanism/all_spec.rb +8 -0
  48. data/spec/mechanism/exists_spec.rb +14 -6
  49. data/spec/mechanism/include_spec.rb +13 -1
  50. data/spec/mechanism/ip4_spec.rb +15 -5
  51. data/spec/mechanism/ip6_spec.rb +18 -11
  52. data/spec/mechanism/mx_spec.rb +56 -0
  53. data/spec/mechanism/ptr_spec.rb +7 -0
  54. data/spec/modifier/exp_spec.rb +10 -0
  55. data/spec/modifier/redirect_spec.rb +10 -0
  56. data/spec/open_spf/ALL_mechanism_syntax_spec.rb +6 -6
  57. data/spec/open_spf/A_mechanism_syntax_spec.rb +30 -30
  58. data/spec/open_spf/EXISTS_mechanism_syntax_spec.rb +8 -8
  59. data/spec/open_spf/IP4_mechanism_syntax_spec.rb +10 -10
  60. data/spec/open_spf/IP6_mechanism_syntax_spec.rb +10 -10
  61. data/spec/open_spf/Include_mechanism_semantics_and_syntax_spec.rb +10 -10
  62. data/spec/open_spf/Initial_processing_spec.rb +21 -14
  63. data/spec/open_spf/MX_mechanism_syntax_spec.rb +22 -22
  64. data/spec/open_spf/Macro_expansion_rules_spec.rb +26 -30
  65. data/spec/open_spf/PTR_mechanism_syntax_spec.rb +7 -7
  66. data/spec/open_spf/Processing_limits_spec.rb +12 -12
  67. data/spec/open_spf/Record_evaluation_spec.rb +13 -13
  68. data/spec/open_spf/Record_lookup_spec.rb +8 -8
  69. data/spec/open_spf/Selecting_records_spec.rb +11 -11
  70. data/spec/open_spf/Semantics_of_exp_and_other_modifiers_spec.rb +27 -25
  71. data/spec/open_spf/Test_cases_from_implementation_bugs_spec.rb +2 -2
  72. data/spec/record_spec.rb +34 -13
  73. data/spec/request_context_spec.rb +1 -1
  74. data/spec/rfc7208-tests.yml +10 -0
  75. metadata +59 -48
  76. data/lib/coppertone/dns/error.rb +0 -9
  77. data/lib/coppertone/dns/mock_client.rb +0 -106
  78. data/lib/coppertone/dns/resolv_client.rb +0 -110
  79. data/lib/coppertone/dns.rb +0 -3
  80. data/lib/resolv/dns/resource/in/spf.rb +0 -15
  81. 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) { Coppertone::DNS::MockClient.new(zonefile) }
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(%i(permerror)).to include(result.code)
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(%i(permerror)).to include(result.code)
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(%i(permerror)).to include(result.code)
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(%i(neutral pass)).to include(result.code)
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(%i(pass)).to include(result.code)
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(%i(pass)).to include(result.code)
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(%i(permerror)).to include(result.code)
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(%i(pass)).to include(result.code)
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(%i(permerror)).to include(result.code)
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(%i(neutral)).to include(result.code)
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(%i(permerror)).to include(result.code)
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) { Coppertone::DNS::MockClient.new(zonefile) }
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(%i(permerror)).to include(result.code)
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(%i(pass)).to include(result.code)
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(%i(permerror)).to include(result.code)
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(%i(permerror)).to include(result.code)
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(%i(softfail)).to include(result.code)
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(%i(fail)).to include(result.code)
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(%i(neutral)).to include(result.code)
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(%i(permerror)).to include(result.code)
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(%i(permerror)).to include(result.code)
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(%i(fail permerror)).to include(result.code)
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(%i(fail permerror)).to include(result.code)
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(%i(fail permerror)).to include(result.code)
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) { Coppertone::DNS::MockClient.new(zonefile) }
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(%i(fail)).to include(result.code)
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(%i(fail)).to include(result.code)
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(%i(none)).to include(result.code)
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(%i(fail)).to include(result.code)
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(%i(temperror)).to include(result.code)
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(%i(temperror)).to include(result.code)
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(%i(temperror)).to include(result.code)
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) { Coppertone::DNS::MockClient.new(zonefile) }
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(%i(none)).to include(result.code)
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(%i(neutral)).to include(result.code)
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(%i(pass)).to include(result.code)
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(%i(fail)).to include(result.code)
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(%i(permerror)).to include(result.code)
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(%i(permerror)).to include(result.code)
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(%i(permerror fail)).to include(result.code)
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(%i(pass)).to include(result.code)
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(%i(none)).to include(result.code)
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(%i(softfail)).to include(result.code)
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) { Coppertone::DNS::MockClient.new(zonefile) }
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(%i(permerror)).to include(result.code)
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(%i(fail)).to include(result.code)
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(%i(permerror)).to include(result.code)
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(%i(fail)).to include(result.code)
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(%i(fail)).to include(result.code)
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(%i(permerror)).to include(result.code)
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(%i(permerror)).to include(result.code)
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(%i(fail)).to include(result.code)
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(%i(fail)).to include(result.code)
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(%i(fail)).to include(result.code)
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(%i(fail)).to include(result.code)
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(%i(permerror)).to include(result.code)
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(%i(fail)).to include(result.code)
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(%i(permerror)).to include(result.code)
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(%i(permerror)).to include(result.code)
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(%i(permerror)).to include(result.code)
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(%i(permerror)).to include(result.code)
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(%i(permerror)).to include(result.code)
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(%i(neutral)).to include(result.code)
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(%i(neutral)).to include(result.code)
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(%i(fail)).to include(result.code)
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(%i(fail)).to include(result.code)
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(%i(fail)).to include(result.code)
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(%i(pass)).to include(result.code)
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) { Coppertone::DNS::MockClient.new(zonefile) }
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(%i(pass)).to include(result.code)
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 'parsing' do
20
- it 'should return nil for nil' do
21
- expect(Coppertone::Record.parse(nil)).to be_nil
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 return a nil for text without the prefix' do
25
- expect(Coppertone::Record.parse('not a record')).to be_nil
26
- expect(Coppertone::Record.parse('v=spf ~all')).to be_nil
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.parse('v=spf1 ~all')
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.parse('V=sPf1 ~all')
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.parse('v=spf1 mx -all exp=explain._spf.%{d}')
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.parse(rec)
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.parse("v=spf1 a:ctrl.example.com\x0dptr -all")
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.parse('v=spf1 ip4:1.2.3.4 -all moo')
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.parse('v=spf1 a:foo-bar')
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(Coppertone::DNS::ResolvClient)
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)