ssl-test 1.5.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/ruby.yml +8 -0
- data/README.md +41 -11
- data/lib/ssl-test/crl.rb +23 -12
- data/lib/ssl-test/memory_store.rb +81 -0
- data/lib/ssl-test/ocsp.rb +15 -10
- data/lib/ssl-test.rb +34 -23
- data/spec/cache_backends_spec.rb +103 -0
- data/spec/fixtures/digicert_com_ca_bundle.pem +25 -27
- data/spec/fixtures/digicert_com_client.pem +25 -25
- data/spec/fixtures/google_com_ca_bundle.pem +46 -77
- data/spec/fixtures/google_com_client.pem +46 -77
- data/spec/fixtures/revoked_badssl_ca_bundle.pem +19 -21
- data/spec/fixtures/revoked_badssl_client.pem +19 -19
- data/spec/fixtures/www_demarches-simplifiees_fr_ca_bundle.pem +126 -102
- data/spec/fixtures/www_demarches-simplifiees_fr_client.pem +54 -50
- data/spec/fixtures/www_github_com_ca_bundle.pem +53 -61
- data/spec/fixtures/www_github_com_client.pem +22 -25
- data/spec/memory_store_spec.rb +79 -0
- data/spec/ssl-test_spec.rb +95 -77
- data/ssl-test.gemspec +7 -0
- metadata +63 -2
data/spec/ssl-test_spec.rb
CHANGED
|
@@ -2,13 +2,24 @@ require "ssl-test"
|
|
|
2
2
|
require "benchmark"
|
|
3
3
|
require 'webrick'
|
|
4
4
|
require 'webrick/httpproxy'
|
|
5
|
+
require 'rspec/retry'
|
|
5
6
|
|
|
6
7
|
# Uncomment for debug logging:
|
|
7
8
|
# require "logger"
|
|
8
9
|
# SSLTest.logger = Logger.new(STDOUT)
|
|
9
10
|
|
|
11
|
+
RSpec.configure do |config|
|
|
12
|
+
# The error/revocation examples below hit several public TLS test endpoints
|
|
13
|
+
# (badssl.com, testserver.host, ssl.com) which intermittently reset connections
|
|
14
|
+
# under load. They're spread across a few providers to avoid hammering a single
|
|
15
|
+
# one, and examples tagged `:retry` are re-run a few times (via rspec-retry) so
|
|
16
|
+
# transient network blips don't fail the suite.
|
|
17
|
+
config.verbose_retry = true
|
|
18
|
+
config.default_sleep_interval = 1
|
|
19
|
+
end
|
|
20
|
+
|
|
10
21
|
describe SSLTest do
|
|
11
|
-
before { SSLTest.
|
|
22
|
+
before { SSLTest.cache.clear }
|
|
12
23
|
|
|
13
24
|
let(:proxy_thread) { nil }
|
|
14
25
|
|
|
@@ -48,36 +59,36 @@ describe SSLTest do
|
|
|
48
59
|
expect(cert).to be_a OpenSSL::X509::Certificate
|
|
49
60
|
end
|
|
50
61
|
|
|
51
|
-
it "returns error on self signed certificate" do
|
|
52
|
-
valid, error, cert = SSLTest.test("https://self-signed.
|
|
62
|
+
it "returns error on self signed certificate", :retry => 5 do
|
|
63
|
+
valid, error, cert = SSLTest.test("https://self-signed.testserver.host/")
|
|
53
64
|
expect(error).to eq ("error code 18: self-signed certificate")
|
|
54
65
|
expect(valid).to eq(false)
|
|
55
66
|
expect(cert).to be_a OpenSSL::X509::Certificate
|
|
56
67
|
end
|
|
57
68
|
|
|
58
|
-
it "returns error on incomplete chain" do
|
|
69
|
+
it "returns error on incomplete chain", :retry => 5 do
|
|
59
70
|
valid, error, cert = SSLTest.test("https://incomplete-chain.badssl.com/")
|
|
60
71
|
expect(error).to eq ("error code 20: unable to get local issuer certificate")
|
|
61
72
|
expect(valid).to eq(false)
|
|
62
73
|
expect(cert).to be_a OpenSSL::X509::Certificate
|
|
63
74
|
end
|
|
64
75
|
|
|
65
|
-
it "returns error on untrusted root" do
|
|
66
|
-
valid, error, cert = SSLTest.test("https://untrusted-root.
|
|
76
|
+
it "returns error on untrusted root", :retry => 5 do
|
|
77
|
+
valid, error, cert = SSLTest.test("https://untrusted-root.testserver.host/")
|
|
67
78
|
expect(error).to eq ("error code 19: self-signed certificate in certificate chain")
|
|
68
79
|
expect(valid).to eq(false)
|
|
69
80
|
expect(cert).to be_a OpenSSL::X509::Certificate
|
|
70
81
|
end
|
|
71
82
|
|
|
72
|
-
it "returns error on invalid host" do
|
|
83
|
+
it "returns error on invalid host", :retry => 5 do
|
|
73
84
|
valid, error, cert = SSLTest.test("https://wrong.host.badssl.com/")
|
|
74
85
|
expect(error).to include('error code 62: hostname mismatch')
|
|
75
86
|
expect(valid).to eq(false)
|
|
76
87
|
expect(cert).to be_a OpenSSL::X509::Certificate
|
|
77
88
|
end
|
|
78
89
|
|
|
79
|
-
it "returns error on expired cert" do
|
|
80
|
-
valid, error, cert = SSLTest.test("https://expired.
|
|
90
|
+
it "returns error on expired cert", :retry => 5 do
|
|
91
|
+
valid, error, cert = SSLTest.test("https://expired-rsa-dv.ssl.com/")
|
|
81
92
|
expect(error).to eq ("error code 10: certificate has expired")
|
|
82
93
|
expect(valid).to eq(false)
|
|
83
94
|
expect(cert).to be_a OpenSSL::X509::Certificate
|
|
@@ -86,7 +97,7 @@ describe SSLTest do
|
|
|
86
97
|
it "returns undetermined state on unhandled error" do
|
|
87
98
|
valid, error, cert = SSLTest.test("https://pijoinlrfgind.com")
|
|
88
99
|
expect(error).to include("SSL certificate test failed: Failed to open TCP connection to pijoinlrfgind.com:443")
|
|
89
|
-
expect(error).to
|
|
100
|
+
expect(error).to match(/name.*not known/i)
|
|
90
101
|
expect(valid).to be_nil
|
|
91
102
|
expect(cert).to be_nil
|
|
92
103
|
end
|
|
@@ -94,13 +105,13 @@ describe SSLTest do
|
|
|
94
105
|
it "stops on timeouts" do
|
|
95
106
|
valid, error, cert = SSLTest.test("https://updown.io", open_timeout: 0)
|
|
96
107
|
expect(error).to include("SSL certificate test failed")
|
|
97
|
-
expect(error).to
|
|
108
|
+
expect(error).to match(/timeout/i)
|
|
98
109
|
expect(valid).to be_nil
|
|
99
110
|
expect(cert).to be_nil
|
|
100
111
|
end
|
|
101
112
|
|
|
102
113
|
it "reports revocation exceptions" do
|
|
103
|
-
expect(SSLTest).to receive(:
|
|
114
|
+
expect(SSLTest).to receive(:follow_crl_redirects).and_raise(ArgumentError.new("test"))
|
|
104
115
|
valid, error, cert = SSLTest.test("https://digicert.com")
|
|
105
116
|
expect(error).to eq ("SSL certificate test failed: test")
|
|
106
117
|
expect(valid).to be_nil
|
|
@@ -108,45 +119,48 @@ describe SSLTest do
|
|
|
108
119
|
end
|
|
109
120
|
|
|
110
121
|
it "returns error on revoked cert (OCSP)" do
|
|
122
|
+
# CRL is tried first; disable it so OCSP performs the revocation check
|
|
123
|
+
expect(SSLTest).to receive(:test_crl_revocation).once.and_return([false, "skip CRL", nil])
|
|
111
124
|
expect(SSLTest).to receive(:follow_ocsp_redirects).once.and_call_original
|
|
112
|
-
expect(SSLTest).not_to receive(:follow_crl_redirects)
|
|
113
125
|
valid, error, cert = SSLTest.test("https://revoked-rsa-dv.ssl.com/")
|
|
114
|
-
expect(error).to eq ("SSL certificate revoked: The certificate was revoked for an
|
|
126
|
+
expect(error).to eq ("SSL certificate revoked: The certificate was revoked for an unspecified reason (revocation date: 2026-06-09 14:37:38 UTC)")
|
|
115
127
|
expect(valid).to eq(false)
|
|
116
128
|
expect(cert).to be_a OpenSSL::X509::Certificate
|
|
117
129
|
end
|
|
118
130
|
|
|
119
|
-
it "returns error on revoked cert (CRL)" do
|
|
120
|
-
|
|
131
|
+
it "returns error on revoked cert (CRL)", :retry => 5 do
|
|
132
|
+
# CRL is tried first and detects the revocation, so OCSP is never used
|
|
121
133
|
expect(SSLTest).to receive(:follow_crl_redirects).once.and_call_original
|
|
134
|
+
expect(SSLTest).not_to receive(:test_ocsp_revocation)
|
|
122
135
|
valid, error, cert = SSLTest.test("https://revoked.badssl.com/")
|
|
123
|
-
expect(error).to eq ("SSL certificate revoked: Key Compromise (revocation date:
|
|
136
|
+
expect(error).to eq ("SSL certificate revoked: Key Compromise (revocation date: 2026-05-12 21:01:31 UTC)")
|
|
124
137
|
expect(valid).to eq(false)
|
|
125
138
|
expect(cert).to be_a OpenSSL::X509::Certificate
|
|
126
139
|
end
|
|
127
140
|
|
|
128
141
|
it "stops following redirection after the limit for the revoked certs check" do
|
|
129
142
|
valid, error, cert = SSLTest.test("https://github.com/", redirection_limit: 0)
|
|
130
|
-
expect(error).to include("Revocation test couldn't be performed:
|
|
143
|
+
expect(error).to include("Revocation test couldn't be performed: CRL: Missing crlDistributionPoints extension")
|
|
144
|
+
expect(error).to include("OCSP: Request failed")
|
|
131
145
|
expect(error).to include("Too many redirections (> 0)")
|
|
132
146
|
expect(valid).to eq(true)
|
|
133
147
|
expect(cert).to be_a OpenSSL::X509::Certificate
|
|
134
148
|
end
|
|
135
149
|
|
|
136
150
|
it "warns when the OCSP URI is missing" do
|
|
137
|
-
# Disable CRL
|
|
138
|
-
expect(SSLTest).to receive(:test_crl_revocation).
|
|
151
|
+
# Disable CRL (tried first) to see the OCSP error message
|
|
152
|
+
expect(SSLTest).to receive(:test_crl_revocation).twice.and_return([false, "skip CRL", nil])
|
|
139
153
|
expect(SSLTest).to receive(:follow_ocsp_redirects).once.and_call_original
|
|
140
154
|
valid, error, cert = SSLTest.test("https://google.com")
|
|
141
|
-
expect(error).to eq ("Revocation test couldn't be performed: OCSP: Missing OCSP URI in authorityInfoAccess extension
|
|
155
|
+
expect(error).to eq ("Revocation test couldn't be performed: CRL: skip CRL, OCSP: Missing OCSP URI in authorityInfoAccess extension")
|
|
142
156
|
expect(valid).to eq(true)
|
|
143
157
|
expect(cert).to be_a OpenSSL::X509::Certificate
|
|
144
158
|
end
|
|
145
159
|
|
|
146
160
|
it "works with CRL only" do
|
|
147
|
-
#
|
|
148
|
-
expect(SSLTest).to receive(:test_ocsp_revocation).twice.and_return([false, "skip OCSP", nil])
|
|
161
|
+
# CRL is tried first and succeeds for both certs, so OCSP is never used
|
|
149
162
|
expect(SSLTest).to receive(:follow_crl_redirects).twice.and_call_original
|
|
163
|
+
expect(SSLTest).not_to receive(:test_ocsp_revocation)
|
|
150
164
|
valid, error, cert = SSLTest.test("https://www.demarches-simplifiees.fr")
|
|
151
165
|
expect(error).to be_nil
|
|
152
166
|
expect(valid).to eq(true)
|
|
@@ -158,20 +172,20 @@ describe SSLTest do
|
|
|
158
172
|
expect(SSLTest).to receive(:test_ocsp_revocation).once.and_return([false, "skip OCSP", nil])
|
|
159
173
|
expect(SSLTest).not_to receive(:follow_crl_redirects)
|
|
160
174
|
valid, error, cert = SSLTest.test("https://github.com")
|
|
161
|
-
expect(error).to eq ("Revocation test couldn't be performed:
|
|
175
|
+
expect(error).to eq ("Revocation test couldn't be performed: CRL: Missing crlDistributionPoints extension, OCSP: skip OCSP")
|
|
162
176
|
expect(valid).to eq(true)
|
|
163
177
|
expect(cert).to be_a OpenSSL::X509::Certificate
|
|
164
178
|
end
|
|
165
179
|
|
|
166
|
-
it "works with OCSP for first cert and CRL for intermediate (
|
|
180
|
+
it "works with OCSP for first cert and CRL for intermediate (GitHub)" do
|
|
167
181
|
expect(SSLTest).to receive(:follow_ocsp_redirects).once.and_call_original
|
|
168
182
|
expect(SSLTest).to receive(:follow_crl_redirects).once.and_call_original
|
|
169
|
-
valid, error, cert = SSLTest.test("https://
|
|
183
|
+
valid, error, cert = SSLTest.test("https://github.com")
|
|
170
184
|
expect(error).to be_nil
|
|
171
185
|
expect(valid).to eq(true)
|
|
172
186
|
expect(cert).to be_a OpenSSL::X509::Certificate
|
|
173
187
|
# make sure both were used
|
|
174
|
-
expect(SSLTest.
|
|
188
|
+
expect(SSLTest.cache.size).to match({
|
|
175
189
|
crl: hash_including(lists: 1),
|
|
176
190
|
ocsp: hash_including(responses: 1, errors: 0)
|
|
177
191
|
})
|
|
@@ -222,35 +236,8 @@ describe SSLTest do
|
|
|
222
236
|
end
|
|
223
237
|
end
|
|
224
238
|
|
|
225
|
-
describe '.cache_size' do
|
|
226
|
-
before { SSLTest.flush_cache }
|
|
227
|
-
|
|
228
|
-
it "returns 0 by default" do
|
|
229
|
-
expect(SSLTest.cache_size).to eq({
|
|
230
|
-
crl: { bytes: 0, lists: 0 },
|
|
231
|
-
ocsp: { bytes: 0, errors: 0, responses: 0 }
|
|
232
|
-
})
|
|
233
|
-
end
|
|
234
|
-
|
|
235
|
-
it "returns CRL cache size properly" do
|
|
236
|
-
SSLTest.send(:follow_crl_redirects, URI("http://crl.certigna.fr/certigna.crl")) # 1.1k
|
|
237
|
-
SSLTest.send(:follow_crl_redirects, URI("http://crl3.digicert.com/DigiCertTLSHybridECCSHA3842020CA1-1.crl")) # 26k
|
|
238
|
-
expect(SSLTest.cache_size[:crl][:lists]).to eq(2)
|
|
239
|
-
expect(SSLTest.cache_size[:crl][:bytes]).to be > 6000
|
|
240
|
-
end
|
|
241
|
-
|
|
242
|
-
it "returns OCSP cache size properly" do
|
|
243
|
-
SSLTest.test("https://google.com")
|
|
244
|
-
expect(SSLTest.cache_size[:ocsp][:responses]).to eq(1)
|
|
245
|
-
expect(SSLTest.cache_size[:ocsp][:errors]).to eq(0)
|
|
246
|
-
expect(SSLTest.cache_size[:ocsp][:bytes]).to be > 150
|
|
247
|
-
expect(SSLTest.cache_size[:crl][:lists]).to eq(1)
|
|
248
|
-
expect(SSLTest.cache_size[:crl][:bytes]).to be > 500
|
|
249
|
-
end
|
|
250
|
-
end
|
|
251
|
-
|
|
252
239
|
describe '.follow_crl_redirects' do
|
|
253
|
-
before { SSLTest.
|
|
240
|
+
before { SSLTest.cache.clear }
|
|
254
241
|
# 19MB: http://crl3.digicert.com/ssca-sha2-g6.crl
|
|
255
242
|
it "fetch CRL list and updates cache" do
|
|
256
243
|
uri = URI("http://crl.certigna.fr/certigna.crl")
|
|
@@ -259,11 +246,11 @@ describe SSLTest do
|
|
|
259
246
|
expect(error).to be_nil
|
|
260
247
|
|
|
261
248
|
# Check cache status
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
expect(
|
|
265
|
-
expect(
|
|
266
|
-
expect(
|
|
249
|
+
cache_key = "ssl-test/crl/#{uri}"
|
|
250
|
+
entry = SSLTest.cache.read(cache_key)
|
|
251
|
+
expect(entry).not_to be_nil
|
|
252
|
+
expect(entry.keys).to match_array [:body, :expires, :etag, :last_mod]
|
|
253
|
+
expect(entry[:expires]).to be > (Time.now + 3590)
|
|
267
254
|
|
|
268
255
|
# Make sure we return value from cache
|
|
269
256
|
body2, error2 = nil, nil
|
|
@@ -272,7 +259,7 @@ describe SSLTest do
|
|
|
272
259
|
expect(body2).to be(body) # using cache
|
|
273
260
|
|
|
274
261
|
# Make sure we return cached value in case of 304
|
|
275
|
-
cache
|
|
262
|
+
SSLTest.cache.write(cache_key, entry.merge(expires: Time.now), expires_in: nil) # cache is now expired
|
|
276
263
|
body2, error2 = nil, nil
|
|
277
264
|
time = Benchmark.realtime { body2, error2 = SSLTest.send(:follow_crl_redirects, uri) }
|
|
278
265
|
expect(time).to be > 0.001 # a request is made
|
|
@@ -280,6 +267,33 @@ describe SSLTest do
|
|
|
280
267
|
end
|
|
281
268
|
end
|
|
282
269
|
|
|
270
|
+
describe '.cache' do
|
|
271
|
+
# Restore the default in-process store after tests that swap the backend so
|
|
272
|
+
# global state doesn't leak between examples.
|
|
273
|
+
after { SSLTest.cache = SSLTest::MemoryStore.new }
|
|
274
|
+
|
|
275
|
+
it "defaults to an in-process MemoryStore" do
|
|
276
|
+
SSLTest.instance_variable_set(:@cache, nil) # reset memoized default
|
|
277
|
+
expect(SSLTest.cache).to be_a SSLTest::MemoryStore
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
it "uses the configured backend for CRL and OCSP" do
|
|
281
|
+
store = SSLTest::MemoryStore.new
|
|
282
|
+
SSLTest.cache = store
|
|
283
|
+
expect(store).to receive(:write).at_least(:once).and_call_original
|
|
284
|
+
expect(store).to receive(:read).at_least(:once).and_call_original
|
|
285
|
+
SSLTest.test("https://github.com")
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
it "cache_size (removed in 2.0) raises pointing to cache.size" do
|
|
289
|
+
expect { SSLTest.cache_size }.to raise_error(NoMethodError, /SSLTest\.cache\.size/)
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
it "flush_cache (removed in 2.0) raises pointing to cache.clear" do
|
|
293
|
+
expect { SSLTest.flush_cache }.to raise_error(NoMethodError, /SSLTest\.cache\.clear/)
|
|
294
|
+
end
|
|
295
|
+
end
|
|
296
|
+
|
|
283
297
|
describe '.test_cert' do
|
|
284
298
|
it "returns no error on valid SNI website" do
|
|
285
299
|
cert = OpenSSL::X509::Certificate.new(File.read(File.join(__dir__, 'fixtures/www_mycs_com_client.pem')))
|
|
@@ -323,7 +337,7 @@ describe SSLTest do
|
|
|
323
337
|
it "reports revocation exceptions" do
|
|
324
338
|
cert = OpenSSL::X509::Certificate.new(File.read(File.join(__dir__, 'fixtures/digicert_com_client.pem')))
|
|
325
339
|
ca_bundle = OpenSSL::X509::Certificate.load(File.read(File.join(__dir__, 'fixtures/digicert_com_ca_bundle.pem')))
|
|
326
|
-
expect(SSLTest).to receive(:
|
|
340
|
+
expect(SSLTest).to receive(:follow_crl_redirects).and_raise(ArgumentError.new("test"))
|
|
327
341
|
valid, error, cert = SSLTest.test_cert(cert, ca_bundle)
|
|
328
342
|
expect(error).to eq("SSL certificate test failed: test")
|
|
329
343
|
expect(valid).to be_nil
|
|
@@ -334,8 +348,9 @@ describe SSLTest do
|
|
|
334
348
|
cert = OpenSSL::X509::Certificate.new(File.read(File.join(__dir__, 'fixtures/revoked_rsa_dv_client.pem')))
|
|
335
349
|
ca_bundle = OpenSSL::X509::Certificate.load(File.read(File.join(__dir__, 'fixtures/revoked_rsa_dv_ca_bundle.pem')))
|
|
336
350
|
|
|
351
|
+
# CRL is tried first; disable it so OCSP performs the revocation check
|
|
352
|
+
expect(SSLTest).to receive(:test_crl_revocation).once.and_return([false, "skip CRL", nil])
|
|
337
353
|
expect(SSLTest).to receive(:follow_ocsp_redirects).once.and_call_original
|
|
338
|
-
expect(SSLTest).not_to receive(:follow_crl_redirects)
|
|
339
354
|
|
|
340
355
|
valid, error, cert = SSLTest.test_cert(cert, ca_bundle)
|
|
341
356
|
expect(error).to eq ("SSL certificate revoked: The certificate was revoked for an unknown reason (revocation date: 2025-06-09 15:07:39 UTC)")
|
|
@@ -347,10 +362,11 @@ describe SSLTest do
|
|
|
347
362
|
cert = OpenSSL::X509::Certificate.new(File.read(File.join(__dir__, 'fixtures/revoked_badssl_client.pem')))
|
|
348
363
|
ca_bundle = OpenSSL::X509::Certificate.load(File.read(File.join(__dir__, 'fixtures/revoked_badssl_ca_bundle.pem')))
|
|
349
364
|
|
|
350
|
-
|
|
365
|
+
# CRL is tried first and detects the revocation, so OCSP is never used
|
|
351
366
|
expect(SSLTest).to receive(:follow_crl_redirects).once.and_call_original
|
|
367
|
+
expect(SSLTest).not_to receive(:test_ocsp_revocation)
|
|
352
368
|
valid, error, cert = SSLTest.test_cert(cert, ca_bundle)
|
|
353
|
-
expect(error).to eq ("SSL certificate revoked: Key Compromise (revocation date:
|
|
369
|
+
expect(error).to eq ("SSL certificate revoked: Key Compromise (revocation date: 2026-05-12 21:01:31 UTC)")
|
|
354
370
|
expect(valid).to eq(false)
|
|
355
371
|
expect(cert).to eq(cert)
|
|
356
372
|
end
|
|
@@ -360,7 +376,8 @@ describe SSLTest do
|
|
|
360
376
|
ca_bundle = OpenSSL::X509::Certificate.load(File.read(File.join(__dir__, 'fixtures/www_github_com_ca_bundle.pem')))
|
|
361
377
|
|
|
362
378
|
valid, error, cert = SSLTest.test_cert(cert, ca_bundle, redirection_limit: 0)
|
|
363
|
-
expect(error).to include("Revocation test couldn't be performed:
|
|
379
|
+
expect(error).to include("Revocation test couldn't be performed: CRL: Missing crlDistributionPoints extension")
|
|
380
|
+
expect(error).to include("OCSP: Request failed")
|
|
364
381
|
expect(error).to include("Too many redirections (> 0)")
|
|
365
382
|
expect(valid).to eq(true)
|
|
366
383
|
expect(cert).to eq(cert)
|
|
@@ -370,12 +387,12 @@ describe SSLTest do
|
|
|
370
387
|
cert = OpenSSL::X509::Certificate.new(File.read(File.join(__dir__, 'fixtures/google_com_client.pem')))
|
|
371
388
|
ca_bundle = OpenSSL::X509::Certificate.load(File.read(File.join(__dir__, 'fixtures/google_com_ca_bundle.pem')))
|
|
372
389
|
|
|
373
|
-
# Disable CRL
|
|
374
|
-
expect(SSLTest).to receive(:test_crl_revocation).
|
|
390
|
+
# Disable CRL (tried first) to see the OCSP error message
|
|
391
|
+
expect(SSLTest).to receive(:test_crl_revocation).twice.and_return([false, "skip CRL", nil])
|
|
375
392
|
expect(SSLTest).to receive(:follow_ocsp_redirects).once.and_call_original
|
|
376
393
|
|
|
377
394
|
valid, error, cert = SSLTest.test_cert(cert, ca_bundle)
|
|
378
|
-
expect(error).to eq ("Revocation test couldn't be performed: OCSP: Missing OCSP URI in authorityInfoAccess extension
|
|
395
|
+
expect(error).to eq ("Revocation test couldn't be performed: CRL: skip CRL, OCSP: Missing OCSP URI in authorityInfoAccess extension")
|
|
379
396
|
expect(valid).to eq(true)
|
|
380
397
|
expect(cert).to eq(cert)
|
|
381
398
|
end
|
|
@@ -384,9 +401,9 @@ describe SSLTest do
|
|
|
384
401
|
cert = OpenSSL::X509::Certificate.new(File.read(File.join(__dir__, 'fixtures/www_demarches-simplifiees_fr_client.pem')))
|
|
385
402
|
ca_bundle = OpenSSL::X509::Certificate.load(File.read(File.join(__dir__, 'fixtures/www_demarches-simplifiees_fr_ca_bundle.pem')))
|
|
386
403
|
|
|
387
|
-
#
|
|
388
|
-
expect(SSLTest).to receive(:test_ocsp_revocation).twice.and_return([false, "skip OCSP", nil])
|
|
404
|
+
# CRL is tried first and succeeds for both certs, so OCSP is never used
|
|
389
405
|
expect(SSLTest).to receive(:follow_crl_redirects).twice.and_call_original
|
|
406
|
+
expect(SSLTest).not_to receive(:test_ocsp_revocation)
|
|
390
407
|
|
|
391
408
|
valid, error, cert = SSLTest.test_cert(cert, ca_bundle)
|
|
392
409
|
expect(error).to be_nil
|
|
@@ -403,25 +420,25 @@ describe SSLTest do
|
|
|
403
420
|
expect(SSLTest).not_to receive(:follow_crl_redirects)
|
|
404
421
|
|
|
405
422
|
valid, error, cert = SSLTest.test_cert(cert, ca_bundle)
|
|
406
|
-
expect(error).to eq ("Revocation test couldn't be performed:
|
|
423
|
+
expect(error).to eq ("Revocation test couldn't be performed: CRL: Missing crlDistributionPoints extension, OCSP: skip OCSP")
|
|
407
424
|
expect(valid).to eq(true)
|
|
408
425
|
expect(cert).to eq(cert)
|
|
409
426
|
|
|
410
427
|
end
|
|
411
428
|
|
|
412
|
-
it "works with OCSP for first cert and CRL for intermediate (
|
|
429
|
+
it "works with OCSP for first cert and CRL for intermediate (GitHub)" do
|
|
413
430
|
expect(SSLTest).to receive(:follow_ocsp_redirects).once.and_call_original
|
|
414
431
|
expect(SSLTest).to receive(:follow_crl_redirects).once.and_call_original
|
|
415
432
|
|
|
416
|
-
cert = OpenSSL::X509::Certificate.new(File.read(File.join(__dir__, 'fixtures/
|
|
417
|
-
ca_bundle = OpenSSL::X509::Certificate.load(File.read(File.join(__dir__, 'fixtures/
|
|
433
|
+
cert = OpenSSL::X509::Certificate.new(File.read(File.join(__dir__, 'fixtures/www_github_com_client.pem')))
|
|
434
|
+
ca_bundle = OpenSSL::X509::Certificate.load(File.read(File.join(__dir__, 'fixtures/www_github_com_ca_bundle.pem')))
|
|
418
435
|
|
|
419
436
|
valid, error, cert = SSLTest.test_cert(cert, ca_bundle)
|
|
420
437
|
expect(error).to be_nil
|
|
421
438
|
expect(valid).to eq(true)
|
|
422
439
|
expect(cert).to eq(cert)
|
|
423
440
|
# make sure both were used
|
|
424
|
-
expect(SSLTest.
|
|
441
|
+
expect(SSLTest.cache.size).to match({
|
|
425
442
|
crl: hash_including(lists: 1),
|
|
426
443
|
ocsp: hash_including(responses: 1, errors: 0)
|
|
427
444
|
})
|
|
@@ -453,7 +470,8 @@ describe SSLTest do
|
|
|
453
470
|
expect(valid).to eq(true)
|
|
454
471
|
expect(cert).to eq(cert)
|
|
455
472
|
|
|
456
|
-
|
|
473
|
+
# CRL is tried first, so both certs are checked via CRL (GET) through the proxy
|
|
474
|
+
expect($proxy).to have_received(:do_GET).twice
|
|
457
475
|
end
|
|
458
476
|
end
|
|
459
477
|
|
data/ssl-test.gemspec
CHANGED
|
@@ -20,5 +20,12 @@ Gem::Specification.new do |spec|
|
|
|
20
20
|
spec.add_development_dependency "bundler", ">= 1.7"
|
|
21
21
|
spec.add_development_dependency "rake"
|
|
22
22
|
spec.add_development_dependency "rspec"
|
|
23
|
+
spec.add_development_dependency "rspec-retry"
|
|
23
24
|
spec.add_development_dependency "webrick"
|
|
25
|
+
# Used to verify SSLTest.cache works with the classic Rails/ActiveSupport
|
|
26
|
+
# cache stores (MemoryStore, FileStore, NullStore, MemCacheStore via dalli,
|
|
27
|
+
# RedisCacheStore via redis).
|
|
28
|
+
spec.add_development_dependency "activesupport"
|
|
29
|
+
spec.add_development_dependency "dalli"
|
|
30
|
+
spec.add_development_dependency "redis"
|
|
24
31
|
end
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ssl-test
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 2.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Adrien Rey-Jarthon
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date:
|
|
10
|
+
date: 2026-06-17 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: bundler
|
|
@@ -51,6 +51,20 @@ dependencies:
|
|
|
51
51
|
- - ">="
|
|
52
52
|
- !ruby/object:Gem::Version
|
|
53
53
|
version: '0'
|
|
54
|
+
- !ruby/object:Gem::Dependency
|
|
55
|
+
name: rspec-retry
|
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
|
57
|
+
requirements:
|
|
58
|
+
- - ">="
|
|
59
|
+
- !ruby/object:Gem::Version
|
|
60
|
+
version: '0'
|
|
61
|
+
type: :development
|
|
62
|
+
prerelease: false
|
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
64
|
+
requirements:
|
|
65
|
+
- - ">="
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: '0'
|
|
54
68
|
- !ruby/object:Gem::Dependency
|
|
55
69
|
name: webrick
|
|
56
70
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -65,6 +79,48 @@ dependencies:
|
|
|
65
79
|
- - ">="
|
|
66
80
|
- !ruby/object:Gem::Version
|
|
67
81
|
version: '0'
|
|
82
|
+
- !ruby/object:Gem::Dependency
|
|
83
|
+
name: activesupport
|
|
84
|
+
requirement: !ruby/object:Gem::Requirement
|
|
85
|
+
requirements:
|
|
86
|
+
- - ">="
|
|
87
|
+
- !ruby/object:Gem::Version
|
|
88
|
+
version: '0'
|
|
89
|
+
type: :development
|
|
90
|
+
prerelease: false
|
|
91
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
92
|
+
requirements:
|
|
93
|
+
- - ">="
|
|
94
|
+
- !ruby/object:Gem::Version
|
|
95
|
+
version: '0'
|
|
96
|
+
- !ruby/object:Gem::Dependency
|
|
97
|
+
name: dalli
|
|
98
|
+
requirement: !ruby/object:Gem::Requirement
|
|
99
|
+
requirements:
|
|
100
|
+
- - ">="
|
|
101
|
+
- !ruby/object:Gem::Version
|
|
102
|
+
version: '0'
|
|
103
|
+
type: :development
|
|
104
|
+
prerelease: false
|
|
105
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
106
|
+
requirements:
|
|
107
|
+
- - ">="
|
|
108
|
+
- !ruby/object:Gem::Version
|
|
109
|
+
version: '0'
|
|
110
|
+
- !ruby/object:Gem::Dependency
|
|
111
|
+
name: redis
|
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
|
113
|
+
requirements:
|
|
114
|
+
- - ">="
|
|
115
|
+
- !ruby/object:Gem::Version
|
|
116
|
+
version: '0'
|
|
117
|
+
type: :development
|
|
118
|
+
prerelease: false
|
|
119
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
120
|
+
requirements:
|
|
121
|
+
- - ">="
|
|
122
|
+
- !ruby/object:Gem::Version
|
|
123
|
+
version: '0'
|
|
68
124
|
email:
|
|
69
125
|
- jobs@adrienjarthon.com
|
|
70
126
|
executables: []
|
|
@@ -80,8 +136,10 @@ files:
|
|
|
80
136
|
- Rakefile
|
|
81
137
|
- lib/ssl-test.rb
|
|
82
138
|
- lib/ssl-test/crl.rb
|
|
139
|
+
- lib/ssl-test/memory_store.rb
|
|
83
140
|
- lib/ssl-test/object_size.rb
|
|
84
141
|
- lib/ssl-test/ocsp.rb
|
|
142
|
+
- spec/cache_backends_spec.rb
|
|
85
143
|
- spec/fixtures/digicert_com_ca_bundle.pem
|
|
86
144
|
- spec/fixtures/digicert_com_client.pem
|
|
87
145
|
- spec/fixtures/expired_cert_ca_bundle.pem
|
|
@@ -102,6 +160,7 @@ files:
|
|
|
102
160
|
- spec/fixtures/www_github_com_client.pem
|
|
103
161
|
- spec/fixtures/www_mycs_com_ca_bundle.pem
|
|
104
162
|
- spec/fixtures/www_mycs_com_client.pem
|
|
163
|
+
- spec/memory_store_spec.rb
|
|
105
164
|
- spec/ssl-test_spec.rb
|
|
106
165
|
- ssl-test.gemspec
|
|
107
166
|
homepage: https://github.com/jarthod/ssl-test
|
|
@@ -126,6 +185,7 @@ rubygems_version: 3.6.2
|
|
|
126
185
|
specification_version: 4
|
|
127
186
|
summary: Test website SSL certificate validity
|
|
128
187
|
test_files:
|
|
188
|
+
- spec/cache_backends_spec.rb
|
|
129
189
|
- spec/fixtures/digicert_com_ca_bundle.pem
|
|
130
190
|
- spec/fixtures/digicert_com_client.pem
|
|
131
191
|
- spec/fixtures/expired_cert_ca_bundle.pem
|
|
@@ -146,4 +206,5 @@ test_files:
|
|
|
146
206
|
- spec/fixtures/www_github_com_client.pem
|
|
147
207
|
- spec/fixtures/www_mycs_com_ca_bundle.pem
|
|
148
208
|
- spec/fixtures/www_mycs_com_client.pem
|
|
209
|
+
- spec/memory_store_spec.rb
|
|
149
210
|
- spec/ssl-test_spec.rb
|