Browse Source

Fix grades

new-scoring
aeris 2 years ago
parent
commit
5c7b9e7a37

+ 5
- 6
lib/cryptcheck.rb View File

@@ -4,12 +4,11 @@ require 'timeout'
4 4
 require 'yaml'
5 5
 
6 6
 module CryptCheck
7
-	PARALLEL_ANALYSIS     = 10
8
-
9
-
7
+	PARALLEL_ANALYSIS = 10
10 8
 
11 9
 	class NoTLSAvailableServer
12 10
 		attr_reader :server
11
+
13 12
 		def initialize(server)
14 13
 			@server = OpenStruct.new hostname: server
15 14
 		end
@@ -20,6 +19,7 @@ module CryptCheck
20 19
 	end
21 20
 
22 21
 	autoload :State, 'cryptcheck/state'
22
+	autoload :Grade, 'cryptcheck/grade'
23 23
 	autoload :Logger, 'cryptcheck/logger'
24 24
 	autoload :Tls, 'cryptcheck/tls'
25 25
 	module Tls
@@ -31,7 +31,6 @@ module CryptCheck
31 31
 		autoload :Server, 'cryptcheck/tls/server'
32 32
 		autoload :TcpServer, 'cryptcheck/tls/server'
33 33
 		autoload :UdpServer, 'cryptcheck/tls/server'
34
-		autoload :Grade, 'cryptcheck/tls/grade'
35 34
 		autoload :Host, 'cryptcheck/tls/host'
36 35
 
37 36
 		autoload :Https, 'cryptcheck/tls/https'
@@ -94,7 +93,7 @@ module CryptCheck
94 93
 				end
95 94
 			rescue => e
96 95
 				e = Tls::Server::TLSException.new "Too long analysis (max #{MAX_ANALYSIS_DURATION.humanize})" \
97
- 						if e.message == 'execution expired'
96
+ 						  if e.message == 'execution expired'
98 97
 				raise unless e.is_a? Tls::Server::TLSException
99 98
 				Logger.error e
100 99
 				[key, AnalysisFailure.new(e)]
@@ -107,7 +106,7 @@ module CryptCheck
107 106
 			addresses host
108 107
 		rescue ::SocketError => e
109 108
 			Logger::error e
110
-			key = [host, nil, port]
109
+			key   = [host, nil, port]
111 110
 			error = AnalysisFailure.new "Unable to resolve #{host}"
112 111
 			return { key => error }
113 112
 		end

+ 61
- 0
lib/cryptcheck/grade.rb View File

@@ -0,0 +1,61 @@
1
+module CryptCheck
2
+	module Grade
3
+		def grade
4
+			@grade ||= calculate_grade
5
+		end
6
+
7
+		GRADES        = %i(A+ A B+ B C+ C D E F G V T X)
8
+		GRADE_STATUS  = {
9
+				:'A+' => :best,
10
+				A:    :best,
11
+				:'B+' => :great,
12
+				B:    :great,
13
+				:'C+' => :good,
14
+				C:    :good,
15
+				D:    nil,
16
+				E:    :warning,
17
+				F:    :error,
18
+				G:    :critical,
19
+
20
+				V:    :critical,
21
+				T:    :critical,
22
+				X:    :critical
23
+		}
24
+		STATUS_GRADES = {
25
+				critical: :G,
26
+				error:    :F,
27
+				warning:  :E,
28
+				default:  :D,
29
+				good:     :C,
30
+				great:    :B,
31
+				best:     :A
32
+		}
33
+
34
+		def grade_status
35
+			GRADE_STATUS.fetch self.grade, :unknown
36
+		end
37
+
38
+		private
39
+		def calculate_grade
40
+			return :V unless self.valid?
41
+			return :T unless self.trusted?
42
+
43
+			states = self.states
44
+			states = State.collect { |s| [s, State.state(states, s)] }.to_h
45
+
46
+			State::BADS.each do |s|
47
+				return STATUS_GRADES[s] if states[s]
48
+			end
49
+
50
+			grade = STATUS_GRADES[:default]
51
+			State::GOODS.each do |s|
52
+				state = states[s]
53
+				return grade if state == false
54
+				grade = STATUS_GRADES[s]
55
+				return grade if state == :some
56
+				grade = "#{grade}+".to_sym
57
+			end
58
+			grade
59
+		end
60
+	end
61
+end

+ 31
- 0
lib/cryptcheck/state.rb View File

@@ -30,10 +30,41 @@ module CryptCheck
30 30
 		def self.good?(level)
31 31
 			GOODS.include? level
32 32
 		end
33
+
33 34
 		def self.bad?(level)
34 35
 			BADS.include? level
35 36
 		end
36 37
 
38
+		def self.good_or_bad(level)
39
+			if self.good?(level)
40
+				:good
41
+			else
42
+				:bad
43
+			end
44
+		end
45
+
46
+		def self.state(states, level)
47
+			state =states[level].values.uniq
48
+			case State.good_or_bad(level)
49
+				when :bad
50
+					if state.include? true
51
+						true
52
+					else
53
+						false
54
+					end
55
+				when :good
56
+					if state.include? false
57
+						if state.include? true
58
+							:some
59
+						else
60
+							false
61
+						end
62
+					else
63
+						:all
64
+					end
65
+			end
66
+		end
67
+
37 68
 		extend Enumerable
38 69
 
39 70
 		def self.each(&block)

+ 0
- 52
lib/cryptcheck/tls/grade.rb View File

@@ -1,52 +0,0 @@
1
-module CryptCheck
2
-	module Tls
3
-		module Grade
4
-			def grade
5
-				@grade ||= calculate_grade
6
-			end
7
-
8
-			GRADES = %i(A+ A B+ B C+ C D E F G V T X)
9
-			GRADE_STATUS = {
10
-					V: :critical,
11
-					T: :critical,
12
-
13
-					G: :critical,
14
-					F: :error,
15
-					E: :warning,
16
-					D: nil,
17
-					C: :good,
18
-					:'C+' => :good,
19
-					B: :great,
20
-					:'B+' => :great,
21
-					A: :best,
22
-					:'A+' => :best
23
-			}
24
-			def grade_status
25
-				GRADE_STATUS.fetch self.grade, :unknown
26
-			end
27
-
28
-			private
29
-			def calculate_grade
30
-				return :V unless self.valid?
31
-				return :T unless self.trusted?
32
-
33
-				states = self.states
34
-
35
-				{ critical: :G, error: :F, warning: :E }.each do |type, grade|
36
-					return grade if states[type].any? { |s| s == true }
37
-				end
38
-
39
-				{good: %i(D C), great: %i(C B), best: %i(B A)}.each do |type, scores|
40
-					state = states[type]
41
-					return scores.first if state.all? { |s| s != false }
42
-					if state.any? { |s| s == false }
43
-						Logger.info { "Missing #{type} : #{states[type].select { |s| s == false }.collect &:key}" }
44
-						return scores.last
45
-					end
46
-				end
47
-
48
-				:'A+'
49
-			end
50
-		end
51
-	end
52
-end

+ 149
- 0
spec/cryptcheck/grade_spec.rb View File

@@ -0,0 +1,149 @@
1
+require 'awesome_print'
2
+
3
+module CryptCheck
4
+	describe Grade do
5
+		describe '#grade' do
6
+			def obj(trust: true, valid: true, **states)
7
+				Class.new do
8
+					def initialize(trust, valid, states)
9
+						@trust, @valid, @states = trust, valid, states
10
+					end
11
+
12
+					include Grade
13
+
14
+					def trusted?
15
+						@trust
16
+					end
17
+
18
+					def valid?
19
+						@valid
20
+					end
21
+
22
+					def states
23
+						State.empty.merge @states
24
+					end
25
+				end.new trust, valid, states
26
+			end
27
+
28
+			it 'must return :V if not valid' do
29
+				obj = obj valid:   false, critical: { foo: false, bar: nil },
30
+						  error:   { foo: false, bar: nil },
31
+						  warning: { foo: false, bar: nil },
32
+						  good:    { foo: nil, bar: true },
33
+						  great:   { foo: nil, bar: true },
34
+						  best:    { foo: nil, bar: true }
35
+				expect(obj.grade).to eq :V
36
+			end
37
+
38
+			it 'must return :T if not trusted' do
39
+				obj = obj trust:   false, critical: { foo: false, bar: nil },
40
+						  error:   { foo: false, bar: nil },
41
+						  warning: { foo: false, bar: nil },
42
+						  good:    { foo: nil, bar: true },
43
+						  great:   { foo: nil, bar: true },
44
+						  best:    { foo: nil, bar: true }
45
+				expect(obj.grade).to eq :T
46
+			end
47
+
48
+			it 'must return :G if critical' do
49
+				obj = obj critical: { foo: false, bar: nil, baz: true },
50
+						  error:    { foo: false, bar: nil },
51
+						  warning:  { foo: false, bar: nil },
52
+						  good:     { foo: nil, bar: true },
53
+						  great:    { foo: nil, bar: true },
54
+						  best:     { foo: nil, bar: true }
55
+				expect(obj.grade).to eq :G
56
+			end
57
+
58
+			it 'must return :F if error' do
59
+				obj = obj critical: { foo: false, bar: nil },
60
+						  error:    { foo: false, bar: nil, baz: true },
61
+						  warning:  { foo: false, bar: nil },
62
+						  good:     { foo: nil, bar: true },
63
+						  great:    { foo: nil, bar: true },
64
+						  best:     { foo: nil, bar: true }
65
+				expect(obj.grade).to eq :F
66
+			end
67
+
68
+			it 'must return :E if warning' do
69
+				obj = obj critical: { foo: false, bar: nil },
70
+						  error:    { foo: false, bar: nil },
71
+						  warning:  { foo: false, bar: nil, baz: true },
72
+						  good:     { foo: nil, bar: true },
73
+						  great:    { foo: nil, bar: true },
74
+						  best:     { foo: nil, bar: true }
75
+				expect(obj.grade).to eq :E
76
+			end
77
+
78
+			it 'must return :D if nor good nor bad' do
79
+				obj = obj critical: { foo: false, bar: nil },
80
+						  error:    { foo: false, bar: nil },
81
+						  warning:  { foo: false, bar: nil },
82
+						  good:     { foo: false, bar: nil },
83
+						  great:    { foo: nil, bar: true },
84
+						  best:     { foo: nil, bar: true }
85
+				expect(obj.grade).to eq :D
86
+			end
87
+
88
+			it 'must return :C if some good' do
89
+				obj = obj critical: { foo: false, bar: nil },
90
+						  error:    { foo: false, bar: nil },
91
+						  warning:  { foo: false, bar: nil },
92
+						  good:     { foo: false, bar: nil, baz: true },
93
+						  great:    { foo: nil, bar: true },
94
+						  best:     { foo: nil, bar: true }
95
+				expect(obj.grade).to eq :C
96
+			end
97
+
98
+			it 'must return :C+ if all good' do
99
+				obj = obj critical: { foo: false, bar: nil },
100
+						  error:    { foo: false, bar: nil },
101
+						  warning:  { foo: false, bar: nil },
102
+						  good:     { foo: nil, bar: true },
103
+						  great:    { foo: false, bar: nil },
104
+						  best:     { foo: nil, bar: true }
105
+				expect(obj.grade).to eq :'C+'
106
+			end
107
+
108
+			it 'must return :B if some great' do
109
+				obj = obj critical: { foo: false, bar: nil },
110
+						  error:    { foo: false, bar: nil },
111
+						  warning:  { foo: false, bar: nil },
112
+						  good:     { foo: nil, bar: true },
113
+						  great:    { foo: false, bar: nil, baz: true },
114
+						  best:     { foo: true, bar: nil }
115
+				expect(obj.grade).to eq :B
116
+			end
117
+
118
+			it 'must return :B+ if all great' do
119
+				obj = obj critical: { foo: false, bar: nil },
120
+						  error:    { foo: false, bar: nil },
121
+						  warning:  { foo: false, bar: nil },
122
+						  good:     { foo: nil, bar: true },
123
+						  great:    { foo: nil, bar: true },
124
+						  best:     { foo: false, bar: nil }
125
+				expect(obj.grade).to eq :'B+'
126
+			end
127
+
128
+			it 'must return :A if some best' do
129
+				obj = obj critical: { foo: false, bar: nil },
130
+						  error:    { foo: false, bar: nil },
131
+						  warning:  { foo: false, bar: nil },
132
+						  good:     { foo: nil, bar: true },
133
+						  great:    { foo: nil, bar: true },
134
+						  best:     { foo: false, bar: nil, baz: true }
135
+				expect(obj.grade).to eq :A
136
+			end
137
+
138
+			it 'must return :A+ if all best' do
139
+				obj = obj critical: { foo: false, bar: nil },
140
+						  error:    { foo: false, bar: nil },
141
+						  warning:  { foo: false, bar: nil },
142
+						  good:     { foo: nil, bar: true },
143
+						  great:    { foo: nil, bar: true },
144
+						  best:     { foo: nil, bar: true }
145
+				expect(obj.grade).to eq :'A+'
146
+			end
147
+		end
148
+	end
149
+end

