ruby-sslyze 0.2.1 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (146) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +6 -4
  3. data/.travis.yml +15 -7
  4. data/ChangeLog.md +29 -12
  5. data/Gemfile +3 -2
  6. data/LICENSE.txt +1 -1
  7. data/README.md +5 -5
  8. data/Rakefile +1 -1
  9. data/lib/sslyze/cipher_suites.rb +176 -0
  10. data/lib/sslyze/program.rb +8 -8
  11. data/lib/sslyze/task.rb +40 -33
  12. data/lib/sslyze/version.rb +1 -1
  13. data/lib/sslyze/{certificate/domain_name.rb → x509/domain.rb} +5 -3
  14. data/lib/sslyze/x509/extension.rb +15 -0
  15. data/lib/sslyze/x509/extension_set.rb +140 -0
  16. data/lib/sslyze/x509/extensions.rb +6 -0
  17. data/lib/sslyze/x509/extensions/basic_constraints.rb +41 -0
  18. data/lib/sslyze/x509/extensions/certificate_policies.rb +108 -0
  19. data/lib/sslyze/x509/extensions/crl_distribution_points.rb +47 -0
  20. data/lib/sslyze/x509/extensions/extended_key_usage.rb +58 -0
  21. data/lib/sslyze/x509/extensions/key_usage.rb +66 -0
  22. data/lib/sslyze/x509/extensions/subject_alt_name.rb +144 -0
  23. data/lib/sslyze/x509/name.rb +194 -0
  24. data/lib/sslyze/x509/public_key.rb +53 -0
  25. data/lib/sslyze/xml.rb +26 -37
  26. data/lib/sslyze/xml/attributes.rb +5 -0
  27. data/lib/sslyze/xml/attributes/error.rb +30 -0
  28. data/lib/sslyze/xml/attributes/exception.rb +30 -0
  29. data/lib/sslyze/xml/attributes/is_supported.rb +29 -0
  30. data/lib/sslyze/xml/attributes/is_vulnerable.rb +29 -0
  31. data/lib/sslyze/xml/attributes/title.rb +31 -0
  32. data/lib/sslyze/xml/certinfo.rb +67 -0
  33. data/lib/sslyze/xml/certinfo/certificate.rb +202 -0
  34. data/lib/sslyze/xml/certinfo/certificate_validation.rb +69 -0
  35. data/lib/sslyze/xml/certinfo/certificate_validation/hostname_validation.rb +54 -0
  36. data/lib/sslyze/xml/certinfo/certificate_validation/path_validation.rb +84 -0
  37. data/lib/sslyze/xml/certinfo/certificate_validation/verified_certificate_chain.rb +41 -0
  38. data/lib/sslyze/xml/certinfo/has_certificates.rb +102 -0
  39. data/lib/sslyze/xml/certinfo/ocsp_stapling.rb +45 -0
  40. data/lib/sslyze/xml/certinfo/ocsp_stapling/ocsp_response.rb +87 -0
  41. data/lib/sslyze/xml/certinfo/received_certificate_chain.rb +48 -0
  42. data/lib/sslyze/xml/compression.rb +33 -0
  43. data/lib/sslyze/xml/compression/compression_method.rb +38 -0
  44. data/lib/sslyze/xml/fallback.rb +34 -0
  45. data/lib/sslyze/xml/fallback/tls_fallback_scsv.rb +27 -0
  46. data/lib/sslyze/xml/heartbleed.rb +38 -0
  47. data/lib/sslyze/xml/heartbleed/openssl_heartbleed.rb +29 -0
  48. data/lib/sslyze/xml/http_headers.rb +42 -0
  49. data/lib/sslyze/xml/http_headers/http_public_key_pinning.rb +121 -0
  50. data/lib/sslyze/xml/http_headers/http_strict_transport_security.rb +59 -0
  51. data/lib/sslyze/xml/invalid_target.rb +33 -0
  52. data/lib/sslyze/xml/openssl_ccs.rb +34 -0
  53. data/lib/sslyze/xml/openssl_ccs/openssl_ccs_injection.rb +26 -0
  54. data/lib/sslyze/xml/plugin.rb +27 -0
  55. data/lib/sslyze/xml/protocol.rb +143 -0
  56. data/lib/sslyze/xml/protocol/cipher_suite.rb +93 -0
  57. data/lib/sslyze/xml/protocol/cipher_suite/key_exchange.rb +127 -0
  58. data/lib/sslyze/xml/reneg.rb +28 -0
  59. data/lib/sslyze/xml/reneg/session_renegotiation.rb +51 -0
  60. data/lib/sslyze/xml/resum.rb +42 -0
  61. data/lib/sslyze/xml/resum/session_resumption_with_session_ids.rb +94 -0
  62. data/lib/sslyze/xml/resum/session_resumption_with_tls_tickets.rb +69 -0
  63. data/lib/sslyze/xml/resum_rate.rb +30 -0
  64. data/lib/sslyze/xml/target.rb +371 -0
  65. data/lib/sslyze/xml/types.rb +19 -0
  66. data/ruby-sslyze.gemspec +3 -3
  67. data/spec/spec_helper.rb +2 -4
  68. data/spec/sslyze.xml +2356 -2580
  69. data/spec/x509/domain_spec.rb +125 -0
  70. data/spec/x509/extension_set_spec.rb +208 -0
  71. data/spec/x509/extension_spec.rb +58 -0
  72. data/spec/x509/extensions/basic_constraints_spec.rb +41 -0
  73. data/spec/x509/extensions/certificate_policies_spec.rb +38 -0
  74. data/spec/x509/extensions/crl_distribution_points_spec.rb +38 -0
  75. data/spec/x509/extensions/extended_key_usage_spec.rb +58 -0
  76. data/spec/x509/extensions/key_usage_spec.rb +84 -0
  77. data/spec/x509/extensions/subject_alt_name_spec.rb +146 -0
  78. data/spec/x509/name_spec.rb +85 -0
  79. data/spec/x509/public_key_spec.rb +113 -0
  80. data/spec/xml/certinfo/certificate_spec.rb +166 -0
  81. data/spec/xml/certinfo/certificate_validation/hostname_validation_spec.rb +23 -0
  82. data/spec/xml/certinfo/certificate_validation/path_validation_spec.rb +107 -0
  83. data/spec/xml/certinfo/certificate_validation/verified_certificate_chain_spec.rb +163 -0
  84. data/spec/xml/certinfo/certificate_validation_spec.rb +40 -0
  85. data/spec/xml/certinfo/ocsp_stapling/ocsp_response_spec.rb +61 -0
  86. data/spec/xml/certinfo/ocsp_stapling_spec.rb +31 -0
  87. data/spec/xml/certinfo/received_certificate_chain_spec.rb +165 -0
  88. data/spec/xml/certinfo_spec.rb +45 -0
  89. data/spec/xml/compression/compression_method_spec.rb +23 -0
  90. data/spec/xml/compression_spec.rb +23 -0
  91. data/spec/xml/heartbleed/openssl_heartbleed_spec.rb +17 -0
  92. data/spec/xml/heartbleed_spec.rb +37 -0
  93. data/spec/xml/http_headers/http_public_key_pinning_spec.rb +73 -0
  94. data/spec/xml/http_headers/http_strict_transport_security_spec.rb +107 -0
  95. data/spec/xml/http_headers_spec.rb +63 -0
  96. data/spec/xml/invalid_target_spec.rb +23 -0
  97. data/spec/xml/plugin_examples.rb +14 -0
  98. data/spec/{key_exchange_spec.rb → xml/protocol/cipher_suite/key_exchange_spec.rb} +9 -3
  99. data/spec/xml/protocol/cipher_suite_spec.rb +66 -0
  100. data/spec/xml/protocol_spec.rb +115 -0
  101. data/spec/xml/reneg/session_renegotiation_spec.rb +23 -0
  102. data/spec/xml/reneg_spec.rb +35 -0
  103. data/spec/xml/resum/session_resumption_with_session_ids_spec.rb +103 -0
  104. data/spec/xml/resum/session_resumption_with_tls_tickets_spec.rb +121 -0
  105. data/spec/xml/resum_rate_spec.rb +30 -0
  106. data/spec/xml/resum_spec.rb +47 -0
  107. data/spec/{target_spec.rb → xml/target_spec.rb} +73 -27
  108. data/spec/xml_spec.rb +13 -21
  109. metadata +138 -61
  110. data/lib/sslyze/cert_info.rb +0 -57
  111. data/lib/sslyze/certificate.rb +0 -139
  112. data/lib/sslyze/certificate/extensions.rb +0 -127
  113. data/lib/sslyze/certificate/extensions/authority_information_access.rb +0 -38
  114. data/lib/sslyze/certificate/extensions/extension.rb +0 -26
  115. data/lib/sslyze/certificate/extensions/x509v3_basic_constraints.rb +0 -60
  116. data/lib/sslyze/certificate/extensions/x509v3_certificate_policies.rb +0 -50
  117. data/lib/sslyze/certificate/extensions/x509v3_crl_distribution_points.rb +0 -32
  118. data/lib/sslyze/certificate/extensions/x509v3_extended_key_usage.rb +0 -32
  119. data/lib/sslyze/certificate/extensions/x509v3_key_usage.rb +0 -50
  120. data/lib/sslyze/certificate/extensions/x509v3_subject_alternative_name.rb +0 -71
  121. data/lib/sslyze/certificate/issuer.rb +0 -56
  122. data/lib/sslyze/certificate/public_key.rb +0 -9
  123. data/lib/sslyze/certificate/subject.rb +0 -117
  124. data/lib/sslyze/certificate/subject_public_key_info.rb +0 -53
  125. data/lib/sslyze/certificate/validity.rb +0 -9
  126. data/lib/sslyze/certificate_chain.rb +0 -89
  127. data/lib/sslyze/certificate_validation.rb +0 -70
  128. data/lib/sslyze/cipher_suite.rb +0 -237
  129. data/lib/sslyze/invalid_target.rb +0 -35
  130. data/lib/sslyze/key_exchange.rb +0 -106
  131. data/lib/sslyze/ocsp_response.rb +0 -87
  132. data/lib/sslyze/protocol.rb +0 -133
  133. data/lib/sslyze/target.rb +0 -312
  134. data/lib/sslyze/types.rb +0 -17
  135. data/spec/cert_info_spec.rb +0 -29
  136. data/spec/certificate/subject_name_spec.rb +0 -72
  137. data/spec/certificate_chain_spec.rb +0 -61
  138. data/spec/certificate_spec.rb +0 -330
  139. data/spec/certificate_validation_spec.rb +0 -39
  140. data/spec/cipher_suite_spec.rb +0 -50
  141. data/spec/invalid_target_spec.rb +0 -21
  142. data/spec/issuer_spec.rb +0 -33
  143. data/spec/ocsp_response_spec.rb +0 -59
  144. data/spec/protocol_spec.rb +0 -99
  145. data/spec/subject_public_key_info_spec.rb +0 -35
  146. data/spec/subject_spec.rb +0 -69
