ssl-test 1.5.0 → 1.6.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/README.md +8 -7
- data/lib/ssl-test/crl.rb +3 -3
- data/lib/ssl-test.rb +9 -9
- 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/ssl-test_spec.rb +61 -42
- data/ssl-test.gemspec +1 -0
- metadata +16 -2
data/spec/ssl-test_spec.rb
CHANGED
|
@@ -2,11 +2,23 @@ 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.display_try_failure_messages = true
|
|
19
|
+
config.default_sleep_interval = 1
|
|
20
|
+
end
|
|
21
|
+
|
|
10
22
|
describe SSLTest do
|
|
11
23
|
before { SSLTest.flush_cache }
|
|
12
24
|
|
|
@@ -48,36 +60,36 @@ describe SSLTest do
|
|
|
48
60
|
expect(cert).to be_a OpenSSL::X509::Certificate
|
|
49
61
|
end
|
|
50
62
|
|
|
51
|
-
it "returns error on self signed certificate" do
|
|
52
|
-
valid, error, cert = SSLTest.test("https://self-signed.
|
|
63
|
+
it "returns error on self signed certificate", :retry => 5 do
|
|
64
|
+
valid, error, cert = SSLTest.test("https://self-signed.testserver.host/")
|
|
53
65
|
expect(error).to eq ("error code 18: self-signed certificate")
|
|
54
66
|
expect(valid).to eq(false)
|
|
55
67
|
expect(cert).to be_a OpenSSL::X509::Certificate
|
|
56
68
|
end
|
|
57
69
|
|
|
58
|
-
it "returns error on incomplete chain" do
|
|
70
|
+
it "returns error on incomplete chain", :retry => 5 do
|
|
59
71
|
valid, error, cert = SSLTest.test("https://incomplete-chain.badssl.com/")
|
|
60
72
|
expect(error).to eq ("error code 20: unable to get local issuer certificate")
|
|
61
73
|
expect(valid).to eq(false)
|
|
62
74
|
expect(cert).to be_a OpenSSL::X509::Certificate
|
|
63
75
|
end
|
|
64
76
|
|
|
65
|
-
it "returns error on untrusted root" do
|
|
66
|
-
valid, error, cert = SSLTest.test("https://untrusted-root.
|
|
77
|
+
it "returns error on untrusted root", :retry => 5 do
|
|
78
|
+
valid, error, cert = SSLTest.test("https://untrusted-root.testserver.host/")
|
|
67
79
|
expect(error).to eq ("error code 19: self-signed certificate in certificate chain")
|
|
68
80
|
expect(valid).to eq(false)
|
|
69
81
|
expect(cert).to be_a OpenSSL::X509::Certificate
|
|
70
82
|
end
|
|
71
83
|
|
|
72
|
-
it "returns error on invalid host" do
|
|
84
|
+
it "returns error on invalid host", :retry => 5 do
|
|
73
85
|
valid, error, cert = SSLTest.test("https://wrong.host.badssl.com/")
|
|
74
86
|
expect(error).to include('error code 62: hostname mismatch')
|
|
75
87
|
expect(valid).to eq(false)
|
|
76
88
|
expect(cert).to be_a OpenSSL::X509::Certificate
|
|
77
89
|
end
|
|
78
90
|
|
|
79
|
-
it "returns error on expired cert" do
|
|
80
|
-
valid, error, cert = SSLTest.test("https://expired.
|
|
91
|
+
it "returns error on expired cert", :retry => 5 do
|
|
92
|
+
valid, error, cert = SSLTest.test("https://expired-rsa-dv.ssl.com/")
|
|
81
93
|
expect(error).to eq ("error code 10: certificate has expired")
|
|
82
94
|
expect(valid).to eq(false)
|
|
83
95
|
expect(cert).to be_a OpenSSL::X509::Certificate
|
|
@@ -86,7 +98,7 @@ describe SSLTest do
|
|
|
86
98
|
it "returns undetermined state on unhandled error" do
|
|
87
99
|
valid, error, cert = SSLTest.test("https://pijoinlrfgind.com")
|
|
88
100
|
expect(error).to include("SSL certificate test failed: Failed to open TCP connection to pijoinlrfgind.com:443")
|
|
89
|
-
expect(error).to
|
|
101
|
+
expect(error).to match(/name.*not known/i)
|
|
90
102
|
expect(valid).to be_nil
|
|
91
103
|
expect(cert).to be_nil
|
|
92
104
|
end
|
|
@@ -94,13 +106,13 @@ describe SSLTest do
|
|
|
94
106
|
it "stops on timeouts" do
|
|
95
107
|
valid, error, cert = SSLTest.test("https://updown.io", open_timeout: 0)
|
|
96
108
|
expect(error).to include("SSL certificate test failed")
|
|
97
|
-
expect(error).to
|
|
109
|
+
expect(error).to match(/timeout/i)
|
|
98
110
|
expect(valid).to be_nil
|
|
99
111
|
expect(cert).to be_nil
|
|
100
112
|
end
|
|
101
113
|
|
|
102
114
|
it "reports revocation exceptions" do
|
|
103
|
-
expect(SSLTest).to receive(:
|
|
115
|
+
expect(SSLTest).to receive(:follow_crl_redirects).and_raise(ArgumentError.new("test"))
|
|
104
116
|
valid, error, cert = SSLTest.test("https://digicert.com")
|
|
105
117
|
expect(error).to eq ("SSL certificate test failed: test")
|
|
106
118
|
expect(valid).to be_nil
|
|
@@ -108,45 +120,48 @@ describe SSLTest do
|
|
|
108
120
|
end
|
|
109
121
|
|
|
110
122
|
it "returns error on revoked cert (OCSP)" do
|
|
123
|
+
# CRL is tried first; disable it so OCSP performs the revocation check
|
|
124
|
+
expect(SSLTest).to receive(:test_crl_revocation).once.and_return([false, "skip CRL", nil])
|
|
111
125
|
expect(SSLTest).to receive(:follow_ocsp_redirects).once.and_call_original
|
|
112
|
-
expect(SSLTest).not_to receive(:follow_crl_redirects)
|
|
113
126
|
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
|
|
127
|
+
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
128
|
expect(valid).to eq(false)
|
|
116
129
|
expect(cert).to be_a OpenSSL::X509::Certificate
|
|
117
130
|
end
|
|
118
131
|
|
|
119
|
-
it "returns error on revoked cert (CRL)" do
|
|
120
|
-
|
|
132
|
+
it "returns error on revoked cert (CRL)", :retry => 5 do
|
|
133
|
+
# CRL is tried first and detects the revocation, so OCSP is never used
|
|
121
134
|
expect(SSLTest).to receive(:follow_crl_redirects).once.and_call_original
|
|
135
|
+
expect(SSLTest).not_to receive(:test_ocsp_revocation)
|
|
122
136
|
valid, error, cert = SSLTest.test("https://revoked.badssl.com/")
|
|
123
|
-
expect(error).to eq ("SSL certificate revoked: Key Compromise (revocation date:
|
|
137
|
+
expect(error).to eq ("SSL certificate revoked: Key Compromise (revocation date: 2026-05-12 21:01:31 UTC)")
|
|
124
138
|
expect(valid).to eq(false)
|
|
125
139
|
expect(cert).to be_a OpenSSL::X509::Certificate
|
|
126
140
|
end
|
|
127
141
|
|
|
128
142
|
it "stops following redirection after the limit for the revoked certs check" do
|
|
129
143
|
valid, error, cert = SSLTest.test("https://github.com/", redirection_limit: 0)
|
|
130
|
-
expect(error).to include("Revocation test couldn't be performed:
|
|
144
|
+
expect(error).to include("Revocation test couldn't be performed: CRL: Missing crlDistributionPoints extension")
|
|
145
|
+
expect(error).to include("OCSP: Request failed")
|
|
131
146
|
expect(error).to include("Too many redirections (> 0)")
|
|
132
147
|
expect(valid).to eq(true)
|
|
133
148
|
expect(cert).to be_a OpenSSL::X509::Certificate
|
|
134
149
|
end
|
|
135
150
|
|
|
136
151
|
it "warns when the OCSP URI is missing" do
|
|
137
|
-
# Disable CRL
|
|
138
|
-
expect(SSLTest).to receive(:test_crl_revocation).
|
|
152
|
+
# Disable CRL (tried first) to see the OCSP error message
|
|
153
|
+
expect(SSLTest).to receive(:test_crl_revocation).twice.and_return([false, "skip CRL", nil])
|
|
139
154
|
expect(SSLTest).to receive(:follow_ocsp_redirects).once.and_call_original
|
|
140
155
|
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
|
|
156
|
+
expect(error).to eq ("Revocation test couldn't be performed: CRL: skip CRL, OCSP: Missing OCSP URI in authorityInfoAccess extension")
|
|
142
157
|
expect(valid).to eq(true)
|
|
143
158
|
expect(cert).to be_a OpenSSL::X509::Certificate
|
|
144
159
|
end
|
|
145
160
|
|
|
146
161
|
it "works with CRL only" do
|
|
147
|
-
#
|
|
148
|
-
expect(SSLTest).to receive(:test_ocsp_revocation).twice.and_return([false, "skip OCSP", nil])
|
|
162
|
+
# CRL is tried first and succeeds for both certs, so OCSP is never used
|
|
149
163
|
expect(SSLTest).to receive(:follow_crl_redirects).twice.and_call_original
|
|
164
|
+
expect(SSLTest).not_to receive(:test_ocsp_revocation)
|
|
150
165
|
valid, error, cert = SSLTest.test("https://www.demarches-simplifiees.fr")
|
|
151
166
|
expect(error).to be_nil
|
|
152
167
|
expect(valid).to eq(true)
|
|
@@ -158,15 +173,15 @@ describe SSLTest do
|
|
|
158
173
|
expect(SSLTest).to receive(:test_ocsp_revocation).once.and_return([false, "skip OCSP", nil])
|
|
159
174
|
expect(SSLTest).not_to receive(:follow_crl_redirects)
|
|
160
175
|
valid, error, cert = SSLTest.test("https://github.com")
|
|
161
|
-
expect(error).to eq ("Revocation test couldn't be performed:
|
|
176
|
+
expect(error).to eq ("Revocation test couldn't be performed: CRL: Missing crlDistributionPoints extension, OCSP: skip OCSP")
|
|
162
177
|
expect(valid).to eq(true)
|
|
163
178
|
expect(cert).to be_a OpenSSL::X509::Certificate
|
|
164
179
|
end
|
|
165
180
|
|
|
166
|
-
it "works with OCSP for first cert and CRL for intermediate (
|
|
181
|
+
it "works with OCSP for first cert and CRL for intermediate (GitHub)" do
|
|
167
182
|
expect(SSLTest).to receive(:follow_ocsp_redirects).once.and_call_original
|
|
168
183
|
expect(SSLTest).to receive(:follow_crl_redirects).once.and_call_original
|
|
169
|
-
valid, error, cert = SSLTest.test("https://
|
|
184
|
+
valid, error, cert = SSLTest.test("https://github.com")
|
|
170
185
|
expect(error).to be_nil
|
|
171
186
|
expect(valid).to eq(true)
|
|
172
187
|
expect(cert).to be_a OpenSSL::X509::Certificate
|
|
@@ -236,11 +251,11 @@ describe SSLTest do
|
|
|
236
251
|
SSLTest.send(:follow_crl_redirects, URI("http://crl.certigna.fr/certigna.crl")) # 1.1k
|
|
237
252
|
SSLTest.send(:follow_crl_redirects, URI("http://crl3.digicert.com/DigiCertTLSHybridECCSHA3842020CA1-1.crl")) # 26k
|
|
238
253
|
expect(SSLTest.cache_size[:crl][:lists]).to eq(2)
|
|
239
|
-
expect(SSLTest.cache_size[:crl][:bytes]).to be >
|
|
254
|
+
expect(SSLTest.cache_size[:crl][:bytes]).to be > 2000
|
|
240
255
|
end
|
|
241
256
|
|
|
242
257
|
it "returns OCSP cache size properly" do
|
|
243
|
-
SSLTest.test("https://
|
|
258
|
+
SSLTest.test("https://github.com")
|
|
244
259
|
expect(SSLTest.cache_size[:ocsp][:responses]).to eq(1)
|
|
245
260
|
expect(SSLTest.cache_size[:ocsp][:errors]).to eq(0)
|
|
246
261
|
expect(SSLTest.cache_size[:ocsp][:bytes]).to be > 150
|
|
@@ -323,7 +338,7 @@ describe SSLTest do
|
|
|
323
338
|
it "reports revocation exceptions" do
|
|
324
339
|
cert = OpenSSL::X509::Certificate.new(File.read(File.join(__dir__, 'fixtures/digicert_com_client.pem')))
|
|
325
340
|
ca_bundle = OpenSSL::X509::Certificate.load(File.read(File.join(__dir__, 'fixtures/digicert_com_ca_bundle.pem')))
|
|
326
|
-
expect(SSLTest).to receive(:
|
|
341
|
+
expect(SSLTest).to receive(:follow_crl_redirects).and_raise(ArgumentError.new("test"))
|
|
327
342
|
valid, error, cert = SSLTest.test_cert(cert, ca_bundle)
|
|
328
343
|
expect(error).to eq("SSL certificate test failed: test")
|
|
329
344
|
expect(valid).to be_nil
|
|
@@ -334,8 +349,9 @@ describe SSLTest do
|
|
|
334
349
|
cert = OpenSSL::X509::Certificate.new(File.read(File.join(__dir__, 'fixtures/revoked_rsa_dv_client.pem')))
|
|
335
350
|
ca_bundle = OpenSSL::X509::Certificate.load(File.read(File.join(__dir__, 'fixtures/revoked_rsa_dv_ca_bundle.pem')))
|
|
336
351
|
|
|
352
|
+
# CRL is tried first; disable it so OCSP performs the revocation check
|
|
353
|
+
expect(SSLTest).to receive(:test_crl_revocation).once.and_return([false, "skip CRL", nil])
|
|
337
354
|
expect(SSLTest).to receive(:follow_ocsp_redirects).once.and_call_original
|
|
338
|
-
expect(SSLTest).not_to receive(:follow_crl_redirects)
|
|
339
355
|
|
|
340
356
|
valid, error, cert = SSLTest.test_cert(cert, ca_bundle)
|
|
341
357
|
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 +363,11 @@ describe SSLTest do
|
|
|
347
363
|
cert = OpenSSL::X509::Certificate.new(File.read(File.join(__dir__, 'fixtures/revoked_badssl_client.pem')))
|
|
348
364
|
ca_bundle = OpenSSL::X509::Certificate.load(File.read(File.join(__dir__, 'fixtures/revoked_badssl_ca_bundle.pem')))
|
|
349
365
|
|
|
350
|
-
|
|
366
|
+
# CRL is tried first and detects the revocation, so OCSP is never used
|
|
351
367
|
expect(SSLTest).to receive(:follow_crl_redirects).once.and_call_original
|
|
368
|
+
expect(SSLTest).not_to receive(:test_ocsp_revocation)
|
|
352
369
|
valid, error, cert = SSLTest.test_cert(cert, ca_bundle)
|
|
353
|
-
expect(error).to eq ("SSL certificate revoked: Key Compromise (revocation date:
|
|
370
|
+
expect(error).to eq ("SSL certificate revoked: Key Compromise (revocation date: 2026-05-12 21:01:31 UTC)")
|
|
354
371
|
expect(valid).to eq(false)
|
|
355
372
|
expect(cert).to eq(cert)
|
|
356
373
|
end
|
|
@@ -360,7 +377,8 @@ describe SSLTest do
|
|
|
360
377
|
ca_bundle = OpenSSL::X509::Certificate.load(File.read(File.join(__dir__, 'fixtures/www_github_com_ca_bundle.pem')))
|
|
361
378
|
|
|
362
379
|
valid, error, cert = SSLTest.test_cert(cert, ca_bundle, redirection_limit: 0)
|
|
363
|
-
expect(error).to include("Revocation test couldn't be performed:
|
|
380
|
+
expect(error).to include("Revocation test couldn't be performed: CRL: Missing crlDistributionPoints extension")
|
|
381
|
+
expect(error).to include("OCSP: Request failed")
|
|
364
382
|
expect(error).to include("Too many redirections (> 0)")
|
|
365
383
|
expect(valid).to eq(true)
|
|
366
384
|
expect(cert).to eq(cert)
|
|
@@ -370,12 +388,12 @@ describe SSLTest do
|
|
|
370
388
|
cert = OpenSSL::X509::Certificate.new(File.read(File.join(__dir__, 'fixtures/google_com_client.pem')))
|
|
371
389
|
ca_bundle = OpenSSL::X509::Certificate.load(File.read(File.join(__dir__, 'fixtures/google_com_ca_bundle.pem')))
|
|
372
390
|
|
|
373
|
-
# Disable CRL
|
|
374
|
-
expect(SSLTest).to receive(:test_crl_revocation).
|
|
391
|
+
# Disable CRL (tried first) to see the OCSP error message
|
|
392
|
+
expect(SSLTest).to receive(:test_crl_revocation).twice.and_return([false, "skip CRL", nil])
|
|
375
393
|
expect(SSLTest).to receive(:follow_ocsp_redirects).once.and_call_original
|
|
376
394
|
|
|
377
395
|
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
|
|
396
|
+
expect(error).to eq ("Revocation test couldn't be performed: CRL: skip CRL, OCSP: Missing OCSP URI in authorityInfoAccess extension")
|
|
379
397
|
expect(valid).to eq(true)
|
|
380
398
|
expect(cert).to eq(cert)
|
|
381
399
|
end
|
|
@@ -384,9 +402,9 @@ describe SSLTest do
|
|
|
384
402
|
cert = OpenSSL::X509::Certificate.new(File.read(File.join(__dir__, 'fixtures/www_demarches-simplifiees_fr_client.pem')))
|
|
385
403
|
ca_bundle = OpenSSL::X509::Certificate.load(File.read(File.join(__dir__, 'fixtures/www_demarches-simplifiees_fr_ca_bundle.pem')))
|
|
386
404
|
|
|
387
|
-
#
|
|
388
|
-
expect(SSLTest).to receive(:test_ocsp_revocation).twice.and_return([false, "skip OCSP", nil])
|
|
405
|
+
# CRL is tried first and succeeds for both certs, so OCSP is never used
|
|
389
406
|
expect(SSLTest).to receive(:follow_crl_redirects).twice.and_call_original
|
|
407
|
+
expect(SSLTest).not_to receive(:test_ocsp_revocation)
|
|
390
408
|
|
|
391
409
|
valid, error, cert = SSLTest.test_cert(cert, ca_bundle)
|
|
392
410
|
expect(error).to be_nil
|
|
@@ -403,18 +421,18 @@ describe SSLTest do
|
|
|
403
421
|
expect(SSLTest).not_to receive(:follow_crl_redirects)
|
|
404
422
|
|
|
405
423
|
valid, error, cert = SSLTest.test_cert(cert, ca_bundle)
|
|
406
|
-
expect(error).to eq ("Revocation test couldn't be performed:
|
|
424
|
+
expect(error).to eq ("Revocation test couldn't be performed: CRL: Missing crlDistributionPoints extension, OCSP: skip OCSP")
|
|
407
425
|
expect(valid).to eq(true)
|
|
408
426
|
expect(cert).to eq(cert)
|
|
409
427
|
|
|
410
428
|
end
|
|
411
429
|
|
|
412
|
-
it "works with OCSP for first cert and CRL for intermediate (
|
|
430
|
+
it "works with OCSP for first cert and CRL for intermediate (GitHub)" do
|
|
413
431
|
expect(SSLTest).to receive(:follow_ocsp_redirects).once.and_call_original
|
|
414
432
|
expect(SSLTest).to receive(:follow_crl_redirects).once.and_call_original
|
|
415
433
|
|
|
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/
|
|
434
|
+
cert = OpenSSL::X509::Certificate.new(File.read(File.join(__dir__, 'fixtures/www_github_com_client.pem')))
|
|
435
|
+
ca_bundle = OpenSSL::X509::Certificate.load(File.read(File.join(__dir__, 'fixtures/www_github_com_ca_bundle.pem')))
|
|
418
436
|
|
|
419
437
|
valid, error, cert = SSLTest.test_cert(cert, ca_bundle)
|
|
420
438
|
expect(error).to be_nil
|
|
@@ -453,7 +471,8 @@ describe SSLTest do
|
|
|
453
471
|
expect(valid).to eq(true)
|
|
454
472
|
expect(cert).to eq(cert)
|
|
455
473
|
|
|
456
|
-
|
|
474
|
+
# CRL is tried first, so both certs are checked via CRL (GET) through the proxy
|
|
475
|
+
expect($proxy).to have_received(:do_GET).twice
|
|
457
476
|
end
|
|
458
477
|
end
|
|
459
478
|
|
data/ssl-test.gemspec
CHANGED
|
@@ -20,5 +20,6 @@ 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"
|
|
24
25
|
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: 1.
|
|
4
|
+
version: 1.6.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-16 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
|