+ 406
- 0
spec/cryptcheck/state_spec.rb View File

@@ -0,0 +1,406 @@
1
+require 'ostruct'
2
+
3
+module CryptCheck
4
+	describe State do
5
+		describe '::status' do
6
+			it 'must handle empty list' do
7
+				expect(State.status []).to be_nil
8
+			end
9
+
10
+			it 'must answer correctly' do
11
+				{
12
+						[:critical, :critical] => :critical,
13
+						[:critical, :error]    => :critical,
14
+						[:critical, :warning]  => :critical,
15
+						[:critical, nil]       => :critical,
16
+						[:critical, :good]     => :critical,
17
+						[:critical, :great]    => :critical,
18
+						[:critical, :best]     => :critical,
19
+
20
+						[:error, :critical]    => :critical,
21
+						[:error, :error]       => :error,
22
+						[:error, :warning]     => :error,
23
+						[:error, nil]          => :error,
24
+						[:error, :good]        => :error,
25
+						[:error, :great]       => :error,
26
+						[:error, :best]        => :error,
27
+
28
+						[:warning, :critical]  => :critical,
29
+						[:warning, :error]     => :error,
30
+						[:warning, :warning]   => :warning,
31
+						[:warning, nil]        => :warning,
32
+						[:warning, :good]      => :warning,
33
+						[:warning, :great]     => :warning,
34
+						[:warning, :best]      => :warning,
35
+
36
+						[:good, :critical]     => :critical,
37
+						[:good, :error]        => :error,
38
+						[:good, :warning]      => :warning,
39
+						[:good, nil]           => :good,
40
+						[:good, :good]         => :good,
41
+						[:good, :great]        => :good,
42
+						[:good, :best]         => :good,
43
+
44
+						[:great, :critical]    => :critical,
45
+						[:great, :error]       => :error,
46
+						[:great, :warning]     => :warning,
47
+						[:great, nil]          => :great,
48
+						[:great, :good]        => :good,
49
+						[:great, :great]       => :great,
50
+						[:great, :best]        => :great,
51
+
52
+						[:best, :critical]     => :critical,
53
+						[:best, :error]        => :error,
54
+						[:best, :warning]      => :warning,
55
+						[:best, nil]           => :best,
56
+						[:best, :good]         => :good,
57
+						[:best, :great]        => :great,
58
+						[:best, :best]         => :best
59
+				}.each do |levels, result|
60
+					got = State.status levels
61
+					expect(got).to be(result), "#{levels} : expected #{result.inspect}, got #{got.inspect}"
62
+				end
63
+			end
64
+
65
+			it 'must handle object list' do
66
+				critical = OpenStruct.new status: :critical
67
+				warning  = OpenStruct.new status: :warning
68
+				expect(State.status [critical, warning]).to be :critical
69
+			end
70
+		end
71
+
72
+		describe '::problem' do
73
+			it 'must answer correctly' do
74
+				{
75
+						[:critical, :critical] => :critical,
76
+						[:critical, :error]    => :critical,
77
+						[:critical, :warning]  => :critical,
78
+						[:critical, nil]       => :critical,
79
+						[:critical, :good]     => :critical,
80
+						[:critical, :great]    => :critical,
81
+						[:critical, :best]     => :critical,
82
+
83
+						[:error, :critical]    => :critical,
84
+						[:error, :error]       => :error,
85
+						[:error, :warning]     => :error,
86
+						[:error, nil]          => :error,
87
+						[:error, :good]        => :error,
88
+						[:error, :great]       => :error,
89
+						[:error, :best]        => :error,
90
+
91
+						[:warning, :critical]  => :critical,
92
+						[:warning, :error]     => :error,
93
+						[:warning, :warning]   => :warning,
94
+						[:warning, nil]        => :warning,
95
+						[:warning, :good]      => :warning,
96
+						[:warning, :great]     => :warning,
97
+						[:warning, :best]      => :warning,
98
+
99
+						[:good, :critical]     => :critical,
100
+						[:good, :error]        => :error,
101
+						[:good, :warning]      => :warning,
102
+						[:good, nil]           => nil,
103
+						[:good, :good]         => nil,
104
+						[:good, :great]        => nil,
105
+						[:good, :best]         => nil,
106
+
107
+						[:great, :critical]    => :critical,
108
+						[:great, :error]       => :error,
109
+						[:great, :warning]     => :warning,
110
+						[:great, nil]          => nil,
111
+						[:great, :good]        => nil,
112
+						[:great, :great]       => nil,
113
+						[:great, :best]        => nil,
114
+
115
+						[:best, :critical]     => :critical,
116
+						[:best, :error]        => :error,
117
+						[:best, :warning]      => :warning,
118
+						[:best, nil]           => nil,
119
+						[:best, :good]         => nil,
120
+						[:best, :great]        => nil,
121
+						[:best, :best]         => nil
122
+				}.each do |levels, result|
123
+					got = State.problem levels
124
+					expect(got).to be(result), "#{levels} : expected #{result.inspect}, got #{got.inspect}"
125
+				end
126
+			end
127
+
128
+			it 'must handle object list' do
129
+				critical = OpenStruct.new status: :critical
130
+				warning  = OpenStruct.new status: :warning
131
+				expect(State.problem [critical, warning]).to be :critical
132
+			end
133
+		end
134
+
135
+		describe '#states' do
136
+			def match_states(obj, **expected)
137
+				expected = State.empty.merge expected
138
+				expect(obj.states).to eq expected
139
+			end
140
+
141
+
142
+			let(:empty) do
143
+				Class.new do
144
+					include State
145
+
146
+					def available_checks
147
+						[]
148
+					end
149
+				end.new
150
+			end
151
+			let(:childless) do
152
+				Class.new do
153
+					include State
154
+
155
+					def available_checks
156
+						[
157
+								[:foo, :critical, -> (_) { true }],
158
+								[:bar, :error, -> (_) { true }],
159
+								[:baz, :warning, -> (_) { false }]
160
+						]
161
+					end
162
+				end.new
163
+			end
164
+			let(:parent) do
165
+				child = Class.new do
166
+					include State
167
+
168
+					def available_checks
169
+						[[:bar, :error, -> (_) { true }]]
170
+					end
171
+				end.new
172
+				Class.new do
173
+					include State
174
+
175
+					def initialize(child)
176
+						@child = child
177
+					end
178
+
179
+					def available_checks
180
+						[[:foo, :critical, -> (_) { true }]]
181
+					end
182
+
183
+					def children
184
+						[@child]
185
+					end
186
+				end.new child
187
+			end
188
+			let(:duplicated) do
189
+				child = Class.new do
190
+					include State
191
+
192
+					def available_checks
193
+						[[:foo, :error, -> (_) { true }]]
194
+					end
195
+				end.new
196
+				Class.new do
197
+					include State
198
+
199
+					def initialize(child)
200
+						@child = child
201
+					end
202
+
203
+					def available_checks
204
+						[[:foo, :critical, -> (_) { true }]]
205
+					end
206
+
207
+					def children
208
+						[@child]
209
+					end
210
+				end.new(child)
211
+			end
212
+
213
+			it 'must return the level if single level specified' do
214
+				obj = Class.new do
215
+					include State
216
+
217
+					def available_checks
218
+						[[:foo, :critical, -> (_) { true }]]
219
+					end
220
+				end.new
221
+				match_states obj, critical: { foo: true }
222
+
223
+				obj = Class.new do
224
+					include State
225
+
226
+					def available_checks
227
+						[[:foo, :critical, -> (_) { false }]]
228
+					end
229
+				end.new
230
+				match_states obj, critical: { foo: false }
231
+
232
+				obj = Class.new do
233
+					include State
234
+
235
+					def available_checks
236
+						[[:foo, :critical, -> (_) { nil }]]
237
+					end
238
+				end.new
239
+				match_states obj, critical: { foo: nil }
240
+			end
241
+
242
+			it 'must return all levels if multiple levels specified' do
243
+				obj = Class.new do
244
+					include State
245
+
246
+					def available_checks
247
+						[[:foo, %i(critical error good great), -> (_) { :critical }]]
248
+					end
249
+				end.new
250
+				match_states obj, critical: { foo: true },
251
+							 error:         { foo: true },
252
+							 good:          { foo: false },
253
+							 great:         { foo: false }
254
+
255
+				obj = Class.new do
256
+					include State
257
+
258
+					def available_checks
259
+						[[:foo, %i(critical error good great), -> (_) { :error }]]
260
+					end
261
+				end.new
262
+				match_states obj, critical: { foo: false },
263
+							 error:         { foo: true },
264
+							 good:          { foo: false },
265
+							 great:         { foo: false }
266
+
267
+
268
+				obj = Class.new do
269
+					include State
270
+
271
+					def available_checks
272
+						[[:foo, %i(critical error good great), -> (_) { :great }]]
273
+					end
274
+				end.new
275
+				match_states obj, critical: { foo: false },
276
+							 error:         { foo: false },
277
+							 good:          { foo: true },
278
+							 great:         { foo: true }
279
+
280
+
281
+				obj = Class.new do
282
+					include State
283
+
284
+					def available_checks
285
+						[[:foo, %i(critical error good great), -> (_) { :good }]]
286
+					end
287
+				end.new
288
+				match_states obj, critical: { foo: false },
289
+							 error:         { foo: false },
290
+							 good:          { foo: true },
291
+							 great:         { foo: false }
292
+
293
+				obj = Class.new do
294
+					include State
295
+
296
+					def available_checks
297
+						[[:foo, %i(critical error good great), -> (_) { nil }]]
298
+					end
299
+				end.new
300
+				match_states obj, critical: { foo: nil },
301
+							 error:         { foo: nil },
302
+							 good:          { foo: nil },
303
+							 great:         { foo: nil }
304
+			end
305
+
306
+			it 'must return empty if no check nor child' do
307
+				match_states empty
308
+			end
309
+
310
+			it 'must return personal status if no child' do
311
+				match_states childless, critical: { foo: true }, error: { bar: true }, warning: { baz: false }
312
+			end
313
+
314
+			it 'must return personal and children statuses' do
315
+				match_states parent, critical: { foo: true }, error: { bar: true }
316
+			end
317
+
318
+			it 'must return remove duplicated status' do
319
+				match_states duplicated, critical: { foo: true }, error: { foo: true }
320
+			end
321
+		end
322
+
323
+		describe '#status' do
324
+			it 'must return nil if nothing special' do
325
+				empty = Class.new do
326
+					include State
327
+
328
+					def available_checks
329
+						[]
330
+					end
331
+				end.new
332
+				expect(empty.status).to be_nil
333
+			end
334
+
335
+			it 'must return the status if only one' do
336
+				empty = Class.new do
337
+					include State
338
+
339
+					def available_checks
340
+						[[:foo, :critical, -> (_) { true }]]
341
+					end
342
+				end.new
343
+				expect(empty.status).to be :critical
344
+			end
345
+
346
+			it 'must return the worst status if multiple' do
347
+				empty = Class.new do
348
+					include State
349
+
350
+					def available_checks
351
+						[[:foo, :critical, -> (_) { true }],
352
+						 [:bar, :error, -> (_) { true }]]
353
+					end
354
+				end.new
355
+				expect(empty.status).to be :critical
356
+			end
357
+		end
358
+
359
+		describe '::state' do
360
+			it 'must return false on bad case with only nil' do
361
+				states = { critical: { foo: nil } }
362
+				expect(State.state states, :critical).to be_falsey
363
+			end
364
+
365
+			it 'must return false on bad case with all false or nil' do
366
+				states = { critical: { foo: false, bar: nil } }
367
+				expect(State.state states, :critical).to be_falsey
368
+			end
369
+
370
+			it 'must return true on bad case with a single true' do
371
+				states = { critical: { foo: false, bar: nil, baz: true } }
372
+				expect(State.state states, :critical).to be_truthy
373
+			end
374
+
375
+			it 'must return :all on good case with only nil' do
376
+				states = { best: { foo: nil } }
377
+				expect(State.state states, :best).to eq :all
378
+			end
379
+
380
+			it 'must return false on good case with only false' do
381
+				states = { best: { foo: false } }
382
+				expect(State.state states, :best).to be_falsey
383
+			end
384
+
385
+			it 'must return false on good case with only false or nil' do
386
+				states = { best: { foo: false, bar: nil } }
387
+				expect(State.state states, :best).to be_falsey
388
+			end
389
+
390
+			it 'must return :some on good case with a single true' do
391
+				states = { best: { foo: false, bar: nil, baz: true } }
392
+				expect(State.state states, :best).to eq :some
393
+			end
394
+
395
+			it 'must return :all on good case with only true' do
396
+				states = { best: { bar: true } }
397
+				expect(State.state states, :best).to eq :all
398
+			end
399
+
400
+			it 'must return :all on good case with only true or nil' do
401
+				states = { best: { foo: nil, bar: true } }
402
+				expect(State.state states, :best).to eq :all
403
+			end
404
+		end
405
+	end
406
+end