@@ -0,0 +1,73 @@
1
+ require 'spec_helper'
2
+ require 'xml_examples'
3
+ require 'sslyze/xml/http_headers/http_public_key_pinning'
4
+
5
+ describe SSLyze::XML::HTTPHeaders::HTTPPublicKeyPinning do
6
+ include_examples "XML specs"
7
+
8
+ let(:xpath) { '/document/results/target/http_headers/httpPublicKeyPinning' }
9
+
10
+ subject do
11
+ described_class.new(xml.at(xpath))
12
+ end
13
+
14
+ describe "#include_sub_domains?" do
15
+ context "when the 'includeSubDomains' attribute is present" do
16
+ let(:xpath) { "#{super()}[@includeSubDomains]" }
17
+
18
+ context "and it is 'True'" do
19
+ let(:xpath) { "#{super()}[@includeSubDomains='True']" }
20
+
21
+ it do
22
+ pending "find domain where includeSubDomains='True'"
23
+
24
+ expect(subject.include_sub_domains?).to be(true)
25
+ end
26
+ end
27
+
28
+ context "but it is 'False'" do
29
+ let(:xpath) { "#{super()}[@includeSubDomains='False']" }
30
+
31
+ it do
32
+ expect(subject.include_sub_domains?).to be(false)
33
+ end
34
+ end
35
+ end
36
+
37
+ context "when the 'includeSubDomains' attribute is not present" do
38
+ let(:xpath) { "#{super()}[not(@includeSubDomains)]" }
39
+
40
+ it { expect(subject.include_sub_domains?).to be nil }
41
+ end
42
+ end
43
+
44
+ describe "#is_supported?" do
45
+ context "when the 'isSupported' attribute is 'True'" do
46
+ let(:xpath) { "#{super()}[@isSupported='True']" }
47
+
48
+ it { expect(subject.is_supported?).to be true }
49
+ end
50
+
51
+ context "when the 'isSupported' attribute is 'False'" do
52
+ let(:xpath) { "#{super()}[@isSupported='False']" }
53
+
54
+ it { expect(subject.is_supported?).to be false }
55
+ end
56
+ end
57
+
58
+ describe "#max_age" do
59
+ context "when the 'maxAge' attribute is present" do
60
+ let(:xpath) { "#{super()}[@maxAge]" }
61
+
62
+ it "should return the 'maxAge' attribute" do
63
+ expect(subject.max_age).to be > 0
64
+ end
65
+ end
66
+
67
+ context "when the 'maxAge' attribute is not present" do
68
+ let(:xpath) { "#{super()}[not(@maxAge)]" }
69
+
70
+ it { expect(subject.max_age).to be nil }
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,107 @@
1
+ require 'spec_helper'
2
+ require 'xml_examples'
3
+ require 'sslyze/xml/http_headers/http_strict_transport_security'
4
+
5
+ describe SSLyze::XML::HTTPHeaders::HTTPStrictTransportSecurity do
6
+ include_examples "XML specs"
7
+
8
+ let(:xpath) { '/document/results/target/http_headers/httpStrictTransportSecurity' }
9
+
10
+ subject do
11
+ described_class.new(xml.at(xpath))
12
+ end
13
+
14
+ describe "#include_sub_domains?" do
15
+ context "when the 'includeSubDomains' attribute is present" do
16
+ subject do
17
+ described_class.new(xml.at("#{xpath}[@includeSubDomains]"))
18
+ end
19
+
20
+ it "should return a Boolean value" do
21
+ expect(subject.include_sub_domains?).to be(true).or(be(false))
22
+ end
23
+ end
24
+
25
+ context "when the 'includeSubDomains' attribute is not present" do
26
+ subject do
27
+ described_class.new(xml.at("#{xpath}[not(@includeSubDomains)]"))
28
+ end
29
+
30
+ it do
31
+ pending "need an example with no 'includeSubDomains' attribute"
32
+
33
+ expect(subject.include_sub_domains?).to be nil
34
+ end
35
+ end
36
+ end
37
+
38
+ describe "#is_supported?" do
39
+ context "when the 'isSupported' attribute is 'True'" do
40
+ subject do
41
+ described_class.new(xml.at("#{xpath}[@isSupported=\"True\"]"))
42
+ end
43
+
44
+ it { expect(subject.is_supported?).to be true }
45
+ end
46
+
47
+ context "when the 'isSupported' attribute is 'False'" do
48
+ subject do
49
+ described_class.new(xml.at("#{xpath}[@isSupported=\"False\"]"))
50
+ end
51
+
52
+ it do
53
+ pending "need an example where 'isSupported' is 'False'"
54
+
55
+ expect(subject.is_supported?).to be false
56
+ end
57
+ end
58
+ end
59
+
60
+ describe "#max_age" do
61
+ context "when the 'maxAge' attribute is present" do
62
+ subject do
63
+ described_class.new(xml.at("#{xpath}[@maxAge]"))
64
+ end
65
+
66
+ it "should return the 'maxAge' attribute" do
67
+ expect(subject.max_age).to be > 0
68
+ end
69
+ end
70
+
71
+ context "when the 'maxAge' attribute is not present" do
72
+ subject do
73
+ described_class.new(xml.at("#{xpath}[not(@maxAge)]"))
74
+ end
75
+
76
+ it do
77
+ pending "need an example with no 'maxAge' attribute"
78
+
79
+ expect(subject.max_age).to be nil
80
+ end
81
+ end
82
+ end
83
+
84
+ describe "#preload?" do
85
+ context "when the 'preload' attribute is present" do
86
+ subject do
87
+ described_class.new(xml.at("#{xpath}[@preload]"))
88
+ end
89
+
90
+ it "should return a Boolean value" do
91
+ expect(subject.preload?).to be(true).or(be(false))
92
+ end
93
+ end
94
+
95
+ context "when the 'preload' attribute is not present" do
96
+ subject do
97
+ described_class.new(xml.at("#{xpath}[not(@preload)]"))
98
+ end
99
+
100
+ it do
101
+ pending "need an example with no 'preload' attribute"
102
+
103
+ expect(subject.preload?).to be nil
104
+ end
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,63 @@
1
+ require 'spec_helper'
2
+ require 'xml_examples'
3
+ require 'xml/plugin_examples'
4
+ require 'sslyze/xml/http_headers'
5
+
6
+ describe SSLyze::XML::HTTPHeaders do
7
+ include_examples "XML specs"
8
+ include_examples "Plugin element"
9
+
10
+ let(:xpath) { '/document/results/target/http_headers' }
11
+
12
+ subject { described_class.new(xml.at(xpath)) }
13
+
14
+ describe "#http_strict_transport_security" do
15
+ context "when the '<httpStrictTransportSecurity/>' XML element is present" do
16
+ subject do
17
+ described_class.new(xml.at("#{xpath}[httpStrictTransportSecurity]"))
18
+ end
19
+
20
+ it do
21
+ expect(subject.http_strict_transport_security).to \
22
+ be_kind_of(described_class::HTTPStrictTransportSecurity)
23
+ end
24
+ end
25
+
26
+ context "when the '<httpStrictTransportSecurity/>' XML element is omitted" do
27
+ subject do
28
+ described_class.new(xml.at("#{xpath}[not(./httpStrictTransportSecurity)]"))
29
+ end
30
+
31
+ it do
32
+ pending "need an example with no '<httpStrictTransportSecurity/>'"
33
+
34
+ expect(subject.http_strict_transport_security).to be nil
35
+ end
36
+ end
37
+ end
38
+
39
+ describe "#http_public_key_pinning" do
40
+ context "when the '<httpPublicKeyPinning/>' XML element is present" do
41
+ subject do
42
+ described_class.new(xml.at("#{xpath}[httpPublicKeyPinning]"))
43
+ end
44
+
45
+ it do
46
+ expect(subject.http_public_key_pinning).to \
47
+ be_kind_of(described_class::HTTPPublicKeyPinning)
48
+ end
49
+ end
50
+
51
+ context "when the '<httpPublicKeyPinning/>' XML element is omitted" do
52
+ subject do
53
+ described_class.new(xml.at("#{xpath}[not(./httpPublicKeyPinning)]"))
54
+ end
55
+
56
+ it do
57
+ pending "need an example with no '<httpPublicKeyPinning/>'"
58
+
59
+ expect(subject.http_public_key_pinning).to be nil
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,23 @@
1
+ require 'spec_helper'
2
+ require 'xml_examples'
3
+ require 'sslyze/xml/invalid_target'
4
+
5
+ describe SSLyze::XML::InvalidTarget do
6
+ include_examples "XML specs"
7
+
8
+ let(:xpath) { '/document/invalidTargets/invalidTarget' }
9
+
10
+ subject { described_class.new(xml.at(xpath)) }
11
+
12
+ describe "#host" do
13
+ it "must parse the host attribute" do
14
+ expect(subject.host).to be == 'foo'
15
+ end
16
+ end
17
+
18
+ describe "#error" do
19
+ it "must parse the ip attribute" do
20
+ expect(subject.error).to be == 'Could not resolve foo'
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,14 @@
1
+ require 'rspec'
2
+
3
+ shared_examples_for "Plugin element" do
4
+ describe "#title" do
5
+ it "should parse the title attribute" do
6
+ expect(subject.title).to be_kind_of(String)
7
+ expect(subject.title).not_to be_empty
8
+ end
9
+ end
10
+
11
+ describe "#exception" do
12
+ pending "need an examples of plugin elements with exception messages"
13
+ end
14
+ end
@@ -1,11 +1,11 @@
1
1
  require 'spec_helper'
