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.
- checksums.yaml +4 -4
- data/README.md +57 -0
- data/Rakefile +8 -3
- data/doc.rb +48 -8
- data/ext/curb.c +24 -0
- data/ext/curb.h +3 -3
- data/ext/curb_easy.c +1378 -55
- data/ext/curb_easy.h +26 -0
- data/ext/curb_errors.c +2 -0
- data/ext/curb_errors.h +1 -0
- data/ext/curb_multi.c +48 -2
- data/ext/curb_multi.h +1 -0
- data/ext/extconf.rb +8 -0
- data/lib/curl/download.rb +160 -0
- data/lib/curl/easy.rb +113 -13
- data/lib/curl/multi.rb +172 -39
- data/lib/curl.rb +471 -11
- data/tests/bug_poison.rb +29 -0
- data/tests/tc_curl_download.rb +86 -0
- data/tests/tc_curl_easy.rb +76 -0
- data/tests/tc_curl_maxfilesize.rb +201 -1
- data/tests/tc_curl_multi.rb +258 -0
- data/tests/tc_curl_network_policy.rb +1475 -0
- data/tests/tc_curl_protocols.rb +351 -0
- data/tests/tc_fiber_scheduler.rb +41 -0
- metadata +7 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 11348a158e7da3d3406acd021be1a15f881ce2c607ff04e60b522a3bd92cd828
|
|
4
|
+
data.tar.gz: 0c524ce914df77b109a2aad85289386b2bf54028733fcf4ec5657a84abf511b8
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b4ebd6b04943bfc099a6593ede1c70298555ed1a6613958e8628cde43c30b8c7f8b86990d91c7a350481589495fe7d5dc4ad1ac3c313823bb7fb21e483e8910c
|
|
7
|
+
data.tar.gz: 0a78bc41496259eae9989d25e4a47c57a9a9aed6f79b00e77efae691b487a730e71e6cdf8081ae35d33893a84b033219ae24c40cf47effd8e3d1b0ca3591f222
|
data/README.md
CHANGED
|
@@ -199,6 +199,63 @@ Curl::Multi.download(urls, options) do |curl, file_path|
|
|
|
199
199
|
end
|
|
200
200
|
```
|
|
201
201
|
|
|
202
|
+
## Security considerations
|
|
203
|
+
|
|
204
|
+
`curb` is a libcurl binding and intentionally supports protocols beyond HTTP.
|
|
205
|
+
Do not pass untrusted URLs to `Curl.get`, `Curl::Easy.new`, or related raw
|
|
206
|
+
helpers without application-level validation. For user-supplied URLs, enable the
|
|
207
|
+
process-wide safety policy before making requests:
|
|
208
|
+
|
|
209
|
+
```ruby
|
|
210
|
+
Curl.safe! do |config|
|
|
211
|
+
config.network_policy = :public # block local/private destination IPs
|
|
212
|
+
config.max_body_bytes = 1_000_000 # cap buffered/callback response bytes
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
curl = Curl.get(user_url) # allows only http/https, including redirects
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
To allow a different protocol set, configure it explicitly. Redirects default to
|
|
219
|
+
the same protocol list:
|
|
220
|
+
|
|
221
|
+
```ruby
|
|
222
|
+
Curl.safe! do |config|
|
|
223
|
+
config.protocols = [:http, :ftp]
|
|
224
|
+
config.max_body_bytes = 1_000_000
|
|
225
|
+
end
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
For local per-handle policy instead of process-wide policy, use
|
|
229
|
+
`easy.safe_http!` and `easy.max_body_bytes = ...` before `perform`.
|
|
230
|
+
|
|
231
|
+
With `network_policy = :public`, curb checks peer addresses when libcurl opens
|
|
232
|
+
the socket and blocks local/private destinations. Proxies, `resolve`,
|
|
233
|
+
`connect_to`, DoH URL overrides, and Unix socket paths are disabled by default
|
|
234
|
+
under this policy unless explicitly allowed in the safety config. Custom DNS
|
|
235
|
+
server overrides are rejected. To use a trusted explicit proxy without
|
|
236
|
+
re-enabling environment proxies, set `allowed_proxy_hosts` and configure
|
|
237
|
+
`easy.proxy_url` on the request.
|
|
238
|
+
|
|
239
|
+
For stricter egress, combine the public network policy with host and CIDR
|
|
240
|
+
allowlists. Host allowlists gate the configured request URL and, when supported
|
|
241
|
+
by libcurl, each followed redirect before the request is sent. CIDR allowlists
|
|
242
|
+
are checked against the resolved peer address at socket-open time:
|
|
243
|
+
|
|
244
|
+
```ruby
|
|
245
|
+
Curl.safe! do |config|
|
|
246
|
+
config.network_policy = :public
|
|
247
|
+
config.allowed_hosts = ["api.example.com"]
|
|
248
|
+
config.allowed_proxy_hosts = ["proxy.example.com"]
|
|
249
|
+
config.allowed_cidrs = ["93.184.216.0/24", "2606:2800:220::/48"]
|
|
250
|
+
end
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
By default, responses are buffered into `body` when no `on_body` callback is
|
|
254
|
+
configured. For untrusted or large responses, use `on_body`, `download`, and/or
|
|
255
|
+
`max_body_bytes` so a remote endpoint cannot force unbounded memory growth.
|
|
256
|
+
`max_body_bytes` is enforced for downloads as well as buffered responses and
|
|
257
|
+
custom body callbacks.
|
|
258
|
+
|
|
202
259
|
## You will need
|
|
203
260
|
|
|
204
261
|
* A working Ruby installation (`2.0.0+` will work but `2.1+` preferred) (it's possible it still works with 1.8.7 but you'd have to tell me if not...)
|
data/Rakefile
CHANGED
|
@@ -107,9 +107,10 @@ else
|
|
|
107
107
|
end
|
|
108
108
|
|
|
109
109
|
ruby_memcheck_config = { binary_name: 'curb_core' }
|
|
110
|
+
ruby_version = Gem::Version.new(RUBY_VERSION)
|
|
110
111
|
|
|
111
|
-
if RUBY_ENGINE == 'ruby' &&
|
|
112
|
-
# Ruby 4.0.4 reports fiber/block-handler VM stack accesses under Valgrind.
|
|
112
|
+
if RUBY_ENGINE == 'ruby' && ruby_version >= Gem::Version.new('4.0.4') && ruby_version < Gem::Version.new('4.1.0')
|
|
113
|
+
# Ruby 4.0.4+ reports fiber/block-handler VM stack accesses under Valgrind.
|
|
113
114
|
# Keep reporting errors that originate in curb_core, but filter Ruby-side noise.
|
|
114
115
|
ruby_memcheck_config[:filter_all_errors] = true
|
|
115
116
|
if RubyMemcheck::Configuration.instance_method(:initialize).parameters.any? { |type, name|
|
|
@@ -183,7 +184,11 @@ end
|
|
|
183
184
|
# RDoc Tasks ---------------------------------------------------------
|
|
184
185
|
desc "Create the RDOC documentation"
|
|
185
186
|
task :doc do
|
|
186
|
-
|
|
187
|
+
doc_opts = Shellwords.split(ENV.fetch('DOC_OPTS', ''))
|
|
188
|
+
unsupported_opts = doc_opts - ['--cpp']
|
|
189
|
+
fail "Unsupported DOC_OPTS: #{unsupported_opts.join(' ')}" unless unsupported_opts.empty?
|
|
190
|
+
|
|
191
|
+
ruby 'doc.rb', *doc_opts
|
|
187
192
|
end
|
|
188
193
|
|
|
189
194
|
desc "Publish the RDoc documentation to project web site"
|
data/doc.rb
CHANGED
|
@@ -1,10 +1,35 @@
|
|
|
1
1
|
require 'fileutils'
|
|
2
|
+
require 'open3'
|
|
3
|
+
require 'shellwords'
|
|
2
4
|
include FileUtils
|
|
3
5
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
+
def makefile_variables(path)
|
|
7
|
+
variables = {}
|
|
8
|
+
|
|
9
|
+
File.foreach(path) do |line|
|
|
10
|
+
line = line.chomp
|
|
11
|
+
variables[$1] = $2.strip if line =~ /\A([A-Za-z_]\w*)\s*=\s*(.*)\z/
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
variables
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def expand_make_variables(value, variables, seen = [])
|
|
18
|
+
value.gsub(/\$\(([^)]+)\)/) do
|
|
19
|
+
key = Regexp.last_match(1)
|
|
20
|
+
raise ArgumentError, "unsupported Makefile variable in INCFLAGS: #{key}" unless variables.key?(key)
|
|
21
|
+
raise ArgumentError, "recursive Makefile variable in INCFLAGS: #{key}" if seen.include?(key)
|
|
22
|
+
|
|
23
|
+
expand_make_variables(variables[key], variables, seen + [key])
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def makefile_incflags
|
|
28
|
+
variables = makefile_variables('ext/Makefile')
|
|
29
|
+
Shellwords.split(expand_make_variables(variables.fetch('INCFLAGS', ''), variables))
|
|
6
30
|
rescue Errno::ENOENT
|
|
7
31
|
$stderr.puts("No makefile found; run `rake ext/Makefile' first.")
|
|
32
|
+
[]
|
|
8
33
|
end
|
|
9
34
|
|
|
10
35
|
pp_srcdir = 'ext'
|
|
@@ -15,26 +40,41 @@ mkdir(tmpdir)
|
|
|
15
40
|
begin
|
|
16
41
|
if ARGV.include?('--cpp')
|
|
17
42
|
begin
|
|
18
|
-
|
|
43
|
+
cpp_version, status = Open3.capture2e('cpp', '--version')
|
|
44
|
+
|
|
45
|
+
if status.success? && cpp_version =~ /\(GCC\)/
|
|
19
46
|
# gnu cpp
|
|
20
47
|
$stderr.puts "Running GNU cpp over source"
|
|
48
|
+
incflags = makefile_incflags
|
|
21
49
|
|
|
22
|
-
Dir['ext/*.c'].each do |fn|
|
|
23
|
-
|
|
24
|
-
|
|
50
|
+
Dir['ext/*.c'].sort.each do |fn|
|
|
51
|
+
out = File.join(tmpdir, File.basename(fn))
|
|
52
|
+
abort "cpp failed for #{fn}" unless system('cpp', '-DRDOC_NEVER_DEFINED', '-C', *incflags, '-o', out, fn)
|
|
25
53
|
end
|
|
26
54
|
|
|
27
55
|
pp_srcdir = tmpdir
|
|
28
56
|
else
|
|
29
57
|
$stderr.puts "Not running cpp (non-GNU)"
|
|
30
58
|
end
|
|
31
|
-
rescue
|
|
59
|
+
rescue Errno::ENOENT
|
|
32
60
|
# no cpp
|
|
33
61
|
$stderr.puts "No cpp found"
|
|
62
|
+
rescue ArgumentError => e
|
|
63
|
+
abort e.message
|
|
34
64
|
end
|
|
35
65
|
end
|
|
36
66
|
|
|
37
|
-
|
|
67
|
+
main = File.exist?('README.md') ? 'README.md' : 'README'
|
|
68
|
+
sources = Dir[File.join(pp_srcdir, '*.c')].sort
|
|
69
|
+
abort 'rdoc failed' unless system(
|
|
70
|
+
'rdoc',
|
|
71
|
+
'--title', 'Curb - libcurl bindings for ruby',
|
|
72
|
+
'--main', main,
|
|
73
|
+
*sources,
|
|
74
|
+
main,
|
|
75
|
+
'LICENSE',
|
|
76
|
+
'lib/curb.rb'
|
|
77
|
+
)
|
|
38
78
|
ensure
|
|
39
79
|
rm_rf(tmpdir)
|
|
40
80
|
end
|
data/ext/curb.c
CHANGED
|
@@ -457,6 +457,12 @@ void Init_curb_core() {
|
|
|
457
457
|
#endif
|
|
458
458
|
#ifdef HAVE_CURLOPT_OPENSOCKETDATA
|
|
459
459
|
CURB_DEFINE(CURLOPT_OPENSOCKETDATA);
|
|
460
|
+
#endif
|
|
461
|
+
#ifdef HAVE_CURLOPT_PREREQFUNCTION
|
|
462
|
+
CURB_DEFINE(CURLOPT_PREREQFUNCTION);
|
|
463
|
+
#endif
|
|
464
|
+
#ifdef HAVE_CURLOPT_PREREQDATA
|
|
465
|
+
CURB_DEFINE(CURLOPT_PREREQDATA);
|
|
460
466
|
#endif
|
|
461
467
|
/* CURLOPT_PROGRESSFUNCTION deprecated since 7.32.0, use XFERINFOFUNCTION */
|
|
462
468
|
#ifdef HAVE_CURLOPT_PROGRESSFUNCTION
|
|
@@ -557,6 +563,9 @@ void Init_curb_core() {
|
|
|
557
563
|
CURB_DEFINE(CURLOPT_LOCALPORT);
|
|
558
564
|
#endif
|
|
559
565
|
CURB_DEFINE(CURLOPT_DNS_CACHE_TIMEOUT);
|
|
566
|
+
#ifdef HAVE_CURLOPT_DNS_SERVERS
|
|
567
|
+
CURB_DEFINE(CURLOPT_DNS_SERVERS);
|
|
568
|
+
#endif
|
|
560
569
|
/* CURLOPT_DNS_USE_GLOBAL_CACHE deprecated since 7.11.1, does nothing since 7.62.0 */
|
|
561
570
|
#ifdef HAVE_CURLOPT_DNS_USE_GLOBAL_CACHE
|
|
562
571
|
CURB_DEFINE(CURLOPT_DNS_USE_GLOBAL_CACHE);
|
|
@@ -955,6 +964,9 @@ void Init_curb_core() {
|
|
|
955
964
|
#ifdef HAVE_CURLUSESSL_ALL
|
|
956
965
|
CURB_DEFINE(CURLUSESSL_ALL);
|
|
957
966
|
#endif
|
|
967
|
+
#ifdef HAVE_CURLOPT_CONNECT_TO
|
|
968
|
+
CURB_DEFINE(CURLOPT_CONNECT_TO);
|
|
969
|
+
#endif
|
|
958
970
|
#ifdef HAVE_CURLOPT_RESOLVE
|
|
959
971
|
CURB_DEFINE(CURLOPT_RESOLVE);
|
|
960
972
|
#endif
|
|
@@ -1011,6 +1023,18 @@ void Init_curb_core() {
|
|
|
1011
1023
|
#ifdef HAVE_CURLOPT_SSL_VERIFYPEER
|
|
1012
1024
|
CURB_DEFINE(CURLOPT_SSL_VERIFYPEER);
|
|
1013
1025
|
#endif
|
|
1026
|
+
#ifdef HAVE_CURLOPT_DOH_URL
|
|
1027
|
+
CURB_DEFINE(CURLOPT_DOH_URL);
|
|
1028
|
+
#endif
|
|
1029
|
+
#ifdef HAVE_CURLOPT_DOH_SSL_VERIFYPEER
|
|
1030
|
+
CURB_DEFINE(CURLOPT_DOH_SSL_VERIFYPEER);
|
|
1031
|
+
#endif
|
|
1032
|
+
#ifdef HAVE_CURLOPT_DOH_SSL_VERIFYHOST
|
|
1033
|
+
CURB_DEFINE(CURLOPT_DOH_SSL_VERIFYHOST);
|
|
1034
|
+
#endif
|
|
1035
|
+
#ifdef HAVE_CURLOPT_DOH_SSL_VERIFYSTATUS
|
|
1036
|
+
CURB_DEFINE(CURLOPT_DOH_SSL_VERIFYSTATUS);
|
|
1037
|
+
#endif
|
|
1014
1038
|
#ifdef HAVE_CURLOPT_CAINFO
|
|
1015
1039
|
CURB_DEFINE(CURLOPT_CAINFO);
|
|
1016
1040
|
#endif
|
data/ext/curb.h
CHANGED
|
@@ -28,11 +28,11 @@
|
|
|
28
28
|
#include "curb_macros.h"
|
|
29
29
|
|
|
30
30
|
// These should be managed from the Rake 'release' task.
|
|
31
|
-
#define CURB_VERSION "1.3.
|
|
32
|
-
#define CURB_VER_NUM
|
|
31
|
+
#define CURB_VERSION "1.3.6"
|
|
32
|
+
#define CURB_VER_NUM 1036
|
|
33
33
|
#define CURB_VER_MAJ 1
|
|
34
34
|
#define CURB_VER_MIN 3
|
|
35
|
-
#define CURB_VER_MIC
|
|
35
|
+
#define CURB_VER_MIC 6
|
|
36
36
|
#define CURB_VER_PATCH 0
|
|
37
37
|
|
|
38
38
|
|