+ 0
- 357
spec/cryptcheck/status_spec.rb View File

@@ -1,357 +0,0 @@
1
-require 'ostruct'
2
-
3
-describe CryptCheck::State do
4
-	describe '::status' do
5
-		it 'must handle empty list' do
6
-			expect(CryptCheck::State.status []).to be_nil
7
-		end
8
-
9
-		it 'must answer correctly' do
10
-			{
11
-					[:critical, :critical] => :critical,
12
-					[:critical, :error]    => :critical,
13
-					[:critical, :warning]  => :critical,
14
-					[:critical, nil]       => :critical,
15
-					[:critical, :good]     => :critical,
16
-					[:critical, :great]    => :critical,
17
-					[:critical, :best]     => :critical,
18
-
19
-					[:error, :critical]    => :critical,
20
-					[:error, :error]       => :error,
21
-					[:error, :warning]     => :error,
22
-					[:error, nil]          => :error,
23
-					[:error, :good]        => :error,
24
-					[:error, :great]       => :error,
25
-					[:error, :best]        => :error,
26
-
27
-					[:warning, :critical]  => :critical,
28
-					[:warning, :error]     => :error,
29
-					[:warning, :warning]   => :warning,
30
-					[:warning, nil]        => :warning,
31
-					[:warning, :good]      => :warning,
32
-					[:warning, :great]     => :warning,
33
-					[:warning, :best]      => :warning,
34
-
35
-					[:good, :critical]     => :critical,
36
-					[:good, :error]        => :error,
37
-					[:good, :warning]      => :warning,
38
-					[:good, nil]           => :good,
39
-					[:good, :good]         => :good,
40
-					[:good, :great]        => :good,
41
-					[:good, :best]         => :good,
42
-
43
-					[:great, :critical]    => :critical,
44
-					[:great, :error]       => :error,
45
-					[:great, :warning]     => :warning,
46
-					[:great, nil]          => :great,
47
-					[:great, :good]        => :good,
48
-					[:great, :great]       => :great,
49
-					[:great, :best]        => :great,
50
-
51
-					[:best, :critical]     => :critical,
52
-					[:best, :error]        => :error,
53
-					[:best, :warning]      => :warning,
54
-					[:best, nil]           => :best,
55
-					[:best, :good]         => :good,
56
-					[:best, :great]        => :great,
57
-					[:best, :best]         => :best
58
-			}.each do |levels, result|
59
-				got = CryptCheck::State.status levels
60
-				expect(got).to be(result), "#{levels} : expected #{result.inspect}, got #{got.inspect}"
61
-			end
62
-		end
63
-
64
-		it 'must handle object list' do
65
-			critical = OpenStruct.new status: :critical
66
-			warning  = OpenStruct.new status: :warning
67
-			expect(CryptCheck::State.status [critical, warning]).to be :critical
68
-		end
69
-	end
70
-
71
-	describe '::problem' do
72
-		it 'must answer correctly' do
73
-			{
74
-					[:critical, :critical] => :critical,
75
-					[:critical, :error]    => :critical,
76
-					[:critical, :warning]  => :critical,
77
-					[:critical, nil]       => :critical,
78
-					[:critical, :good]     => :critical,
79
-					[:critical, :great]    => :critical,
80
-					[:critical, :best]     => :critical,
81
-
82
-					[:error, :critical]    => :critical,
83
-					[:error, :error]       => :error,
84
-					[:error, :warning]     => :error,
85
-					[:error, nil]          => :error,
86
-					[:error, :good]        => :error,
87
-					[:error, :great]       => :error,
88
-					[:error, :best]        => :error,
89
-
90
-					[:warning, :critical]  => :critical,
91
-					[:warning, :error]     => :error,
92
-					[:warning, :warning]   => :warning,
93
-					[:warning, nil]        => :warning,
94
-					[:warning, :good]      => :warning,
95
-					[:warning, :great]     => :warning,
96
-					[:warning, :best]      => :warning,
97
-
98
-					[:good, :critical]     => :critical,
99
-					[:good, :error]        => :error,
100
-					[:good, :warning]      => :warning,
101
-					[:good, nil]           => nil,
102
-					[:good, :good]         => nil,
103
-					[:good, :great]        => nil,
104
-					[:good, :best]         => nil,
105
-
106
-					[:great, :critical]    => :critical,
107
-					[:great, :error]       => :error,
108
-					[:great, :warning]     => :warning,
109
-					[:great, nil]          => nil,
110
-					[:great, :good]        => nil,
111
-					[:great, :great]       => nil,
112
-					[:great, :best]        => nil,
113
-
114
-					[:best, :critical]     => :critical,
115
-					[:best, :error]        => :error,
116
-					[:best, :warning]      => :warning,
117
-					[:best, nil]           => nil,
118
-					[:best, :good]         => nil,
119
-					[:best, :great]        => nil,
120
-					[:best, :best]         => nil
121
-			}.each do |levels, result|
122
-				got = CryptCheck::State.problem levels
123
-				expect(got).to be(result), "#{levels} : expected #{result.inspect}, got #{got.inspect}"
124
-			end
125
-		end
126
-
127
-		it 'must handle object list' do
128
-			critical = OpenStruct.new status: :critical
129
-			warning  = OpenStruct.new status: :warning
130
-			expect(CryptCheck::State.problem [critical, warning]).to be :critical
131
-		end
132
-	end
133
-
134
-	describe '#states' do
135
-		def match_states(obj, **expected)
136
-			expected = CryptCheck::State.empty.merge expected
137
-			expect(obj.states).to eq expected
138
-		end
139
-
140
-
141
-		let(:empty) do
142
-			Class.new do
143
-				include ::CryptCheck::State
144
-
145
-				def available_checks
146
-					[]
147
-				end
148
-			end.new
149
-		end
150
-		let(:childless) do
151
-			Class.new do
152
-				include ::CryptCheck::State
153
-
154
-				def available_checks
155
-					[
156
-							[:foo, :critical, -> (_) { true }],
157
-							[:bar, :error, -> (_) { true }],
158
-							[:baz, :warning, -> (_) { false }]
159
-					]
160
-				end
161
-			end.new
162
-		end
163
-		let(:parent) do
164
-			child = Class.new do
165
-				include ::CryptCheck::State
166
-
167
-				def available_checks
168
-					[[:bar, :error, -> (_) { true }]]
169
-				end
170
-			end.new
171
-			Class.new do
172
-				include ::CryptCheck::State
173
-
174
-				def initialize(child)
175
-					@child = child
176
-				end
177
-
178
-				def available_checks
179
-					[[:foo, :critical, -> (_) { true }]]
180
-				end
181
-
182
-				def children
183
-					[@child]
184
-				end
185
-			end.new child
186
-		end
187
-		let(:duplicated) do
188
-			child = Class.new do
189
-				include ::CryptCheck::State
190
-
191
-				def available_checks
192
-					[[:foo, :error, -> (_) { true }]]
193
-				end
194
-			end.new
195
-			Class.new do
196
-				include ::CryptCheck::State
197
-
198
-				def initialize(child)
199
-					@child = child
200
-				end
201
-
202
-				def available_checks
203
-					[[:foo, :critical, -> (_) { true }]]
204
-				end
205
-
206
-				def children
207
-					[@child]
208
-				end
209
-			end.new(child)
210
-		end
211
-
212
-		it 'must return the level if single level specified' do
213
-			obj = Class.new do
214
-				include ::CryptCheck::State
215
-
216
-				def available_checks
217
-					[[:foo, :critical, -> (_) { true }]]
218
-				end
219
-			end.new
220
-			match_states obj, critical: { foo: true }
221
-
222
-			obj = Class.new do
223
-				include ::CryptCheck::State
224
-
225
-				def available_checks
226
-					[[:foo, :critical, -> (_) { false }]]
227
-				end
228
-			end.new
229
-			match_states obj, critical: { foo: false }
230
-
231
-			obj = Class.new do
232
-				include ::CryptCheck::State
233
-
234
-				def available_checks
235
-					[[:foo, :critical, -> (_) { nil }]]
236
-				end
237
-			end.new
238
-			match_states obj, critical: { foo: nil }
239
-		end
240
-
241
-		it 'must return all levels if multiple levels specified' do
242
-			obj = Class.new do
243
-				include ::CryptCheck::State
244
-
245
-				def available_checks
246
-					[[:foo, %i(critical error good great), -> (_) { :critical }]]
247
-				end
248
-			end.new
249
-			match_states obj, critical: { foo: true },
250
-						 error:         { foo: true },
251
-						 good:          { foo: false },
252
-						 great:         { foo: false }
253
-
254
-			obj = Class.new do
255
-				include ::CryptCheck::State
256
-
257
-				def available_checks
258
-					[[:foo, %i(critical error good great), -> (_) { :error }]]
259
-				end
260
-			end.new
261
-			match_states obj, critical: { foo: false },
262
-						 error:         { foo: true },
263
-						 good:          { foo: false },
264
-						 great:         { foo: false }
265
-
266
-
267
-			obj = Class.new do
268
-				include ::CryptCheck::State
269
-
270
-				def available_checks
271
-					[[:foo, %i(critical error good great), -> (_) { :great }]]
272
-				end
273
-			end.new
274
-			match_states obj, critical: { foo: false },
275
-						 error:         { foo: false },
276
-						 good:          { foo: true },
277
-						 great:         { foo: true }
278
-
279
-
280
-			obj = Class.new do
281
-				include ::CryptCheck::State
282
-
283
-				def available_checks
284
-					[[:foo, %i(critical error good great), -> (_) { :good }]]
285
-				end
286
-			end.new
287
-			match_states obj, critical: { foo: false },
288
-						 error:         { foo: false },
289
-						 good:          { foo: true },
290
-						 great:         { foo: false }
291
-
292
-			obj = Class.new do
293
-				include ::CryptCheck::State
294
-
295
-				def available_checks
296
-					[[:foo, %i(critical error good great), -> (_) { nil }]]
297
-				end
298
-			end.new
299
-			match_states obj, critical: { foo: nil },
300
-						 error:         { foo: nil },
301
-						 good:          { foo: nil },
302
-						 great:         { foo: nil }
303
-		end
304
-
305
-		it 'must return empty if no check nor child' do
306
-			match_states empty
307
-		end
308
-
309
-		it 'must return personal status if no child' do
310
-			match_states childless, critical: { foo: true }, error: { bar: true }, warning: { baz: false }
311
-		end
312
-
313
-		it 'must return personal and children statuses' do
314
-			match_states parent, critical: { foo: true }, error: { bar: true }
315
-		end
316
-
317
-		it 'must return remove duplicated status' do
318
-			match_states duplicated, critical: { foo: true }, error: { foo: true }
319
-		end
320
-	end
321
-
322
-	describe '#status' do
323
-		it 'must return nil if nothing special' do
324
-			empty = Class.new do
325
-				include ::CryptCheck::State
326
-
327
-				def available_checks
328
-					[]
329
-				end
330
-			end.new
331
-			expect(empty.status).to be_nil
332
-		end
333
-
334
-		it 'must return the status if only one' do
335
-			empty = Class.new do
336
-				include ::CryptCheck::State
337
-
338
-				def available_checks
339
-					[[:foo, :critical, -> (_) { true }]]
340
-				end
341
-			end.new
342
-			expect(empty.status).to be :critical
343
-		end
344
-
345
-		it 'must return the worst status if multiple' do
346
-			empty = Class.new do
347
-				include ::CryptCheck::State
348
-
349
-				def available_checks
350
-					[[:foo, :critical, -> (_) { true }],
351
-					 [:bar, :error, -> (_) { true }]]
352
-				end
353
-			end.new
354
-			expect(empty.status).to be :critical
355
-		end
356
-	end
357
-end

