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'
require 'yaml'

module CryptCheck
PARALLEL_ANALYSIS = 10


PARALLEL_ANALYSIS = 10

class NoTLSAvailableServer
attr_reader :server

def initialize(server)
@server = OpenStruct.new hostname: server
end
@@ -20,6 +19,7 @@ module CryptCheck
end

autoload :State, 'cryptcheck/state'
autoload :Grade, 'cryptcheck/grade'
autoload :Logger, 'cryptcheck/logger'
autoload :Tls, 'cryptcheck/tls'
module Tls
@@ -31,7 +31,6 @@ module CryptCheck
autoload :Server, 'cryptcheck/tls/server'
autoload :TcpServer, 'cryptcheck/tls/server'
autoload :UdpServer, 'cryptcheck/tls/server'
autoload :Grade, 'cryptcheck/tls/grade'
autoload :Host, 'cryptcheck/tls/host'

autoload :Https, 'cryptcheck/tls/https'
@@ -94,7 +93,7 @@ module CryptCheck
end
rescue => e
e = Tls::Server::TLSException.new "Too long analysis (max #{MAX_ANALYSIS_DURATION.humanize})" \
if e.message == 'execution expired'
if e.message == 'execution expired'
raise unless e.is_a? Tls::Server::TLSException
Logger.error e
[key, AnalysisFailure.new(e)]
@@ -107,7 +106,7 @@ module CryptCheck
addresses host
rescue ::SocketError => e
Logger::error e
key = [host, nil, port]
key = [host, nil, port]
error = AnalysisFailure.new "Unable to resolve #{host}"
return { key => error }
end

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

@@ -0,0 +1,61 @@
module CryptCheck
module Grade
def grade
@grade ||= calculate_grade
end

GRADES = %i(A+ A B+ B C+ C D E F G V T X)
GRADE_STATUS = {
:'A+' => :best,
A: :best,
:'B+' => :great,
B: :great,
:'C+' => :good,
C: :good,
D: nil,
E: :warning,
F: :error,
G: :critical,

V: :critical,
T: :critical,
X: :critical
}
STATUS_GRADES = {
critical: :G,
error: :F,
warning: :E,
default: :D,
good: :C,
great: :B,
best: :A
}

def grade_status
GRADE_STATUS.fetch self.grade, :unknown
end

private
def calculate_grade
return :V unless self.valid?
return :T unless self.trusted?

states = self.states
states = State.collect { |s| [s, State.state(states, s)] }.to_h

State::BADS.each do |s|
return STATUS_GRADES[s] if states[s]
end

grade = STATUS_GRADES[:default]
State::GOODS.each do |s|
state = states[s]
return grade if state == false
grade = STATUS_GRADES[s]
return grade if state == :some
grade = "#{grade}+".to_sym
end
grade
end
end
end

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

@@ -30,10 +30,41 @@ module CryptCheck
def self.good?(level)
GOODS.include? level
end

def self.bad?(level)
BADS.include? level
end

def self.good_or_bad(level)
if self.good?(level)
:good
else
:bad
end
end

def self.state(states, level)
state =states[level].values.uniq
case State.good_or_bad(level)
when :bad
if state.include? true
true
else
false
end
when :good
if state.include? false
if state.include? true
:some
else
false
end
else
:all
end
end
end

extend Enumerable

def self.each(&block)

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

@@ -1,52 +0,0 @@
module CryptCheck
module Tls
module Grade
def grade
@grade ||= calculate_grade
end

GRADES = %i(A+ A B+ B C+ C D E F G V T X)
GRADE_STATUS = {
V: :critical,
T: :critical,

G: :critical,
F: :error,
E: :warning,
D: nil,
C: :good,
:'C+' => :good,
B: :great,
:'B+' => :great,
A: :best,
:'A+' => :best
}
def grade_status
GRADE_STATUS.fetch self.grade, :unknown
end

private
def calculate_grade
return :V unless self.valid?
return :T unless self.trusted?

states = self.states

{ critical: :G, error: :F, warning: :E }.each do |type, grade|
return grade if states[type].any? { |s| s == true }
end

{good: %i(D C), great: %i(C B), best: %i(B A)}.each do |type, scores|
state = states[type]
return scores.first if state.all? { |s| s != false }
if state.any? { |s| s == false }
Logger.info { "Missing #{type} : #{states[type].select { |s| s == false }.collect &:key}" }
return scores.last
end
end

:'A+'
end
end
end
end

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

@@ -0,0 +1,149 @@
require 'awesome_print'

module CryptCheck
describe Grade do
describe '#grade' do
def obj(trust: true, valid: true, **states)
Class.new do
def initialize(trust, valid, states)
@trust, @valid, @states = trust, valid, states
end

include Grade

def trusted?
@trust
end

def valid?
@valid
end

def states
State.empty.merge @states
end
end.new trust, valid, states
end

it 'must return :V if not valid' do
obj = obj valid: false, critical: { foo: false, bar: nil },
error: { foo: false, bar: nil },
warning: { foo: false, bar: nil },
good: { foo: nil, bar: true },
great: { foo: nil, bar: true },
best: { foo: nil, bar: true }
expect(obj.grade).to eq :V
end

it 'must return :T if not trusted' do
obj = obj trust: false, critical: { foo: false, bar: nil },
error: { foo: false, bar: nil },
warning: { foo: false, bar: nil },
good: { foo: nil, bar: true },
great: { foo: nil, bar: true },
best: { foo: nil, bar: true }
expect(obj.grade).to eq :T
end

it 'must return :G if critical' do
obj = obj critical: { foo: false, bar: nil, baz: true },
error: { foo: false, bar: nil },
warning: { foo: false, bar: nil },
good: { foo: nil, bar: true },
great: { foo: nil, bar: true },
best: { foo: nil, bar: true }
expect(obj.grade).to eq :G
end

it 'must return :F if error' do
obj = obj critical: { foo: false, bar: nil },
error: { foo: false, bar: nil, baz: true },
warning: { foo: false, bar: nil },
good: { foo: nil, bar: true },
great: { foo: nil, bar: true },
best: { foo: nil, bar: true }
expect(obj.grade).to eq :F
end

it 'must return :E if warning' do
obj = obj critical: { foo: false, bar: nil },
error: { foo: false, bar: nil },
warning: { foo: false, bar: nil, baz: true },
good: { foo: nil, bar: true },
great: { foo: nil, bar: true },
best: { foo: nil, bar: true }
expect(obj.grade).to eq :E
end

it 'must return :D if nor good nor bad' do
obj = obj critical: { foo: false, bar: nil },
error: { foo: false, bar: nil },
warning: { foo: false, bar: nil },
good: { foo: false, bar: nil },
great: { foo: nil, bar: true },
best: { foo: nil, bar: true }
expect(obj.grade).to eq :D
end

it 'must return :C if some good' do
obj = obj critical: { foo: false, bar: nil },
error: { foo: false, bar: nil },
warning: { foo: false, bar: nil },
good: { foo: false, bar: nil, baz: true },
great: { foo: nil, bar: true },
best: { foo: nil, bar: true }
expect(obj.grade).to eq :C
end

it 'must return :C+ if all good' do
obj = obj critical: { foo: false, bar: nil },
error: { foo: false, bar: nil },
warning: { foo: false, bar: nil },
good: { foo: nil, bar: true },
great: { foo: false, bar: nil },
best: { foo: nil, bar: true }
expect(obj.grade).to eq :'C+'
end

it 'must return :B if some great' do
obj = obj critical: { foo: false, bar: nil },
error: { foo: false, bar: nil },
warning: { foo: false, bar: nil },
good: { foo: nil, bar: true },
great: { foo: false, bar: nil, baz: true },
best: { foo: true, bar: nil }
expect(obj.grade).to eq :B
end

