Statistics pages

master
Norore 2022-07-02 19:55:43 +02:00 committed by aeris
parent e7bf9635f5
commit c117131411
12 changed files with 357 additions and 24 deletions

3
.rspec 100644
View File

@ -0,0 +1,3 @@
--require spec_helper
--format progress
--format html --out tmp/rspec.html

View File

@ -19,6 +19,7 @@ gem 'http_accept_language'
gem 'recursive-open-struct'
gem 'ruby-progressbar'
gem 'public_suffix'
gem 'amazing_print'
gem 'uglifier'
gem 'sass-rails'
@ -46,7 +47,6 @@ end
group :development, :test do
gem 'pry-byebug'
gem 'amazing_print'
end
group :test do

View File

@ -0,0 +1,2 @@
class StatsController < ApplicationController
end

View File

@ -87,6 +87,7 @@ td.error {
$color-critical: #d9534f;
$color-error: #e4804e;
$color-warning: #f0ad4e;
$color-effort: #6c757d;
$color-good: #beb052;
$color-best: #8db457;
$color-great: #5cb85c;
@ -195,3 +196,71 @@ table.scoring img {
max-width: 140px;
}
}
/** Navigation tabs */
.tab-content {
border: 1px solid $nav-pills-link-active-bg;
border-bottom-left-radius: .3rem;
border-bottom-right-radius: .3rem;
}
.nav-pills .nav-link {
border-radius: .3rem;
font-weight: bold;
}
.nav-pills .nav-link.active {
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
font-weight: bolder;
}
/** Chart CSS */
.cumulative-datas {
position: relative;
display: flex;
flex-direction: row;
width: 100%;
&-content {
position: absolute;
height: 2rem;
}
}
.cumulative-data {
height: 2rem;
line-height: 2rem;
display: inline-block;
white-space: nowrap;
text-overflow: ellipsis;
margin-right: .1rem;
padding-left: .3rem;
}
/** Progress bar */
.progress {
box-shadow: 0 0 .1rem $dark;
height: 1.4rem;
}
.progress-critical {
background-color: $color-critical;
}
.progress-error {
background-color: $color-error;
}
.progress-warning {
background-color: $color-warning;
}
.progress-effort {
background-color: $color-effort;
}
.progress-good {
background-color: $color-good;
}
.progress-best {
background-color: $color-best;
}
.progress-great {
background-color: $color-great;
}

View File

@ -1 +1,2 @@
import 'css/application'
import 'bootstrap/js/dist/tab'

View File

@ -0,0 +1,167 @@
<% colors = {
"A+" => 'great',
"good" => 'great',
"A" => 'great',
"B+" => 'best',
"B" => 'best',
"C+" => 'good',
"C" => 'good',
"D" => 'effort',
"E" => 'warning',
"F" => 'error',
"G" => 'critical',
"bad" => 'critical',
"ssl" => 'critical',
"tls" => 'error',
"tls1_2" => 'effort',
"tls1_2_only" => 'great'
} %>
<ul class="nav nav-pills nav-fill" id="pills-tab" role="Navigation stats list">
<% %i[https smtp tls xmpp].each do |service| %>
<li class="nav-item" role="presentation">
<button class="nav-link<%= " active btn-dark" if service.to_s == "https" %>"
id="nav-<%= service %>-pill" data-bs-toggle="pill"
data-bs-target="#nav-<%= service %>" type="button" role="Button to show <%= service %> stats" aria-controls="nav-<%= service %>" aria-selected="true">
<%= service.to_s.upcase %>
</button>
</li>
<% end %>
</ul>
<div class="tab-content p-2 mb-4" id="nav-pillsContent">
<% %i[https smtp tls xmpp].each do |service| %>
<div class="tab-pane fade show <%= "active" if service.to_s == "https" %>"
id="nav-<%= service %>" role="<%= service.to_s %>> stats"
aria-labelledby="nav-<%= service %>-pill">
<h2>Grades for service <%= service.to_s.upcase %></h2>
<%
grades = Stat["grades_for_#{service}"]
total = grades.data.collect { _2 }.sum
%>
<p>Over <%= total %> URL tested with a grade.</p>
<table class="table table-bordered table-striped">
<thead class="bg-dark text-light">
<tr>
<th>Grade</th>
<th>Count</th>
<th></th>
</tr>
</thead>
<tbody>
<% sorted_grades = grades.data.keys.sort &CryptCheck::Grade.method(:compare)
sorted_grades.each do |grade|
unless %w(T V).include?(grade)
count = grades.data[grade].to_i
percent = (count.to_f / total.to_f) * 100.0
color = CryptCheck::Grade::GRADE_STATUS.fetch grade.to_sym %>
<tr>
<td class="col-4"><%= grade.capitalize %></td>
<td class="col-4"><%= count %>
(<%= percent.round %>%)
</td>
<td class="col-4">
<div class="progress bg-light">
<div class="progress-bar progress-<%= color %> border-dark"
style="width: <%= percent.round %>%" role="progressbar"
aria-valuenow="<%= percent.round %>" aria-valuemin="0"
aria-valuemax="100"></div>
</div>
</td>
</tr>
<% end
end %>
</tbody>
</table>
<h2>Ciphers for service <%= service.to_s.upcase %></h2>
<%
name = "ciphers_for_#{service}"
ciphers = Stat[name]
total = ciphers.data.collect { _2 }.sum
%>
<p>Over <%= total %> URL tested with a cipher.</p>
<table class="table table-bordered table-striped">
<thead class="bg-dark text-light">
<tr>
<%= content_tag :th, t(:'.ciphers.title') %>
<th>Count</th>
<th></th>
</tr>
</thead>
<tbody>
<%
ciphers = ciphers.data
%w[good bad].each do |grade|
count = ciphers.fetch grade
percent = (count.to_f / total.to_f) * 100.0
color = colors[grade]
%>
<tr>
<td class="col-4"><%= t ".ciphers.#{grade}" %></td>
<td class="col-4"><%= count %> (<%= percent.round %>
%)
</td>
<td class="col-4">
<div class="progress bg-light">
<div class="progress-bar progress-<%= color %>"
style="width: <%= percent.round %>%" role="progressbar"
aria-valuenow="<%= percent.round %>" aria-valuemin="0"
aria-valuemax="100"></div>
</div>
</td>
</tr>
<% end %>
</tbody>
</table>
<h2>TLS for service <%= service.to_s.upcase %></h2>
<%
name = "tls_for_#{service}"
tls = Stat[name]
total = tls.data.collect { _2 }.sum
%>
<p>Over <%= total %> URL tested with TLS.</p>
<table class="table table-bordered table-striped">
<thead class="bg-dark text-light">
<tr>
<%= content_tag :th, t(:'.tls.title') %>
<th>Count</th>
<th></th>
</tr>
</thead>
<tbody>
<%
tls = tls.data
%w[tls1_2_only tls1_2 tls ssl].each do |grade|
count = tls.fetch grade
percent = (count.to_f / total.to_f) * 100.0
color = colors[grade]
%>
<tr>
<td class="col-4"><%= t ".tls.#{grade}" %></td>
<td class="col-4"><%= count %> (<%= percent.round %>
%)
</td>
<td class="col-4">
<div class="progress bg-light">
<div class="progress-bar progress-<%= color %>"
style="width: <%= percent.round %>%" role="progressbar"
aria-valuenow="<%= percent.round %>" aria-valuemin="0"
aria-valuemax="100"></div>
</div>
</td>
</tr>
<% end %>
</tbody>
</table>
</div>
<% end %>
</div>