+ 68
- 66
spec/cryptcheck/tls/cert_spec.rb View File

@@ -1,90 +1,92 @@
1
-describe CryptCheck::Tls::Cert do
2
-	describe '::trusted?' do
3
-		it 'must accept valid certificate' do
4
-			FakeTime.freeze Time.utc(2000, 1, 1) do
5
-				cert, *chain, ca = chain(%w(ecdsa-prime256v1 intermediate ca))
6
-				trust            = ::CryptCheck::Tls::Cert.trusted? cert, chain, roots: ca
7
-				expect(trust).to eq :trusted
1
+module CryptCheck::Tls
2
+	describe Cert do
3
+		describe '::trusted?' do
4
+			it 'must accept valid certificate' do
5
+				FakeTime.freeze Time.utc(2000, 1, 1) do
6
+					cert, *chain, ca = chain(%w(ecdsa-prime256v1 intermediate ca))
7
+					trust            = Cert.trusted? cert, chain, roots: ca
8
+					expect(trust).to eq :trusted
9
+				end
8 10
 			end
9
-		end
10 11
 
11
-		it 'must reject self signed certificate' do
12
-			cert, ca = chain(%w(self-signed ca))
13
-			trust    = ::CryptCheck::Tls::Cert.trusted? cert, [], roots: ca
14
-			expect(trust).to eq 'self signed certificate'
12
+			it 'must reject self signed certificate' do
13
+				cert, ca = chain(%w(self-signed ca))
14
+				trust    = Cert.trusted? cert, [], roots: ca
15
+				expect(trust).to eq 'self signed certificate'
15 16
 
16
-			# Case for SSLv2
17
-			cert, ca = chain(%w(self-signed ca))
18
-			trust    = ::CryptCheck::Tls::Cert.trusted? cert, nil, roots: ca
19
-			expect(trust).to eq 'self signed certificate'
20
-		end
17
+				# Case for SSLv2
18
+				cert, ca = chain(%w(self-signed ca))
19
+				trust    = Cert.trusted? cert, nil, roots: ca
20
+				expect(trust).to eq 'self signed certificate'
21
+			end
21 22
 
22
-		it 'must reject unknown CA' do
23
-			cert, *chain = chain(%w(ecdsa-prime256v1 intermediate ca))
24
-			trust        = ::CryptCheck::Tls::Cert.trusted? cert, chain, roots: []
25
-			expect(trust).to eq 'unable to get issuer certificate'
26
-		end
23
+			it 'must reject unknown CA' do
24
+				cert, *chain = chain(%w(ecdsa-prime256v1 intermediate ca))
25
+				trust        = Cert.trusted? cert, chain, roots: []
26
+				expect(trust).to eq 'unable to get issuer certificate'
27
+			end
27 28
 
28
-		it 'must reject missing intermediate chain' do
29
-			cert, ca = chain(%w(ecdsa-prime256v1 ca))
30
-			chain    = []
31
-			trust    = ::CryptCheck::Tls::Cert.trusted? cert, chain, roots: ca
32
-			expect(trust).to eq 'unable to get local issuer certificate'
33
-		end
29
+			it 'must reject missing intermediate chain' do
30
+				cert, ca = chain(%w(ecdsa-prime256v1 ca))
31
+				chain    = []
32
+				trust    = Cert.trusted? cert, chain, roots: ca
33
+				expect(trust).to eq 'unable to get local issuer certificate'
34
+			end
34 35
 
35
-		it 'must reject expired certificate' do
36
-			FakeTime.freeze Time.utc(2002, 1, 1) do
37
-				cert, *chain, ca = chain(%w(ecdsa-prime256v1 intermediate ca))
38
-				trust            = ::CryptCheck::Tls::Cert.trusted? cert, chain, roots: ca
39
-				expect(trust).to eq 'certificate has expired'
36
+			it 'must reject expired certificate' do
37
+				FakeTime.freeze Time.utc(2002, 1, 1) do
38
+					cert, *chain, ca = chain(%w(ecdsa-prime256v1 intermediate ca))
39
+					trust            = Cert.trusted? cert, chain, roots: ca
40
+					expect(trust).to eq 'certificate has expired'
41
+				end
40 42
 			end
41
-		end
42 43
 
43
-		it 'must reject not yet valid certificate' do
44
-			FakeTime.freeze Time.utc(1999, 1, 1) do
45
-				cert, *chain, ca = chain(%w(ecdsa-prime256v1 intermediate ca))
46
-				trust            = ::CryptCheck::Tls::Cert.trusted? cert, chain, roots: ca
47
-				expect(trust).to eq 'certificate is not yet valid'
44
+			it 'must reject not yet valid certificate' do
45
+				FakeTime.freeze Time.utc(1999, 1, 1) do
46
+					cert, *chain, ca = chain(%w(ecdsa-prime256v1 intermediate ca))
47
+					trust            = Cert.trusted? cert, chain, roots: ca
48
+					expect(trust).to eq 'certificate is not yet valid'
49
+				end
48 50
 			end