it 'must return :B+ if all great' do
obj = obj critical: { foo: false, bar: nil },
error: { foo: false, bar: nil },
warning: { foo: false, bar: nil },
good: { foo: nil, bar: true },
great: { foo: nil, bar: true },
best: { foo: false, bar: nil }
expect(obj.grade).to eq :'B+'
end

it 'must return :A if some best' do
obj = obj critical: { foo: false, bar: nil },
error: { foo: false, bar: nil },
warning: { foo: false, bar: nil },
good: { foo: nil, bar: true },
great: { foo: nil, bar: true },
best: { foo: false, bar: nil, baz: true }
expect(obj.grade).to eq :A
end

it 'must return :A+ if all best' do
obj = obj critical: { foo: false, bar: nil },
error: { foo: false, bar: nil },
warning: { foo: false, bar: nil },
good: { foo: nil, bar: true },
great: { foo: nil, bar: true },
best: { foo: nil, bar: true }
expect(obj.grade).to eq :'A+'
end
end
end
end

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

@@ -0,0 +1,406 @@
require 'ostruct'

module CryptCheck
describe State do
describe '::status' do
it 'must handle empty list' do
expect(State.status []).to be_nil
end

it 'must answer correctly' do
{
[:critical, :critical] => :critical,
[:critical, :error] => :critical,
[:critical, :warning] => :critical,
[:critical, nil] => :critical,
[:critical, :good] => :critical,
[:critical, :great] => :critical,
[:critical, :best] => :critical,

[:error, :critical] => :critical,
[:error, :error] => :error,
[:error, :warning] => :error,
[:error, nil] => :error,
[:error, :good] => :error,
[:error, :great] => :error,
[:error, :best] => :error,

[:warning, :critical] => :critical,
[:warning, :error] => :error,
[:warning, :warning] => :warning,
[:warning, nil] => :warning,
[:warning, :good] => :warning,
[:warning, :great] => :warning,
[:warning, :best] => :warning,

[:good, :critical] => :critical,
[:good, :error] => :error,
[:good, :warning] => :warning,
[:good, nil] => :good,
[:good, :good] => :good,
[:good, :great] => :good,
[:good, :best] => :good,

[:great, :critical] => :critical,
[:great, :error] => :error,
[:great, :warning] => :warning,
[:great, nil] => :great,
[:great, :good] => :good,
[:great, :great] => :great,
[:great, :best] => :great,

[:best, :critical] => :critical,
[:best, :error] => :error,
[:best, :warning] => :warning,
[:best, nil] => :best,
[:best, :good] => :good,
[:best, :great] => :great,
[:best, :best] => :best
}.each do |levels, result|
got = State.status levels
expect(got).to be(result), "#{levels} : expected #{result.inspect}, got #{got.inspect}"
end
end

it 'must handle object list' do
critical = OpenStruct.new status: :critical
warning = OpenStruct.new status: :warning
expect(State.status [critical, warning]).to be :critical
end
end

describe '::problem' do
it 'must answer correctly' do
{
[:critical, :critical] => :critical,
[:critical, :error] => :critical,
[:critical, :warning] => :critical,
[:critical, nil] => :critical,
[:critical, :good] => :critical,
[:critical, :great] => :critical,
[:critical, :best] => :critical,

[:error, :critical] => :critical,
[:error, :error] => :error,
[:error, :warning] => :error,
[:error, nil] => :error,
[:error, :good] => :error,
[:error, :great] => :error,
[:error, :best] => :error,

[:warning, :critical] => :critical,
[:warning, :error] => :error,
[:warning, :warning] => :warning,
[:warning, nil] => :warning,
[:warning, :good] => :warning,
[:warning, :great] => :warning,
[:warning, :best] => :warning,

[:good, :critical] => :critical,
[:good, :error] => :error,
[:good, :warning] => :warning,
[:good, nil] => nil,
[:good, :good] => nil,
[:good, :great] => nil,
[:good, :best] => nil,

[:great, :critical] => :critical,
[:great, :error] => :error,
[:great, :warning] => :warning,
[:great, nil] => nil,
[:great, :good] => nil,
[:great, :great] => nil,
[:great, :best] => nil,

[:best, :critical] => :critical,
[:best, :error] => :error,
[:best, :warning] => :warning,
[:best, nil] => nil,
[:best, :good] => nil,
[:best, :great] => nil,
[:best, :best] => nil
}.each do |levels, result|
got = State.problem levels
expect(got).to be(result), "#{levels} : expected #{result.inspect}, got #{got.inspect}"
end
end

it 'must handle object list' do
critical = OpenStruct.new status: :critical
warning = OpenStruct.new status: :warning
expect(State.problem [critical, warning]).to be :critical
end
end

describe '#states' do
def match_states(obj, **expected)
expected = State.empty.merge expected
expect(obj.states).to eq expected
end


let(:empty) do
Class.new do
include State

def available_checks
[]
end
end.new
end
let(:childless) do
Class.new do
include State

def available_checks
[
[:foo, :critical, -> (_) { true }],
[:bar, :error, -> (_) { true }],
[:baz, :warning, -> (_) { false }]
]
end
end.new
end
let(:parent) do
child = Class.new do
include State

def available_checks
[[:bar, :error, -> (_) { true }]]
end
end.new
Class.new do
include State

def initialize(child)
@child = child
end

def available_checks
[[:foo, :critical, -> (_) { true }]]
end

def children
[@child]
end
end.new child
end
let(:duplicated) do
child = Class.new do
include State

def available_checks
[[:foo, :error, -> (_) { true }]]
end
end.new
Class.new do
include State

def initialize(child)
@child = child
end

def available_checks
[[:foo, :critical, -> (_) { true }]]
end

def children
[@child]
end
end.new(child)
end

it 'must return the level if single level specified' do
obj = Class.new do
include State

def available_checks
[[:foo, :critical, -> (_) { true }]]
end
end.new
match_states obj, critical: { foo: true }

obj = Class.new do
include State

def available_checks
[[:foo, :critical, -> (_) { false }]]
end
end.new
match_states obj, critical: { foo: false }

obj = Class.new do
include State

def available_checks
[[:foo, :critical, -> (_) { nil }]]
end
end.new
match_states obj, critical: { foo: nil }
end

it 'must return all levels if multiple levels specified' do
obj = Class.new do
include State

def available_checks
[[:foo, %i(critical error good great), -> (_) { :critical }]]
end
end.new
match_states obj, critical: { foo: true },
error: { foo: true },
good: { foo: false },
great: { foo: false }

obj = Class.new do
include State

def available_checks
[[:foo, %i(critical error good great), -> (_) { :error }]]
end
end.new
match_states obj, critical: { foo: false },
error: { foo: true },
good: { foo: false },
great: { foo: false }


obj = Class.new do
include State

def available_checks
[[:foo, %i(critical error good great), -> (_) { :great }]]
end
end.new
match_states obj, critical: { foo: false },
error: { foo: false },
good: { foo: true },
great: { foo: true }


obj = Class.new do
include State

def available_checks
[[:foo, %i(critical error good great), -> (_) { :good }]]
end
end.new
match_states obj, critical: { foo: false },
error: { foo: false },
good: { foo: true },
great: { foo: false }

obj = Class.new do
include State

def available_checks
[[:foo, %i(critical error good great), -> (_) { nil }]]
end
end.new
match_states obj, critical: { foo: nil },
error: { foo: nil },
good: { foo: nil },
great: { foo: nil }
end

it 'must return empty if no check nor child' do
match_states empty
end

it 'must return personal status if no child' do
match_states childless, critical: { foo: true }, error: { bar: true }, warning: { baz: false }
end

it 'must return personal and children statuses' do
match_states parent, critical: { foo: true }, error: { bar: true }
end

it 'must return remove duplicated status' do
match_states duplicated, critical: { foo: true }, error: { foo: true }
end
end

describe '#status' do
it 'must return nil if nothing special' do
empty = Class.new do
include State

def available_checks
[]
end
end.new
expect(empty.status).to be_nil
end

