ssrf_filter 1.0.8 → 1.1.0

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: 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