ssl-test 1.1.0 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.travis.yml +5 -0
- data/README.md +4 -2
- data/lib/ssl-test.rb +29 -3
- data/ssl-test.gemspec +2 -1
- data/test/ssl-test_test.rb +27 -6
- metadata +22 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 3f6f73059bf9880985e8564282104fa677ecf3016872948d4047d47589f4ff12
|
4
|
+
data.tar.gz: 2c5580091f64589290c5bda90b25172c5d78dab67c9a1a40829e3e2cc35bc98f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 280ea4637845f7df589dfbc07ea51459a5fefaa36b36bb17614beb6fb809bc71f5d36a70e3ccc7e8fba61383b5ad0ba24e9fd49f066636792968ac6f132e5793
|
7
|
+
data.tar.gz: dc96640201b5098695be42467d052f592a2d3bf59d38e9cfbe1028bf7984226d46ec058ad35381383886cb84e7e348af07bf8429c67eed7d4fcafc8c89c3fe8f
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# SSLTest
|
1
|
+
# SSLTest [![Build Status](https://travis-ci.org/jarthod/ssl-test.svg?branch=master)](https://travis-ci.org/jarthod/ssl-test) [![Depfu](https://badges.depfu.com/badges/0d732c9cbec3fdaaac7c5ba5583269db/overview.svg)](https://depfu.com/github/jarthod/ssl-test)
|
2
2
|
|
3
3
|
A small ruby gem to help you test a website's SSL certificate.
|
4
4
|
|
@@ -58,12 +58,14 @@ Pretty much the same errors `curl` will:
|
|
58
58
|
- Incomplete certificate chain (missing intermediary)
|
59
59
|
- Self signed certificates
|
60
60
|
- Valid certs used with incorect hostname
|
61
|
+
- Untrusted root (if your system is up-to-date)
|
62
|
+
- And more...
|
61
63
|
|
62
64
|
### GOTCHA: errors SSLTest will NOT detect
|
63
65
|
|
64
66
|
There is a spefic kind or error this code will **NOT** detect: *revoked certificates*. This is much more complex to handle because it needs an up to date database of revoked certs to check with. This is implemented in most modern browsers but the results vary greatly (chrome ignores this for example).
|
65
67
|
|
66
|
-
Here is an example of website with a revoked certificate: https://revoked.
|
68
|
+
Here is an example of website with a revoked certificate: https://revoked.badssl.com/
|
67
69
|
|
68
70
|
Any contribution to add this feature is greatly appreciated :)
|
69
71
|
|
data/lib/ssl-test.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
require "net/https"
|
2
2
|
|
3
3
|
module SSLTest
|
4
|
-
VERSION = "1.
|
4
|
+
VERSION = "1.2.0"
|
5
5
|
|
6
6
|
def self.test url, open_timeout: 5, read_timeout: 5
|
7
7
|
uri = URI.parse(url)
|
@@ -15,19 +15,45 @@ module SSLTest
|
|
15
15
|
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
16
16
|
http.verify_callback = -> (verify_ok, store_context) {
|
17
17
|
cert = store_context.current_cert
|
18
|
-
failed_cert_reason =
|
18
|
+
failed_cert_reason = [store_context.error, store_context.error_string] if store_context.error != 0
|
19
19
|
verify_ok
|
20
20
|
}
|
21
21
|
|
22
22
|
begin
|
23
|
-
|
23
|
+
http.start { }
|
24
24
|
return [true, nil, cert]
|
25
25
|
rescue OpenSSL::SSL::SSLError => e
|
26
26
|
error = e.message
|
27
27
|
error = "error code %d: %s" % failed_cert_reason if failed_cert_reason
|
28
|
+
if error =~ /certificate verify failed/
|
29
|
+
domains = cert_domains(cert)
|
30
|
+
if matching_domains(domains, uri.host).none?
|
31
|
+
error = "hostname \"#{uri.host}\" does not match the server certificate (#{domains.join(', ')})"
|
32
|
+
end
|
33
|
+
end
|
28
34
|
return [false, error, cert]
|
29
35
|
rescue => e
|
30
36
|
return [nil, "SSL certificate test failed: #{e.message}"]
|
31
37
|
end
|
32
38
|
end
|
39
|
+
|
40
|
+
def self.cert_field_to_hash field
|
41
|
+
field.to_a.each.with_object({}) do |v, h|
|
42
|
+
v = v.to_a
|
43
|
+
h[v[0]] = v[1].encode('UTF-8', undef: :replace, invalid: :replace)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.cert_domains cert
|
48
|
+
(Array(cert_field_to_hash(cert.subject)['CN']) +
|
49
|
+
cert_field_to_hash(cert.extensions)['subjectAltName'].split(/\s*,\s*/))
|
50
|
+
.compact
|
51
|
+
.map {|s| s.gsub(/^DNS:/, '') }
|
52
|
+
.uniq
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.matching_domains domains, hostname
|
56
|
+
domains.map {|s| Regexp.new("\A#{Regexp.escape(s).gsub('\*', '[^.]+')}\z") }
|
57
|
+
.select {|domain| domain.match?(hostname) }
|
58
|
+
end
|
33
59
|
end
|
data/ssl-test.gemspec
CHANGED
@@ -18,5 +18,6 @@ Gem::Specification.new do |spec|
|
|
18
18
|
spec.require_paths = ["lib"]
|
19
19
|
|
20
20
|
spec.add_development_dependency "bundler", "~> 1.7"
|
21
|
-
spec.add_development_dependency "rake"
|
21
|
+
spec.add_development_dependency "rake"
|
22
|
+
spec.add_development_dependency "minitest"
|
22
23
|
end
|
data/test/ssl-test_test.rb
CHANGED
@@ -11,6 +11,20 @@ describe SSLTest do
|
|
11
11
|
cert.must_be_instance_of OpenSSL::X509::Certificate
|
12
12
|
end
|
13
13
|
|
14
|
+
it "returns no error on valid SAN" do
|
15
|
+
valid, error, cert = SSLTest.test("https://1000-sans.badssl.com/")
|
16
|
+
error.must_be_nil
|
17
|
+
valid.must_equal true
|
18
|
+
cert.must_be_instance_of OpenSSL::X509::Certificate
|
19
|
+
end
|
20
|
+
|
21
|
+
it "returns no error when no CN" do
|
22
|
+
valid, error, cert = SSLTest.test("https://no-common-name.badssl.com/")
|
23
|
+
error.must_be_nil
|
24
|
+
valid.must_equal true
|
25
|
+
cert.must_be_instance_of OpenSSL::X509::Certificate
|
26
|
+
end
|
27
|
+
|
14
28
|
it "works with websites blocking http requests" do
|
15
29
|
valid, error, cert = SSLTest.test("https://obyava.ua")
|
16
30
|
error.must_be_nil
|
@@ -19,21 +33,28 @@ describe SSLTest do
|
|
19
33
|
end
|
20
34
|
|
21
35
|
it "returns error on self signed certificate" do
|
22
|
-
valid, error, cert = SSLTest.test("https://
|
36
|
+
valid, error, cert = SSLTest.test("https://self-signed.badssl.com/")
|
23
37
|
error.must_equal "error code 18: self signed certificate"
|
24
38
|
valid.must_equal false
|
25
39
|
cert.must_be_instance_of OpenSSL::X509::Certificate
|
26
40
|
end
|
27
41
|
|
42
|
+
it "returns error on untrusted root" do
|
43
|
+
valid, error, cert = SSLTest.test("https://untrusted-root.badssl.com/")
|
44
|
+
error.must_equal "error code 20: unable to get local issuer certificate"
|
45
|
+
valid.must_equal false
|
46
|
+
cert.must_be_instance_of OpenSSL::X509::Certificate
|
47
|
+
end
|
48
|
+
|
28
49
|
it "returns error on invalid host" do
|
29
|
-
valid, error, cert = SSLTest.test("https://
|
30
|
-
error.must_equal 'hostname "
|
50
|
+
valid, error, cert = SSLTest.test("https://wrong.host.badssl.com/")
|
51
|
+
error.must_equal 'hostname "wrong.host.badssl.com" does not match the server certificate (*.badssl.com, badssl.com)'
|
31
52
|
valid.must_equal false
|
32
53
|
cert.must_be_instance_of OpenSSL::X509::Certificate
|
33
54
|
end
|
34
55
|
|
35
56
|
it "returns error on expired cert" do
|
36
|
-
valid, error, cert = SSLTest.test("https://
|
57
|
+
valid, error, cert = SSLTest.test("https://expired.badssl.com/")
|
37
58
|
error.must_equal "error code 10: certificate has expired"
|
38
59
|
valid.must_equal false
|
39
60
|
cert.must_be_instance_of OpenSSL::X509::Certificate
|
@@ -55,9 +76,9 @@ describe SSLTest do
|
|
55
76
|
|
56
77
|
# Not implemented yet
|
57
78
|
# it "returns error on revoked cert" do
|
58
|
-
# valid, error, cert = SSLTest.test("https://revoked.
|
59
|
-
# valid.must_equal false
|
79
|
+
# valid, error, cert = SSLTest.test("https://revoked.badssl.com/")
|
60
80
|
# error.must_equal "error code XX: certificate has been revoked"
|
81
|
+
# valid.must_equal false
|
61
82
|
# cert.must_be_instance_of OpenSSL::X509::Certificate
|
62
83
|
# end
|
63
84
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ssl-test
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Adrien Jarthon
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2018-03-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -28,16 +28,30 @@ dependencies:
|
|
28
28
|
name: rake
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- - "
|
31
|
+
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
33
|
+
version: '0'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- - "
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: minitest
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
39
53
|
- !ruby/object:Gem::Version
|
40
|
-
version: '
|
54
|
+
version: '0'
|
41
55
|
description:
|
42
56
|
email:
|
43
57
|
- jobs@adrienjarthon.com
|
@@ -46,6 +60,7 @@ extensions: []
|
|
46
60
|
extra_rdoc_files: []
|
47
61
|
files:
|
48
62
|
- ".gitignore"
|
63
|
+
- ".travis.yml"
|
49
64
|
- Gemfile
|
50
65
|
- LICENSE.txt
|
51
66
|
- README.md
|
@@ -73,10 +88,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
73
88
|
version: '0'
|
74
89
|
requirements: []
|
75
90
|
rubyforge_project:
|
76
|
-
rubygems_version: 2.
|
91
|
+
rubygems_version: 2.7.3
|
77
92
|
signing_key:
|
78
93
|
specification_version: 4
|
79
94
|
summary: Test website SSL certificate validity
|
80
95
|
test_files:
|
81
96
|
- test/ssl-test_test.rb
|
82
|
-
has_rdoc:
|