it 'must return the status if only one' do
empty = Class.new do
include State

def available_checks
[[:foo, :critical, -> (_) { true }]]
end
end.new
expect(empty.status).to be :critical
end

it 'must return the worst status if multiple' do
empty = Class.new do
include State

def available_checks
[[:foo, :critical, -> (_) { true }],
[:bar, :error, -> (_) { true }]]
end
end.new
expect(empty.status).to be :critical
end
end

describe '::state' do
it 'must return false on bad case with only nil' do
states = { critical: { foo: nil } }
expect(State.state states, :critical).to be_falsey
end

it 'must return false on bad case with all false or nil' do
states = { critical: { foo: false, bar: nil } }
expect(State.state states, :critical).to be_falsey
end

it 'must return true on bad case with a single true' do
states = { critical: { foo: false, bar: nil, baz: true } }
expect(State.state states, :critical).to be_truthy
end

it 'must return :all on good case with only nil' do
states = { best: { foo: nil } }
expect(State.state states, :best).to eq :all
end

it 'must return false on good case with only false' do
states = { best: { foo: false } }
expect(State.state states, :best).to be_falsey
end

it 'must return false on good case with only false or nil' do
states = { best: { foo: false, bar: nil } }
expect(State.state states, :best).to be_falsey
end

it 'must return :some on good case with a single true' do
states = { best: { foo: false, bar: nil, baz: true } }
expect(State.state states, :best).to eq :some
end

it 'must return :all on good case with only true' do
states = { best: { bar: true } }
expect(State.state states, :best).to eq :all
end

it 'must return :all on good case with only true or nil' do
states = { best: { foo: nil, bar: true } }
expect(State.state states, :best).to eq :all
end
end
end
end

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

@@ -1,357 +0,0 @@
require 'ostruct'

describe CryptCheck::State do
describe '::status' do
it 'must handle empty list' do
expect(CryptCheck::State.status []).to be_nil
end

it 'must answer correctly' do
{
[:critical, :critical] => :critical,
[:critical, :error] => :critical,
[:critical, :warning] => :critical,
[:critical, nil] => :critical,
[:critical, :good] => :critical,
[:critical, :great] => :critical,
[:critical, :best] => :critical,

[:error, :critical] => :critical,
[:error, :error] => :error,
[:error, :warning] => :error,
[:error, nil] => :error,
[:error, :good] => :error,
[:error, :great] => :error,
[:error, :best] => :error,

[:warning, :critical] => :critical,
[:warning, :error] => :error,
[:warning, :warning] => :warning,
[:warning, nil] => :warning,
[:warning, :good] => :warning,
[:warning, :great] => :warning,
[:warning, :best] => :warning,

[:good, :critical] => :critical,
[:good, :error] => :error,
[:good, :warning] => :warning,
[:good, nil] => :good,
[:good, :good] => :good,
[:good, :great] => :good,
[:good, :best] => :good,

[:great, :critical] => :critical,
[:great, :error] => :error,
[:great, :warning] => :warning,
[:great, nil] => :great,
[:great, :good] => :good,
[:great, :great] => :great,
[:great, :best] => :great,

[:best, :critical] => :critical,
[:best, :error] => :error,
[:best, :warning] => :warning,
[:best, nil] => :best,
[:best, :good] => :good,
[:best, :great] => :great,
[:best, :best] => :best
}.each do |levels, result|
got = CryptCheck::State.status levels
expect(got).to be(result), "#{levels} : expected #{result.inspect}, got #{got.inspect}"
end
end

it 'must handle object list' do
critical = OpenStruct.new status: :critical
warning = OpenStruct.new status: :warning
expect(CryptCheck::State.status [critical, warning]).to be :critical
end
end

describe '::problem' do
it 'must answer correctly' do
{
[:critical, :critical] => :critical,
[:critical, :error] => :critical,
[:critical, :warning] => :critical,
[:critical, nil] => :critical,
[:critical, :good] => :critical,
[:critical, :great] => :critical,
[:critical, :best] => :critical,

[:error, :critical] => :critical,
[:error, :error] => :error,
[:error, :warning] => :error,
[:error, nil] => :error,
[:error, :good] => :error,
[:error, :great] => :error,
[:error, :best] => :error,

[:warning, :critical] => :critical,
[:warning, :error] => :error,
[:warning, :warning] => :warning,
[:warning, nil] => :warning,
[:warning, :good] => :warning,
[:warning, :great] => :warning,
[:warning, :best] => :warning,

[:good, :critical] => :critical,
[:good, :error] => :error,
[:good, :warning] => :warning,
[:good, nil] => nil,
[:good, :good] => nil,
[:good, :great] => nil,
[:good, :best] => nil,

[:great, :critical] => :critical,
[:great, :error] => :error,
[:great, :warning] => :warning,
[:great, nil] => nil,
[:great, :good] => nil,
[:great, :great] => nil,
[:great, :best] => nil,

[:best, :critical] => :critical,
[:best, :error] => :error,
[:best, :warning] => :warning,
[:best, nil] => nil,
[:best, :good] => nil,
[:best, :great] => nil,
[:best, :best] => nil
}.each do |levels, result|
got = CryptCheck::State.problem levels
expect(got).to be(result), "#{levels} : expected #{result.inspect}, got #{got.inspect}"
end
end

it 'must handle object list' do
critical = OpenStruct.new status: :critical
warning = OpenStruct.new status: :warning
expect(CryptCheck::State.problem [critical, warning]).to be :critical
end
end

describe '#states' do
def match_states(obj, **expected)
expected = CryptCheck::State.empty.merge expected
expect(obj.states).to eq expected
end


let(:empty) do
Class.new do
include ::CryptCheck::State

def available_checks
[]
end
end.new
end
let(:childless) do
Class.new do
include ::CryptCheck::State

def available_checks
[
[:foo, :critical, -> (_) { true }],
[:bar, :error, -> (_) { true }],
[:baz, :warning, -> (_) { false }]
]
end
end.new
end
let(:parent) do
child = Class.new do
include ::CryptCheck::State

def available_checks
[[:bar, :error, -> (_) { true }]]
end
end.new
Class.new do
include ::CryptCheck::State

def initialize(child)
@child = child
end

def available_checks
[[:foo, :critical, -> (_) { true }]]
end

def children
[@child]
end
end.new child
end
let(:duplicated) do
child = Class.new do
include ::CryptCheck::State

def available_checks
[[:foo, :error, -> (_) { true }]]
end
end.new
Class.new do
include ::CryptCheck::State

def initialize(child)
@child = child
end

def available_checks
[[:foo, :critical, -> (_) { true }]]
end

def children
[@child]
end
end.new(child)
end

it 'must return the level if single level specified' do
obj = Class.new do
include ::CryptCheck::State

def available_checks
[[:foo, :critical, -> (_) { true }]]
end
end.new
match_states obj, critical: { foo: true }

obj = Class.new do
include ::CryptCheck::State

def available_checks
[[:foo, :critical, -> (_) { false }]]
end
end.new
match_states obj, critical: { foo: false }

obj = Class.new do
include ::CryptCheck::State

def available_checks
[[:foo, :critical, -> (_) { nil }]]
end
end.new
match_states obj, critical: { foo: nil }
end

it 'must return all levels if multiple levels specified' do
obj = Class.new do
include ::CryptCheck::State

def available_checks
[[:foo, %i(critical error good great), -> (_) { :critical }]]
end
end.new
match_states obj, critical: { foo: true },
error: { foo: true },
good: { foo: false },
great: { foo: false }

obj = Class.new do
include ::CryptCheck::State

def available_checks
[[:foo, %i(critical error good great), -> (_) { :error }]]
end
end.new
match_states obj, critical: { foo: false },
error: { foo: true },
good: { foo: false },
great: { foo: false }


obj = Class.new do
include ::CryptCheck::State

