curb 1.3.5 → 1.3.6

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.
@@ -1,3 +1,5 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), 'helper'))
2
+
1
3
  class TestCurbCurlProtocols < Test::Unit::TestCase
2
4
  include TestServerMethods
3
5
 
@@ -8,6 +10,11 @@ class TestCurbCurlProtocols < Test::Unit::TestCase
8
10
  server_setup
9
11
  end
10
12
 
13
+ def teardown
14
+ Curl.__send__(:clear_safe!) if Curl.respond_to?(:clear_safe!, true)
15
+ super
16
+ end
17
+
11
18
  def test_protocol_allowed
12
19
  @easy.set :url, "http://127.0.0.1:9129/this_file_does_not_exist.html"
13
20
  @easy.perform
@@ -34,4 +41,348 @@ class TestCurbCurlProtocols < Test::Unit::TestCase
34
41
  @easy.perform
35
42
  end
36
43
  end
44
+
45
+ def test_protocols_str_setopt_supported
46
+ omit('CURLOPT_PROTOCOLS_STR not supported by this libcurl') unless Curl.const_defined?(:CURLOPT_PROTOCOLS_STR)
47
+
48
+ @easy.set :protocols_str, 'http,https'
49
+ @easy.set :url, "http://127.0.0.1:9129/this_file_does_not_exist.html"
50
+ @easy.perform
51
+ assert_equal 404, @easy.response_code
52
+ end
53
+
54
+ def test_redir_protocols_str_setopt_supported
55
+ omit('CURLOPT_REDIR_PROTOCOLS_STR not supported by this libcurl') unless Curl.const_defined?(:CURLOPT_REDIR_PROTOCOLS_STR)
56
+
57
+ @easy.set :url, TestServlet.url + "/redirect"
58
+ @easy.set :redir_protocols_str, 'https'
59
+ assert_raises Curl::Err::UnsupportedProtocolError do
60
+ @easy.perform
61
+ end
62
+ end
63
+
64
+ def test_allowed_protocols_denies_file_protocol
65
+ easy = Curl::Easy.new($TEST_URL)
66
+ easy.allowed_protocols = [:http, :https]
67
+
68
+ assert_raises Curl::Err::UnsupportedProtocolError do
69
+ easy.perform
70
+ end
71
+ end
72
+
73
+ def test_safe_http_denies_file_protocol
74
+ easy = Curl::Easy.new($TEST_URL)
75
+ easy.safe_http!
76
+
77
+ assert_raises Curl::Err::UnsupportedProtocolError do
78
+ easy.perform
79
+ end
80
+ end
81
+
82
+ def test_safe_http_override_is_cleared_by_close
83
+ easy = Curl::Easy.new($TEST_URL)
84
+ easy.safe_http!
85
+ easy.close
86
+ easy.url = $TEST_URL
87
+
88
+ easy.perform
89
+
90
+ assert_match(/TESTMODEL/, easy.body_str)
91
+ end
92
+
93
+ def test_safe_get_denies_file_protocol
94
+ assert_raises Curl::Err::UnsupportedProtocolError do
95
+ Curl.safe_get($TEST_URL)
96
+ end
97
+ end
98
+
99
+ def test_safe_get_reapplies_protocols_after_user_block
100
+ assert_raises Curl::Err::UnsupportedProtocolError do
101
+ Curl.safe_get($TEST_URL) do |easy|
102
+ easy.allowed_protocols = [:file]
103
+ end
104
+ end
105
+ end
106
+
107
+ def test_safe_get_allows_http_protocol
108
+ easy = Curl.safe_get(TestServlet.url)
109
+
110
+ assert_equal 200, easy.response_code
111
+ assert_match(/GET/, easy.body_str)
112
+ end
113
+
114
+ def test_safe_bang_denies_file_protocol_for_easy_perform
115
+ Curl.safe!
116
+ easy = Curl::Easy.new($TEST_URL)
117
+
118
+ assert_raises Curl::Err::UnsupportedProtocolError do
119
+ easy.perform
120
+ end
121
+ end
122
+
123
+ def test_safe_bang_denies_file_protocol_for_shortcut_helpers
124
+ Curl.safe!
125
+
126
+ assert_raises Curl::Err::UnsupportedProtocolError do
127
+ Curl.get($TEST_URL)
128
+ end
129
+ end
130
+
131
+ def test_safe_bang_reapplies_protocols_after_shortcut_block
132
+ Curl.safe!
133
+
134
+ assert_raises Curl::Err::UnsupportedProtocolError do
135
+ Curl.get($TEST_URL) do |easy|
136
+ easy.allowed_protocols = [:file]
137
+ end
138
+ end
139
+ end
140
+
141
+ def test_safe_bang_allows_configured_protocols
142
+ Curl.safe! do |config|
143
+ config.protocols = [:http, :file]
144
+ end
145
+
146
+ easy = Curl::Easy.new($TEST_URL)
147
+ easy.perform
148
+
149
+ assert_match(/DO NOT REMOVE THIS COMMENT/, easy.body_str)
150
+ end
151
+
152
+ def test_safe_get_remains_http_safe_with_broader_global_policy
153
+ Curl.safe! do |config|
154
+ config.protocols = [:http, :file]
155
+ end
156
+
157
+ assert_raises Curl::Err::UnsupportedProtocolError do
158
+ Curl.safe_get($TEST_URL)
159
+ end
160
+ end
161
+
162
+ def test_safe_bang_allows_http_protocol
163
+ Curl.safe!
164
+ easy = Curl.get(TestServlet.url)
165
+
166
+ assert_equal 200, easy.response_code
167
+ assert_match(/GET/, easy.body_str)
168
+ end
169
+
170
+ def test_safe_bang_applies_to_multi_perform
171
+ Curl.safe!
172
+ easy = Curl::Easy.new($TEST_URL)
173
+ multi = Curl::Multi.new
174
+
175
+ multi.add(easy)
176
+ multi.perform
177
+
178
+ assert_equal Curl::Err::UnsupportedProtocolError, Curl::Easy.error(easy.last_result).first
179
+ ensure
180
+ multi.close if defined?(multi) && multi
181
+ end
182
+
183
+ def test_safe_http_denies_file_redirect
184
+ with_file_redirect_server do |url|
185
+ easy = Curl::Easy.new(url)
186
+ easy.follow_location = true
187
+ easy.safe_http!
188
+
189
+ assert_raises Curl::Err::UnsupportedProtocolError do
190
+ easy.perform
191
+ end
192
+ end
193
+ end
194
+
195
+ def test_safe_bang_denies_file_redirect
196
+ with_file_redirect_server do |url|
197
+ Curl.safe!
198
+
199
+ easy = Curl::Easy.new(url)
200
+ easy.follow_location = true
201
+
202
+ assert_raises Curl::Err::UnsupportedProtocolError do
203
+ easy.perform
204
+ end
205
+ end
206
+ end
207
+
208
+ def test_safe_http_allows_http_to_https_redirect
209
+ with_http_https_redirect_servers(:http, :https) do |url|
210
+ easy = Curl::Easy.new(url)
211
+ easy.follow_location = true
212
+ easy.ssl_verify_peer = false
213
+ easy.ssl_verify_host = false
214
+ easy.safe_http!
215
+
216
+ easy.perform
217
+
218
+ assert_equal 200, easy.response_code
219
+ assert_equal 'https target', easy.body_str
220
+ end
221
+ end
222
+
223
+ def test_safe_http_allows_https_to_http_redirect
224
+ with_http_https_redirect_servers(:https, :http) do |url|
225
+ easy = Curl::Easy.new(url)
226
+ easy.follow_location = true
227
+ easy.ssl_verify_peer = false
228
+ easy.ssl_verify_host = false
229
+ easy.safe_http!
230
+
231
+ easy.perform
232
+
233
+ assert_equal 200, easy.response_code
234
+ assert_equal 'http target', easy.body_str
235
+ end
236
+ end
237
+
238
+ def test_safe_http_honors_redirect_limit
239
+ with_redirect_loop_server do |url, hits|
240
+ easy = Curl::Easy.new(url)
241
+ easy.follow_location = true
242
+ easy.max_redirects = 2
243
+ easy.safe_http!
244
+
245
+ assert_raises Curl::Err::TooManyRedirectsError do
246
+ easy.perform
247
+ end
248
+
249
+ assert_operator hits[:loop], :>=, 2
250
+ end
251
+ end
252
+
253
+ private
254
+
255
+ def with_file_redirect_server
256
+ port_socket = TCPServer.new('127.0.0.1', 0)
257
+ port = port_socket.addr[1]
258
+ port_socket.close
259
+
260
+ server = WEBrick::HTTPServer.new(:Port => port, :Logger => WEBRICK_TEST_LOG, :AccessLog => [])
261
+ server.mount_proc('/redirect-file') do |_req, res|
262
+ res.status = 302
263
+ res['Location'] = $TEST_URL
264
+ res.body = 'redirect'
265
+ end
266
+
267
+ thread = Thread.new(server) { |srv| srv.start }
268
+ wait_for_server_ready(port, thread: thread)
269
+
270
+ yield "http://127.0.0.1:#{port}/redirect-file"
271
+ ensure
272
+ server.shutdown if defined?(server) && server
273
+ thread.join(server_shutdown_timeout) if defined?(thread) && thread
274
+ if defined?(thread) && thread&.alive?
275
+ thread.kill
276
+ thread.join(server_shutdown_timeout)
277
+ end
278
+ end
279
+
280
+ def with_redirect_loop_server
281
+ port_socket = TCPServer.new('127.0.0.1', 0)
282
+ port = port_socket.addr[1]
283
+ port_socket.close
284
+
285
+ hits = Hash.new(0)
286
+ server = WEBrick::HTTPServer.new(:Port => port, :Logger => WEBRICK_TEST_LOG, :AccessLog => [])
287
+ server.mount_proc('/loop') do |_req, res|
288
+ hits[:loop] += 1
289
+ res.status = 302
290
+ res['Location'] = '/loop'
291
+ res.body = 'redirect'
292
+ end
293
+
294
+ thread = Thread.new(server) { |srv| srv.start }
295
+ wait_for_server_ready(port, thread: thread)
296
+
297
+ yield "http://127.0.0.1:#{port}/loop", hits
298
+ ensure
299
+ server.shutdown if defined?(server) && server
300
+ thread.join(server_shutdown_timeout) if defined?(thread) && thread
301
+ if defined?(thread) && thread&.alive?
302
+ thread.kill
303
+ thread.join(server_shutdown_timeout)
304
+ end
305
+ end
306
+
307
+ def with_http_https_redirect_servers(initial_scheme, target_scheme)
308
+ require 'webrick/https'
309
+ require 'openssl'
310
+
311
+ omit('HTTPS redirect fixtures require OpenSSL') unless defined?(OpenSSL::PKey::RSA)
312
+
313
+ initial_server = nil
314
+ target_server = nil
315
+ initial_thread = nil
316
+ target_thread = nil
317
+
318
+ initial_port = unused_redirect_port
319
+ target_port = unused_redirect_port
320
+ target_server = redirect_matrix_server(target_scheme, target_port)
321
+ target_server.mount_proc('/target') do |_req, res|
322
+ res['Content-Type'] = 'text/plain'
323
+ res.body = "#{target_scheme} target"
324
+ end
325
+
326
+ target_thread = Thread.new(target_server) { |srv| srv.start }
327
+ wait_for_server_ready(target_port, thread: target_thread)
328
+
329
+ initial_server = redirect_matrix_server(initial_scheme, initial_port)
330
+ initial_server.mount_proc('/redirect') do |_req, res|
331
+ res.status = 302
332
+ res['Location'] = "#{target_scheme}://127.0.0.1:#{target_port}/target"
333
+ res.body = 'redirect'
334
+ end
335
+
336
+ initial_thread = Thread.new(initial_server) { |srv| srv.start }
337
+ wait_for_server_ready(initial_port, thread: initial_thread)
338
+
339
+ yield "#{initial_scheme}://127.0.0.1:#{initial_port}/redirect"
340
+ ensure
341
+ initial_server.shutdown if initial_server
342
+ target_server.shutdown if target_server
343
+ initial_thread.join(server_shutdown_timeout) if initial_thread
344
+ target_thread.join(server_shutdown_timeout) if target_thread
345
+ [initial_thread, target_thread].compact.each do |thread|
346
+ next unless thread.alive?
347
+
348
+ thread.kill
349
+ thread.join(server_shutdown_timeout)
350
+ end
351
+ end
352
+
353
+ def redirect_matrix_server(scheme, port)
354
+ options = {:BindAddress => '127.0.0.1', :Port => port, :Logger => WEBRICK_TEST_LOG, :AccessLog => []}
355
+ return WEBrick::HTTPServer.new(options) if scheme == :http
356
+
357
+ cert, key = redirect_matrix_certificate
358
+ WEBrick::HTTPServer.new(options.merge(:SSLEnable => true, :SSLCertificate => cert, :SSLPrivateKey => key))
359
+ end
360
+
361
+ def redirect_matrix_certificate
362
+ key = OpenSSL::PKey::RSA.new(2048)
363
+ cert = OpenSSL::X509::Certificate.new
364
+ cert.version = 2
365
+ cert.serial = 1
366
+ cert.subject = OpenSSL::X509::Name.parse('/CN=127.0.0.1')
367
+ cert.issuer = cert.subject
368
+ cert.public_key = key.public_key
369
+ cert.not_before = Time.now - 60
370
+ cert.not_after = Time.now + 3600
371
+
372
+ extensions = OpenSSL::X509::ExtensionFactory.new
373
+ extensions.subject_certificate = cert
374
+ extensions.issuer_certificate = cert
375
+ cert.add_extension(extensions.create_extension('basicConstraints', 'CA:FALSE', true))
376
+ cert.add_extension(extensions.create_extension('subjectAltName', 'IP:127.0.0.1', false))
377
+ cert.sign(key, OpenSSL::Digest::SHA256.new)
378
+
379
+ [cert, key]
380
+ end
381
+
382
+ def unused_redirect_port
383
+ socket = TCPServer.new('127.0.0.1', 0)
384
+ socket.addr[1]
385
+ ensure
386
+ socket.close if socket
387
+ end
37
388
  end