49 51
 		end
50
-	end
51 52
 
52
-	describe '#md5?' do
53
-		it 'must detect md5 certificate' do
54
-			cert = ::CryptCheck::Tls::Cert.new cert(:md5)
55
-			expect(cert.md5?).to be true
53
+		describe '#md5?' do
54
+			it 'must detect md5 certificate' do
55
+				cert = Cert.new cert(:md5)
56
+				expect(cert.md5?).to be true
56 57
 
57
-			cert = ::CryptCheck::Tls::Cert.new cert(:sha1)
58
-			expect(cert.md5?).to be false
58
+				cert = Cert.new cert(:sha1)
59
+				expect(cert.md5?).to be false
59 60
 
60
-			cert = ::CryptCheck::Tls::Cert.new cert(:ecdsa, :prime256v1)
61
-			expect(cert.md5?).to be false
61
+				cert = Cert.new cert(:ecdsa, :prime256v1)
62
+				expect(cert.md5?).to be false
63
+			end
62 64
 		end
63
-	end
64 65
 
65
-	describe '#sha1?' do
66
-		it 'must detect sha1 certificate' do
67
-			cert = ::CryptCheck::Tls::Cert.new cert(:md5)
68
-			expect(cert.sha1?).to be false
66
+		describe '#sha1?' do
67
+			it 'must detect sha1 certificate' do
68
+				cert = Cert.new cert(:md5)
69
+				expect(cert.sha1?).to be false
69 70
 
70
-			cert = ::CryptCheck::Tls::Cert.new cert(:sha1)
71
-			expect(cert.sha1?).to be true
71
+				cert = Cert.new cert(:sha1)
72
+				expect(cert.sha1?).to be true
72 73
 
73
-			cert = ::CryptCheck::Tls::Cert.new cert(:ecdsa, :prime256v1)
74
-			expect(cert.sha1?).to be false
74
+				cert = Cert.new cert(:ecdsa, :prime256v1)
75
+				expect(cert.sha1?).to be false
76
+			end
75 77
 		end
76
-	end
77 78
 
78
-	describe '#sha2?' do
79
-		it 'must detect sha2 certificate' do
80
-			cert = ::CryptCheck::Tls::Cert.new cert(:md5)
81
-			expect(cert.sha2?).to be false
79
+		describe '#sha2?' do
80
+			it 'must detect sha2 certificate' do
81
+				cert = Cert.new cert(:md5)
82
+				expect(cert.sha2?).to be false
82 83
 
83
-			cert = ::CryptCheck::Tls::Cert.new cert(:sha1)
84
-			expect(cert.sha2?).to be false
84
+				cert = Cert.new cert(:sha1)
85
+				expect(cert.sha2?).to be false
85 86
 
86
-			cert = ::CryptCheck::Tls::Cert.new cert(:ecdsa, :prime256v1)
87
-			expect(cert.sha2?).to be true
87
+				cert = Cert.new cert(:ecdsa, :prime256v1)
88
+				expect(cert.sha2?).to be true
89
+			end
88 90
 		end
89 91
 	end
90 92
 end

+ 103
- 102
spec/cryptcheck/tls/host_spec.rb View File

@@ -1,129 +1,130 @@
1
-describe CryptCheck::Tls::Host do
2
-	def host(*args, **kargs)
3
-		do_in_serv *args, **kargs do |host, port|
4
-			CryptCheck::Tls::Host.new host, port
1
+module CryptCheck::Tls
2
+	describe Host do
3
+		def host(*args, **kargs)
4
+			do_in_serv *args, **kargs do |host, port|
5
+				Host.new host, port
6
+			end
5 7
 		end
6
-	end
7
-
8
-	def servers(*args, **kargs)
9
-		host(*args, **kargs).servers
10
-	end
11 8
 
12
-	def error(*args, **kargs)
13
-		host(*args, **kargs).error
14
-	end
15
-
16
-	it 'return 1 grade with IPv4' do
17
-		servers = servers()
18
-		expect(servers.size).to be 1
19
-		expect_grade servers, Helpers::DEFAULT_HOST, Helpers::DEFAULT_IPv4, Helpers::DEFAULT_PORT, :ipv4
20
-	end
21
-
22
-	it 'return 1 grade with IPv6' do
23
-		addresses = [Helpers::DEFAULT_IPv6]
24
-		allow(Addrinfo).to receive(:getaddrinfo).with(Helpers::DEFAULT_HOST, nil, nil, :STREAM) do
25
-			addresses.collect { |a| Addrinfo.new Socket.sockaddr_in(nil, a) }
9
+		def servers(*args, **kargs)
10
+			host(*args, **kargs).servers
26 11
 		end
27 12
 
28
-		servers = servers(host: Helpers::DEFAULT_IPv6)
29
-		expect(servers.size).to be 1
30
-		expect_grade servers, Helpers::DEFAULT_HOST, Helpers::DEFAULT_IPv6, Helpers::DEFAULT_PORT, :ipv6
31
-	end
32
-
33
-	it 'return 2 grades with hostname (IPv4 & IPv6)' do
34
-		addresses = [Helpers::DEFAULT_IPv4, Helpers::DEFAULT_IPv6]
35
-		allow(Addrinfo).to receive(:getaddrinfo).with(Helpers::DEFAULT_HOST, nil, nil, :STREAM) do
36
-			addresses.collect { |a| Addrinfo.new Socket.sockaddr_in(nil, a) }
13
+		def error(*args, **kargs)
14
+			host(*args, **kargs).error
37 15
 		end
38 16
 
39
-		servers = servers(host: '::')
40
-		expect(servers.size).to be 2
41
-		expect_grade servers, Helpers::DEFAULT_HOST, Helpers::DEFAULT_IPv4, Helpers::DEFAULT_PORT, :ipv4
42
-		expect_grade servers, Helpers::DEFAULT_HOST, Helpers::DEFAULT_IPv6, Helpers::DEFAULT_PORT, :ipv6
43
-	end
44
-
45
-	it 'return error if DNS resolution problem' do
46
-		allow(Addrinfo).to receive(:getaddrinfo).with(Helpers::DEFAULT_HOST, nil, nil, :STREAM)
47
-								   .and_raise SocketError, 'getaddrinfo: Name or service not known'
17
+		it 'return 1 grade with IPv4' do
18
+			servers = servers()
19
+			expect(servers.size).to be 1
20
+			expect_grade servers, Helpers::DEFAULT_HOST, Helpers::DEFAULT_IPv4, Helpers::DEFAULT_PORT, :ipv4
21
+		end
48 22
 
49
-		error = error()
50
-		expect_error error, ::SocketError, 'getaddrinfo: Name or service not known'
51
-	end
23
+		it 'return 1 grade with IPv6' do
24
+			addresses = [Helpers::DEFAULT_IPv6]
25
+			allow(Addrinfo).to receive(:getaddrinfo).with(Helpers::DEFAULT_HOST, nil, nil, :STREAM) do
26
+				addresses.collect { |a| Addrinfo.new Socket.sockaddr_in(nil, a) }
27
+			end
52 28
 
53
-	it 'return error if analysis too long' do
54
-		stub_const 'CryptCheck::Tls::Host::MAX_ANALYSIS_DURATION', 1
55
-		allow_any_instance_of(CryptCheck::Tls::Host).to receive(:server) { sleep 2 }
29
+			servers = servers(host: Helpers::DEFAULT_IPv6)
30
+			expect(servers.size).to be 1
31
+			expect_grade servers, Helpers::DEFAULT_HOST, Helpers::DEFAULT_IPv6, Helpers::DEFAULT_PORT, :ipv6
32
+		end
56 33
 
57
-		servers = servers()
58
-		expect_grade_error servers, Helpers::DEFAULT_HOST, Helpers::DEFAULT_IPv4, Helpers::DEFAULT_PORT,
59
-						   'Too long analysis (max 1 second)'
60
-	end
34
+		it 'return 2 grades with hostname (IPv4 & IPv6)' do
35
+			addresses = [Helpers::DEFAULT_IPv4, Helpers::DEFAULT_IPv6]
36
+			allow(Addrinfo).to receive(:getaddrinfo).with(Helpers::DEFAULT_HOST, nil, nil, :STREAM) do
37
+				addresses.collect { |a| Addrinfo.new Socket.sockaddr_in(nil, a) }
38
+			end
61 39
 
62
-	it 'return error if unable to connect' do
63
-		addresses = [Helpers::DEFAULT_IPv4, Helpers::DEFAULT_IPv6]
64
-		allow(Addrinfo).to receive(:getaddrinfo).with(Helpers::DEFAULT_HOST, nil, nil, :STREAM) do
65
-			addresses.collect { |a| Addrinfo.new Socket.sockaddr_in(nil, a) }
40
+			servers = servers(host: '::')
41
+			expect(servers.size).to be 2
42
+			expect_grade servers, Helpers::DEFAULT_HOST, Helpers::DEFAULT_IPv4, Helpers::DEFAULT_PORT, :ipv4
43
+			expect_grade servers, Helpers::DEFAULT_HOST, Helpers::DEFAULT_IPv6, Helpers::DEFAULT_PORT, :ipv6
66 44
 		end
67 45
 
68
-		servers = servers(host: Helpers::DEFAULT_IPv6)
69
-		expect_grade_error servers, Helpers::DEFAULT_HOST, Helpers::DEFAULT_IPv4, Helpers::DEFAULT_PORT,
70
-						   'Connection refused - connect(2) for 127.0.0.1:15000'
71
-		expect_grade servers, Helpers::DEFAULT_HOST, Helpers::DEFAULT_IPv6, Helpers::DEFAULT_PORT, :ipv6
72
-	end
46
+		it 'return error if DNS resolution problem' do
47
+			allow(Addrinfo).to receive(:getaddrinfo).with(Helpers::DEFAULT_HOST, nil, nil, :STREAM)
48
+									   .and_raise SocketError, 'getaddrinfo: Name or service not known'
73 49
 
74
-	it 'return error if TCP timeout' do
75
-		stub_const 'CryptCheck::Tls::Engine::TCP_TIMEOUT', 1
76
-		addresses = [Helpers::DEFAULT_IPv4, Helpers::DEFAULT_IPv6]
77
-		allow(Addrinfo).to receive(:getaddrinfo).with(Helpers::DEFAULT_HOST, nil, nil, :STREAM) do
78
-			addresses.collect { |a| Addrinfo.new Socket.sockaddr_in(nil, a) }
50
+			error = error()
51
+			expect_error error, ::SocketError, 'getaddrinfo: Name or service not known'
79 52
 		end