def available_checks
[[:foo, %i(critical error good great), -> (_) { :great }]]
end
end.new
match_states obj, critical: { foo: false },
error: { foo: false },
good: { foo: true },
great: { foo: true }


obj = Class.new do
include ::CryptCheck::State

def available_checks
[[:foo, %i(critical error good great), -> (_) { :good }]]
end
end.new
match_states obj, critical: { foo: false },
error: { foo: false },
good: { foo: true },
great: { foo: false }

obj = Class.new do
include ::CryptCheck::State

def available_checks
[[:foo, %i(critical error good great), -> (_) { nil }]]
end
end.new
match_states obj, critical: { foo: nil },
error: { foo: nil },
good: { foo: nil },
great: { foo: nil }
end

it 'must return empty if no check nor child' do
match_states empty
end

it 'must return personal status if no child' do
match_states childless, critical: { foo: true }, error: { bar: true }, warning: { baz: false }
end

it 'must return personal and children statuses' do
match_states parent, critical: { foo: true }, error: { bar: true }
end

it 'must return remove duplicated status' do
match_states duplicated, critical: { foo: true }, error: { foo: true }
end
end

describe '#status' do
it 'must return nil if nothing special' do
empty = Class.new do
include ::CryptCheck::State

def available_checks
[]
end
end.new
expect(empty.status).to be_nil
end

it 'must return the status if only one' do
empty = Class.new do
include ::CryptCheck::State

def available_checks
[[:foo, :critical, -> (_) { true }]]
end
end.new
expect(empty.status).to be :critical
end

it 'must return the worst status if multiple' do
empty = Class.new do
include ::CryptCheck::State

def available_checks
[[:foo, :critical, -> (_) { true }],
[:bar, :error, -> (_) { true }]]
end
end.new
expect(empty.status).to be :critical
end
end
end

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

@@ -1,90 +1,92 @@
describe CryptCheck::Tls::Cert do
describe '::trusted?' do
it 'must accept valid certificate' do
FakeTime.freeze Time.utc(2000, 1, 1) do
cert, *chain, ca = chain(%w(ecdsa-prime256v1 intermediate ca))
trust = ::CryptCheck::Tls::Cert.trusted? cert, chain, roots: ca
expect(trust).to eq :trusted
module CryptCheck::Tls
describe Cert do
describe '::trusted?' do
it 'must accept valid certificate' do
FakeTime.freeze Time.utc(2000, 1, 1) do
cert, *chain, ca = chain(%w(ecdsa-prime256v1 intermediate ca))
trust = Cert.trusted? cert, chain, roots: ca
expect(trust).to eq :trusted
end
end
end

it 'must reject self signed certificate' do
cert, ca = chain(%w(self-signed ca))
trust = ::CryptCheck::Tls::Cert.trusted? cert, [], roots: ca
expect(trust).to eq 'self signed certificate'
it 'must reject self signed certificate' do
cert, ca = chain(%w(self-signed ca))
trust = Cert.trusted? cert, [], roots: ca
expect(trust).to eq 'self signed certificate'

# Case for SSLv2
cert, ca = chain(%w(self-signed ca))
trust = ::CryptCheck::Tls::Cert.trusted? cert, nil, roots: ca
expect(trust).to eq 'self signed certificate'
end
# Case for SSLv2
cert, ca = chain(%w(self-signed ca))
trust = Cert.trusted? cert, nil, roots: ca
expect(trust).to eq 'self signed certificate'
end

it 'must reject unknown CA' do
cert, *chain = chain(%w(ecdsa-prime256v1 intermediate ca))
trust = ::CryptCheck::Tls::Cert.trusted? cert, chain, roots: []
expect(trust).to eq 'unable to get issuer certificate'
end
it 'must reject unknown CA' do
cert, *chain = chain(%w(ecdsa-prime256v1 intermediate ca))
trust = Cert.trusted? cert, chain, roots: []
expect(trust).to eq 'unable to get issuer certificate'
end

it 'must reject missing intermediate chain' do
cert, ca = chain(%w(ecdsa-prime256v1 ca))
chain = []
trust = ::CryptCheck::Tls::Cert.trusted? cert, chain, roots: ca
expect(trust).to eq 'unable to get local issuer certificate'
end
it 'must reject missing intermediate chain' do
cert, ca = chain(%w(ecdsa-prime256v1 ca))
chain = []
trust = Cert.trusted? cert, chain, roots: ca
expect(trust).to eq 'unable to get local issuer certificate'
end

it 'must reject expired certificate' do
FakeTime.freeze Time.utc(2002, 1, 1) do
cert, *chain, ca = chain(%w(ecdsa-prime256v1 intermediate ca))
trust = ::CryptCheck::Tls::Cert.trusted? cert, chain, roots: ca
expect(trust).to eq 'certificate has expired'
it 'must reject expired certificate' do
FakeTime.freeze Time.utc(2002, 1, 1) do
cert, *chain, ca = chain(%w(ecdsa-prime256v1 intermediate ca))
trust = Cert.trusted? cert, chain, roots: ca
expect(trust).to eq 'certificate has expired'
end
end
end

it 'must reject not yet valid certificate' do
FakeTime.freeze Time.utc(1999, 1, 1) do
cert, *chain, ca = chain(%w(ecdsa-prime256v1 intermediate ca))
trust = ::CryptCheck::Tls::Cert.trusted? cert, chain, roots: ca
expect(trust).to eq 'certificate is not yet valid'
it 'must reject not yet valid certificate' do
FakeTime.freeze Time.utc(1999, 1, 1) do
cert, *chain, ca = chain(%w(ecdsa-prime256v1 intermediate ca))
trust = Cert.trusted? cert, chain, roots: ca
expect(trust).to eq 'certificate is not yet valid'
end
end
end
end

describe '#md5?' do
it 'must detect md5 certificate' do
cert = ::CryptCheck::Tls::Cert.new cert(:md5)
expect(cert.md5?).to be true
describe '#md5?' do
it 'must detect md5 certificate' do
cert = Cert.new cert(:md5)
expect(cert.md5?).to be true

cert = ::CryptCheck::Tls::Cert.new cert(:sha1)
expect(cert.md5?).to be false
cert = Cert.new cert(:sha1)
expect(cert.md5?).to be false

cert = ::CryptCheck::Tls::Cert.new cert(:ecdsa, :prime256v1)
expect(cert.md5?).to be false
cert = Cert.new cert(:ecdsa, :prime256v1)
expect(cert.md5?).to be false
end
end
end

describe '#sha1?' do
it 'must detect sha1 certificate' do
cert = ::CryptCheck::Tls::Cert.new cert(:md5)
expect(cert.sha1?).to be false
describe '#sha1?' do
it 'must detect sha1 certificate' do
cert = Cert.new cert(:md5)
expect(cert.sha1?).to be false

cert = ::CryptCheck::Tls::Cert.new cert(:sha1)
expect(cert.sha1?).to be true
cert = Cert.new cert(:sha1)
expect(cert.sha1?).to be true

cert = ::CryptCheck::Tls::Cert.new cert(:ecdsa, :prime256v1)
expect(cert.sha1?).to be false
cert = Cert.new cert(:ecdsa, :prime256v1)
expect(cert.sha1?).to be false
end
end
end

describe '#sha2?' do
it 'must detect sha2 certificate' do
cert = ::CryptCheck::Tls::Cert.new cert(:md5)
expect(cert.sha2?).to be false
describe '#sha2?' do
it 'must detect sha2 certificate' do
cert = Cert.new cert(:md5)
expect(cert.sha2?).to be false

cert = ::CryptCheck::Tls::Cert.new cert(:sha1)
expect(cert.sha2?).to be false
cert = Cert.new cert(:sha1)
expect(cert.sha2?).to be false

cert = ::CryptCheck::Tls::Cert.new cert(:ecdsa, :prime256v1)
expect(cert.sha2?).to be true
cert = Cert.new cert(:ecdsa, :prime256v1)
expect(cert.sha2?).to be true
end
end
end
end

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