@@ -449,6 +449,47 @@ class TestCurbFiberScheduler < Test::Unit::TestCase
449
449
  end
450
450
  end
451
451
 
452
+ def test_easy_perform_isolates_public_network_policy_block_under_scheduler
453
+ if skip_no_async
454
+ return
455
+ end
456
+
457
+ with_ephemeral_http_server do |port, hits|
458
+ results = {}
459
+ details = {}
460
+
461
+ async_run do |top|
462
+ blocked = top.async do
463
+ easy = Curl::Easy.new("http://127.0.0.1:#{port}/fast")
464
+ easy.network_policy = :public
465
+ easy.perform
466
+ results[:blocked] = :returned
467
+ rescue => e
468
+ results[:blocked] = e
469
+ details[:blocked_error] = easy.unsafe_destination_error if defined?(easy) && easy
470
+ end
471
+
472
+ successful = top.async do
473
+ easy = Curl::Easy.new("http://127.0.0.1:#{port}/slow")
474
+ easy.perform
475
+ results[:successful] = easy.response_code
476
+ rescue => e
477
+ results[:successful] = e
478
+ end
479
+
480
+ blocked.wait
481
+ successful.wait
482
+ end
483
+
484
+ assert_equal 200, results[:successful], "scheduler peer without public policy should return normally"
485
+ assert_kind_of Curl::Err::UnsafeDestinationError, results[:blocked]
486
+ assert_match(/127\.0\.0\.1/, results[:blocked].message)
487
+ assert_match(/127\.0\.0\.1/, details[:blocked_error])
488
+ assert_equal 0, hits[:fast], "blocked public-policy peer should not reach the server"
489
+ assert_equal 1, hits[:slow]
490
+ end
491
+ end
492
+
452
493
  def test_drain_scheduler_pending_does_not_drop_work_rejected_during_deferred_abort
