ssrf_filter 1.0.8 → 1.1.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: 3ad08c7683abaf9c1cce31358e91eebd54e2276911da99c3d7bdbc1481ec6a8f
4
- data.tar.gz: 4993290726f69399264c18dfe8b4ef1c908b78c726f0cfb5dcbc5b9c09043ead
3
+ metadata.gz: 97b1c63593031040d2f23b6fbb055c44490fc2624a5b9901203053c971991ba6
4
+ data.tar.gz: ca816401abe0177feea5fd70d01afd19758598cd9864659a8eb97e7184367830
5
5
  SHA512:
6
- metadata.gz: 522368253a813feb3ac735f419b8d9fe6ac3b006debf3e3c003f6909d809b5b51af06ccc9c42b7cd8d18e90d54a298d3a39fee0514304a1c3a2a1ffd1d54580d
7
- data.tar.gz: 21cd1fe841b77536db7e51e9cddc94b7ed96305bf766bb16df77e5b83029a6b6c44e042d1036ae52a135419e72ae70ec1155c471fdc6596eb9dab07809e54e36
6
+ metadata.gz: 75ae3f265b486cb8768ef0c49d3dd0313a218b3863a2c1dfc8615727453aea45ab7ae5f71289e3748eecb9e17dc09443870413937c7fbad56a9a7a225b2fbb89
7
+ data.tar.gz: c881ea003dafd105bbbff4001646a879fc4cefb18d722fc60f21281c8ebaca8b19a324dccadb9acdf799e56501905addebcfe8f91253ee52136a9dab433335ad
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class SsrfFilter
2
4
  module Patch
3
5
  module SSLSocket
@@ -10,7 +10,9 @@ class SsrfFilter
10
10
  mask_addr = ipaddr.instance_variable_get('@mask_addr')
11
11
  raise ArgumentError, 'Invalid mask' if mask_addr.zero?
12
12
 
13
- mask_addr >>= 1 while (mask_addr & 0x1).zero?
13
+ while (mask_addr & 0x1).zero?
14
+ mask_addr >>= 1
15
+ end
14
16
 
15
17
  length = 0
16
18
  while mask_addr & 0x1 == 0x1
@@ -104,7 +106,6 @@ class SsrfFilter
104
106
  %i[get put post delete head patch].each do |method|
105
107
  define_singleton_method(method) do |url, options = {}, &block|
106
108
  ::SsrfFilter::Patch::SSLSocket.apply!
107
- ::SsrfFilter::Patch::HTTPGenericRequest.apply!
108
109
 
109
110
  original_url = url
110
111
  scheme_whitelist = options[:scheme_whitelist] || DEFAULT_SCHEME_WHITELIST
@@ -126,16 +127,8 @@ class SsrfFilter
126
127
  public_addresses = ip_addresses.reject(&method(:unsafe_ip_address?))
127
128
  raise PrivateIPAddress, "Hostname '#{hostname}' has no public ip addresses" if public_addresses.empty?
128
129
 
129
- response = fetch_once(uri, public_addresses.sample.to_s, method, options, &block)
130
-
131
- case response
132
- when ::Net::HTTPRedirection then
133
- url = response['location']
134
- # Handle relative redirects
135
- url = "#{uri.scheme}://#{hostname}:#{uri.port}#{url}" if url.start_with?('/')
136
- else
137
- return response
138
- end
130
+ response, url = fetch_once(uri, public_addresses.sample.to_s, method, options, &block)
131
+ return response if url.nil?
139
132
  end
140
133
 
141
134
  raise TooManyRedirects, "Got #{max_redirects} redirects fetching #{original_url}"
@@ -171,7 +164,7 @@ class SsrfFilter
171
164
 
172
165
  def self.fetch_once(uri, ip, verb, options, &block)
173
166
  if options[:params]
174
- params = uri.query ? ::Hash[::URI.decode_www_form(uri.query)] : {}
167
+ params = uri.query ? ::URI.decode_www_form(uri.query).to_h : {}
175
168
  params.merge!(options[:params])
176
169
  uri.query = ::URI.encode_www_form(params)
177
170
  end
@@ -188,15 +181,26 @@ class SsrfFilter
188
181
 
189
182
  request.body = options[:body] if options[:body]
190
183
 
191
- block.call(request) if block_given?
184
+ options[:request_proc].call(request) if options[:request_proc].respond_to?(:call)
192
185
  validate_request(request)
193
186
 
194
187
  http_options = options[:http_options] || {}
195
188
  http_options[:use_ssl] = (uri.scheme == 'https')
196
189
 
197
190
  with_forced_hostname(hostname) do