@@ -1,129 +1,130 @@
describe CryptCheck::Tls::Host do
def host(*args, **kargs)
do_in_serv *args, **kargs do |host, port|
CryptCheck::Tls::Host.new host, port
module CryptCheck::Tls
describe Host do
def host(*args, **kargs)
do_in_serv *args, **kargs do |host, port|
Host.new host, port
end
end
end

def servers(*args, **kargs)
host(*args, **kargs).servers
end

def error(*args, **kargs)
host(*args, **kargs).error
end

it 'return 1 grade with IPv4' do
servers = servers()
expect(servers.size).to be 1
expect_grade servers, Helpers::DEFAULT_HOST, Helpers::DEFAULT_IPv4, Helpers::DEFAULT_PORT, :ipv4
end

it 'return 1 grade with IPv6' do
addresses = [Helpers::DEFAULT_IPv6]
allow(Addrinfo).to receive(:getaddrinfo).with(Helpers::DEFAULT_HOST, nil, nil, :STREAM) do
addresses.collect { |a| Addrinfo.new Socket.sockaddr_in(nil, a) }
def servers(*args, **kargs)
host(*args, **kargs).servers
end

servers = servers(host: Helpers::DEFAULT_IPv6)
expect(servers.size).to be 1
expect_grade servers, Helpers::DEFAULT_HOST, Helpers::DEFAULT_IPv6, Helpers::DEFAULT_PORT, :ipv6
end

it 'return 2 grades with hostname (IPv4 & IPv6)' do
addresses = [Helpers::DEFAULT_IPv4, Helpers::DEFAULT_IPv6]
allow(Addrinfo).to receive(:getaddrinfo).with(Helpers::DEFAULT_HOST, nil, nil, :STREAM) do
addresses.collect { |a| Addrinfo.new Socket.sockaddr_in(nil, a) }
def error(*args, **kargs)
host(*args, **kargs).error
end

servers = servers(host: '::')
expect(servers.size).to be 2
expect_grade servers, Helpers::DEFAULT_HOST, Helpers::DEFAULT_IPv4, Helpers::DEFAULT_PORT, :ipv4
expect_grade servers, Helpers::DEFAULT_HOST, Helpers::DEFAULT_IPv6, Helpers::DEFAULT_PORT, :ipv6
end

it 'return error if DNS resolution problem' do
allow(Addrinfo).to receive(:getaddrinfo).with(Helpers::DEFAULT_HOST, nil, nil, :STREAM)
.and_raise SocketError, 'getaddrinfo: Name or service not known'
it 'return 1 grade with IPv4' do
servers = servers()
expect(servers.size).to be 1
expect_grade servers, Helpers::DEFAULT_HOST, Helpers::DEFAULT_IPv4, Helpers::DEFAULT_PORT, :ipv4
end

error = error()
expect_error error, ::SocketError, 'getaddrinfo: Name or service not known'
end
it 'return 1 grade with IPv6' do
addresses = [Helpers::DEFAULT_IPv6]
allow(Addrinfo).to receive(:getaddrinfo).with(Helpers::DEFAULT_HOST, nil, nil, :STREAM) do
addresses.collect { |a| Addrinfo.new Socket.sockaddr_in(nil, a) }
end

it 'return error if analysis too long' do
stub_const 'CryptCheck::Tls::Host::MAX_ANALYSIS_DURATION', 1
allow_any_instance_of(CryptCheck::Tls::Host).to receive(:server) { sleep 2 }
servers = servers(host: Helpers::DEFAULT_IPv6)
expect(servers.size).to be 1
expect_grade servers, Helpers::DEFAULT_HOST, Helpers::DEFAULT_IPv6, Helpers::DEFAULT_PORT, :ipv6
end

servers = servers()
expect_grade_error servers, Helpers::DEFAULT_HOST, Helpers::DEFAULT_IPv4, Helpers::DEFAULT_PORT,
'Too long analysis (max 1 second)'
end
it 'return 2 grades with hostname (IPv4 & IPv6)' do
addresses = [Helpers::DEFAULT_IPv4, Helpers::DEFAULT_IPv6]
allow(Addrinfo).to receive(:getaddrinfo).with(Helpers::DEFAULT_HOST, nil, nil, :STREAM) do
addresses.collect { |a| Addrinfo.new Socket.sockaddr_in(nil, a) }
end

it 'return error if unable to connect' do
addresses = [Helpers::DEFAULT_IPv4, Helpers::DEFAULT_IPv6]
allow(Addrinfo).to receive(:getaddrinfo).with(Helpers::DEFAULT_HOST, nil, nil, :STREAM) do
addresses.collect { |a| Addrinfo.new Socket.sockaddr_in(nil, a) }
servers = servers(host: '::')
expect(servers.size).to be 2
expect_grade servers, Helpers::DEFAULT_HOST, Helpers::DEFAULT_IPv4, Helpers::DEFAULT_PORT, :ipv4
expect_grade servers, Helpers::DEFAULT_HOST, Helpers::DEFAULT_IPv6, Helpers::DEFAULT_PORT, :ipv6
end

servers = servers(host: Helpers::DEFAULT_IPv6)
expect_grade_error servers, Helpers::DEFAULT_HOST, Helpers::DEFAULT_IPv4, Helpers::DEFAULT_PORT,
'Connection refused - connect(2) for 127.0.0.1:15000'
expect_grade servers, Helpers::DEFAULT_HOST, Helpers::DEFAULT_IPv6, Helpers::DEFAULT_PORT, :ipv6
end
it 'return error if DNS resolution problem' do
allow(Addrinfo).to receive(:getaddrinfo).with(Helpers::DEFAULT_HOST, nil, nil, :STREAM)
.and_raise SocketError, 'getaddrinfo: Name or service not known'

it 'return error if TCP timeout' do
stub_const 'CryptCheck::Tls::Engine::TCP_TIMEOUT', 1
addresses = [Helpers::DEFAULT_IPv4, Helpers::DEFAULT_IPv6]
allow(Addrinfo).to receive(:getaddrinfo).with(Helpers::DEFAULT_HOST, nil, nil, :STREAM) do
addresses.collect { |a| Addrinfo.new Socket.sockaddr_in(nil, a) }
error = error()
expect_error error, ::SocketError, 'getaddrinfo: Name or service not known'
end

original = IO.method :select
allow(IO).to receive(:select) do |*args, &block|
socket = [args[0]&.first, args[1]&.first].compact.first
next nil if socket.is_a?(Socket) && (socket.local_address.afamily == Socket::AF_INET)
original.call *args, &block
it 'return error if analysis too long' do
stub_const 'CryptCheck::Tls::Host::MAX_ANALYSIS_DURATION', 1
allow_any_instance_of(Host).to receive(:server) { sleep 2 }

servers = servers()
expect_grade_error servers, Helpers::DEFAULT_HOST, Helpers::DEFAULT_IPv4, Helpers::DEFAULT_PORT,
'Too long analysis (max 1 second)'
end

servers = servers(host: '::')
expect_grade_error servers, Helpers::DEFAULT_HOST, Helpers::DEFAULT_IPv4, Helpers::DEFAULT_PORT,
'Timeout when connecting to 127.0.0.1:15000 (max 1 second)'
expect_grade servers, Helpers::DEFAULT_HOST, Helpers::DEFAULT_IPv6, Helpers::DEFAULT_PORT, :ipv6
end
it 'return error if unable to connect' do
addresses = [Helpers::DEFAULT_IPv4, Helpers::DEFAULT_IPv6]
allow(Addrinfo).to receive(:getaddrinfo).with(Helpers::DEFAULT_HOST, nil, nil, :STREAM) do
addresses.collect { |a| Addrinfo.new Socket.sockaddr_in(nil, a) }
end