80 53
 
81
-		original = IO.method :select
82
-		allow(IO).to receive(:select) do |*args, &block|
83
-			socket = [args[0]&.first, args[1]&.first].compact.first
84
-			next nil if socket.is_a?(Socket) && (socket.local_address.afamily == Socket::AF_INET)
85
-			original.call *args, &block
54
+		it 'return error if analysis too long' do
55
+			stub_const 'CryptCheck::Tls::Host::MAX_ANALYSIS_DURATION', 1
56
+			allow_any_instance_of(Host).to receive(:server) { sleep 2 }
57
+
58
+			servers = servers()
59
+			expect_grade_error servers, Helpers::DEFAULT_HOST, Helpers::DEFAULT_IPv4, Helpers::DEFAULT_PORT,
60
+							   'Too long analysis (max 1 second)'
86 61
 		end
87 62
 
88
-		servers = servers(host: '::')
89
-		expect_grade_error servers, Helpers::DEFAULT_HOST, Helpers::DEFAULT_IPv4, Helpers::DEFAULT_PORT,
90
-						   'Timeout when connecting to 127.0.0.1:15000 (max 1 second)'
91
-		expect_grade servers, Helpers::DEFAULT_HOST, Helpers::DEFAULT_IPv6, Helpers::DEFAULT_PORT, :ipv6
92
-	end
63
+		it 'return error if unable to connect' do
64
+			addresses = [Helpers::DEFAULT_IPv4, Helpers::DEFAULT_IPv6]
65
+			allow(Addrinfo).to receive(:getaddrinfo).with(Helpers::DEFAULT_HOST, nil, nil, :STREAM) do
66
+				addresses.collect { |a| Addrinfo.new Socket.sockaddr_in(nil, a) }
67
+			end
93 68
 
94
-	it 'return error if TLS timeout' do
95
-		stub_const 'CryptCheck::Tls::Engine::TLS_TIMEOUT', 1
96
-		addresses = [Helpers::DEFAULT_IPv4, Helpers::DEFAULT_IPv6]
97
-		allow(Addrinfo).to receive(:getaddrinfo).with(Helpers::DEFAULT_HOST, nil, nil, :STREAM) do
98
-			addresses.collect { |a| Addrinfo.new Socket.sockaddr_in(nil, a) }
69
+			servers = servers(host: Helpers::DEFAULT_IPv6)
70
+			expect_grade_error servers, Helpers::DEFAULT_HOST, Helpers::DEFAULT_IPv4, Helpers::DEFAULT_PORT,
71
+							   'Connection refused - connect(2) for 127.0.0.1:15000'
72
+			expect_grade servers, Helpers::DEFAULT_HOST, Helpers::DEFAULT_IPv6, Helpers::DEFAULT_PORT, :ipv6
99 73
 		end
100 74
 
101
-		original = IO.method :select
102
-		allow(IO).to receive(:select) do |*args, &block|
103
-			socket = [args[0]&.first, args[1]&.first].compact.first
104
-			next nil if socket.is_a?(OpenSSL::SSL::SSLSocket) && (socket.io.local_address.afamily == Socket::AF_INET)
105
-			original.call *args, &block
75
+		it 'return error if TCP timeout' do
76
+			stub_const 'CryptCheck::Tls::Engine::TCP_TIMEOUT', 1
77
+			addresses = [Helpers::DEFAULT_IPv4, Helpers::DEFAULT_IPv6]
78
+			allow(Addrinfo).to receive(:getaddrinfo).with(Helpers::DEFAULT_HOST, nil, nil, :STREAM) do
79
+				addresses.collect { |a| Addrinfo.new Socket.sockaddr_in(nil, a) }
80
+			end
81
+
82
+			original = IO.method :select
83
+			allow(IO).to receive(:select) do |*args, &block|
84
+				socket = [args[0]&.first, args[1]&.first].compact.first
85
+				next nil if socket.is_a?(Socket) && (socket.local_address.afamily == Socket::AF_INET)
86
+				original.call *args, &block
87
+			end
88
+
89
+			servers = servers(host: '::')
90
+			expect_grade_error servers, Helpers::DEFAULT_HOST, Helpers::DEFAULT_IPv4, Helpers::DEFAULT_PORT,
91
+							   'Timeout when connecting to 127.0.0.1:15000 (max 1 second)'
92
+			expect_grade servers, Helpers::DEFAULT_HOST, Helpers::DEFAULT_IPv6, Helpers::DEFAULT_PORT, :ipv6
106 93
 		end
107 94
 
108
-		servers = servers(host: '::')
109
-		expect_grade_error servers, Helpers::DEFAULT_HOST, Helpers::DEFAULT_IPv4, Helpers::DEFAULT_PORT,
110
-						   'Timeout when TLS connecting to 127.0.0.1:15000 (max 1 second)'
111
-		expect_grade servers, Helpers::DEFAULT_HOST, Helpers::DEFAULT_IPv6, Helpers::DEFAULT_PORT, :ipv6
112
-	end
113
-
114
-	it 'return error if plain server' do
115
-		stub_const 'CryptCheck::Tls::ENGINE::TLS_TIMEOUT', 1
116
-		addresses = [Helpers::DEFAULT_IPv4, Helpers::DEFAULT_IPv6]
117
-		allow(Addrinfo).to receive(:getaddrinfo).with(Helpers::DEFAULT_HOST, nil, nil, :STREAM) do
118
-			addresses.collect { |a| Addrinfo.new Socket.sockaddr_in(nil, a) }
95
+		it 'return error if TLS timeout' do
96
+			stub_const 'CryptCheck::Tls::Engine::TLS_TIMEOUT', 1
97
+			addresses = [Helpers::DEFAULT_IPv4, Helpers::DEFAULT_IPv6]
98
+			allow(Addrinfo).to receive(:getaddrinfo).with(Helpers::DEFAULT_HOST, nil, nil, :STREAM) do
99
+				addresses.collect { |a| Addrinfo.new Socket.sockaddr_in(nil, a) }
100
+			end
101
+
102
+			original = IO.method :select
103
+			allow(IO).to receive(:select) do |*args, &block|
104
+				socket = [args[0]&.first, args[1]&.first].compact.first
105
+				next nil if socket.is_a?(OpenSSL::SSL::SSLSocket) && (socket.io.local_address.afamily == Socket::AF_INET)
106
+				original.call *args, &block
107
+			end
108
+
109
+			servers = servers(host: '::')
110
+			expect_grade_error servers, Helpers::DEFAULT_HOST, Helpers::DEFAULT_IPv4, Helpers::DEFAULT_PORT,
111
+							   'Timeout when TLS connecting to 127.0.0.1:15000 (max 1 second)'
112
+			expect_grade servers, Helpers::DEFAULT_HOST, Helpers::DEFAULT_IPv6, Helpers::DEFAULT_PORT, :ipv6
119 113
 		end
120 114
 
121
-		servers = plain_serv Helpers::DEFAULT_IPv4 do
122
-			servers(host: Helpers::DEFAULT_IPv6)
115
+		it 'return error if plain server' do
116
+			stub_const 'ENGINE::TLS_TIMEOUT', 1
117
+			addresses = [Helpers::DEFAULT_IPv4, Helpers::DEFAULT_IPv6]
118
+			allow(Addrinfo).to receive(:getaddrinfo).with(Helpers::DEFAULT_HOST, nil, nil, :STREAM) do
119
+				addresses.collect { |a| Addrinfo.new Socket.sockaddr_in(nil, a) }
120
+			end
121
+
122
+			servers = plain_serv Helpers::DEFAULT_IPv4 do
123
+				servers(host: Helpers::DEFAULT_IPv6)
124
+			end
125
+			expect_grade_error servers, Helpers::DEFAULT_HOST, Helpers::DEFAULT_IPv4, Helpers::DEFAULT_PORT,
126
+							   'TLS seems not supported on this server'
127
+			expect_grade servers, Helpers::DEFAULT_HOST, Helpers::DEFAULT_IPv6, Helpers::DEFAULT_PORT, :ipv6
123 128
 		end
124
-		expect_grade_error servers, Helpers::DEFAULT_HOST, Helpers::DEFAULT_IPv4, Helpers::DEFAULT_PORT,
125
-						   'TLS seems not supported on this server'
126
-		expect_grade servers, Helpers::DEFAULT_HOST, Helpers::DEFAULT_IPv6, Helpers::DEFAULT_PORT, :ipv6
127 129
 	end
128
-
129 130
 end

+ 59
- 57
spec/cryptcheck/tls/method_spec.rb View File

@@ -1,61 +1,63 @@
1
-describe CryptCheck::Tls::Method do
2
-	describe '#==' do
3
-		it 'must be equals to corresponding symbol' do
4
-			method = ::CryptCheck::Tls::Method[:TLSv1_2]
5
-			expect(method == :SSLv2).to be false
6
-			expect(method == :SSLv3).to be false
7
-			expect(method == :TLSv1).to be false
8
-			expect(method == :TLSv1_1).to be false
9
-			expect(method == :TLSv1_2).to be true
10
-		end
1
+module CryptCheck::Tls
2
+	describe Method do
3
+		describe '#==' do
4
+			it 'must be equals to corresponding symbol' do
5
+				method = Method[:TLSv1_2]
6
+				expect(method == :SSLv2).to be false
7
+				expect(method == :SSLv3).to be false
8
+				expect(method == :TLSv1).to be false
9
+				expect(method == :TLSv1_1).to be false
10
+				expect(method == :TLSv1_2).to be true
11
+			end
11 12
 
12
-		it 'must be equals to corresponding method' do
13
-			method = ::CryptCheck::Tls::Method[:TLSv1_2]
14
-			expect(method == ::CryptCheck::Tls::Method[:SSLv2]).to be false
15
-			expect(method == ::CryptCheck::Tls::Method[:SSLv3]).to be false
16
-			expect(method == ::CryptCheck::Tls::Method[:TLSv1]).to be false
17
-			expect(method == ::CryptCheck::Tls::Method[:TLSv1_1]).to be false
18
-			expect(method == ::CryptCheck::Tls::Method[:TLSv1_2]).to be true
13
+			it 'must be equals to corresponding method' do
14
+				method = Method[:TLSv1_2]
15
+				expect(method == Method[:SSLv2]).to be false
16
+				expect(method == Method[:SSLv3]).to be false
17
+				expect(method == Method[:TLSv1]).to be false
18
+				expect(method == Method[:TLSv1_1]).to be false
19
+				expect(method == Method[:TLSv1_2]).to be true
20
+			end
19 21
 		end
