forked from aeris/cryptcheck-rails
27 changed files with 393 additions and 115 deletions
@ -0,0 +1,3 @@ |
|||
--require spec_helper |
|||
--format progress |
|||
--format html --out tmp/rspec.html |
@ -1,36 +1,36 @@ |
|||
class Analysis < ApplicationRecord |
|||
enum service: %i[https smtp xmpp tls ssh].collect { |e| [e, e.to_s] }.to_h |
|||
validates :service, presence: true |
|||
validates :host, presence: true |
|||
enum service: %i[https smtp xmpp tls ssh].collect { |e| [e, e.to_s] }.to_h |
|||
validates :service, presence: true |
|||
validates :host, presence: true |
|||
|
|||
def self.[](service, host, args) |
|||
key = self.key service, host, args |
|||
self.find_by key |
|||
end |
|||
def self.[](service, host, args) |
|||
key = self.key service, host, args |
|||
self.find_by key |
|||
end |
|||
|
|||
def self.pending!(service, host, args) |
|||
key = self.key service, host, args |
|||
analysis = self.find_or_create_by! key |
|||
analysis.pending! |
|||
end |
|||
def self.pending!(service, host, args) |
|||
key = self.key service, host, args |
|||
analysis = self.find_or_create_by! key |
|||
analysis.pending! |
|||
end |
|||
|
|||
def pending! |
|||
self.update! pending: true |
|||
self |
|||
end |
|||
def pending! |
|||
self.update! pending: true |
|||
self |
|||
end |
|||
|
|||
def self.post!(service, host, args, result) |
|||
analysis = self[service, host, args] |
|||
analysis.post! result |
|||
end |
|||
def self.post!(service, host, args, result) |
|||
analysis = self[service, host, args] |
|||
analysis.post! result |
|||
end |
|||
|
|||
def post!(result) |
|||
self.update! pending: false, result: result |
|||
end |
|||
def post!(result) |
|||
self.update! pending: false, result: result |
|||
end |
|||
|
|||
private |
|||
private |
|||
|
|||
def self.key(service, host, args) |
|||
{ service: service, host: host, args: args } |
|||
end |
|||
def self.key(service, host, args) |
|||
{ service: service, host: host, args: args } |
|||
end |
|||
end |
|||
|
@ -1,2 +1,10 @@ |
|||
class Stat < ApplicationRecord |
|||
def self.create!(name, data, date = Date.today) |
|||
self.delete_by name: name, date: date |
|||
super name: name, date: date, data: data |
|||
end |
|||
|
|||
def self.[](name) |
|||
self.where(name: name).order(date: :desc).limit(1).first |
|||
end |
|||
end |
|||
|
@ -0,0 +1,29 @@ |
|||
#!/usr/bin/env ruby |
|||
# frozen_string_literal: true |
|||
|
|||
# |
|||
# This file was generated by Bundler. |
|||
# |
|||
# The application 'rspec' is installed as part of a gem, and |
|||
# this file is here to facilitate running it. |
|||
# |
|||
|
|||
require "pathname" |
|||
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", |
|||
Pathname.new(__FILE__).realpath) |
|||
|
|||
bundle_binstub = File.expand_path("../bundle", __FILE__) |
|||
|
|||
if File.file?(bundle_binstub) |
|||
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/ |
|||
load(bundle_binstub) |
|||
else |
|||
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run. |
|||
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.") |
|||
end |
|||
end |
|||
|
|||
require "rubygems" |
|||
require "bundler/setup" |
|||
|
|||
load Gem.bin_path("rspec-core", "rspec") |
@ -1,37 +1,97 @@ |
|||
#!./bin/rails runner |
|||
TODAY = Date.today |
|||
# general stat |
|||
stat_name = "request_per_service" |
|||
services = Analysis.group(:service).order(service: :asc).count |
|||
json = { |
|||
labels: services.keys, |
|||
dataset: services.values |
|||
} |
|||
Stat.delete_by name: stat_name, date: TODAY |
|||
Stat.create!( |
|||
name: stat_name, |
|||
date: TODAY, |
|||
dataset: json |
|||
) |
|||
|
|||
# Profit from open class to add stats methods only on this script |
|||
class Analysis |
|||
def grade |
|||
return if self.pending |
|||
grades = self.result.collect { _1['grade'] }.compact |
|||
CryptCheck::Grade.worst grades |
|||
end |
|||
|
|||
def tls |
|||
return if self.pending |
|||
return unless (result = self.result) |
|||
protocols = result.collect { |r| r.dig('handshakes', 'protocols') |
|||
&.collect { |p| p['protocol'].to_sym } } |
|||
.compact.flatten.uniq |
|||
|
|||
return :ssl if %i[SSLv2 SSLv3].any? { protocols.include? _1 } |
|||
return :tls unless protocols.include? :TLSv1_2 |
|||
return :tls1_2 unless protocols == %i[TLSv1_2] |
|||
return :tls1_2_only |
|||
end |
|||
|
|||
def ciphers |
|||
return if self.pending |
|||
return unless (result = self.result) |
|||
status = result.collect do |r| |
|||
r.dig('handshakes', 'ciphers')&.collect do |c| |
|||
s = CryptCheck::Tls::Cipher |
|||
.new(nil, c.fetch('name')).status |
|||
CryptCheck::State.good_or_bad s |
|||
end |
|||
end.compact.flatten.uniq |
|||
|
|||
return :bad unless status.include? :bad |
|||
:good |
|||
end |
|||
|
|||
def pfs |
|||
return if self.pending |
|||
return unless (result = self.result) |
|||
ciphers = result.collect do |r| |
|||
r.dig('handshakes', 'ciphers')&.collect do |c| |
|||
CryptCheck::Tls::Cipher |
|||
.new(nil, c.fetch('name')) |
|||
.pfs? |
|||
end |
|||
end.compact.flatten.uniq |
|||
|
|||
return :no_pfs unless ciphers.include? true |
|||
return :pfs unless ciphers == [true] |
|||
return :pfs_only |
|||
end |
|||
end |
|||
|
|||
services = Analysis.group(:service).count |
|||
Stat.create! :request_per_service, services |
|||
|
|||
# grade per service for https, smtp, tls and xmpp |
|||
%i[https smtp tls xmpp].each do |service_name| |
|||
stat_name = "grades_for_" + service_name.to_s |
|||
services = Analysis.where(service: service_name, pending: false) |
|||
%i[https smtp tls xmpp].each do |service| |
|||
services = Analysis.where service: service, pending: false |
|||
|
|||
grades = Hash.new 0 |
|||
tls = %i[tls1_2_only tls1_2 tls ssl].to_h { [_1, 0] } |
|||
ciphers = %i[good bad].to_h { [_1, 0] } |
|||
pfs = %i[pfs_only pfs no_pfs].to_h { [_1, 0] } |
|||
|
|||
grades = %w[A+ A B+ B C+ C D E F G].collect { |g| [g, 0] }.to_h |
|||
services.each do |service| |
|||
next unless (result = service.result) |
|||
result.each do |r| |
|||
next unless (grade = r['grade']) |
|||
grades[grade] += 1 if grades.has_key?(grade) |
|||
if (g = service.grade) |
|||
grades[g] += 1 |
|||
end |
|||
|
|||
if (t = service.tls) |
|||
tls[t] += 1 |
|||
end |
|||
|
|||
if (c = service.ciphers) |
|||
ciphers[c] += 1 |
|||
end |
|||
|
|||
if (p = service.pfs) |
|||
pfs[p] += 1 |
|||
end |
|||
end |
|||
|
|||
Stat.delete_by name: stat_name, date: TODAY |
|||
Stat.create!( |
|||
name: stat_name, |
|||
date: TODAY, |
|||
dataset: grades |
|||
) |
|||
end |
|||
ap "grades_for_#{service}" => grades |
|||
Stat.create! "grades_for_#{service}", grades |
|||
|
|||
ap "tls_for_#{service}" => tls |
|||
Stat.create! "tls_for_#{service}", tls |
|||
|
|||
ap "ciphers_for_#{service}" => ciphers |
|||
Stat.create! "ciphers_for_#{service}", ciphers |
|||
|
|||
ap "pfs_for_#{service}" => pfs |
|||
Stat.create! "pfs_for_#{service}", pfs |
|||
end |
|||
|
@ -0,0 +1,65 @@ |
|||
require 'rails_helper' |
|||
|
|||
describe Stat do |
|||
before do |
|||
Stat.delete_all |
|||
end |
|||
|
|||
describe '#create!' do |
|||
it 'must create stats if not existing' do |
|||
name = 'foo' |
|||
today = Date.today |
|||
data = { 'bar' => 'baz' } |
|||
expect { Stat.create! name, data } |
|||
.to change { Stat.count }.from(0).to(1) |
|||
stat = Stat.first |
|||
expect(stat.name).to eq name |
|||
expect(stat.date).to eq today |
|||
expect(stat.data).to eq data |
|||
end |
|||
|
|||
it 'must delete existing date if exist' do |
|||
name = 'foo' |
|||
today = Date.today |
|||
data = { 'bar' => 'baz' } |
|||
expect { Stat.create! name, data } |
|||
.to change { Stat.count }.from(0).to(1) |
|||
|
|||
data = { 'bar' => 'qux' } |
|||
expect { Stat.create! name, data } |
|||
.to_not change { Stat.count } |
|||
|
|||
stat = Stat.first |
|||
expect(stat.name).to eq name |
|||
expect(stat.date).to eq today |
|||
expect(stat.data).to eq data |
|||
end |
|||
end |
|||
|
|||
describe '#[]' do |
|||
it 'must retrieve the youngest data' do |
|||
name = 'foo' |
|||
|
|||
stat = Stat[name] |
|||
expect(stat).to be_nil |
|||
|
|||
data = { 'bar' => 'baz' } |
|||
date = Date.new 1970, 1, 1 |
|||
Stat.create! name, data, date |
|||
|
|||
stat = Stat[name] |
|||
expect(stat).to_not be_nil |
|||
expect(stat.date).to eq date |
|||
expect(stat.data).to eq data |
|||
|
|||
data = { 'bar' => 'qux' } |
|||
date = Date.new 1970, 1, 2 |
|||
Stat.create! name, data, date |
|||
|
|||
stat = Stat[name] |
|||
expect(stat).to_not be_nil |
|||
expect(stat.date).to eq date |
|||
expect(stat.data).to eq data |
|||
end |
|||
end |
|||
end |
@ -0,0 +1,64 @@ |
|||
# This file is copied to spec/ when you run 'rails generate rspec:install' |
|||
require 'spec_helper' |
|||
ENV['RAILS_ENV'] ||= 'test' |
|||
require_relative '../config/environment' |
|||
# Prevent database truncation if the environment is production |
|||
abort("The Rails environment is running in production mode!") if Rails.env.production? |
|||
require 'rspec/rails' |
|||
# Add additional requires below this line. Rails is not loaded until this point! |
|||
|
|||
# Requires supporting ruby files with custom matchers and macros, etc, in |
|||
# spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are |
|||
# run as spec files by default. This means that files in spec/support that end |
|||
# in _spec.rb will both be required and run as specs, causing the specs to be |
|||
# run twice. It is recommended that you do not name files matching this glob to |
|||
# end with _spec.rb. You can configure this pattern with the --pattern |
|||
# option on the command line or in ~/.rspec, .rspec or `.rspec-local`. |
|||
# |
|||
# The following line is provided for convenience purposes. It has the downside |
|||
# of increasing the boot-up time by auto-requiring all files in the support |
|||
# directory. Alternatively, in the individual `*_spec.rb` files, manually |
|||
# require only the support files necessary. |
|||
# |
|||
# Dir[Rails.root.join('spec', 'support', '**', '*.rb')].sort.each { |f| require f } |
|||
|
|||
# Checks for pending migrations and applies them before tests are run. |
|||
# If you are not using ActiveRecord, you can remove these lines. |
|||
begin |
|||
ActiveRecord::Migration.maintain_test_schema! |
|||
rescue ActiveRecord::PendingMigrationError => e |
|||
puts e.to_s.strip |
|||
exit 1 |
|||
end |
|||
RSpec.configure do |config| |
|||
# Remove this line if you're not using ActiveRecord or ActiveRecord fixtures |
|||
config.fixture_path = "#{::Rails.root}/spec/fixtures" |
|||
|
|||
# If you're not using ActiveRecord, or you'd prefer not to run each of your |
|||
# examples within a transaction, remove the following line or assign false |
|||
# instead of true. |
|||
config.use_transactional_fixtures = true |
|||
|
|||
# You can uncomment this line to turn off ActiveRecord support entirely. |
|||
# config.use_active_record = false |
|||
|
|||
# RSpec Rails can automatically mix in different behaviours to your tests |
|||
# based on their file location, for example enabling you to call `get` and |
|||
# `post` in specs under `spec/controllers`. |
|||
# |
|||
# You can disable this behaviour by removing the line below, and instead |
|||
# explicitly tag your specs with their type, e.g.: |
|||
# |
|||
# RSpec.describe UsersController, type: :controller do |
|||
# # ... |
|||
# end |
|||
# |
|||
# The different available types are documented in the features, such as in |
|||
# https://relishapp.com/rspec/rspec-rails/docs |
|||
config.infer_spec_type_from_file_location! |
|||
|
|||
# Filter lines from Rails gems in backtraces. |
|||
config.filter_rails_from_backtrace! |
|||
# arbitrary gems may also be filtered via: |
|||
# config.filter_gems_from_backtrace("gem name") |
|||
end |
@ -0,0 +1,94 @@ |
|||
# This file was generated by the `rails generate rspec:install` command. Conventionally, all |
|||
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`. |
|||
# The generated `.rspec` file contains `--require spec_helper` which will cause |
|||
# this file to always be loaded, without a need to explicitly require it in any |
|||
# files. |
|||
# |
|||
# Given that it is always loaded, you are encouraged to keep this file as |
|||
# light-weight as possible. Requiring heavyweight dependencies from this file |
|||
# will add to the boot time of your test suite on EVERY test run, even for an |
|||
# individual file that may not need all of that loaded. Instead, consider making |
|||
# a separate helper file that requires the additional dependencies and performs |
|||
# the additional setup, and require it from the spec files that actually need |
|||
# it. |
|||
# |
|||
# See https://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration |
|||
RSpec.configure do |config| |
|||
# rspec-expectations config goes here. You can use an alternate |
|||
# assertion/expectation library such as wrong or the stdlib/minitest |
|||
# assertions if you prefer. |
|||
config.expect_with :rspec do |expectations| |
|||
# This option will default to `true` in RSpec 4. It makes the `description` |
|||
# and `failure_message` of custom matchers include text for helper methods |
|||
# defined using `chain`, e.g.: |
|||
# be_bigger_than(2).and_smaller_than(4).description |
|||
# # => "be bigger than 2 and smaller than 4" |
|||
# ...rather than: |
|||
# # => "be bigger than 2" |
|||
expectations.include_chain_clauses_in_custom_matcher_descriptions = true |
|||
end |
|||
|
|||
# rspec-mocks config goes here. You can use an alternate test double |
|||
# library (such as bogus or mocha) by changing the `mock_with` option here. |
|||
config.mock_with :rspec do |mocks| |
|||
# Prevents you from mocking or stubbing a method that does not exist on |
|||
# a real object. This is generally recommended, and will default to |
|||
# `true` in RSpec 4. |
|||
mocks.verify_partial_doubles = true |
|||
end |
|||
|
|||
# This option will default to `:apply_to_host_groups` in RSpec 4 (and will |
|||
# have no way to turn it off -- the option exists only for backwards |
|||
# compatibility in RSpec 3). It causes shared context metadata to be |
|||
# inherited by the metadata hash of host groups and examples, rather than |
|||
# triggering implicit auto-inclusion in groups with matching metadata. |
|||
config.shared_context_metadata_behavior = :apply_to_host_groups |
|||
|
|||
# The settings below are suggested to provide a good initial experience |
|||
# with RSpec, but feel free to customize to your heart's content. |
|||
=begin |
|||
# This allows you to limit a spec run to individual examples or groups |
|||
# you care about by tagging them with `:focus` metadata. When nothing |
|||
# is tagged with `:focus`, all examples get run. RSpec also provides |
|||
# aliases for `it`, `describe`, and `context` that include `:focus` |
|||
# metadata: `fit`, `fdescribe` and `fcontext`, respectively. |
|||
config.filter_run_when_matching :focus |
|||
|
|||
# Allows RSpec to persist some state between runs in order to support |
|||
# the `--only-failures` and `--next-failure` CLI options. We recommend |
|||
# you configure your source control system to ignore this file. |
|||
config.example_status_persistence_file_path = "spec/examples.txt" |
|||
|
|||
# Limits the available syntax to the non-monkey patched syntax that is |
|||
# recommended. For more details, see: |
|||
# https://relishapp.com/rspec/rspec-core/docs/configuration/zero-monkey-patching-mode |
|||
config.disable_monkey_patching! |
|||
|
|||
# Many RSpec users commonly either run the entire suite or an individual |
|||
# file, and it's useful to allow more verbose output when running an |
|||
# individual spec file. |
|||
if config.files_to_run.one? |
|||
# Use the documentation formatter for detailed output, |
|||
# unless a formatter has already been configured |
|||
# (e.g. via a command-line flag). |
|||
config.default_formatter = "doc" |
|||
end |
|||
|
|||
# Print the 10 slowest examples and example groups at the |
|||
# end of the spec run, to help surface which specs are running |
|||
# particularly slow. |
|||
config.profile_examples = 10 |
|||
|
|||
# Run specs in random order to surface order dependencies. If you find an |
|||
# order dependency and want to debug it, you can fix the order by providing |
|||
# the seed, which is printed after each run. |
|||
# --seed 1234 |
|||
config.order = :random |
|||
|
|||
# Seed global randomization in this process using the `--seed` CLI option. |
|||
# Setting this allows you to use `--seed` to deterministically reproduce |
|||
# test failures related to randomization by passing the same `--seed` value |
|||
# as the one that triggered the failure. |
|||
Kernel.srand config.seed |
|||
=end |
|||
end |
@ -1,4 +0,0 @@ |
|||
require 'test_helper' |
|||
|
|||
class CheckControllerTest < ActionController::TestCase |
|||
end |
@ -1,4 +0,0 @@ |
|||
require 'test_helper' |
|||
|
|||
class HttpsControllerTest < ActionController::TestCase |
|||
end |
@ -1,4 +0,0 @@ |
|||
require 'test_helper' |
|||
|
|||
class SiteControllerTest < ActionController::TestCase |
|||
end |
@ -1,4 +0,0 @@ |
|||
require 'test_helper' |
|||
|
|||
class SmtpControllerTest < ActionController::TestCase |
|||
end |
@ -1,4 +0,0 @@ |
|||
require 'test_helper' |
|||
|
|||
class SshControllerTest < ActionController::TestCase |
|||
end |
@ -1,7 +0,0 @@ |
|||
require "test_helper" |
|||
|
|||
class StatisticsControllerTest < ActionDispatch::IntegrationTest |
|||
# test "the truth" do |
|||
# assert true |
|||
# end |
|||
end |
@ -1,19 +0,0 @@ |
|||
require 'test_helper' |
|||
|
|||
class TlsControllerTest < ActionController::TestCase |
|||
test "should get index" do |
|||
get :index |
|||
assert_response :success |
|||
end |
|||
|
|||
test "should get show" do |
|||
get :show |
|||
assert_response :success |
|||
end |
|||
|
|||
test "should get refresh" do |
|||
get :refresh |
|||
assert_response :success |
|||
end |
|||
|
|||
end |
@ -1,4 +0,0 @@ |
|||
require 'test_helper' |
|||
|
|||
class XmppControllerTest < ActionController::TestCase |
|||
end |
@ -1,3 +0,0 @@ |
|||
ENV['RAILS_ENV'] ||= 'test' |
|||
require File.expand_path('../../config/environment', __FILE__) |
|||
require 'rails/test_help' |
Loading…
Reference in new issue