ssrf_filter 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d68f597eb7885a7c9941fc8f134f7a6b87eec03fc0c92b6b6684a00d1be00069
4
- data.tar.gz: 2c934e958e0542c2ae1606b0657cb2096d54647969f291aa41579fd3f0363c67
3
+ metadata.gz: '05509e34ae84e22f9e746604ec7717a095ea88a237c13dfabb2d840e24b38816'
4
+ data.tar.gz: 1c6694c52b643ec4992fd19c1822eb225c4e87d9cf75bd2b1b2654a5f06f9b10
5
5
  SHA512:
6
- metadata.gz: 231e5f001e5540067710bc8894364592707fde79adafcbed2d4ea019646eb4b71fe60fae76caa562e8161090cffcb49390521045f57d153d8569eeeeaef85e79
7
- data.tar.gz: 6854c8b312de12da5ea27bf842533eb48719ed98668d83838b4e07c1408ccacb302cfa7830b23c810e27d8f46e0f95cfcabd9919c29c86b351b8f285dac25934
6
+ metadata.gz: 352ca8aa91c7408f1b84d0791378e463da9e356feab9559209a05c43049879acf5f88445126fbb9532ce979a5ffed7960a057d9409399f5cba805cb062e19761
7
+ data.tar.gz: dbbdbb309c241adc37d81dc2f7a128834ce0bf04f0b87662121280aa37e81ddce6a39b2a206972fc63d72c492db81108ec67165bb7d3d5b8be45f1c46c579a55
@@ -59,13 +59,18 @@ class SsrfFilter
59
59
  NAT64_LOCAL_PREFIX = ::IPAddr.new('64:ff9b:1::/48').freeze
60
60
 
61
61
  IPV6_BLACKLIST = ([
62
+ ::IPAddr.new('::/128'), # Unspecified address
62
63
  ::IPAddr.new('::1/128'), # Loopback
63
64
  ::IPAddr.new('100::/64'), # Discard prefix (RFC 6666)
65
+ ::IPAddr.new('100:0:0:1::/64'), # Dummy IPv6 prefix (RFC 9780)
64
66
  ::IPAddr.new('2001::/32'), # Teredo tunneling
67
+ ::IPAddr.new('2001:2::/48'), # Benchmarking (RFC 5180)
65
68
  ::IPAddr.new('2001:10::/28'), # Deprecated (previously ORCHID)
66
69
  ::IPAddr.new('2001:20::/28'), # ORCHIDv2
67
70
  ::IPAddr.new('2001:db8::/32'), # Addresses used in documentation and example source code
68
71
  ::IPAddr.new('2002::/16'), # 6to4
72
+ ::IPAddr.new('3fff::/20'), # Documentation (RFC 9637)
73
+ ::IPAddr.new('5f00::/16'), # Segment Routing (SRv6) SIDs (RFC 9602)
69
74
  ::IPAddr.new('fc00::/7'), # Unique local address
70
75
  ::IPAddr.new('fe80::/10'), # Link-local address
71
76
  ::IPAddr.new('ff00::/8') # Multicast
@@ -94,6 +99,15 @@ class SsrfFilter
94
99
  DEFAULT_SENSITIVE_HEADERS = %w[authorization cookie].freeze
95
100
  DEFAULT_ON_CROSS_ORIGIN_REDIRECT = :strip
96
101
 
102
+ CONNECTION_ERRORS = [
103
+ ::Errno::EHOSTUNREACH,
104
+ ::Errno::ENETUNREACH,
105
+ ::Errno::EADDRNOTAVAIL,
106
+ ::Errno::ECONNREFUSED,
107
+ ::Errno::ETIMEDOUT,
108
+ ::Net::OpenTimeout
109
+ ].freeze
110
+
97
111
  VERB_MAP = {
98
112
  get: ::Net::HTTP::Get,
99
113
  put: ::Net::HTTP::Put,
@@ -156,7 +170,7 @@ class SsrfFilter
156
170
  []
157
171
  end
158
172
 
159
- response, url = fetch_once(uri, public_addresses.sample.to_s, method,
173
+ response, url = fetch_with_fallback(uri, public_addresses, method,
160
174
  options.merge(headers_to_strip: headers_to_strip), &block)
161
175
  return response if url.nil?
162
176
  end
@@ -206,6 +220,18 @@ class SsrfFilter
206
220
  end
207
221
  end
208
222
 
223
+ private_class_method def self.fetch_with_fallback(uri, public_addresses, verb, options, &block)
224
+ last_error = nil
225
+
226
+ public_addresses.shuffle.each do |ip_address|
227
+ return fetch_once(uri.dup, ip_address.to_s, verb, options, &block)
228
+ rescue *CONNECTION_ERRORS => e
229
+ last_error = e
230
+ end
231
+
232
+ raise last_error
233
+ end
234
+
209
235
  private_class_method def self.fetch_once(uri, ip, verb, options, &block)
210
236
  if options[:params]
211
237
  params = uri.query ? ::URI.decode_www_form(uri.query).to_h : {}
@@ -217,6 +243,10 @@ class SsrfFilter
217
243
  request['host'] = normalized_hostname(uri)
218
244
 
219
245
  Array(options[:headers]).each do |header, value|
246
+ # Newer rubies raise ArgumentError when assigning a header name with control characters, which
247
+ # would pre-empt the CRLFInjection check in validate_request, so reject it with our own error here
248
+ raise CRLFInjection, "CRLF injection in header #{header} with value #{value}" if header.to_s.count("\r\n") != 0
249
+
220
250
  request[header] = value
221
251
  end
222
252
 
@@ -244,7 +274,7 @@ class SsrfFilter
244
274
  ipaddr: ip
245
275
  )
246
276
 
247
- ::Net::HTTP.start(uri.hostname, uri.port, **http_options) do |http|
277
+ ::Net::HTTP.start(uri.hostname, uri.port, nil, **http_options) do |http|
248
278
  response = http.request(request) do |res|
249
279
  block&.call(res)
250
280
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class SsrfFilter
4
- VERSION = '1.5.0'
4
+ VERSION = '1.6.0'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ssrf_filter
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.5.0
4
+ version: 1.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Arkadiy Tetelman
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-04-03 00:00:00.000000000 Z
11
+ date: 2026-09-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: base64
@@ -181,7 +181,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
181
181
  - !ruby/object:Gem::Version
182
182
  version: '0'
183
183
  requirements: []
184
- rubygems_version: 3.5.16
184
+ rubygems_version: 3.3.7
185
185
  signing_key:
186
186
  specification_version: 4
187
187
  summary: A gem that makes it easy to prevent server side request forgery (SSRF) attacks