Skip to content

Commit

Permalink
Resolve conflicts
Browse files Browse the repository at this point in the history
  • Loading branch information
arkadiyt committed Sep 1, 2023
1 parent 49e2997 commit 7f0b048
Show file tree
Hide file tree
Showing 2 changed files with 31 additions and 5 deletions.
21 changes: 16 additions & 5 deletions lib/ssrf_filter/ssrf_filter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ def self.prefixlen_from_ipaddr(ipaddr)
::Resolv.getaddresses(hostname).map { |ip| ::IPAddr.new(ip) }
end

DEFAULT_ALLOW_UNFOLLOWED_REDIRECTS = false
DEFAULT_MAX_REDIRECTS = 10

VERB_MAP = {
Expand Down Expand Up @@ -110,10 +111,13 @@ class CRLFInjection < Error
original_url = url
scheme_whitelist = options[:scheme_whitelist] || DEFAULT_SCHEME_WHITELIST
resolver = options[:resolver] || DEFAULT_RESOLVER
allow_unfollowed_redirects = options.fetch(:allow_unfollowed_redirects) { DEFAULT_ALLOW_UNFOLLOWED_REDIRECTS }
max_redirects = options[:max_redirects] || DEFAULT_MAX_REDIRECTS
url = url.to_s

(max_redirects + 1).times do
redirects = 0

loop do
uri = URI(url)

unless scheme_whitelist.include?(uri.scheme)
Expand All @@ -128,10 +132,17 @@ class CRLFInjection < Error
raise PrivateIPAddress, "Hostname '#{hostname}' has no public ip addresses" if public_addresses.empty?

response, url = fetch_once(uri, public_addresses.sample.to_s, method, options, &block)
return response if url.nil?
end

raise TooManyRedirects, "Got #{max_redirects} redirects fetching #{original_url}"
break response if url.nil?

if max_redirects <= redirects
break response if allow_unfollowed_redirects

raise TooManyRedirects, "Got #{max_redirects} redirects fetching #{original_url}"
end

redirects += 1
end
end
end

Expand Down Expand Up @@ -197,7 +208,7 @@ def self.fetch_once(uri, ip, verb, options, &block)
url = response['location']
# Handle relative redirects
url = "#{uri.scheme}://#{hostname}:#{uri.port}#{url}" if url.start_with?('/')
return nil, url
return response, url
else
return response, nil
end
Expand Down
15 changes: 15 additions & 0 deletions spec/lib/ssrf_filter_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,21 @@ def inject_custom_trust_store(*certificates)
end.to raise_error(described_class::TooManyRedirects)
end

it 'returns the last response if there are too many redirects and unfollowed redirects are allowed' do
stub_request(:get, "https://#{public_ipv4}").with(headers: {host: 'www.example.com'})
.to_return(status: 301, headers: {location: 'https://www.example2.com'})
resolver = proc { [public_ipv4] }
response =
described_class.get(
'https://www.example.com',
resolver: resolver,
allow_unfollowed_redirects: true,
max_redirects: 0
)
expect(response.code).to eq('301')
expect(response['location']).to eq('https://www.example2.com')
end

it 'fails if the redirected url is not in the scheme whitelist' do
stub_request(:put, "https://#{public_ipv4}").with(headers: {host: 'www.example.com'})
.to_return(status: 301, headers: {location: 'ftp://www.example.com'})
Expand Down

0 comments on commit 7f0b048

Please sign in to comment.