20
-	end
21 22
 
22
-	# describe '#eql?' do
23
-	# 	it 'must be equals to corresponding symbol' do
24
-	# 		method = ::CryptCheck::Tls::Method[:TLSv1_2]
25
-	# 		expect(method.eql? :SSLv2).to be false
26
-	# 		expect(method.eql? :SSLv3).to be false
27
-	# 		expect(method.eql? :TLSv1).to be false
28
-	# 		expect(method.eql? :TLSv1_1).to be false
29
-	# 		expect(method.eql? :TLSv1_2).to be true
30
-	# 	end
31
-	#
32
-	# 	it 'must be equals to corresponding method' do
33
-	# 		method = ::CryptCheck::Tls::Method[:TLSv1_2]
34
-	# 		expect(method.eql? ::CryptCheck::Tls::Method[:SSLv2]).to be false
35
-	# 		expect(method.eql? ::CryptCheck::Tls::Method[:SSLv3]).to be false
36
-	# 		expect(method.eql? ::CryptCheck::Tls::Method[:TLSv1]).to be false
37
-	# 		expect(method.eql? ::CryptCheck::Tls::Method[:TLSv1_1]).to be false
38
-	# 		expect(method.eql? ::CryptCheck::Tls::Method[:TLSv1_2]).to be true
39
-	# 	end
40
-	# end
41
-	#
42
-	# describe '#equal?' do
43
-	# 	it 'must be equals to corresponding symbol' do
44
-	# 		method = ::CryptCheck::Tls::Method[:TLSv1_2]
45
-	# 		expect(method.equal? :SSLv2).to be false
46
-	# 		expect(method.equal? :SSLv3).to be false
47
-	# 		expect(method.equal? :TLSv1).to be false
48
-	# 		expect(method.equal? :TLSv1_1).to be false
49
-	# 		expect(method.equal? :TLSv1_2).to be true
50
-	# 	end
51
-	#
52
-	# 	it 'must be equals to corresponding method' do
53
-	# 		method = ::CryptCheck::Tls::Method[:TLSv1_2]
54
-	# 		expect(method.equal? ::CryptCheck::Tls::Method[:SSLv2]).to be false
55
-	# 		expect(method.equal? ::CryptCheck::Tls::Method[:SSLv3]).to be false
56
-	# 		expect(method.equal? ::CryptCheck::Tls::Method[:TLSv1]).to be false
57
-	# 		expect(method.equal? ::CryptCheck::Tls::Method[:TLSv1_1]).to be false
58
-	# 		expect(method.equal? ::CryptCheck::Tls::Method[:TLSv1_2]).to be true
59
-	# 	end
60
-	# end
23
+		# describe '#eql?' do
24
+		# 	it 'must be equals to corresponding symbol' do
25
+		# 		method = Method[:TLSv1_2]
26
+		# 		expect(method.eql? :SSLv2).to be false
27
+		# 		expect(method.eql? :SSLv3).to be false
28
+		# 		expect(method.eql? :TLSv1).to be false
29
+		# 		expect(method.eql? :TLSv1_1).to be false
30
+		# 		expect(method.eql? :TLSv1_2).to be true
31
+		# 	end
32
+		#
33
+		# 	it 'must be equals to corresponding method' do
34
+		# 		method = Method[:TLSv1_2]
35
+		# 		expect(method.eql? Method[:SSLv2]).to be false
36
+		# 		expect(method.eql? Method[:SSLv3]).to be false
37
+		# 		expect(method.eql? Method[:TLSv1]).to be false
38
+		# 		expect(method.eql? Method[:TLSv1_1]).to be false
39
+		# 		expect(method.eql? Method[:TLSv1_2]).to be true
40
+		# 	end
41
+		# end
42
+		#
43
+		# describe '#equal?' do
44
+		# 	it 'must be equals to corresponding symbol' do
45
+		# 		method = Method[:TLSv1_2]
46
+		# 		expect(method.equal? :SSLv2).to be false
47
+		# 		expect(method.equal? :SSLv3).to be false
48
+		# 		expect(method.equal? :TLSv1).to be false
49
+		# 		expect(method.equal? :TLSv1_1).to be false
50
+		# 		expect(method.equal? :TLSv1_2).to be true
51
+		# 	end
52
+		#
53
+		# 	it 'must be equals to corresponding method' do
54
+		# 		method = Method[:TLSv1_2]
55
+		# 		expect(method.equal? Method[:SSLv2]).to be false
56
+		# 		expect(method.equal? Method[:SSLv3]).to be false
57
+		# 		expect(method.equal? Method[:TLSv1]).to be false
58
+		# 		expect(method.equal? Method[:TLSv1_1]).to be false
59
+		# 		expect(method.equal? Method[:TLSv1_2]).to be true
60
+		# 	end
61
+		# end
62
+	end
61 63
 end

+ 178
- 176
spec/cryptcheck/tls/server_spec.rb View File

@@ -1,187 +1,189 @@
1
-describe CryptCheck::Tls::Server do
2
-	before :all do
3
-		FakeTime.freeze Time.utc(2000, 1, 1)
4
-	end
5
-
6
-	after :all do
7
-		FakeTime.unfreeze
8
-	end
9
-
10
-	def server(*args, **kargs)
11
-		do_in_serv *args, **kargs do |host, port|
12
-			CryptCheck::Tls::TcpServer.new 'localhost', host, ::Socket::PF_INET, port
13
-		end
14
-	end
15
-
16
-	describe '#certs' do
17
-		it 'must detect RSA certificate' do
18
-			certs = server(:rsa).certs.collect &:fingerprint
19
-			expect(certs).to match_array %w(a11802a4407aaeb93ccd0bd8c8a61be17eaba6b378433af5ad45ecbb1d633f71)
20
-		end
21
-
22
-		it 'must detect ECDSA certificate' do
23
-			certs = server.certs.collect &:fingerprint
24
-			expect(certs).to match_array %w(531ab9545f052818ff0559f648a147b104223834cc8f780516b3aacf1fdc8c06)
25
-		end
26
-
27
-		it 'must detect RSA and ECDSA certificates' do
28
-			certs = server(:mixed).certs.collect &:fingerprint
29
-			expect(certs).to match_array %w(531ab9545f052818ff0559f648a147b104223834cc8f780516b3aacf1fdc8c06
30
-												a11802a4407aaeb93ccd0bd8c8a61be17eaba6b378433af5ad45ecbb1d633f71)
31
-		end
32
-	end
33
-
34
-	describe '#supported_methods' do
35
-		it 'must detect SSLv2' do
36
-			s       = server :sslv2
37
-			methods = s.supported_methods.collect &:to_sym
38
-			expect(methods).to match_array %i(SSLv2)
39
-		end
40
-
41
-		it 'must detect SSLv3' do
42
-			server  = server methods: %i(SSLv3)
43
-			methods = server.supported_methods.collect &:to_sym
44
-			expect(methods).to match_array %i(SSLv3)
45
-		end
46
-
47
-		it 'must detect TLSv1.0' do
48
-			server  = server methods: %i(TLSv1)
49
-			methods = server.supported_methods.collect &:to_sym
50
-			expect(methods).to match_array %i(TLSv1)
51
-		end
52
-
53
-		it 'must detect TLSv1.1' do
54
-			server  = server methods: %i(TLSv1_1)
55
-			methods = server.supported_methods.collect &:to_sym
56
-			expect(methods).to match_array %i(TLSv1_1)
57
-		end
58
-
59
-		it 'must detect TLSv1.2' do
60
-			server  = server methods: %i(TLSv1_2)
61
-			methods = server.supported_methods.collect &:to_sym
62
-			expect(methods).to match_array %i(TLSv1_2)
63
-		end
64
-
65
-		it 'must detect mixed methods' do
66
-			server  = server methods: %i(SSLv3 TLSv1 TLSv1_1 TLSv1_2)
67
-			methods = server.supported_methods.collect &:to_sym
68
-			expect(methods).to match_array %i(SSLv3 TLSv1 TLSv1_1 TLSv1_2)
69
-		end
70
-	end
71
-
72
-	describe '#supported_ciphers' do
73
-		it 'must detect supported cipher' do
74
-			ciphers = server.supported_ciphers
75
-							  .map { |k, v| [k.to_sym, v.keys.collect(&:name)] }
76
-							  .to_h[:TLSv1_2]
77
-			expect(ciphers).to match_array %w(ECDHE-ECDSA-AES128-SHA)
1
+module CryptCheck::Tls
2
+	describe Server do
3
+		before :all do
4
+			FakeTime.freeze Time.utc(2000, 1, 1)
78 5
 		end
79
-	end
80
-
81
-	describe '#supported_curves' do
82
-		it 'must detect no supported curves' do
83
-			s      = server :rsa, ciphers: %w(AES128-SHA)
84
-			curves = s.supported_curves.collect &:name
85
-			expect(curves).to be_empty
86
-		end
87
-
88
-		it 'must detect supported curves for RSA' do
89
-			s      = server :rsa, curves: %i(prime256v1 sect571r1)
90
-			curves = s.supported_curves.collect &:name
91
-			expect(curves).to contain_exactly :prime256v1, :sect571r1
92
-		end
93
-
94
-		it 'must detect supported curves from ECDSA' do
95
-			server = server server_preference: false
96
-			curves = server.supported_curves.collect &:name
97
-			expect(curves).to contain_exactly :prime256v1
98
-		end
99
-
100
-		it 'must detect supported curves from ECDSA and ECDHE' do
101
-			server = server curves: %i(prime256v1 sect571r1), server_preference: false
102
-			curves = server.supported_curves.collect &:name
103
-			expect(curves).to contain_exactly :prime256v1, :sect571r1
104
-		end
105
-
106
-		# No luck here :'(
107
-		it 'can\'t detect supported curves from ECDHE if server preference enforced' do
108
-			server = server curves: %i(prime256v1 sect571r1)
109
-			curves = server.supported_curves.collect &:name
110
-			expect(curves).to contain_exactly :prime256v1
111
-
112
-			server = server curves: %i(sect571r1 prime256v1)
113
-			curves = server.supported_curves.collect &:name
114
-			expect(curves).to contain_exactly :prime256v1, :sect571r1
115
-		end
116
-	end
117
-
118
-	describe '#curves_preference' do
119
-		it 'must report N/A if no curve on RSA' do
120
-			s      = server :rsa, ciphers: %w(AES128-GCM-SHA256)
121
-			curves = s.curves_preference
122
-			expect(curves).to be_nil
123 6
 