198
- ::Net::HTTP.start(uri.hostname, uri.port, http_options) do |http|
199
- http.request(request)
191
+ ::Net::HTTP.start(uri.hostname, uri.port, **http_options) do |http|
192
+ http.request(request) do |response|
193
+ case response
194
+ when ::Net::HTTPRedirection
195
+ url = response['location']
196
+ # Handle relative redirects
197
+ url = "#{uri.scheme}://#{hostname}:#{uri.port}#{url}" if url.start_with?('/')
198
+ return nil, url
199
+ else
200
+ block&.call(response)
201
+ return response, nil
202
+ end
203
+ end
200
204
  end
201
205
  end
202
206
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class SsrfFilter
4
- VERSION = '1.0.8'.freeze
4
+ VERSION = '1.1.0'
5
5
  end
data/lib/ssrf_filter.rb CHANGED
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'ssrf_filter/patch/http_generic_request'
4
3
  require 'ssrf_filter/patch/ssl_socket'
5
4
  require 'ssrf_filter/ssrf_filter'
6
5
  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.0.8
4
+ version: 1.1.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: 2022-08-04 00:00:00.000000000 Z
11
+ date: 2022-08-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler-audit
@@ -16,126 +16,126 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 0.6.1
19
+ version: 0.9.1
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: 0.6.1
26
+ version: 0.9.1
27
27
  - !ruby/object:Gem::Dependency
28
- name: coveralls
28
+ name: pry-byebug
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - "~>"
31
+ - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: 0.8.22
33
+ version: '0'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - "~>"
38
+ - - ">="
39
39
  - !ruby/object:Gem::Version
40
- version: 0.8.22
40
+ version: '0'
41
41
  - !ruby/object:Gem::Dependency
42
- name: psych
42
+ name: rspec
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - "<"
45
+ - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '4'
47
+ version: 3.11.0
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - "<"
52
+ - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '4'
54
+ version: 3.11.0
55
55
  - !ruby/object:Gem::Dependency
56
- name: rspec
56
+ name: rubocop
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: 3.8.0
61
+ version: 1.35.0
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: 3.8.0
68
+ version: 1.35.0
69
69
  - !ruby/object:Gem::Dependency
70
- name: webmock
70
+ name: rubocop-rspec
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
- - - ">="
73
+ - - "~>"
74
74
  - !ruby/object:Gem::Version
75
- version: 3.5.1
75
+ version: 2.12.1
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
- - - ">="
80
+ - - "~>"
81
81
  - !ruby/object:Gem::Version
82
- version: 3.5.1
82
+ version: 2.12.1
83
83
  - !ruby/object:Gem::Dependency
84
- name: webrick
84
+ name: simplecov
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
- - - ">="
87
+ - - "~>"
88
88
  - !ruby/object:Gem::Version
89
- version: '0'
89
+ version: 0.21.2
90
90
  type: :development
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
- - - ">="
94
+ - - "~>"
95
95
  - !ruby/object:Gem::Version
96
- version: '0'
96
+ version: 0.21.2
97
97
  - !ruby/object:Gem::Dependency
98
- name: public_suffix
98
+ name: simplecov-lcov
99
99
  requirement: !ruby/object:Gem::Requirement
100
100
  requirements:
101
- - - '='
101
+ - - "~>"
102
102
  - !ruby/object:Gem::Version
103
- version: 2.0.5
103
+ version: 0.8.0
104
104
  type: :development
105
105
  prerelease: false
106
106
  version_requirements: !ruby/object:Gem::Requirement
107
107
  requirements:
108
- - - '='
108
+ - - "~>"
109
109
  - !ruby/object:Gem::Version
110
- version: 2.0.5
110
+ version: 0.8.0
111
111
  - !ruby/object:Gem::Dependency
112
- name: rexml
112
+ name: webmock
113
113
  requirement: !ruby/object:Gem::Requirement
114
114
  requirements:
115
- - - '='
115
+ - - ">="
116
116
  - !ruby/object:Gem::Version
117
- version: 3.2.4
117
+ version: 3.18.0
118
118
  type: :development
119
119
  prerelease: false
120
120
  version_requirements: !ruby/object:Gem::Requirement
121
121
  requirements:
122
- - - '='
122
+ - - ">="
123
123
  - !ruby/object:Gem::Version
124
- version: 3.2.4
124
+ version: 3.18.0
125
125
  - !ruby/object:Gem::Dependency
126
- name: rubocop
126
+ name: webrick
127
127
  requirement: !ruby/object:Gem::Requirement
128
128
  requirements:
129
- - - "~>"
129
+ - - ">="
130
130
  - !ruby/object:Gem::Version
131
- version: 0.50.0
131
+ version: '0'
132
132
  type: :development
133
133
  prerelease: false
134
134
  version_requirements: !ruby/object:Gem::Requirement
135
135
  requirements:
136
- - - "~>"
136
+ - - ">="
137
137
  - !ruby/object:Gem::Version
