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 +4 -4
- data/lib/ssrf_filter/patch/resolv.rb +44 -0
- data/lib/ssrf_filter/patch/ssl_socket.rb +3 -2
- data/lib/ssrf_filter/ssrf_filter.rb +9 -5
- data/lib/ssrf_filter/version.rb +1 -1
- data/lib/ssrf_filter.rb +1 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f0230e20c7cd24b24dad277ee01042f3a4885fa68a8ea593fe0607de92023026
|
4
|
+
data.tar.gz: 6ce71a83907b1acfc382acc5d859a26fc1471dc3cd2dbc48d14b3e1dfca12051
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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::
|
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::
|
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
|
-
|
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[
|
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[
|
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
|
data/lib/ssrf_filter/version.rb
CHANGED
data/lib/ssrf_filter.rb
CHANGED
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.
|
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-
|
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
|