Rewrote the error handling module to check for object keys and report the correct error code associated with the failure, as well as includes default cases.
215 lines
6.4 KiB
JavaScript
215 lines
6.4 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) {
|
|
var options = {
|
|
hostname: 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)
|
|
}
|
|
};
|
|
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 = 'info';
|
|
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 = 'info';
|
|
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 = 'info';
|
|
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 = 'info';
|
|
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 = 'info';
|
|
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(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) {
|
|
console.log('Error writing file. \n');
|
|
}
|
|
})
|
|
};
|
|
|
|
/**
|
|
* 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");
|
|
}
|
|
};
|