Hall of (flame|shame)

stats
aeris 2022-06-26 15:34:24 +02:00
parent 792bdf6d24
commit 089a3b98be
12 changed files with 292 additions and 30 deletions

View File

@ -36,7 +36,6 @@ class SiteController < ApplicationController
end
def help
end
def about

View File

@ -0,0 +1,14 @@
class SitesController < ApplicationController
@@sites = YAML.load_file Rails.root.join 'config/sites.yml'
@@sites.keys.each do |name|
define_method(name) { sites name }
end
private
def sites(name)
@name = name
@sites = Stat[:"sites_#{name}"].data
render :sites
end
end

View File

@ -1,2 +0,0 @@
module ApplicationHelper
end

View File

@ -1,16 +1,14 @@
module CheckHelper
private def __label(value, color, state = true)
color = :default unless color
color = "state-#{color}" if state
"<span class=\"badge badge-#{color}\">#{value}</span>"
end
include ActionView::Helpers::TagHelper
def label(value, color, state = true)
__label(value, color, state).html_safe
color = :default unless color
color = "state-#{color}" if state
content_tag :span, value.to_s.html_safe, class: [:badge, :"badge-#{color}"]
end
def cell(value, color, state = true)
"<td class=\"badge-state-#{color}\">#{value}</td>".html_safe
content_tag :td, value, class: :"table-#{color}"
end
def labels(level, states, state = true)
@ -22,7 +20,7 @@ module CheckHelper
else
value ? :success : :danger
end
__label name, color, state
label name, color, state
end.join(' ').html_safe
end
@ -30,7 +28,7 @@ module CheckHelper
::CryptCheck::State.collect do |level|
states[level].each_pair
.select { |_, v| v == true }
.collect { |name, _| __label name, level }
.collect { |name, _| label name, level }
end.flatten(1).join(' ').html_safe
end
@ -54,6 +52,7 @@ module CheckHelper
end
def rank_label(rank)
rank = rank&.to_sym
l = %i(0 V T X).include? rank
label rank, rank_color(rank), !l
end

View File

@ -0,0 +1,48 @@
module SitesHelper
include CheckHelper
def domain_cell(domain, grade)
link = link_to domain, https_show_path(domain)
content_tag :th, (rank_label(grade) + ' ' + link).html_safe
end
def tls_cell(tls)
return unless tls
color = case tls.to_sym
when :tls1_2_only
:success
when :tls1_2
:error
else
:critical
end
content = content_tag :div, color, class: %i[sr-only]
content_tag :td, label(' ' + content, color), class: %i[text-center]
end
def ciphers_cell(ciphers)
return unless ciphers
color = case ciphers.to_sym
when :good
:success
else
:critical
end
content = content_tag :div, color, class: %i[sr-only]
content_tag :td, label(' ' + content, color), class: %i[text-center]
end
def pfs_cell(pfs)
return unless pfs
color = case pfs.to_sym
when :pfs_only
:success
when :pfs
:error
else
:critical
end
content = content_tag :div, color, class: %i[sr-only]
content_tag :td, label(' ' + content, color), class: %i[text-center]
end
end

View File

