forked from aeris/cryptcheck-rails
Compare commits
8 Commits
ae0223186c
...
d1eed36b6e
Author | SHA1 | Date |
---|---|---|
|
d1eed36b6e | |
|
a815f1c168 | |
|
896664af07 | |
|
3316d1f98a | |
|
de51a93fde | |
|
5760e3b4dd | |
|
4826c679e8 | |
|
4914bf1764 |
|
@ -0,0 +1,3 @@
|
|||
--require spec_helper
|
||||
--format progress
|
||||
--format html --out tmp/rspec.html
|
5
Gemfile
5
Gemfile
|
@ -42,3 +42,8 @@ group :development do
|
|||
gem 'guard-livereload', require: false
|
||||
gem 'rack-livereload'
|
||||
end
|
||||
|
||||
group :development, :test do
|
||||
gem 'rspec-rails'
|
||||
gem 'pry-byebug'
|
||||
end
|
||||
|
|
2
Procfile
2
Procfile
|
@ -1,4 +1,4 @@
|
|||
web: bundle exec guard -i
|
||||
webpack: bundle exec webpack-dev-server
|
||||
webpack: bundle exec bin/webpack-dev-server
|
||||
sidekiq: bundle exec sidekiq -q default
|
||||
sidekiq_1_0: BUNDLE_GEMFILE=Gemfile-2.3 bin/sidekiq 1.0 -q tls_1_0
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
class StatisticsController < ApplicationController
|
||||
TODAY = Date.today
|
||||
|
||||
def show
|
||||
service = params.fetch :id
|
||||
respond_to do |format|
|
||||
format.json do
|
||||
json = Stat.where(name: "grades_for_#{service}").order(date: :desc).first.data
|
||||
render json: json, status: :ok
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def ciphers
|
||||
service = params.fetch :id
|
||||
render json: Stat.where(name: "ciphers_for_#{service}").order(date: :desc).first.data
|
||||
end
|
||||
|
||||
def tls
|
||||
service = params.fetch :id
|
||||
render json: Stat.where(name: "tls_for_#{service}").order(date: :desc).first.data
|
||||
end
|
||||
end
|
|
@ -0,0 +1,2 @@
|
|||
module StatisticsHelper
|
||||
end
|
|
@ -0,0 +1,5 @@
|
|||
$dark: #3f4853;
|
||||
|
||||
$link-color: #135262;
|
||||
|
||||
$nav-pills-link-active-bg: #535d6c;
|
|
@ -1,3 +1,4 @@
|
|||
@import "mixin";
|
||||
//@import "~bootstrap/scss/bootstrap";
|
||||
|
||||
$dark: #3f4853;
|
||||
|
@ -52,36 +53,45 @@ $input-border-color: $dark;
|
|||
$fa-font-path: "~font-awesome/fonts";
|
||||
@import "~font-awesome/scss/font-awesome";
|
||||
|
||||
body {
|
||||
background-color: #fafafa;
|
||||
}
|
||||
|
||||
/** Headings */
|
||||
h1, h2, h3, h4 {
|
||||
color: #3f4853;
|
||||
}
|
||||
|
||||
.badge-error, .progress-bar-error {
|
||||
background-color: #000;
|
||||
background-color: #000;
|
||||
}
|
||||
|
||||
.badge-critical, .progress-bar-critical {
|
||||
background-color: #000;
|
||||
background-color: #000;
|
||||
}
|
||||
|
||||
table.center {
|
||||
td {
|
||||
text-align: center;
|
||||
td {
|
||||
text-align: center;
|
||||
|
||||
&.left {
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
&.left {
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
td.error {
|
||||
background-color: #ddd;
|
||||
background-color: #ddd;
|
||||
}
|
||||
|
||||
.progress {
|
||||
margin: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#check,
|
||||
#ssh_check,
|
||||
#tls_check {
|
||||
margin-top: 100px;
|
||||
margin-top: 100px;
|
||||
}
|
||||
|
||||
$color-critical: #d9534f;
|
||||
|
@ -92,106 +102,179 @@ $color-best: #8db457;
|
|||
$color-great: #5cb85c;
|
||||
|
||||
.badge-state-critical, td.badge-state-critical {
|
||||
background-color: $color-critical;
|
||||
background-color: $color-critical;
|
||||
}
|
||||
|
||||
.badge-state-error, td.badge-state-error {
|
||||
background-color: $color-error;
|
||||
background-color: $color-error;
|
||||
}
|
||||
|
||||
.badge-state-warning, td.badge-state-warning {
|
||||
background-color: $color-warning;
|
||||
background-color: $color-warning;
|
||||
}
|
||||
|
||||
.badge-state-good, td.badge-state-good {
|
||||
background-color: $color-good;
|
||||
background-color: $color-good;
|
||||
}
|
||||
|
||||
.badge-state-best, td.badge-state-best {
|
||||
background-color: $color-best;
|
||||
background-color: $color-best;
|
||||
}
|
||||
|
||||
.badge-state-great, td.badge-state-great {
|
||||
background-color: $color-great;
|
||||
background-color: $color-great;
|
||||
}
|
||||
|
||||
.badge-state-default {
|
||||
background-color: $secondary;
|
||||
background-color: $secondary;
|
||||
}
|
||||
|
||||
.badge-state-success, td.badge-state-success {
|
||||
background-color: $color-great;
|
||||
background-color: $color-great;
|
||||
}
|
||||
|
||||
$lighten-alert: 35%;
|
||||
.alert-critical {
|
||||
background-color: lighten($color-critical, $lighten-alert);
|
||||
color: $color-critical;
|
||||
border-color: $color-critical;
|
||||
background-color: lighten($color-critical, $lighten-alert);
|
||||
color: $color-critical;
|
||||
border-color: $color-critical;
|
||||
}
|
||||
|
||||
.alert-error {
|
||||
background-color: lighten($color-error, $lighten-alert);
|
||||
color: $color-error;
|
||||
border-color: $color-error;
|
||||
background-color: lighten($color-error, $lighten-alert);
|
||||
color: $color-error;
|
||||
border-color: $color-error;
|
||||
}
|
||||
|
||||
.alert-warning {
|
||||
background-color: lighten($color-warning, $lighten-alert);
|
||||
color: $color-warning;
|
||||
border-color: $color-warning;
|
||||
background-color: lighten($color-warning, $lighten-alert);
|
||||
color: $color-warning;
|
||||
border-color: $color-warning;
|
||||
}
|
||||
|
||||
.alert-good {
|
||||
background-color: lighten($color-good, $lighten-alert);
|
||||
color: $color-good;
|
||||
border-color: $color-good;
|
||||
background-color: lighten($color-good, $lighten-alert);
|
||||
color: $color-good;
|
||||
border-color: $color-good;
|
||||
}
|
||||
|
||||
.alert-best {
|
||||
background-color: lighten($color-best, $lighten-alert);
|
||||
color: $color-best;
|
||||
border-color: $color-best;
|
||||
background-color: lighten($color-best, $lighten-alert);
|
||||
color: $color-best;
|
||||
border-color: $color-best;
|
||||
}
|
||||
|
||||
.alert-great {
|
||||
background-color: lighten($color-great, $lighten-alert);
|
||||
color: $color-great;
|
||||
border-color: $color-great;
|
||||
background-color: lighten($color-great, $lighten-alert);
|
||||
color: $color-great;
|
||||
border-color: $color-great;
|
||||
}
|
||||
|
||||
table.scoring img {
|
||||
width: 30px;
|
||||
width: 30px;
|
||||
}
|
||||
|
||||
#about, #help {
|
||||
margin-bottom: 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
#about, #help .scoring {
|
||||
p {
|
||||
font-size: 1.25em;
|
||||
}
|
||||
p {
|
||||
font-size: 1.25em;
|
||||
}
|
||||
}
|
||||
|
||||
#donorbox {
|
||||
background: #2d81c5 url("../images/donorbox.png") no-repeat 18px center;
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
font-family: Verdana, sans-serif;
|
||||
display: inline-block;
|
||||
font-size: 16px;
|
||||
padding: 13px 17px 13px 56px;
|
||||
-webkit-border-radius: 2px;
|
||||
-moz-border-radius: 2px;
|
||||
border-radius: 2px;
|
||||
box-shadow: 0 1px 0 0 #1f5a89;
|
||||
text-shadow: 0 1px rgba(0, 0, 0, 0.3);
|
||||
background: #2d81c5 url("../images/donorbox.png") no-repeat 18px center;
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
font-family: Verdana, sans-serif;
|
||||
display: inline-block;
|
||||
font-size: 16px;
|
||||
padding: 13px 17px 13px 56px;
|
||||
-webkit-border-radius: 2px;
|
||||
-moz-border-radius: 2px;
|
||||
border-radius: 2px;
|
||||
box-shadow: 0 1px 0 0 #1f5a89;
|
||||
text-shadow: 0 1px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
#liberapay, #donorbox, #paypal {
|
||||
img {
|
||||
height: 50px;
|
||||
max-width: 140px;
|
||||
}
|
||||
img {
|
||||
height: 50px;
|
||||
max-width: 140px;
|
||||
}
|
||||
}
|
||||
|
||||
/** Form part */
|
||||
.form-control,
|
||||
.form-select {
|
||||
border-color: $dark;
|
||||
}
|
||||
|
||||
.input-danger {
|
||||
@extend .form-control;
|
||||
border-color: $danger;
|
||||
}
|
||||
|
||||
label {
|
||||
font-weight: bold;
|
||||
color: $dark;
|
||||
}
|
||||
|
||||
label.required:after {
|
||||
font-weight: normal;
|
||||
content: '*';
|
||||
color: red;
|
||||
}
|
||||
|
||||
fieldset {
|
||||
border: 1px solid $dark;
|
||||
border-radius: .5rem;
|
||||
}
|
||||
|
||||
legend {
|
||||
border-radius: .5rem;
|
||||
padding: .2rem .4rem;
|
||||
}
|
||||
|
||||
/** 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;
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
document.addEventListener("DOMContentLoaded", () => {
|
||||
if (document.getElementById("pills-tab")) {
|
||||
let background = [
|
||||
'#d9534f', // bad
|
||||
'#5cb85c' // good
|
||||
]
|
||||
|
||||
for (const service of ["https", "smtp", "tls", "xmpp"]) {
|
||||
const name = service.replace(/^\w/, c => c.toUpperCase())
|
||||
const canvas = document.getElementById(`ciphers${name}Chart`).getContext('2d')
|
||||
const chart = new Chart(canvas, {
|
||||
type: 'doughnut', options: {
|
||||
interaction: {
|
||||
intersect: false, mode: 'dataset',
|
||||
},
|
||||
animations: {
|
||||
tension: {
|
||||
duration: 100,
|
||||
easing: 'linear',
|
||||
from: 1,
|
||||
to: 0,
|
||||
loop: false
|
||||
}
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
fetch(`/statistics/${service}/ciphers.json`).then((response) => {
|
||||
if (response.status === 200) {
|
||||
response.json().then((data) => {
|
||||
console.info(data)
|
||||
const labels = Object.keys(data)
|
||||
const dataset = Object.values(data)
|
||||
chart.data.labels = labels
|
||||
chart.data.datasets = [{
|
||||
label: 'Number of request', data: dataset, backgroundColor: background
|
||||
}]
|
||||
chart.update()
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
|
@ -0,0 +1,40 @@
|
|||
document.addEventListener("DOMContentLoaded", () => {
|
||||
let gradesChart = document.getElementById('gradesChart').getContext('2d')
|
||||
|
||||
let background = [
|
||||
'rgba(255, 99, 132, 1)',
|
||||
'rgba(54, 162, 235, 1)',
|
||||
'rgba(255, 206, 86, 1)',
|
||||
'rgba(75, 192, 192, 1)',
|
||||
'rgba(153, 102, 255, 1)',
|
||||
'rgba(255, 159, 64, 1)'
|
||||
]
|
||||
|
||||
let createGradesChart = new Chart(gradesChart, {
|
||||
type: 'bar'
|
||||
})
|
||||
|
||||
let generateGraphs = function () {
|
||||
fetch(window.location.href, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Accept": "application/json",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}).then((response) => {
|
||||
response.json().then((data) => {
|
||||
if (response.status === 200) {
|
||||
createGradesChart.data.labels = data["labels"]
|
||||
createGradesChart.data.datasets = [{
|
||||
label: 'Number',
|
||||
data: data["dataset"],
|
||||
backgroundColor: background
|
||||
}]
|
||||
createGradesChart.update()
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
generateGraphs()
|
||||
});
|
|
@ -0,0 +1,73 @@
|
|||
document.addEventListener("DOMContentLoaded", () => {
|
||||
let background = [
|
||||
'#5cb85c', // A+
|
||||
'#5cb85c', // A
|
||||
'#8db457', // B+
|
||||
'#8db457', // B
|
||||
'#beb052', // C+
|
||||
'#beb052', // C
|
||||
'#6c757d', // D
|
||||
'#f0ad4e', // E
|
||||
'#e4804e', // F
|
||||
'#d9534f' // G
|
||||
]
|
||||
|
||||
for (const service of ["https", "smtp", "tls", "xmpp"]) {
|
||||
const name = service.replace(/^\w/, c => c.toUpperCase())
|
||||
console.info(`grades${name}Chart`)
|
||||
const canvas = document.getElementById(`grades${name}Chart`).getContext('2d')
|
||||
// const chart = new Chart(canvas, {
|
||||
// type: 'pie',
|
||||
// options: {
|
||||
// interaction: {
|
||||
// intersect: false,
|
||||
// mode: 'dataset',
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
const chart = new Chart(canvas, {
|
||||
type: 'bar',
|
||||
options: {
|
||||
interaction: {
|
||||
intersect: false,
|
||||
mode: 'dataset',
|
||||
},
|
||||
plugins: {
|
||||
datalabels: {
|
||||
anchor: 'end',
|
||||
align: 'top',
|
||||
formatter: ((value, context) => {
|
||||
const index = context.dataIndex
|
||||
const keys = Object.keys(context.dataset.data)
|
||||
value = context.dataset.data[keys[index]]
|
||||
return value
|
||||
}),
|
||||
color: "black",
|
||||
font: {
|
||||
weight: "bold",
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
fetch(`/statistics/${service}.json`).then((response) => {
|
||||
if (response.status === 200) {
|
||||
response.json().then((data) => {
|
||||
console.info(data)
|
||||
const labels = ["A+", "A", "B+", "B", "C+", "C", "D", "E", "F", "G"]
|
||||
const dataset = JSON.parse(JSON.stringify(data, labels, 0))
|
||||
chart.data.labels = labels
|
||||
chart.data.datasets = [{
|
||||
label: 'Number of request',
|
||||
data: dataset,
|
||||
backgroundColor: background
|
||||
}]
|
||||
chart.update()
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
)
|
||||
;
|
|
@ -0,0 +1,46 @@
|
|||
document.addEventListener("DOMContentLoaded", () => {
|
||||
if (document.getElementById("pills-tab")) {
|
||||
let background = [
|
||||
'#d9534f', // ssl
|
||||
'#beb052', // tls
|
||||
'#beb052', // tls
|
||||
'#5cb85c' // tls1_2_only
|
||||
]
|
||||
|
||||
for (const service of ["https", "smtp", "tls", "xmpp"]) {
|
||||
const name = service.replace(/^\w/, c => c.toUpperCase())
|
||||
const canvas = document.getElementById(`tls${name}Chart`).getContext('2d')
|
||||
const chart = new Chart(canvas, {
|
||||
type: 'doughnut', options: {
|
||||
interaction: {
|
||||
intersect: false, mode: 'dataset',
|
||||
},
|
||||
animations: {
|
||||
tension: {
|
||||
duration: 100,
|
||||
easing: 'linear',
|
||||
from: 1,
|
||||
to: 0,
|
||||
loop: false
|
||||
}
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
fetch(`/statistics/${service}/tls.json`).then((response) => {
|
||||
if (response.status === 200) {
|
||||
response.json().then((data) => {
|
||||
console.info(data)
|
||||
const labels = Object.keys(data)
|
||||
const dataset = Object.values(data)
|
||||
chart.data.labels = labels
|
||||
chart.data.datasets = [{
|
||||
label: 'Number of request', data: dataset, backgroundColor: background
|
||||
}]
|
||||
chart.update()
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
|
@ -1 +1,10 @@
|
|||
import 'css/application'
|
||||
|
||||
import 'bootstrap'
|
||||
import Chart from 'chart.js/auto'
|
||||
import ChartDataLabels from 'chartjs-plugin-datalabels'
|
||||
global.Chart = Chart
|
||||
Chart.register(ChartDataLabels)
|
||||
|
||||
import 'js/stats/ciphers'
|
||||
import 'js/stats/tls'
|
|
@ -10,13 +10,13 @@ class Matomo
|
|||
cattr_reader :url, :path, :site
|
||||
|
||||
def self.load
|
||||
@@url = ENV.fetch 'MATOMO_URL'
|
||||
@@site = ENV.fetch 'MATOMO_SITE'
|
||||
@@url = ENV['MATOMO_URL']
|
||||
@@site = ENV['MATOMO_SITE']
|
||||
@@disabled = ENV['MATOMO_DISABLED']
|
||||
end
|
||||
|
||||
def self.enabled?
|
||||
self.url && self.site && @@disabled.nil?
|
||||
@@disabled.nil? && self.url && self.site
|
||||
end
|
||||
|
||||
ActionView::Base.include Helpers
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -0,0 +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
|
|
@ -4,7 +4,7 @@
|
|||
<div id="check" class="container">
|
||||
<div class="row">
|
||||
<div class="col-sm-8 col-sm-offset-2">
|
||||
<h1>
|
||||
<h1 class="text-dark">
|
||||
<i class="fa fa-spinner fa-pulse"></i>
|
||||
[<%= self.type.to_s.upcase %>] <%= t 'Currently analysing %{host}', host: @host %>
|
||||
</h1>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-sm-11">
|
||||
<h1>
|
||||
<h1 class="text-dark">
|
||||
[<%= self.type.to_s.upcase %>] <%= @host %>
|
||||
<span class="small">(<%= l @analysis.updated_at %>)</span>
|
||||
</h1>
|
||||
|
@ -17,7 +17,7 @@
|
|||
if host.error %>
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<h2>
|
||||
<h2 class="text-dark">
|
||||
<%= rank_label :X %>
|
||||
<%= host[:ip] %> : <%= host[:port] %>
|
||||
<span class="small">(<%= host[:hostname] %>)</span>
|
||||
|
@ -29,7 +29,7 @@
|
|||
<% next; end %>
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<h2>
|
||||
<h2 class="text-dark">
|
||||
<%= rank_label host[:grade].to_sym %>
|
||||
<%= host[:ip] %> : <%= host[:port] %>
|
||||
<span class="small">(<%= host[:hostname] %>)</span></h2>
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
<div>
|
||||
Nombre de recherche <%= params[:service] %> : <%= @services.size %>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<canvas id="gradesChart"></canvas>
|
||||
</div>
|
|
@ -0,0 +1,117 @@
|
|||
<% colors = {
|
||||
"A+" => '#5cb85c',
|
||||
"good" => '#5cb85c',
|
||||
"A" => '#5cb85c',
|
||||
"B+" => '#8db457',
|
||||
"B" => '#8db457',
|
||||
"C+" => '#beb052',
|
||||
"C" => '#beb052',
|
||||
"D" => '#6c757d',
|
||||
"E" => '#f0ad4e',
|
||||
"F" => '#e4804e',
|
||||
"G" => '#d9534f',
|
||||
"bad" => '#d9534f' } %>
|
||||
|
||||
<ul class="nav nav-pills nav-fill" id="pills-tab" role="tab-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="pill" aria-controls="nav-<%= service %>" aria-selected="true">
|
||||
<%= service.to_s.upcase %>
|
||||
</button>
|
||||
</li>
|
||||
<% end %>
|
||||
</ul>
|
||||
|
||||
<div class="tab-content p-2" 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="tabpanel" aria-labelledby="nav-<%= service %>-pill">
|
||||
<h2>Grades for service <%= service.to_s.upcase %></h2>
|
||||
|
||||
|
||||
<% grades = Stat.where(name: "grades_for_#{service}").order(date: :desc).first
|
||||
total = grades.data.collect { _2 }.sum %>
|
||||
|
||||
<p>Over <%= total %> URL tested with a grade.</p>
|
||||
|
||||
<div class="cumulative-datas">
|
||||
<% left = 0
|
||||
percent_total = 100
|
||||
grades.data.sort_by(&:first).each do |grade, number|
|
||||
%>
|
||||
<% unless %w(T V).include?(grade)
|
||||
percent = (number.to_f / total.to_f) * 100.0
|
||||
color = colors[grade]
|
||||
if percent > 1 %>
|
||||
<div class="cumulative-data" style="left: <%= left %>; width: <%= percent.round %>%; background-color: <%= color %>;">
|
||||
<%= "#{grade}: #{percent.round}% (#{number})" %>
|
||||
</div>
|
||||
<% left += percent.round
|
||||
end
|
||||
end
|
||||
end
|
||||
percent = percent_total - left %>
|
||||
<div class="cumulative-data" style="left: <%= left %>; width: <%= percent.round %>%; background-color: gray;">
|
||||
<%= "other: #{percent.round}%" %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2>Ciphers for service <%= service.to_s.upcase %></h2>
|
||||
|
||||
<% ciphers = Stat.where(name: "ciphers_for_#{service}").order(date: :desc).first
|
||||
total = ciphers.data.collect { _2 }.sum
|
||||
%>
|
||||
<p>Over <%= total %> URL tested with a cipher.</p>
|
||||
|
||||
<div class="cumulative-datas">
|
||||
<% left = 0
|
||||
percent_total = 100
|
||||
ciphers.data.sort_by(&:first).each do |grade, number|
|
||||
%>
|
||||
<%
|
||||
percent = (number.to_f / total.to_f) * 100.0
|
||||
color = colors[grade] %>
|
||||
<div class="cumulative-data" style="left: <%= left %>; width: <%= percent.round %>%; background-color: <%= color %>;">
|
||||
<%= "#{grade.capitalize}: #{percent.round}% (#{number})" %>
|
||||
</div>
|
||||
<% left += percent.round
|
||||
end
|
||||
percent = percent_total - left
|
||||
if percent > 0 %>
|
||||
<div class="cumulative-data" style="left: <%= left %>; width: <%= percent.round %>%; background-color: gray;">
|
||||
<%= "Other: #{percent.round}%" %>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<h2>TLS for service <%= service.to_s.upcase %></h2>
|
||||
|
||||
<% tls = Stat.where(name: "tls_for_#{service}").order(date: :desc).first
|
||||
total = tls.data.collect { _2 }.sum
|
||||
%>
|
||||
<p>Over <%= total %> URL tested with TLS.</p>
|
||||
|
||||
<div class="cumulative-datas">
|
||||
<% left = 0
|
||||
percent_total = 100
|
||||
tls.data.sort_by(&:first).each do |grade, number|
|
||||
%>
|
||||
<%
|
||||
percent = (number.to_f / total.to_f) * 100.0
|
||||
color = colors[grade] %>
|
||||
<div class="cumulative-data" style="left: <%= left %>; width: <%= percent.round %>%; background-color: gray;">
|
||||
<%= "#{grade.capitalize}: #{percent.round}% (#{number})" %>
|
||||
</div>
|
||||
<% left += percent.round
|
||||
end
|
||||
percent = percent_total - left
|
||||
if percent > 0 %>
|
||||
<div class="cumulative-data" style="left: <%= left %>; width: <%= percent.round %>%; background-color: gray;">
|
||||
<%= "Other: #{percent.round}%" %>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
</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")
|
|
@ -0,0 +1,96 @@
|
|||
#!./bin/rails runner
|
||||
|
||||
# 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]
|
||||
: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]
|
||||
:pfs_only
|
||||
end
|
||||
end
|
||||
|
||||
services = Analysis.group(:service).count
|
||||
Stat.create! :request_per_service, services
|
||||
|
||||
%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] }
|
||||
|
||||
services.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
|
||||
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
|
|
@ -18,6 +18,13 @@ Rails.application.routes.draw do
|
|||
|
||||
get 'sites' => 'site#sites'
|
||||
|
||||
resources :statistics, only: %i[index show] do
|
||||
member do
|
||||
get "ciphers", to: "statistics#ciphers"
|
||||
get "tls", to: "statistics#tls"
|
||||
end
|
||||
end
|
||||
|
||||
if Rails.env.development?
|
||||
require 'sidekiq/web'
|
||||
mount Sidekiq::Web => '/sidekiq'
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
class CreateStats < ActiveRecord::Migration[7.0]
|
||||
def change
|
||||
create_table :stats, id: :uuid do |t|
|
||||
t.string :name
|
||||
t.date :date
|
||||
t.jsonb :data
|
||||
end
|
||||
|
||||
add_index :stats, %i[name]
|
||||
end
|
||||
end
|
24
db/schema.rb
24
db/schema.rb
|
@ -2,16 +2,15 @@
|
|||
# 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"
|
||||
|
@ -21,10 +20,17 @@ ActiveRecord::Schema[6.1].define(version: 2019_12_01_192510) do
|
|||
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"], name: "index_stats_on_name"
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -5,8 +5,11 @@
|
|||
"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",
|
||||
"chart.js": "^3.7.1",
|
||||
"chartjs-plugin-datalabels": "^2.0.0",
|
||||
"font-awesome": "^4.7.0",
|
||||
"webpack": "^4.46.0",
|
||||
"webpack-cli": "^3.3.12"
|
||||
|
|
|
@ -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,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