29
bin/rspec 100755
View File

@ -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")

View File

@ -49,29 +49,78 @@ class Analysis
end
end
sites = YAML.load_file Rails.root.join 'config/sites.yml'
# 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
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)
# general stat
services = Analysis.group(:service).order(service: :asc).count
Stat.create! :request_per_service, { labels: services.keys, dataset: services.values }
# grade per service for https, smtp, tls and xmpp
%i[https smtp tls xmpp].each do |name|
services = Analysis.where service: name, 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] }
progress = ProgressBar.create title: name, total: services.count,
format: '%t | %c/%u %W | %e'
services.find_in_batches do |batch|
batch.each do |service|
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
progress.increment
end
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
ap "grades_for_#{name}" => grades
Stat.create! "grades_for_#{name}", grades
Stat.create! :"sites_#{type}", domains
ap "tls_for_#{name}" => tls
Stat.create! "tls_for_#{name}", tls
ap "ciphers_for_#{name}" => ciphers
Stat.create! "ciphers_for_#{name}", ciphers
ap "pfs_for_#{name}" => pfs
Stat.create! "pfs_for_#{name}", pfs
end

View File

@ -61,6 +61,19 @@ fr:
Fingerprint: Empreinte
"Fingerprint: %{fingerprint}": "Empreinte : %{fingerprint}"
stats:
index:
ciphers:
title: Suites de chiffrement supportées
good: Uniquement des sécurisées
bad: Au moins une non sécurisée
tls:
title: Versions de SSL/TLS supportées
tls1_2_only: TLSv1.2+
tls1_2: TLSv1.2 supporté
tls: TLSv1.0+ mais pas TLSv1.2
ssl: SSLv2 ou SSLv3 supporté
date:
abbr_day_names:
- dim

View File

@ -16,7 +16,7 @@ Rails.application.routes.draw do
root 'site#index'
post '/' => 'site#check'
get 'sites' => 'site#sites'
resources :stats, only: %i[index]
%i[banks insurances gouv.fr].each do |name|
get name, controller: :sites, action: name

View File

@ -33,5 +33,4 @@ ActiveRecord::Schema[7.0].define(version: 2022_06_26_133648) do
t.jsonb "data"
t.index ["name", "date"], name: "index_stats_on_name_and_date", unique: true
end
end

View File

@ -5,6 +5,7 @@
"author": "aeris <aeris@imirhil.fr>",
"license": "AGPL-3.0-or-later",
"dependencies": {
"@popperjs/core": "^2.11.5",
"@rails/webpacker": "5.4.3",
"bootstrap": "^5.1.3",
"font-awesome": "^4.7.0",