@ -91,27 +91,27 @@ $color-good: #beb052;
$color-best: #8db457;
$color-great: #5cb85c;
.badge-state-critical, td.badge-state-critical {
.badge-state-critical, td.table-critical {
background-color: $color-critical;
}
.badge-state-error, td.badge-state-error {
.badge-state-error, td.table-error {
background-color: $color-error;
}
.badge-state-warning, td.badge-state-warning {
.badge-state-warning {
background-color: $color-warning;
}
.badge-state-good, td.badge-state-good {
.badge-state-good, td.table-good {
background-color: $color-good;
}
.badge-state-best, td.badge-state-best {
.badge-state-best, td.table-best {
background-color: $color-best;
}
.badge-state-great, td.badge-state-great {
.badge-state-great, td.table-great {
background-color: $color-great;
}

View File

@ -0,0 +1,50 @@
<h1><%= @name.to_s.capitalize %></h1>
<h2>Columns meaning</h2>
<div class="container">
<div class="form-group row">
<div class="col-sm-1">TLS:</div>
<div class="col-sm-11">
<%= label(' ' + content_tag(:span, :success, class: %i[sr-only]), :success) %> TLS1.2 only supported
<%= label(' ' + content_tag(:span, :error, class: %i[sr-only]), :error) %> No TLS1.2 supported
<%= label(' ' + content_tag(:span, :critical, class: %i[sr-only]), :critical) %> SSLv2 or SSLv3 supported
</div>
</div>
<div class="row">
<div class="col-sm-1">Ciphers:</div>
<div class="col-sm-11">
<%= label(' ' + content_tag(:span, :success, class: %i[sr-only]), :success) %> Only safe cipher supported
<%= label(' ' + content_tag(:span, :critical, class: %i[sr-only]), :critical) %> Unsafe cipher supported
</div>
</div>
<div class="row">
<div class="col-sm-1">PFS:</div>
<div class="col-sm-11">
<%= label(' ' + content_tag(:span, :success, class: %i[sr-only]), :success) %> Only PFS cipher supported
<%= label(' ' + content_tag(:span, :error, class: %i[sr-only]), :error) %> PFS cipher but also no-PFS cipher supported
<%= label(' ' + content_tag(:span, :critical, class: %i[sr-only]), :critical) %> No PFS supported
</div>
</div>
<br/>
<table class="table table-sm">
<thead>
<tr>
<%= content_tag :th, t('.domain') %>
<%= content_tag :th, t('.tls'), class: %i[text-center] %>
<%= content_tag :th, t('.ciphers'), class: %i[text-center] %>
<%= content_tag :th, t('.pfs'), class: %i[text-center] %>
</tr>
</thead>
<tbody>
<% @sites.sort_by { _2.fetch('grade') || 'Z' }.each do |domain, stat| %>
<tr>
<%= domain_cell domain, stat.fetch('grade') %>
<%= tls_cell stat.fetch 'tls' %>
<%= ciphers_cell stat.fetch 'ciphers' %>
<%= pfs_cell stat.fetch 'pfs' %>
</tr>
<% end %>
</tbody>
</table>

View File

@ -30,7 +30,7 @@
<div class="row">
<div class="col-sm-12">
<h2>
<%= rank_label host[:grade].to_sym %>
<%= rank_label host[:grade] %>
<%= host[:ip] %> : <%= host[:port] %>
<span class="small">(<%= host[:hostname] %>)</span></h2>
</div>

77
bin/stats 100755
View File

@ -0,0 +1,77 @@
#!./bin/rails runner
# Profit from open class to add stats methods only on this script
class Analysis
def grade
grades = self.result.collect { _1['grade'] }.compact
CryptCheck::Grade.worst grades
end
def tls
return unless (result = self.result)
protocols = result.collect { |r| r.dig('handshakes', 'protocols')
&.collect { |p| p['protocol'].to_sym } }
.compact.flatten.uniq
return :ssl unless (protocols & %i[SSLv2 SSLv3]).empty?
return :tls unless protocols.include? :TLSv1_2
return :tls1_2_only if protocols == %i[TLSv1_2]
:tls1_2
end
def ciphers
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 if status.include? :bad
:good
end
def pfs
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_only unless ciphers.include? false
:pfs
end
end
sites = YAML.load_file Rails.root.join 'config/sites.yml'
workflows = []
sites.each do |type, domains|
domains.each do |domain|
puts "Refreshing #{domain}"
@analysis = Analysis.pending! :https, domain, 443
workflows << CheckWorkflow.start!(:https, @analysis.host, *@analysis.args)
end
end
workflows.each &:wait
sites.each do |type, domains|
domains = domains.collect do |domain|
analysis = Analysis[:https, domain, 443]
stats = {
grade: analysis.grade,
tls: analysis.tls,
ciphers: analysis.ciphers,
pfs: analysis.pfs
}
[domain, stats]
end.to_h
Stat.create! :"sites_#{type}", domains
end

View File

@ -2,7 +2,7 @@ Rails.application.routes.draw do
%i[https smtp xmpp tls ssh].each do |type|
namespace type, id: /[^\/]+/ do
get '/', action: :index
get ':id/', action: :show
get ':id/', action: :show, as: :show
get ':id/refresh', action: :refresh, as: :refresh
end
end
@ -18,6 +18,10 @@ Rails.application.routes.draw do
get 'sites' => 'site#sites'
%i[banks insurances gouv.fr].each do |name|
get name, controller: :sites, action: name
end
if Rails.env.development?
require 'sidekiq/web'
mount Sidekiq::Web => '/sidekiq'

67
config/sites.yml 100644
View File

@ -0,0 +1,67 @@
banks:
- admin.vybecard.com
- app.n26.com
- app.nickel.eu
- app.qonto.com
- clients.boursorama.com
- clients.cmavignon.com
- connexion-mabanque.bnpparibas
- ebanking-ch3.ubs.com
- epargnants.interepargne.natixis.fr
- espace-client.hellobank.fr
- espace-client.lanef.com
- espaceclient.axa.fr
- linxea-zen.avepargne.fr
- m.ing.fr
- mabanque.bnpparibas
- mabanque.fortuneo.fr
- mon.cmb.fr
- monespace.lcl.fr
- particuliers.societegenerale.fr
- secure.bforbank.com
- transatplan.banquetransatlantique.com
- voscomptesenligne.labanquepostale.fr
- www.altaprofits.com
- www.aviva.fr
- www.banque-rhone-alpes.fr
- www.banquepopulaire.fr
- www.bred.fr
- www.caisse-epargne.fr
- www.cic.fr
- www.credit-agricole.fr
- www.credit-cooperatif.coop
- www.creditmutuel.fr
- www.hsbc.fr
- www.ibps.sud.banquepopulaire.fr
- www.icgauth.banquebcp.fr
- www.labanquepostale.fr
- www.mgen.fr
- www.monabanq.com
- www.previ-direct.com
insurances:
- adherent.gie-afer.fr
- authentification.groupama.fr
- connect.axa.fr
- connect.maif.fr
- connect.sogarep.fr
- epargnant.amundi-ee.com
- espace-assure.gmf.fr
- espace-client.allianz.fr
- espace-client.mma.fr
- espace-personnel.direct-assurance.fr
- espaceperso.mutuelledesmotards.fr
- harmonie-et-moi.fr
- myswisslife.fr
- www.acommeassure.com
- www.assu2000.fr
- www.assurances-collectives.cm-cic.com
- www.aviva.fr
- www.creditmutuel-epargnesalariale.fr
- www.lolivier.fr
- www.maaf.fr
- www.mgen.fr
- www.monabanq.com
- www.monespace.generali.fr
- www.mutavie.fr
- www.sylvea.fr

View File

@ -2,29 +2,35 @@
# of editing this file, please use the migrations feature of Active Record to
# incrementally modify your database, and then regenerate this schema definition.
#
# Note that this schema.rb definition is the authoritative source for your
# database schema. If you need to create the application database on another
# system, you should be using db:schema:load, not running all the migrations
# from scratch. The latter is a flawed and unsustainable approach (the more migrations
# you'll amass, the slower it'll run and the greater likelihood for issues).
# This file is the source Rails uses to define your schema when running `bin/rails
# db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to
# be faster and is potentially less error prone than running all of your
# migrations from scratch. Old migrations may fail to apply correctly if those
# migrations use external dependencies or application code.
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema[6.1].define(version: 2019_12_01_192510) do
ActiveRecord::Schema[7.0].define(version: 2022_03_26_181216) do
# These are extensions that must be enabled in order to support this database
enable_extension "pgcrypto"
enable_extension "plpgsql"
create_table "analyses", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
create_table "analyses", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
t.string "service", null: false
t.string "host", null: false
t.boolean "pending", default: true, null: false
t.jsonb "result"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.datetime "created_at", precision: nil, null: false
t.datetime "updated_at", precision: nil, null: false
t.jsonb "args"
t.index ["service", "host", "args"], name: "index_analyses_on_service_and_host_and_args", unique: true
end
create_table "stats", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.string "name"
t.date "date"
t.jsonb "data"
t.index ["name", "date"], name: "index_stats_on_name_and_date", unique: true
end
end