@@ -30,8 +30,7 @@ module CryptCheck | |||
end | |||
end | |||
autoload :Status, 'cryptcheck/status' | |||
autoload :Statused, 'cryptcheck/statused' | |||
autoload :State, 'cryptcheck/state' | |||
autoload :Logger, 'cryptcheck/logger' | |||
autoload :Tls, 'cryptcheck/tls' | |||
module Tls | |||
@@ -96,7 +95,7 @@ module CryptCheck | |||
else | |||
server.new *a, **kargs | |||
end | |||
ap s.status | |||
ap s.states | |||
exit | |||
if grade | |||
g = grade.new s | |||
@@ -1,51 +0,0 @@ | |||
module CryptCheck | |||
class Status | |||
LEVELS = %i(best perfect good warning error critical).freeze | |||
PROBLEMS = %i(warning error critical).freeze | |||
extend Enumerable | |||
def self.each(&block) | |||
LEVELS.each &block | |||
end | |||
def self.empty | |||
self.collect { |s| [s, []] }.to_h | |||
end | |||
def self.status(statuses) | |||
statuses = self.convert statuses | |||
self.min LEVELS, statuses | |||
end | |||
class << self | |||
alias_method :'[]', :status | |||
end | |||
def self.problem(statuses) | |||
statuses = self.convert statuses | |||
self.min PROBLEMS, statuses | |||
end | |||
def self.sort(statuses) | |||
statuses.sort { |a, b| self.compare a, b } | |||
end | |||
def self.compare(a, b) | |||
LEVELS.find_index(a.status) <=> LEVELS.find_index(b.status) | |||
end | |||
private | |||
def self.convert(statuses) | |||
statuses = [statuses] unless statuses.respond_to? :first | |||
first = statuses.first | |||
statuses = statuses.collect &:status if first.respond_to? :status | |||
statuses | |||
end | |||
def self.min(levels, statuses) | |||
return nil if statuses.empty? | |||
(levels & statuses).last | |||
end | |||
end | |||
end |
@@ -1,48 +0,0 @@ | |||
module CryptCheck | |||
module Statused | |||
def status | |||
@status ||= calculate_status | |||
end | |||
private | |||
def merge(statuses) | |||
Status.collect do |s| | |||
status = statuses.collect { |ss| ss[s] } | |||
status = status.inject &:+ | |||
[s, status.uniq] | |||
end.to_h | |||
end | |||
def checks | |||
[] | |||
end | |||
def children | |||
[] | |||
end | |||
def perform_check(check) | |||
name, check, level = check | |||
result = check.call self | |||
return nil unless result | |||
level ||= result | |||
[level, name] | |||
end | |||
def personal_status | |||
states = Status.empty | |||
checks.each do |check| | |||
level, name = perform_check check | |||
next unless level | |||
states[level] << name | |||
end | |||
states | |||
end | |||
def calculate_status | |||
children_statuses = children.collect(&:status) | |||
statuses = [personal_status] + children_statuses | |||
merge statuses | |||
end | |||
end | |||
end |
@@ -107,7 +107,7 @@ module CryptCheck | |||
@cert.issuer | |||
end | |||
include ::CryptCheck::Statused | |||
include ::CryptCheck::State | |||
CHECKS = [:weak_sign, -> (s) do | |||
not (SIGNATURE_ALGORITHMS_X509[s.signature_algorithm] & WEAK_SIGN).empty? | |||
@@ -17,7 +17,7 @@ class ::OpenSSL::PKey::EC | |||
"ECC #{self.size} bits" | |||
end | |||
include ::CryptCheck::Statused | |||
include ::CryptCheck::State | |||
CHECKS = [ | |||
[:weak_key, -> (s) do | |||
@@ -50,7 +50,7 @@ class ::OpenSSL::PKey::RSA | |||
"RSA #{self.size} bits" | |||
end | |||
include ::CryptCheck::Statused | |||
include ::CryptCheck::State | |||
CHECKS = [ | |||
[:weak_key, -> (s) do | |||
@@ -81,7 +81,7 @@ class ::OpenSSL::PKey::DSA | |||
"DSA #{self.size} bits" | |||
end | |||
include ::CryptCheck::Statused | |||
include ::CryptCheck::State | |||
CHECKS = [ | |||
[:weak_key, -> (_) { :critical }] | |||
@@ -105,7 +105,7 @@ class ::OpenSSL::PKey::DH | |||
"DH #{self.size} bits" | |||
end | |||
include ::CryptCheck::Statused | |||
include ::CryptCheck::State | |||
CHECKS = [ | |||
[:weak_dh, -> (s) do | |||
@@ -69,7 +69,7 @@ module CryptCheck | |||
@cert.extensions.any? { |e| e.oid == '1.3.6.1.5.5.7.1.24' } | |||
end | |||
include Statused | |||
include State | |||
CHECKS = [ | |||
# Protocols | |||
@@ -1,9 +1,9 @@ | |||
require 'ostruct' | |||
describe CryptCheck::Status do | |||
describe CryptCheck::State do | |||
describe '::status' do | |||
it 'must handle empty list' do | |||
expect(CryptCheck::Status.status []).to be_nil | |||
expect(CryptCheck::State.status []).to be_nil | |||
end | |||
it 'must answer correctly' do | |||
@@ -56,7 +56,7 @@ describe CryptCheck::Status do | |||
[:best, :perfect] => :perfect, | |||
[:best, :best] => :best | |||
}.each do |levels, result| | |||
got = CryptCheck::Status.status levels | |||
got = CryptCheck::State.status levels | |||
expect(got).to be(result), "#{levels} : expected #{result.inspect}, got #{got.inspect}" | |||
end | |||
end | |||
@@ -64,7 +64,7 @@ describe CryptCheck::Status do | |||
it 'must handle object list' do | |||
critical = OpenStruct.new status: :critical | |||
warning = OpenStruct.new status: :warning | |||
expect(CryptCheck::Status.status [critical, warning]).to be :critical | |||
expect(CryptCheck::State.status [critical, warning]).to be :critical | |||
end | |||
end | |||
@@ -119,7 +119,7 @@ describe CryptCheck::Status do | |||
[:best, :perfect] => nil, | |||
[:best, :best] => nil | |||
}.each do |levels, result| | |||
got = CryptCheck::Status.problem levels | |||
got = CryptCheck::State.problem levels | |||
expect(got).to be(result), "#{levels} : expected #{result.inspect}, got #{got.inspect}" | |||
end | |||
end | |||
@@ -127,7 +127,137 @@ describe CryptCheck::Status do | |||
it 'must handle object list' do | |||
critical = OpenStruct.new status: :critical | |||
warning = OpenStruct.new status: :warning | |||
expect(CryptCheck::Status.problem [critical, warning]).to be :critical | |||
expect(CryptCheck::State.problem [critical, warning]).to be :critical | |||
end | |||
end | |||
describe '#states' do | |||
def match_states(actual, **expected) | |||
expected = ::CryptCheck::State.empty.merge expected | |||
expect(actual.states).to eq expected | |||
end | |||
let(:empty) do | |||
Class.new do | |||
include ::CryptCheck::State | |||
def checks | |||
[] | |||
end | |||
end.new | |||
end | |||
let(:childless) do | |||
Class.new do | |||
include ::CryptCheck::State | |||
def checks | |||
[ | |||
[:foo, -> (_) { true }, :critical], | |||
[:bar, -> (_) { :error }], | |||
[:baz, -> (_) { false }] | |||
] | |||
end | |||
end.new | |||
end | |||
let(:parent) do | |||
child = Class.new do | |||
include ::CryptCheck::State | |||
def checks | |||
[[:bar, -> (_) { :error }]] | |||
end | |||
end.new | |||
Class.new do | |||
include ::CryptCheck::State | |||
def initialize(child) | |||
@child = child | |||
end | |||
def checks | |||
[[:foo, -> (_) { :critical }]] | |||
end | |||
def children | |||
[@child] | |||
end | |||
end.new(child) | |||
end | |||
let(:duplicated) do | |||
child = Class.new do | |||
include ::CryptCheck::State | |||
def checks | |||
[[:foo, -> (_) { :critical }]] | |||
end | |||
end.new | |||
Class.new do | |||
include ::CryptCheck::State | |||
def initialize(child) | |||
@child = child | |||
end | |||
def checks | |||
[[:foo, -> (_) { :critical }]] | |||
end | |||
def children | |||
[@child] | |||
end | |||
end.new(child) | |||
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: %i(foo), error: %i(bar) | |||
end | |||
it 'must return personal and children statuses' do | |||
match_states parent, critical: %i(foo), error: %i(bar) | |||
end | |||
it 'must return remove duplicated status' do | |||
match_states duplicated, critical: %i(foo) | |||
end | |||
end | |||
describe '#status' do | |||
it 'must return nil if nothing special' do | |||
empty = Class.new do | |||
include ::CryptCheck::State | |||
def 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 checks | |||
[[:foo, -> (_) { :critical }]] | |||
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 checks | |||
[[:foo, -> (_) { :critical }], | |||
[:bar, -> (_) { :error }]] | |||
end | |||
end.new | |||
expect(empty.status).to be :critical | |||
end | |||
end | |||
end |
@@ -1,82 +0,0 @@ | |||
describe CryptCheck::Statused do | |||
def match_status(actual, **expected) | |||
expected = ::CryptCheck::Status.empty.merge expected | |||
expect(actual.status).to eq expected | |||
end | |||
describe '::status' do | |||
it 'must return empty if no check nor child' do | |||
statused = Class.new do | |||
include ::CryptCheck::Statused | |||
end.new | |||
match_status statused | |||
end | |||
it 'must return personal status if no child' do | |||
statused = Class.new do | |||
include ::CryptCheck::Statused | |||
def checks | |||
[ | |||
[:foo, -> (_) { true }, :critical], | |||
[:bar, -> (_) { :error }], | |||
[:baz, -> (_) { false }] | |||
] | |||
end | |||
end.new | |||
match_status statused, critical: %i(foo), error: %i(bar) | |||
end | |||
it 'must return personal and children statuses' do | |||
child = Class.new do | |||
include ::CryptCheck::Statused | |||
def checks | |||
[[:bar, -> (_) { :error }]] | |||
end | |||
end.new | |||
parent = Class.new do | |||
include ::CryptCheck::Statused | |||
def initialize(child) | |||
@child = child | |||
end | |||
def checks | |||
[[:foo, -> (_) { :critical }]] | |||
end | |||
def children | |||
[@child] | |||
end | |||
end.new(child) | |||
match_status parent, critical: %i(foo), error: %i(bar) | |||
end | |||
it 'must return remove duplicated status' do | |||
child = Class.new do | |||
include ::CryptCheck::Statused | |||
def checks | |||
[[:foo, -> (_) { :critical }]] | |||
end | |||
end.new | |||
parent = Class.new do | |||
include ::CryptCheck::Statused | |||
def initialize(child) | |||
@child = child | |||
end | |||
def checks | |||
[[:foo, -> (_) { :critical }]] | |||
end | |||
def children | |||
[@child] | |||
end | |||
end.new(child) | |||
match_status parent, critical: %i(foo) | |||
end | |||
end | |||
end |