it 'return error if TLS timeout' do
stub_const 'CryptCheck::Tls::Engine::TLS_TIMEOUT', 1
addresses = [Helpers::DEFAULT_IPv4, Helpers::DEFAULT_IPv6]
allow(Addrinfo).to receive(:getaddrinfo).with(Helpers::DEFAULT_HOST, nil, nil, :STREAM) do
addresses.collect { |a| Addrinfo.new Socket.sockaddr_in(nil, a) }
servers = servers(host: Helpers::DEFAULT_IPv6)
expect_grade_error servers, Helpers::DEFAULT_HOST, Helpers::DEFAULT_IPv4, Helpers::DEFAULT_PORT,
'Connection refused - connect(2) for 127.0.0.1:15000'
expect_grade servers, Helpers::DEFAULT_HOST, Helpers::DEFAULT_IPv6, Helpers::DEFAULT_PORT, :ipv6
end

original = IO.method :select
allow(IO).to receive(:select) do |*args, &block|
socket = [args[0]&.first, args[1]&.first].compact.first
next nil if socket.is_a?(OpenSSL::SSL::SSLSocket) && (socket.io.local_address.afamily == Socket::AF_INET)
original.call *args, &block
it 'return error if TCP timeout' do
stub_const 'CryptCheck::Tls::Engine::TCP_TIMEOUT', 1
addresses = [Helpers::DEFAULT_IPv4, Helpers::DEFAULT_IPv6]
allow(Addrinfo).to receive(:getaddrinfo).with(Helpers::DEFAULT_HOST, nil, nil, :STREAM) do
addresses.collect { |a| Addrinfo.new Socket.sockaddr_in(nil, a) }
end

original = IO.method :select
allow(IO).to receive(:select) do |*args, &block|
socket = [args[0]&.first, args[1]&.first].compact.first
next nil if socket.is_a?(Socket) && (socket.local_address.afamily == Socket::AF_INET)
original.call *args, &block
end

servers = servers(host: '::')
expect_grade_error servers, Helpers::DEFAULT_HOST, Helpers::DEFAULT_IPv4, Helpers::DEFAULT_PORT,
'Timeout when connecting to 127.0.0.1:15000 (max 1 second)'
expect_grade servers, Helpers::DEFAULT_HOST, Helpers::DEFAULT_IPv6, Helpers::DEFAULT_PORT, :ipv6
end

servers = servers(host: '::')
expect_grade_error servers, Helpers::DEFAULT_HOST, Helpers::DEFAULT_IPv4, Helpers::DEFAULT_PORT,
'Timeout when TLS connecting to 127.0.0.1:15000 (max 1 second)'
expect_grade servers, Helpers::DEFAULT_HOST, Helpers::DEFAULT_IPv6, Helpers::DEFAULT_PORT, :ipv6
end

it 'return error if plain server' do
stub_const 'CryptCheck::Tls::ENGINE::TLS_TIMEOUT', 1
addresses = [Helpers::DEFAULT_IPv4, Helpers::DEFAULT_IPv6]
allow(Addrinfo).to receive(:getaddrinfo).with(Helpers::DEFAULT_HOST, nil, nil, :STREAM) do
addresses.collect { |a| Addrinfo.new Socket.sockaddr_in(nil, a) }
it 'return error if TLS timeout' do
stub_const 'CryptCheck::Tls::Engine::TLS_TIMEOUT', 1
addresses = [Helpers::DEFAULT_IPv4, Helpers::DEFAULT_IPv6]
allow(Addrinfo).to receive(:getaddrinfo).with(Helpers::DEFAULT_HOST, nil, nil, :STREAM) do
addresses.collect { |a| Addrinfo.new Socket.sockaddr_in(nil, a) }
end

original = IO.method :select
allow(IO).to receive(:select) do |*args, &block|
socket = [args[0]&.first, args[1]&.first].compact.first
next nil if socket.is_a?(OpenSSL::SSL::SSLSocket) && (socket.io.local_address.afamily == Socket::AF_INET)
original.call *args, &block
end

servers = servers(host: '::')
expect_grade_error servers, Helpers::DEFAULT_HOST, Helpers::DEFAULT_IPv4, Helpers::DEFAULT_PORT,
'Timeout when TLS connecting to 127.0.0.1:15000 (max 1 second)'
expect_grade servers, Helpers::DEFAULT_HOST, Helpers::DEFAULT_IPv6, Helpers::DEFAULT_PORT, :ipv6
end

servers = plain_serv Helpers::DEFAULT_IPv4 do
servers(host: Helpers::DEFAULT_IPv6)
it 'return error if plain server' do
stub_const 'ENGINE::TLS_TIMEOUT', 1
addresses = [Helpers::DEFAULT_IPv4, Helpers::DEFAULT_IPv6]
allow(Addrinfo).to receive(:getaddrinfo).with(Helpers::DEFAULT_HOST, nil, nil, :STREAM) do
addresses.collect { |a| Addrinfo.new Socket.sockaddr_in(nil, a) }
end

servers = plain_serv Helpers::DEFAULT_IPv4 do
servers(host: Helpers::DEFAULT_IPv6)
end
expect_grade_error servers, Helpers::DEFAULT_HOST, Helpers::DEFAULT_IPv4, Helpers::DEFAULT_PORT,
'TLS seems not supported on this server'
expect_grade servers, Helpers::DEFAULT_HOST, Helpers::DEFAULT_IPv6, Helpers::DEFAULT_PORT, :ipv6
end
expect_grade_error servers, Helpers::DEFAULT_HOST, Helpers::DEFAULT_IPv4, Helpers::DEFAULT_PORT,
'TLS seems not supported on this server'
expect_grade servers, Helpers::DEFAULT_HOST, Helpers::DEFAULT_IPv6, Helpers::DEFAULT_PORT, :ipv6
end

end

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

@@ -1,61 +1,63 @@
describe CryptCheck::Tls::Method do
describe '#==' do
it 'must be equals to corresponding symbol' do
method = ::CryptCheck::Tls::Method[:TLSv1_2]
expect(method == :SSLv2).to be false
expect(method == :SSLv3).to be false
expect(method == :TLSv1).to be false
expect(method == :TLSv1_1).to be false
expect(method == :TLSv1_2).to be true
end
module CryptCheck::Tls
describe Method do
describe '#==' do
it 'must be equals to corresponding symbol' do
method = Method[:TLSv1_2]
expect(method == :SSLv2).to be false
expect(method == :SSLv3).to be false
expect(method == :TLSv1).to be false
expect(method == :TLSv1_1).to be false
expect(method == :TLSv1_2).to be true
end

it 'must be equals to corresponding method' do
method = ::CryptCheck::Tls::Method[:TLSv1_2]
expect(method == ::CryptCheck::Tls::Method[:SSLv2]).to be false
expect(method == ::CryptCheck::Tls::Method[:SSLv3]).to be false
expect(method == ::CryptCheck::Tls::Method[:TLSv1]).to be false
expect(method == ::CryptCheck::Tls::Method[:TLSv1_1]).to be false
expect(method == ::CryptCheck::Tls::Method[:TLSv1_2]).to be true
it 'must be equals to corresponding method' do
method = Method[:TLSv1_2]
expect(method == Method[:SSLv2]).to be false
expect(method == Method[:SSLv3]).to be false
expect(method == Method[:TLSv1]).to be false
expect(method == Method[:TLSv1_1]).to be false
expect(method == Method[:TLSv1_2]).to be true
end
end
end

