tls-dashboard/node_app/get_cert_info.js
Matt Baer 6345e42721 Reduce danger and warning windows:
Danger: 14 days
Warning: 30 days
2022-11-05 23:08:51 -05:00

227 lines
6.9 KiB
JavaScript

/**
* TLS Dashboard by Craine Runton
* Source: https://github.com/cmrunton/tls-dashboard
*
* See /LICENSE for licensing details
*/
var https = require('https');
const fs = require('fs');
const config = require('./config');
const monitored_hosts = require('./monitored_hosts');
const run_date = new Date().toDateString();
var output = {},
iteration = 1,
errors = 0;
// Run the module
monitored_hosts.forEach(get_cert_parameters)
/**
* Creates a connection to the host, and then reads the resulting peer certificate to extract the desired info
*
* @param {string} element The
* @param {int} index
* @param {array} array The
*/
function get_cert_parameters(element, index, array) {
console.log(element);
var options = {
hostname: element.hostname,
port: (element.port ? element.port : 443),
method: 'GET'
};
var req = https.request(options, function(res) {
var cert = res.connection.getPeerCertificate();
var parsed = {
'server': element,
'subject': {
'org': cert.subject.O,
'common_name': cert.subject.CN,
'sans': cert.subjectaltname
},
'issuer': {
'org': cert.issuer.O,
'common_name': cert.issuer.CN
},
'info': {
'valid_from': parse_date(cert.valid_from),
'valid_to': parse_date(cert.valid_to),
'days_left': get_days_left(cert.valid_to),
'sort_order': get_days_left(cert.valid_to),
'background_class': ''
}
};
if (parsed.info.days_left <= 14 ){
parsed.info.background_class = 'danger';
} else if (parsed.info.days_left <= 30 ) {
parsed.info.background_class = 'warning';
} else {
parsed.info.background_class = 'success';
};
add_cert_details(parsed, iteration);
check_iterations();
});
// Abort the request when a timeout event is emitted
req.on('timeout', function () {
this.abort();
});
// Handle errors generated by failed requests
req.on('error', function(e) {
// Increment the error count for the final output
errors++;
var parsed = {
'server': element,
'subject': {
'org': 'Unknown',
'common_name': '',
'sans': 'Unknown'
},
'issuer': {
'org': 'Unknown',
'common_name': ''
},
'info': {
'days_left': '',
'sort_order': 100000,
'background_class': ''
}
};
if (e.hasOwnProperty('code')) {
switch (e.code) {
case 'CERT_HAS_EXPIRED':
assert(false, element+' certificate expired.');
parsed.subject.common_name = 'The certificate has expired';
parsed.issuer.common_name = e.code;
parsed.info.days_left = '0';
parsed.info.sort_order = 0;
parsed.info.background_class = 'danger';
break;
case 'ECONNRESET':
assert(false, element+' connection timed out or was reset.');
parsed.subject.common_name = 'The connection was reset by the server or timed out';
parsed.issuer.common_name = e.code;
parsed.info.days_left = '--';
parsed.info.background_class = 'error';
break;
case 'ECONNREFUSED':
assert(false, element+' connection refused by server.');
parsed.subject.common_name = 'The connection was refused by the remote server';
parsed.issuer.common_name = e.code;
parsed.info.days_left = '--';
parsed.info.background_class = 'error';
break;
case 'UNABLE_TO_VERIFY_LEAF_SIGNATURE':
assert(false, element+' self-signed or incomplete certificate chain.');
parsed.subject.common_name = 'The server provided a self-signed certificate or the provided certificate chain was incomplete';
parsed.issuer.common_name = e.code;
parsed.info.days_left = '--';
parsed.info.background_class = 'error';
break;
default:
assert(false, element+' unspecified error.');
parsed.subject.common_name = 'An unspecified error occured';
parsed.issuer.common_name = e.code;
parsed.info.days_left = '--';
parsed.info.background_class = 'error';
break;
};
} else if (e.hasOwnProperty('reason')) {
switch (e.reason) {
default:
assert(false, element+' hostname mismatch.');
parsed.subject.common_name = 'There was mismatch between the requested hostname and the certificate presented by the server';
parsed.issuer.common_name = 'HOSTNAME_MISMATCH';
parsed.info.days_left = '--';
parsed.info.background_class = 'error';
break;
}
}
add_cert_details(parsed, iteration);
check_iterations();
})
// Set the timeout threshold for the https connection. Set in config.js, default 5000ms
req.setTimeout(config.connection_timeout);
// End the request
req.end();
};
/**
* Parses the date string passed to it and returs a new date object
*
* @param {string} date_string The human readble date string that needs to be parsed
*/
function parse_date(date_string) {
var date = new Date(Date.parse(date_string));
return date;
};
/**
* Takes a date string and returns the nuumber of days between now and the future date
*
* @param {string} date_string The human readble date string that needs to be parsed
*/
function get_days_left(date_string) {
var now = Date.now();
var then = new Date(Date.parse(date_string));
var days_left = Math.round((then - now)/86400000);
return days_left;
};
/**
* Helper function to put the resolved/parsed cert info into the module output object
*
* @param {object} object Contains the parsed certificate info
* @param {string} host The name of the host that the certificate info is taken from
*/
function add_cert_details(object, host) {
output[host] = object;
};
/**
* Checks the iteration count. If the forEach has iterated over all the hosts, then call the write_results function,
* otherwise log the iteration to the console and increment the count
*/
function check_iterations() {
if (iteration === monitored_hosts.length) {
assert(true, 'Scanned '+iteration+' of '+monitored_hosts.length+' urls, with '+errors+' errors');
write_results();
} else {
iteration++;
}
};
/**
* Writes out the final object to a file, along with the run date to be used by the HTML page later
*/
function write_results() {
fs.writeFile(__dirname+'/'+config.output_file.path+config.output_file.name, 'var run_date = \''+run_date+'\'; \nvar cert_info = '+JSON.stringify(output, null, 2), function(err) {
// If the write errored out, notify
if (err) {
assert(false, 'Error writing file to the specified location.');
}
})
};
/**
* Test function and used to write out the final iteration in green
*/
function assert(value, desc) {
if (value) {
console.log("\033[32m "+desc+"\033[0m");
} else {
console.log("\033[31m "+desc+"\033[0m");
}
};