124
-			s      = server :rsa, ciphers: %w(AES128-GCM-SHA256), server_preference: false
125
-			curves = s.curves_preference
126
-			expect(curves).to be_nil
7
+		after :all do
8
+			FakeTime.unfreeze
127 9
 		end
128 10
 
129
-		it 'must report N/A if a single curve on RSA' do
130
-			curves = server(:rsa).curves_preference
131
-			expect(curves).to be_nil
132
-
133
-			curves = server(:rsa, server_preference: false).curves_preference
134
-			expect(curves).to be_nil
135
-		end
136
-
137
-		it 'must report server preference if server preference enforced on RSA' do
138
-			s      = server :rsa, curves: %i(prime256v1 sect571r1)
139
-			curves = s.curves_preference.collect &:name
140
-			expect(curves).to eq %i(prime256v1 sect571r1)
141
-
142
-			s      = server :rsa, curves: %i(sect571r1 prime256v1)
143
-			curves = s.curves_preference.collect &:name
144
-			expect(curves).to eq %i(sect571r1 prime256v1)
145
-		end
146
-
147
-		it 'must report client preference if server preference not enforced on RSA' do
148
-			s      = server :rsa, curves: %i(prime256v1 sect571r1), server_preference: false
149
-			curves = s.curves_preference
150
-			expect(curves).to be :client
151
-
152
-			s      = server :rsa, curves: %i(sect571r1 prime256v1), server_preference: false
153
-			curves = s.curves_preference
154
-			expect(curves).to be :client
11
+		def server(*args, **kargs)
12
+			do_in_serv *args, **kargs do |host, port|
13
+				TcpServer.new 'localhost', host, ::Socket::PF_INET, port
14
+			end
155 15
 		end
156 16
 
157
-		it 'must report N/A if a single curve on ECDSA' do
158
-			curves = server.curves_preference
159
-			expect(curves).to be_nil
17
+		describe '#certs' do
18
+			it 'must detect RSA certificate' do
19
+				certs = server(:rsa).certs.collect &:fingerprint
20
+				expect(certs).to match_array %w(a11802a4407aaeb93ccd0bd8c8a61be17eaba6b378433af5ad45ecbb1d633f71)
21
+			end
160 22
 
161
-			curves = server(server_preference: false).curves_preference
162
-			expect(curves).to be_nil
163
-		end
164
-
165
-		# No luck here :'(
166
-		it 'can\'t detect server preference if server preference enforced on ECDSA with preference on ECDSA curve' do
167
-			curves = server(curves: %i(prime256v1 sect571r1)).curves_preference
168
-			expect(curves).to be_nil
169
-		end
23
+			it 'must detect ECDSA certificate' do
24
+				certs = server.certs.collect &:fingerprint
25
+				expect(certs).to match_array %w(531ab9545f052818ff0559f648a147b104223834cc8f780516b3aacf1fdc8c06)
26
+			end
170 27
 
171
-		it 'must report server preference if server preference enforced on ECDSA with preference not on ECDSA curve' do
172
-			s      = server curves: %i(sect571r1 prime256v1)
173
-			curves = s.curves_preference.collect &:name
174
-			expect(curves).to eq %i(sect571r1 prime256v1)
175
-		end
176
-
177
-		it 'must report client preference if server preference not enforced on ECDSA' do
178
-			s      = server curves: %i(prime256v1 sect571r1), server_preference: false
179
-			curves = s.curves_preference
180
-			expect(curves).to be :client
181
-
182
-			s      = server curves: %i(sect571r1 prime256v1), server_preference: false
183
-			curves = s.curves_preference
184
-			expect(curves).to be :client
28
+			it 'must detect RSA and ECDSA certificates' do
29
+				certs = server(:mixed).certs.collect &:fingerprint
30
+				expect(certs).to match_array %w(531ab9545f052818ff0559f648a147b104223834cc8f780516b3aacf1fdc8c06
31
+												a11802a4407aaeb93ccd0bd8c8a61be17eaba6b378433af5ad45ecbb1d633f71)
32
+			end
33
+		end
34
+
35
+		describe '#supported_methods' do
36
+			it 'must detect SSLv2' do
37
+				s       = server :sslv2
38
+				methods = s.supported_methods.collect &:to_sym
39
+				expect(methods).to match_array %i(SSLv2)
40
+			end
41
+
42
+			it 'must detect SSLv3' do
43
+				server  = server methods: %i(SSLv3)
44
+				methods = server.supported_methods.collect &:to_sym
45
+				expect(methods).to match_array %i(SSLv3)
46
+			end
47
+
48
+			it 'must detect TLSv1.0' do
49
+				server  = server methods: %i(TLSv1)
50
+				methods = server.supported_methods.collect &:to_sym
51
+				expect(methods).to match_array %i(TLSv1)
52
+			end
53
+
54
+			it 'must detect TLSv1.1' do
55
+				server  = server methods: %i(TLSv1_1)
56
+				methods = server.supported_methods.collect &:to_sym
57
+				expect(methods).to match_array %i(TLSv1_1)
58
+			end
59
+
60
+			it 'must detect TLSv1.2' do
61
+				server  = server methods: %i(TLSv1_2)
62
+				methods = server.supported_methods.collect &:to_sym
63
+				expect(methods).to match_array %i(TLSv1_2)
64
+			end
65
+
66
+			it 'must detect mixed methods' do
67
+				server  = server methods: %i(SSLv3 TLSv1 TLSv1_1 TLSv1_2)
68
+				methods = server.supported_methods.collect &:to_sym
69
+				expect(methods).to match_array %i(SSLv3 TLSv1 TLSv1_1 TLSv1_2)
70
+			end
71
+		end
72
+
73
+		describe '#supported_ciphers' do
74
+			it 'must detect supported cipher' do
75
+				ciphers = server.supported_ciphers
76
+								  .map { |k, v| [k.to_sym, v.keys.collect(&:name)] }
77
+								  .to_h[:TLSv1_2]
78
+				expect(ciphers).to match_array %w(ECDHE-ECDSA-AES128-SHA)
79
+			end
80
+		end
81
+
82
+		describe '#supported_curves' do
83
+			it 'must detect no supported curves' do
84
+				s      = server :rsa, ciphers: %w(AES128-SHA)
85
+				curves = s.supported_curves.collect &:name
86
+				expect(curves).to be_empty
87
+			end
88
+
89
+			it 'must detect supported curves for RSA' do
90
+				s      = server :rsa, curves: %i(prime256v1 sect571r1)
91
+				curves = s.supported_curves.collect &:name
92
+				expect(curves).to contain_exactly :prime256v1, :sect571r1
93
+			end
94
+
95
+			it 'must detect supported curves from ECDSA' do
96
+				server = server server_preference: false
97
+				curves = server.supported_curves.collect &:name
98
+				expect(curves).to contain_exactly :prime256v1
99
+			end
100
+
101
+			it 'must detect supported curves from ECDSA and ECDHE' do
102
+				server = server curves: %i(prime256v1 sect571r1), server_preference: false
103
+				curves = server.supported_curves.collect &:name
104
+				expect(curves).to contain_exactly :prime256v1, :sect571r1
105
+			end
106
+
107
+			# No luck here :'(
108
+			it 'can\'t detect supported curves from ECDHE if server preference enforced' do
109
+				server = server curves: %i(prime256v1 sect571r1)
110
+				curves = server.supported_curves.collect &:name
111
+				expect(curves).to contain_exactly :prime256v1
112
+
113
+				server = server curves: %i(sect571r1 prime256v1)
114
+				curves = server.supported_curves.collect &:name
115
+				expect(curves).to contain_exactly :prime256v1, :sect571r1
116
+			end
117
+		end
118
+
119
+		describe '#curves_preference' do
120
+			it 'must report N/A if no curve on RSA' do
121
+				s      = server :rsa, ciphers: %w(AES128-GCM-SHA256)
122
+				curves = s.curves_preference
123
+				expect(curves).to be_nil
124
+
125
+				s      = server :rsa, ciphers: %w(AES128-GCM-SHA256), server_preference: false
126
+				curves = s.curves_preference
127
+				expect(curves).to be_nil
128
+			end
129
+
130
+			it 'must report N/A if a single curve on RSA' do
131
+				curves = server(:rsa).curves_preference
132
+				expect(curves).to be_nil
133
+
134
+				curves = server(:rsa, server_preference: false).curves_preference
135
+				expect(curves).to be_nil
136
+			end
137
+
138
+			it 'must report server preference if server preference enforced on RSA' do
139
+				s      = server :rsa, curves: %i(prime256v1 sect571r1)
140
+				curves = s.curves_preference.collect &:name
141
+				expect(curves).to eq %i(prime256v1 sect571r1)
142
+
143
+				s      = server :rsa, curves: %i(sect571r1 prime256v1)
144
+				curves = s.curves_preference.collect &:name
145
+				expect(curves).to eq %i(sect571r1 prime256v1)
146
+			end
147
+
148
+			it 'must report client preference if server preference not enforced on RSA' do
149
+				s      = server :rsa, curves: %i(prime256v1 sect571r1), server_preference: false
150
+				curves = s.curves_preference
151
+				expect(curves).to be :client
152
+
153
+				s      = server :rsa, curves: %i(sect571r1 prime256v1), server_preference: false
154
+				curves = s.curves_preference
155
+				expect(curves).to be :client
156
+			end
157
+
158
+			it 'must report N/A if a single curve on ECDSA' do
159
+				curves = server.curves_preference
160
+				expect(curves).to be_nil
161
+
162
+				curves = server(server_preference: false).curves_preference
163
+				expect(curves).to be_nil
164
+			end
165
+
166
+			# No luck here :'(
167
+			it 'can\'t detect server preference if server preference enforced on ECDSA with preference on ECDSA curve' do
168
+				curves = server(curves: %i(prime256v1 sect571r1)).curves_preference
169
+				expect(curves).to be_nil
170
+			end
171
+
172
+			it 'must report server preference if server preference enforced on ECDSA with preference not on ECDSA curve' do
173
+				s      = server curves: %i(sect571r1 prime256v1)
174
+				curves = s.curves_preference.collect &:name
175
+				expect(curves).to eq %i(sect571r1 prime256v1)
176
+			end
177
+
178
+			it 'must report client preference if server preference not enforced on ECDSA' do
179
+				s      = server curves: %i(prime256v1 sect571r1), server_preference: false
180
+				curves = s.curves_preference
181
+				expect(curves).to be :client
182
+
183
+				s      = server curves: %i(sect571r1 prime256v1), server_preference: false
184
+				curves = s.curves_preference
185
+				expect(curves).to be :client
186
+			end
185 187
 		end
186 188
 	end
187 189
 end

Loading…
Cancel
Save