# describe '#eql?' do
# it 'must be equals to corresponding symbol' do
# method = ::CryptCheck::Tls::Method[:TLSv1_2]
# expect(method.eql? :SSLv2).to be false
# expect(method.eql? :SSLv3).to be false
# expect(method.eql? :TLSv1).to be false
# expect(method.eql? :TLSv1_1).to be false
# expect(method.eql? :TLSv1_2).to be true
# end
#
# it 'must be equals to corresponding method' do
# method = ::CryptCheck::Tls::Method[:TLSv1_2]
# expect(method.eql? ::CryptCheck::Tls::Method[:SSLv2]).to be false
# expect(method.eql? ::CryptCheck::Tls::Method[:SSLv3]).to be false
# expect(method.eql? ::CryptCheck::Tls::Method[:TLSv1]).to be false
# expect(method.eql? ::CryptCheck::Tls::Method[:TLSv1_1]).to be false
# expect(method.eql? ::CryptCheck::Tls::Method[:TLSv1_2]).to be true
# end
# end
#
# describe '#equal?' do
# it 'must be equals to corresponding symbol' do
# method = ::CryptCheck::Tls::Method[:TLSv1_2]
# expect(method.equal? :SSLv2).to be false
# expect(method.equal? :SSLv3).to be false
# expect(method.equal? :TLSv1).to be false
# expect(method.equal? :TLSv1_1).to be false
# expect(method.equal? :TLSv1_2).to be true
# end
#
# it 'must be equals to corresponding method' do
# method = ::CryptCheck::Tls::Method[:TLSv1_2]
# expect(method.equal? ::CryptCheck::Tls::Method[:SSLv2]).to be false
# expect(method.equal? ::CryptCheck::Tls::Method[:SSLv3]).to be false
# expect(method.equal? ::CryptCheck::Tls::Method[:TLSv1]).to be false
# expect(method.equal? ::CryptCheck::Tls::Method[:TLSv1_1]).to be false
# expect(method.equal? ::CryptCheck::Tls::Method[:TLSv1_2]).to be true
# end
# end
# describe '#eql?' do
# it 'must be equals to corresponding symbol' do
# method = Method[:TLSv1_2]
# expect(method.eql? :SSLv2).to be false
# expect(method.eql? :SSLv3).to be false
# expect(method.eql? :TLSv1).to be false
# expect(method.eql? :TLSv1_1).to be false
# expect(method.eql? :TLSv1_2).to be true
# end
#
# it 'must be equals to corresponding method' do
# method = Method[:TLSv1_2]
# expect(method.eql? Method[:SSLv2]).to be false
# expect(method.eql? Method[:SSLv3]).to be false
# expect(method.eql? Method[:TLSv1]).to be false
# expect(method.eql? Method[:TLSv1_1]).to be false
# expect(method.eql? Method[:TLSv1_2]).to be true
# end
# end
#
# describe '#equal?' do
# it 'must be equals to corresponding symbol' do
# method = Method[:TLSv1_2]
# expect(method.equal? :SSLv2).to be false
# expect(method.equal? :SSLv3).to be false
# expect(method.equal? :TLSv1).to be false
# expect(method.equal? :TLSv1_1).to be false
# expect(method.equal? :TLSv1_2).to be true
# end
#
# it 'must be equals to corresponding method' do
# method = Method[:TLSv1_2]
# expect(method.equal? Method[:SSLv2]).to be false
# expect(method.equal? Method[:SSLv3]).to be false
# expect(method.equal? Method[:TLSv1]).to be false
# expect(method.equal? Method[:TLSv1_1]).to be false
# expect(method.equal? Method[:TLSv1_2]).to be true
# end
# end
end
end

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

@@ -1,187 +1,189 @@
describe CryptCheck::Tls::Server do
before :all do
FakeTime.freeze Time.utc(2000, 1, 1)
end

after :all do
FakeTime.unfreeze
end

def server(*args, **kargs)
do_in_serv *args, **kargs do |host, port|
CryptCheck::Tls::TcpServer.new 'localhost', host, ::Socket::PF_INET, port
end
end

describe '#certs' do
it 'must detect RSA certificate' do
certs = server(:rsa).certs.collect &:fingerprint
expect(certs).to match_array %w(a11802a4407aaeb93ccd0bd8c8a61be17eaba6b378433af5ad45ecbb1d633f71)
end

it 'must detect ECDSA certificate' do
certs = server.certs.collect &:fingerprint
expect(certs).to match_array %w(531ab9545f052818ff0559f648a147b104223834cc8f780516b3aacf1fdc8c06)
end

it 'must detect RSA and ECDSA certificates' do
certs = server(:mixed).certs.collect &:fingerprint
expect(certs).to match_array %w(531ab9545f052818ff0559f648a147b104223834cc8f780516b3aacf1fdc8c06
a11802a4407aaeb93ccd0bd8c8a61be17eaba6b378433af5ad45ecbb1d633f71)
end
end

describe '#supported_methods' do
it 'must detect SSLv2' do
s = server :sslv2
methods = s.supported_methods.collect &:to_sym
expect(methods).to match_array %i(SSLv2)
end

it 'must detect SSLv3' do
server = server methods: %i(SSLv3)
methods = server.supported_methods.collect &:to_sym
expect(methods).to match_array %i(SSLv3)
end

it 'must detect TLSv1.0' do
server = server methods: %i(TLSv1)
methods = server.supported_methods.collect &:to_sym
expect(methods).to match_array %i(TLSv1)
end

it 'must detect TLSv1.1' do
server = server methods: %i(TLSv1_1)
methods = server.supported_methods.collect &:to_sym
expect(methods).to match_array %i(TLSv1_1)
end

it 'must detect TLSv1.2' do
server = server methods: %i(TLSv1_2)
methods = server.supported_methods.collect &:to_sym
expect(methods).to match_array %i(TLSv1_2)
end

it 'must detect mixed methods' do
server = server methods: %i(SSLv3 TLSv1 TLSv1_1 TLSv1_2)
methods = server.supported_methods.collect &:to_sym
expect(methods).to match_array %i(SSLv3 TLSv1 TLSv1_1 TLSv1_2)
end
end

describe '#supported_ciphers' do
it 'must detect supported cipher' do
ciphers = server.supported_ciphers
.map { |k, v| [k.to_sym, v.keys.collect(&:name)] }
.to_h[:TLSv1_2]
expect(ciphers).to match_array %w(ECDHE-ECDSA-AES128-SHA)
module CryptCheck::Tls
describe Server do
before :all do
FakeTime.freeze Time.utc(2000, 1, 1)
end
end

describe '#supported_curves' do
it 'must detect no supported curves' do
s = server :rsa, ciphers: %w(AES128-SHA)
curves = s.supported_curves.collect &:name
expect(curves).to be_empty
end

it 'must detect supported curves for RSA' do
s = server :rsa, curves: %i(prime256v1 sect571r1)
curves = s.supported_curves.collect &:name
expect(curves).to contain_exactly :prime256v1, :sect571r1
end

it 'must detect supported curves from ECDSA' do
server = server server_preference: false
curves = server.supported_curves.collect &:name
expect(curves).to contain_exactly :prime256v1
end

it 'must detect supported curves from ECDSA and ECDHE' do
server = server curves: %i(prime256v1 sect571r1), server_preference: false
curves = server.supported_curves.collect &:name
expect(curves).to contain_exactly :prime256v1, :sect571r1
end

# No luck here :'(
it 'can\'t detect supported curves from ECDHE if server preference enforced' do
server = server curves: %i(prime256v1 sect571r1)
curves = server.supported_curves.collect &:name
expect(curves).to contain_exactly :prime256v1

server = server curves: %i(sect571r1 prime256v1)
curves = server.supported_curves.collect &:name
expect(curves).to contain_exactly :prime256v1, :sect571r1
end
end

describe '#curves_preference' do
it 'must report N/A if no curve on RSA' do
s = server :rsa, ciphers: %w(AES128-GCM-SHA256)
curves = s.curves_preference
expect(curves).to be_nil

s = server :rsa, ciphers: %w(AES128-GCM-SHA256), server_preference: false
curves = s.curves_preference
expect(curves).to be_nil
after :all do
FakeTime.unfreeze
end

it 'must report N/A if a single curve on RSA' do
curves = server(:rsa).curves_preference
expect(curves).to be_nil

curves = server(:rsa, server_preference: false).curves_preference
expect(curves).to be_nil
end

