ssrf_filter 1.1.0 → 1.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 97b1c63593031040d2f23b6fbb055c44490fc2624a5b9901203053c971991ba6
4
- data.tar.gz: ca816401abe0177feea5fd70d01afd19758598cd9864659a8eb97e7184367830
3
+ metadata.gz: f0230e20c7cd24b24dad277ee01042f3a4885fa68a8ea593fe0607de92023026
4
+ data.tar.gz: 6ce71a83907b1acfc382acc5d859a26fc1471dc3cd2dbc48d14b3e1dfca12051
5
5
  SHA512:
6
- metadata.gz: 75ae3f265b486cb8768ef0c49d3dd0313a218b3863a2c1dfc8615727453aea45ab7ae5f71289e3748eecb9e17dc09443870413937c7fbad56a9a7a225b2fbb89
7
- data.tar.gz: c881ea003dafd105bbbff4001646a879fc4cefb18d722fc60f21281c8ebaca8b19a324dccadb9acdf799e56501905addebcfe8f91253ee52136a9dab433335ad
6
+ metadata.gz: cb56468fb4bdcf10168efbc18f004d996363b9dcc8877a1fab1eff508ef1519b86c7eeb750fedd8b565a162804746f9a036b54e584520137c1c2b1a86d5c9421
7
+ data.tar.gz: 9d5e0ea62956d551caa2474a384fa90e827c6d133bbfe04831f77dd49cabe5bcb3abc7008eefc63bccedf1634a33bcc27f8178253ec26f7e051075dcfba7066e
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'resolv'
4
+
5
+ class SsrfFilter
6
+ module Patch
7
+ module Resolv
8
+ # As described in ssl_socket.rb, we want to patch ruby's http connection code to allow us to make outbound network
9
+ # requests while ensuring that both:
10
+ # 1) we're connecting to a public / non-private ip address
11
+ # 2) https connections continue to work
12
+ #
13
+ # This used to work fine prior to this change in ruby's net/http library:
14
+ # https://github.com/ruby/net-http/pull/36
15
+ # After this changed was introduced our patch no longer works - we need to set the hostname to the correct
16
+ # value on the SSLSocket (`s.hostname = ssl_host_address`), but that code path no longer executes due to the
17
+ # modification in the linked pull request.
18
+ #
19
+ # To work around this we introduce the patch below, which forces our ip address string to not match against the
20
+ # Resolv IPv4/IPv6 regular expressions. This is ugly and cumbersome but I didn't see any better path.
21
+ class PatchedRegexp < Regexp
22
+ def ===(other)
23
+ if ::Thread.current.key?(::SsrfFilter::FIBER_ADDRESS_KEY) &&
24
+ other.object_id.equal?(::Thread.current[::SsrfFilter::FIBER_ADDRESS_KEY].object_id)
25
+ false
26
+ else
27
+ super(other)
28
+ end
29
+ end
30
+ end
31
+
32
+ def self.apply!
33
+ return if instance_variable_defined?(:@patched_resolv)
34
+
35
+ @patched_resolv = true
36
+
37
+ old_ipv4 = ::Resolv::IPv4.send(:remove_const, :Regex)
38
+ old_ipv6 = ::Resolv::IPv6.send(:remove_const, :Regex)
39
+ ::Resolv::IPv4.const_set(:Regex, PatchedRegexp.new(old_ipv4))
40
+ ::Resolv::IPv6.const_set(:Regex, PatchedRegexp.new(old_ipv6))
41
+ end
42
+ end
43
+ end
44
+ end
@@ -35,13 +35,14 @@ class SsrfFilter
35
35
  ::OpenSSL::SSL::SSLSocket.class_eval do
36
36
  original_post_connection_check = instance_method(:post_connection_check)
37
37
  define_method(:post_connection_check) do |hostname|
38
- original_post_connection_check.bind(self).call(::Thread.current[::SsrfFilter::FIBER_LOCAL_KEY] || hostname)
38
+ original_post_connection_check.bind(self).call(::Thread.current[::SsrfFilter::FIBER_HOSTNAME_KEY] ||
39
+ hostname)
39
40
  end
