Class: SSLExpiry

Inherits:
Object
  • Object
show all
Defined in:
lib/ssl_expiry.rb,
lib/ssl_expiry/errors.rb,
lib/ssl_expiry/version.rb

Overview

Docs to follow

Defined Under Namespace

Classes: SSLError, UnknownError

Constant Summary collapse

MAJOR =

Current major release.

1
MINOR =

Current minor release.

0
PATCH =

Current patch level.

4
VERSION =

Full release version.

[MAJOR, MINOR, PATCH].join('.').freeze

Class Method Summary collapse

Class Method Details

.check_certificates(domains, date_format = '%d %b %Y') ⇒ Object

This method smells of :reek:DuplicateMethodCall, :reek:UncommunicativeVariableName



57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# File 'lib/ssl_expiry.rb', line 57

def check_certificates(domains, date_format = '%d %b %Y')
    results = {}

    domains = domains.split(',') unless domains.is_a?(Array)

    domains.each do |domain|
        begin
            parts = domain.split(':')

            cert = if parts.length == 2
                       get_cert(parts[0], parts[1])
                   else
                       get_cert(parts[0])
                   end
        rescue SSLError => e
            results[domain] = { 'status' => 400, 'error' => e.message }
            next
        end

        results[domain] = process_certificate(cert, date_format)
    end
    results.sort
end

.display_results(results, width = 120) ⇒ Object

This method smells of :reek:DuplicateMethodCall



82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
# File 'lib/ssl_expiry.rb', line 82

def display_results(results, width = 120)
    delim = '-' * width

    puts(delim)
    printf(" %-30<header1>s | %<header2>s\n", header1: 'Domain', header2: 'Status')
    puts(delim)
    results.each do |domain, details|
        status = if details['status'] == 400
                     details['error']
                 else
                     format('expires on %<expires_in>s (in %<expires_on>s days) [CN=%<common_name>s]', expires_in: details['expires_on'], expires_on: details['expires_in'], common_name: details['common_name'])
                 end
        printf(" %-30<domain_name>s | %<ssl_status>s\n", domain_name: domain, ssl_status: status)
    end
    puts(delim)
end

.get_cert(domain_name, supplied_port = 443) ⇒ Object

This method smells of :reek:NilCheck, :reek:UncommunicativeVariableName, :reek:DuplicateMethodCall



21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# File 'lib/ssl_expiry.rb', line 21

def get_cert(domain_name, supplied_port = 443)
    cert = nil

    uri = URI::HTTPS.build(host: domain_name)
    http = Net::HTTP.new(uri.host, supplied_port)

    http.use_ssl = true
    http.verify_mode = OpenSSL::SSL::VERIFY_PEER
    http.open_timeout = 5
    http.read_timeout = 5
    http.ssl_timeout  = 5

    http.start do |h|
        cert = h.peer_cert
    end
    cert
rescue SocketError, SystemCallError => e
    raise SSLError.new "Bad URL? #{e.message}"
rescue Net::OpenTimeout
    raise SSLError.new 'Timed out. Is the site up?'
rescue OpenSSL::SSL::SSLError => e
    raise SSLError.new "We're trying to validate your certificate using TLSv1 It looks like your server doesn't accept it: [#{$ERROR_INFO.message}]" if e.message =~ /sslv3.+tlsv1 alert/i
end

.process_certificate(cert, date_format) ⇒ Object



45
46
47
48
49
50
51
52
53
54
# File 'lib/ssl_expiry.rb', line 45

def process_certificate(cert, date_format)
    ssl_common_name = cert.subject.to_a.select { |name, _data, _type| name == 'CN' }.first[1]
    issuer_name = cert.issuer.to_a.select { |name, _data, _type| name == 'O' }.first[1]
    expires_on = cert.not_after
    num_days = ((expires_on - Time.now) / 864_00).to_i

    { 'status' => 200, 'expires_on' => expires_on.strftime(date_format), 'expires_in' => num_days, 'common_name' => ssl_common_name, 'issuer' => issuer_name }
rescue StandardError
    { 'status' => 400, 'error' => 'Parsing error' }
end