138
- version: 0.50.0
138
+ version: '0'
139
139
  description: A gem that makes it easy to prevent server side request forgery (SSRF)
140
140
  attacks
141
141
  email:
@@ -144,14 +144,14 @@ extensions: []
144
144
  extra_rdoc_files: []
145
145
  files:
146
146
  - lib/ssrf_filter.rb
147
- - lib/ssrf_filter/patch/http_generic_request.rb
148
147
  - lib/ssrf_filter/patch/ssl_socket.rb
149
148
  - lib/ssrf_filter/ssrf_filter.rb
150
149
  - lib/ssrf_filter/version.rb
151
150
  homepage: https://github.com/arkadiyt/ssrf_filter
152
151
  licenses:
153
152
  - MIT
154
- metadata: {}
153
+ metadata:
154
+ rubygems_mfa_required: 'true'
155
155
  post_install_message:
156
156
  rdoc_options: []
157
157
  require_paths:
@@ -160,14 +160,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
160
160
  requirements:
161
161
  - - ">="
162
162
  - !ruby/object:Gem::Version
163
- version: 2.0.0
163
+ version: 2.6.0
164
164
  required_rubygems_version: !ruby/object:Gem::Requirement
165
165
  requirements:
166
166
  - - ">="
167
167
  - !ruby/object:Gem::Version
168
168
  version: '0'
169
169
  requirements: []
170
- rubygems_version: 3.2.15
170
+ rubygems_version: 3.0.3
171
171
  signing_key:
172
172
  specification_version: 4
173
173
  summary: A gem that makes it easy to prevent server side request forgery (SSRF) attacks
@@ -1,83 +0,0 @@
1
- require 'stringio'
2
-
3
- class SsrfFilter
4
- module Patch
5
- module HTTPGenericRequest
6
- # Ruby had a bug in its Http library where if you set a custom `Host` header on a request it would get
7
- # overwritten. This was tracked in:
8
- # https://bugs.ruby-lang.org/issues/10054
9
- # and resolved with the commit:
10
- # https://github.com/ruby/ruby/commit/70a2eb63999265ff7e8d46d1f5b410c8ee3d30d7#diff-5c08b4ae27d2294a8294a27ff9af4a85
11
- # Versions of Ruby that don't have this fix applied will fail to connect to certain hosts via SsrfFilter. The
12
- # patch below backports the fix from the linked commit.
13
-
14
- def self.should_apply?
15
- # Check if the patch needs to be applied:
16
- # The Ruby bug was that HTTPGenericRequest#exec overwrote the Host header, so this snippet checks
17
- # if we can reproduce that behavior. It does not actually open any network connections.
18
-
19
- uri = URI('https://www.example.com')
20
- request = ::Net::HTTPGenericRequest.new('HEAD', false, false, uri)
21
- request['host'] = '127.0.0.1'
22
- request.exec(StringIO.new, '1.1', '/')
23
- request['host'] == uri.hostname
24
- end
25
-
26
- # Apply the patch from the linked commit onto ::Net::HTTPGenericRequest. Since this is 3rd party code,
27
- # disable code coverage and rubocop linting for this section.
28
- # :nocov:
29
- # rubocop:disable all
30
- def self.apply!
31
- return if instance_variable_defined?(:@checked_http_generic_request)
32
- @checked_http_generic_request = true
33
- return unless should_apply?
34
-
35
- ::Net::HTTPGenericRequest.class_eval do
36
- def exec(sock, ver, path)
37
- if @body
38
- send_request_with_body sock, ver, path, @body
39
- elsif @body_stream
40
- send_request_with_body_stream sock, ver, path, @body_stream
41
- elsif @body_data
42
- send_request_with_body_data sock, ver, path, @body_data
43
- else
44
- write_header sock, ver, path
45
- end
46
- end
47
-
48
- def update_uri(addr, port, ssl)
49
- # reflect the connection and @path to @uri
50
- return unless @uri
51
-
52
- if ssl
53
- scheme = 'https'.freeze
54
- klass = URI::HTTPS
55
- else
56
- scheme = 'http'.freeze
57
- klass = URI::HTTP
58
- end
59
-
60
- if host = self['host']
61
- host.sub!(/:.*/s, ''.freeze)
62
- elsif host = @uri.host
63
- else
64
- host = addr
65
- end
66
- # convert the class of the URI
67
- if @uri.is_a?(klass)
68
- @uri.host = host
69
- @uri.port = port
70
- else
71
- @uri = klass.new(
72
- scheme, @uri.userinfo,
73
- host, port, nil,
74
- @uri.path, nil, @uri.query, nil)
75
- end
76
- end
77
- end
78
- end
79
- # rubocop:enable all
80
- # :nocov:
81
- end
82
- end
83
- end