40
41
 
41
42
  if method_defined?(:hostname=)
42
43
  original_hostname = instance_method(:hostname=)
43
44
  define_method(:hostname=) do |hostname|
44
- original_hostname.bind(self).call(::Thread.current[::SsrfFilter::FIBER_LOCAL_KEY] || hostname)
45
+ original_hostname.bind(self).call(::Thread.current[::SsrfFilter::FIBER_HOSTNAME_KEY] || hostname)
45
46
  end
46
47
  end
47
48
  end
@@ -83,7 +83,8 @@ class SsrfFilter
83
83
  patch: ::Net::HTTP::Patch
84
84
  }.freeze
85
85
 
86
- FIBER_LOCAL_KEY = :__ssrf_filter_hostname
86
+ FIBER_HOSTNAME_KEY = :__ssrf_filter_hostname
87
+ FIBER_ADDRESS_KEY = :__ssrf_filter_address
87
88
 
88
89
  class Error < ::StandardError
89
90
  end
@@ -106,6 +107,7 @@ class SsrfFilter
106
107
  %i[get put post delete head patch].each do |method|
107
108
  define_singleton_method(method) do |url, options = {}, &block|
108
109
  ::SsrfFilter::Patch::SSLSocket.apply!
110
+ ::SsrfFilter::Patch::Resolv.apply!
109
111
 
110
112
  original_url = url
111
113
  scheme_whitelist = options[:scheme_whitelist] || DEFAULT_SCHEME_WHITELIST
@@ -187,7 +189,7 @@ class SsrfFilter
187
189
  http_options = options[:http_options] || {}
188
190
  http_options[:use_ssl] = (uri.scheme == 'https')
189
191
 
190
- with_forced_hostname(hostname) do
192
+ with_forced_hostname(hostname, ip) do
191
193
  ::Net::HTTP.start(uri.hostname, uri.port, **http_options) do |http|
192
194
  http.request(request) do |response|
193
195
  case response
@@ -219,11 +221,13 @@ class SsrfFilter
219
221
  end
220
222
  private_class_method :validate_request
221
223
 
222
- def self.with_forced_hostname(hostname, &_block)
223
- ::Thread.current[FIBER_LOCAL_KEY] = hostname
224
+ def self.with_forced_hostname(hostname, ip, &_block)
225
+ ::Thread.current[FIBER_HOSTNAME_KEY] = hostname
226
+ ::Thread.current[FIBER_ADDRESS_KEY] = ip
224
227
  yield
225
228
  ensure
226
- ::Thread.current[FIBER_LOCAL_KEY] = nil
229
+ ::Thread.current[FIBER_HOSTNAME_KEY] = nil
230
+ ::Thread.current[FIBER_ADDRESS_KEY] = nil
227
231
  end
228
232
  private_class_method :with_forced_hostname
229
233
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class SsrfFilter
4
- VERSION = '1.1.0'
4
+ VERSION = '1.1.1'
5
5
  end
data/lib/ssrf_filter.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'ssrf_filter/patch/resolv'
3
4
  require 'ssrf_filter/patch/ssl_socket'
4
5
  require 'ssrf_filter/ssrf_filter'
5
6
  require 'ssrf_filter/version'
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.1.0
4
+ version: 1.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Arkadiy Tetelman
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-08-28 00:00:00.000000000 Z
11
+ date: 2022-09-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler-audit
@@ -144,6 +144,7 @@ extensions: []
144
144
  extra_rdoc_files: []
145
145
  files:
146
146
  - lib/ssrf_filter.rb
147
+ - lib/ssrf_filter/patch/resolv.rb
147
148
  - lib/ssrf_filter/patch/ssl_socket.rb
148
149
  - lib/ssrf_filter/ssrf_filter.rb
149
150
  - lib/ssrf_filter/version.rb