it 'must report server preference if server preference enforced on RSA' do
s = server :rsa, curves: %i(prime256v1 sect571r1)
curves = s.curves_preference.collect &:name
expect(curves).to eq %i(prime256v1 sect571r1)

s = server :rsa, curves: %i(sect571r1 prime256v1)
curves = s.curves_preference.collect &:name
expect(curves).to eq %i(sect571r1 prime256v1)
end

it 'must report client preference if server preference not enforced on RSA' do
s = server :rsa, curves: %i(prime256v1 sect571r1), server_preference: false
curves = s.curves_preference
expect(curves).to be :client

s = server :rsa, curves: %i(sect571r1 prime256v1), server_preference: false
curves = s.curves_preference
expect(curves).to be :client
def server(*args, **kargs)
do_in_serv *args, **kargs do |host, port|
TcpServer.new 'localhost', host, ::Socket::PF_INET, port
end
end

it 'must report N/A if a single curve on ECDSA' do
curves = server.curves_preference
expect(curves).to be_nil
describe '#certs' do
it 'must detect RSA certificate' do
certs = server(:rsa).certs.collect &:fingerprint
expect(certs).to match_array %w(a11802a4407aaeb93ccd0bd8c8a61be17eaba6b378433af5ad45ecbb1d633f71)
end

curves = server(server_preference: false).curves_preference
expect(curves).to be_nil
end

# No luck here :'(
it 'can\'t detect server preference if server preference enforced on ECDSA with preference on ECDSA curve' do
curves = server(curves: %i(prime256v1 sect571r1)).curves_preference
expect(curves).to be_nil
end
it 'must detect ECDSA certificate' do
certs = server.certs.collect &:fingerprint
expect(certs).to match_array %w(531ab9545f052818ff0559f648a147b104223834cc8f780516b3aacf1fdc8c06)
end

it 'must report server preference if server preference enforced on ECDSA with preference not on ECDSA curve' do
s = server curves: %i(sect571r1 prime256v1)
curves = s.curves_preference.collect &:name
expect(curves).to eq %i(sect571r1 prime256v1)
end

it 'must report client preference if server preference not enforced on ECDSA' do
s = server curves: %i(prime256v1 sect571r1), server_preference: false
curves = s.curves_preference
expect(curves).to be :client

s = server curves: %i(sect571r1 prime256v1), server_preference: false
curves = s.curves_preference
expect(curves).to be :client
it 'must detect RSA and ECDSA certificates' do
certs = server(:mixed).certs.collect &:fingerprint
expect(certs).to match_array %w(531ab9545f052818ff0559f648a147b104223834cc8f780516b3aacf1fdc8c06
a11802a4407aaeb93ccd0bd8c8a61be17eaba6b378433af5ad45ecbb1d633f71)
end
end

describe '#supported_methods' do
it 'must detect SSLv2' do
s = server :sslv2
methods = s.supported_methods.collect &:to_sym
expect(methods).to match_array %i(SSLv2)
end

it 'must detect SSLv3' do
server = server methods: %i(SSLv3)
methods = server.supported_methods.collect &:to_sym
expect(methods).to match_array %i(SSLv3)
end

it 'must detect TLSv1.0' do
server = server methods: %i(TLSv1)
methods = server.supported_methods.collect &:to_sym
expect(methods).to match_array %i(TLSv1)
end

it 'must detect TLSv1.1' do
server = server methods: %i(TLSv1_1)
methods = server.supported_methods.collect &:to_sym
expect(methods).to match_array %i(TLSv1_1)
end

it 'must detect TLSv1.2' do
server = server methods: %i(TLSv1_2)
methods = server.supported_methods.collect &:to_sym
expect(methods).to match_array %i(TLSv1_2)
end

it 'must detect mixed methods' do
server = server methods: %i(SSLv3 TLSv1 TLSv1_1 TLSv1_2)
methods = server.supported_methods.collect &:to_sym
expect(methods).to match_array %i(SSLv3 TLSv1 TLSv1_1 TLSv1_2)
end
end

describe '#supported_ciphers' do
it 'must detect supported cipher' do
ciphers = server.supported_ciphers
.map { |k, v| [k.to_sym, v.keys.collect(&:name)] }
.to_h[:TLSv1_2]
expect(ciphers).to match_array %w(ECDHE-ECDSA-AES128-SHA)
end
end

describe '#supported_curves' do
it 'must detect no supported curves' do
s = server :rsa, ciphers: %w(AES128-SHA)
curves = s.supported_curves.collect &:name
expect(curves).to be_empty
end

it 'must detect supported curves for RSA' do
s = server :rsa, curves: %i(prime256v1 sect571r1)
curves = s.supported_curves.collect &:name
expect(curves).to contain_exactly :prime256v1, :sect571r1
end

it 'must detect supported curves from ECDSA' do
server = server server_preference: false
curves = server.supported_curves.collect &:name
expect(curves).to contain_exactly :prime256v1
end

it 'must detect supported curves from ECDSA and ECDHE' do
server = server curves: %i(prime256v1 sect571r1), server_preference: false
curves = server.supported_curves.collect &:name
expect(curves).to contain_exactly :prime256v1, :sect571r1
end

# No luck here :'(
it 'can\'t detect supported curves from ECDHE if server preference enforced' do
server = server curves: %i(prime256v1 sect571r1)
curves = server.supported_curves.collect &:name
expect(curves).to contain_exactly :prime256v1

server = server curves: %i(sect571r1 prime256v1)
curves = server.supported_curves.collect &:name
expect(curves).to contain_exactly :prime256v1, :sect571r1
end
end

describe '#curves_preference' do
it 'must report N/A if no curve on RSA' do
s = server :rsa, ciphers: %w(AES128-GCM-SHA256)
curves = s.curves_preference
expect(curves).to be_nil

s = server :rsa, ciphers: %w(AES128-GCM-SHA256), server_preference: false
curves = s.curves_preference
expect(curves).to be_nil
end

it 'must report N/A if a single curve on RSA' do
curves = server(:rsa).curves_preference
expect(curves).to be_nil

curves = server(:rsa, server_preference: false).curves_preference
expect(curves).to be_nil
end

it 'must report server preference if server preference enforced on RSA' do
s = server :rsa, curves: %i(prime256v1 sect571r1)
curves = s.curves_preference.collect &:name
expect(curves).to eq %i(prime256v1 sect571r1)

s = server :rsa, curves: %i(sect571r1 prime256v1)
curves = s.curves_preference.collect &:name
expect(curves).to eq %i(sect571r1 prime256v1)
end

it 'must report client preference if server preference not enforced on RSA' do
s = server :rsa, curves: %i(prime256v1 sect571r1), server_preference: false
curves = s.curves_preference
expect(curves).to be :client

s = server :rsa, curves: %i(sect571r1 prime256v1), server_preference: false
curves = s.curves_preference
expect(curves).to be :client
end

it 'must report N/A if a single curve on ECDSA' do
curves = server.curves_preference
expect(curves).to be_nil

curves = server(server_preference: false).curves_preference
expect(curves).to be_nil
end

# No luck here :'(
it 'can\'t detect server preference if server preference enforced on ECDSA with preference on ECDSA curve' do
curves = server(curves: %i(prime256v1 sect571r1)).curves_preference
expect(curves).to be_nil
end

it 'must report server preference if server preference enforced on ECDSA with preference not on ECDSA curve' do
s = server curves: %i(sect571r1 prime256v1)
curves = s.curves_preference.collect &:name
expect(curves).to eq %i(sect571r1 prime256v1)
end

it 'must report client preference if server preference not enforced on ECDSA' do
s = server curves: %i(prime256v1 sect571r1), server_preference: false
curves = s.curves_preference
expect(curves).to be :client

s = server curves: %i(sect571r1 prime256v1), server_preference: false
curves = s.curves_preference
expect(curves).to be :client
end
end
end
end

Loading…
Cancel
Save