Statistics pages
parent
e7bf9635f5
commit
c117131411
|
@ -0,0 +1,3 @@
|
|||
--require spec_helper
|
||||
--format progress
|
||||
--format html --out tmp/rspec.html
|
2
Gemfile
2
Gemfile
|
@ -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
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
class StatsController < ApplicationController
|
||||
end
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
import 'css/application'
|
||||
import 'bootstrap/js/dist/tab'
|
||||
|
|
|
@ -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>
|
|
@ -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")
|
91
bin/stats
91
bin/stats
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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",
|
||||
|
|
Loading…
Reference in New Issue