2
2
  require 'xml_examples'
3
- require 'sslyze/key_exchange'
3
+ require 'sslyze/xml/protocol/cipher_suite/key_exchange'
4
4
 
5
- describe SSLyze::KeyExchange do
5
+ describe SSLyze::XML::Protocol::CipherSuite::KeyExchange do
6
6
  include_examples "XML specs"
7
7
 
8
- subject { described_class.new(xml.at('/document/results/target/tlsv1_2/acceptedCipherSuites/cipherSuite/keyExchange')) }
8
+ subject { described_class.new(xml.at('/document/results/target/tlsv1_2/preferredCipherSuite/cipherSuite/keyExchange')) }
9
9
 
10
10
  describe "#a" do
11
11
  it "should parse the A attribute" do
@@ -49,6 +49,12 @@ describe SSLyze::KeyExchange do
49
49
  end
50
50
  end
51
51
 
52
+ describe "#order" do
53
+ it "should parse the Order attribute" do
54
+ expect(subject.order).to be == '0x00ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551'
55
+ end
56
+ end
57
+
52
58
  describe "#prime" do
53
59
  it "should parse the Prime attribute" do
54
60
  expect(subject.prime).to be == '0x00ffffffff00000001000000000000000000000000ffffffffffffffffffffffff'
@@ -0,0 +1,66 @@
1
+ require 'spec_helper'
2
+ require 'xml_examples'
3
+ require 'sslyze/xml/protocol/cipher_suite'
4
+
5
+ describe SSLyze::XML::Protocol::CipherSuite do
6
+ include_examples "XML specs"
7
+
8
+ subject { described_class.new(xml.at('/document/results/target/tlsv1_2/preferredCipherSuite/cipherSuite')) }
9
+
10
+ describe "#name" do
11
+ it "should parse the name attribute" do
12
+ expect(subject.name).to be == 'TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256'
13
+ end
14
+ end
15
+
16
+ describe "#rfc_name" do
17
+ it "should return the RFC cipher suite name" do
18
+ expect(subject.rfc_name).to be == 'TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256'
19
+ end
20
+ end
21
+
22
+ describe "#openssl_name" do
23
+ it "should map the RFC name back to the OpenSSL name" do
24
+ expect(subject.openssl_name).to be == 'ECDHE-RSA-AES128-GCM-SHA256'
25
+ end
26
+ end
27
+
28
+ describe "#connection_status" do
29
+ it "should parse the connectionStatus attribute" do
30
+ expect(subject.connection_status).to be == 'HTTP 200 OK'
31
+ end
32
+ end
33
+
34
+ describe "#anonymous?" do
35
+ it "should query the anonymous attribute" do
36
+ expect(subject.anonymous?).to be false
37
+ end
38
+ end
39
+
40
+ describe "#key_size" do
41
+ it "should return the keySize attribute" do
42
+ expect(subject.key_size).to be 128
43
+ end
44
+ end
45
+
46
+ describe "#key_exchange" do
47
+ context "when the keyExchange child is present" do
48
+ subject do
49
+ described_class.new(xml.at('/document/results/target/tlsv1_2/acceptedCipherSuites/cipherSuite[keyExchange]'))
50
+ end
51
+
52
+ it do
53
+ expect(subject.key_exchange).to be_kind_of(described_class::KeyExchange)
54
+ end
55
+ end
56
+
57
+ context "when the keyExchange object is missing" do
58
+ subject do
59
+ described_class.new(xml.at('/document/results/target/tlsv1_2/acceptedCipherSuites/cipherSuite[not(./keyExchange)]'))
60
+ end
61
+
62
+ it { expect(subject.key_exchange).to be nil }
63
+ end
64
+ end
65
+
66
+ end
@@ -0,0 +1,115 @@
1
+ require 'spec_helper'
2
+ require 'xml_examples'
3
+ require 'sslyze/xml/protocol'
4
+
5
+ describe SSLyze::XML::Protocol do
6
+ include_examples "XML specs"
7
+
8
+ let(:xpath) { '/document/results/target/tlsv1_2' }
9
+
10
+ subject { described_class.new(xml.at(xpath)) }
11
+
12
+ describe "#name" do
13
+ it "should return the protocol name" do
14
+ expect(subject.name).to be == :tlsv1_2
15
+ end
16
+ end
17
+
18
+ describe "#title" do
19
+ it "must parse the title attribute" do
20
+ expect(subject.title).to be == 'TLSV1_2 Cipher Suites'
21
+ end
22
+ end
23
+
24
+ describe "#preferred_cipher_suite" do
25
+ context "when the 'preferredCipherSuite' XML element has children" do
26
+ let(:xpath) do
27
+ '/document/results/target/*[preferredCipherSuite/cipherSuite]'
28
+ end
29
+
30
+ it do
31
+ expect(subject.preferred_cipher_suite).to be_kind_of(described_class::CipherSuite)
32
+ end
33
+ end
34
+
35
+ context "when the preferredCipherSuite' XML element has no children" do
36
+ let(:xpath) do
37
+ '/document/results/target/*[preferredCipherSuite][not(preferredCipherSuite/cipherSuite)]'
38
+ end
39
+
40
+ it do
41
+ expect(subject.preferred_cipher_suite).to be nil
42
+ end
43
+ end
44
+ end
45
+
46
+ describe "#each_accepted_cipher_suite" do
47
+ it "should yield CipherSuite objects" do
48
+ expect { |b|
49
+ subject.each_accepted_cipher_suite(&b)
50
+ }
51
+ end
52
+
53
+ context "when given no block" do
54
+ it { expect(subject.each_accepted_cipher_suite).to be_an(Enumerator) }
55
+ end
56
+ end
57
+
58
+ describe "#accepted_cipher_suites" do
59
+ it "should return an Array of CipherSuites" do
60
+ expect(subject.accepted_cipher_suites).to be_an(Array).and(
61
+ all(be_a(described_class::CipherSuite))
62
+ )
63
+ end
64
+ end
65
+
66
+ describe "#each_rejected_cipher_suite" do
67
+ it "should yield CipherSuite objects" do
68
+ expect { |b|
69
+ subject.each_rejected_cipher_suite(&b)
70
+ }
71
+ end
72
+
73
+ context "when given no block" do
74
+ it { expect(subject.each_rejected_cipher_suite).to be_an(Enumerator) }
75
+ end
76
+ end
77
+
78
+ describe "#rejected_cipher_suites" do
79
+ it "should return an Array of CipherSuites" do
80
+ expect(subject.rejected_cipher_suites).to be_an(Array).and(
81
+ all(be_a(described_class::CipherSuite))
82
+ )
83
+ end
84
+ end
85
+
86
+ describe "#supported?" do
87
+ context "when there are preferred cipher suites" do
88
+ it "should return true" do
89
+ expect(subject.supported?).to be(true)
90
+ end
91
+ end
92
+ end
93
+
94
+ describe "#each_error" do
95
+ it "should yield CipherSuite objects" do
96
+ pending "need a target with non-empty '<errors/>'"
97
+
98
+ expect { |b|
99
+ subject.each_error(&b)
100
+ }.to yield_successive_args(described_class::CipherSuite)
101
+ end
102
+
103
+ context "when given no block" do
104
+ it { expect(subject.each_error).to be_an(Enumerator) }
105
+ end
106
+ end
107
+
108
+ describe "#errors" do
109
+ it "should return an Array of CipherSuites" do
110
+ expect(subject.errors).to be_an(Array).and(
111
+ all(be_a(described_class::CipherSuite))
112
+ )
113
+ end
114
+ end
115
+ end