453
494
  state = Curl.scheduler_state
454
495
  easy = Curl::Easy.new("http://127.0.0.1:#{@port}/test")
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: curb
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.5
4
+ version: 1.3.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ross Bamford
8
8
  - Todd A. Fisher
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-05-14 00:00:00.000000000 Z
11
+ date: 2026-06-17 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Curb (probably CUrl-RuBy or something) provides Ruby-language bindings
14
14
  for the libcurl(3), a fully-featured client-side URL transfer library. cURL and
@@ -42,6 +42,7 @@ files:
42
42
  - ext/extconf.rb
43
43
  - lib/curb.rb
44
44
  - lib/curl.rb
45
+ - lib/curl/download.rb
45
46
  - lib/curl/easy.rb
46
47
  - lib/curl/multi.rb
47
48
  - tests/alltests.rb
@@ -56,6 +57,7 @@ files:
56
57
  - tests/bug_issue_post_redirect.rb
57
58
  - tests/bug_issue_spnego.rb
58
59
  - tests/bug_multi_segfault.rb
60
+ - tests/bug_poison.rb
59
61
  - tests/bug_postfields_crash.rb
60
62
  - tests/bug_postfields_crash2.rb
61
63
  - tests/bug_raise_on_callback.rb
@@ -76,6 +78,7 @@ files:
76
78
  - tests/tc_curl_maxfilesize.rb
77
79
  - tests/tc_curl_multi.rb
78
80
  - tests/tc_curl_native_coverage.rb
81
+ - tests/tc_curl_network_policy.rb
79
82
  - tests/tc_curl_postfield.rb
80
83
  - tests/tc_curl_protocols.rb
81
84
  - tests/tc_fiber_scheduler.rb
@@ -123,6 +126,7 @@ test_files:
123
126
  - tests/bug_issue_post_redirect.rb
124
127
  - tests/bug_issue_spnego.rb
125
128
  - tests/bug_multi_segfault.rb
129
+ - tests/bug_poison.rb
126
130
  - tests/bug_postfields_crash.rb
127
131
  - tests/bug_postfields_crash2.rb
128
132
  - tests/bug_raise_on_callback.rb
@@ -143,6 +147,7 @@ test_files:
143
147
  - tests/tc_curl_maxfilesize.rb
144
148
  - tests/tc_curl_multi.rb
145
149
  - tests/tc_curl_native_coverage.rb
150
+ - tests/tc_curl_network_policy.rb
146
151
  - tests/tc_curl_postfield.rb
147
152
  - tests/tc_curl_protocols.rb
148
153
  - tests/tc_fiber_scheduler.rb