spec/utility/middleware/restrict_hostnames_spec.rb (153 lines of code) (raw):

# # Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one # or more contributor license agreements. Licensed under the Elastic License; # you may not use this file except in compliance with the Elastic License. # # frozen_string_literal: true require 'faraday_middleware' require 'utility/middleware/restrict_hostnames' describe Utility::Middleware::RestrictHostnames do let(:localhost_ipv4_url) do { :url => Addressable::URI.parse('http://127.0.0.1') } end let(:localhost_url) do { :url => Addressable::URI.parse('http://localhost:8080') } end let(:localhost_ipv6_url) do { :url => Addressable::URI.parse('http://[::1]') } end let(:external_url) do { :url => Addressable::URI.parse('https://google.com') } end class StubbedApp def call(_) 'it worked' end end subject do described_class.new(StubbedApp.new, :allowed_hosts => hosts) end shared_examples_for 'a blocked call' do it 'raises because the address is not allowed' do expect { subject.call(env) }.to raise_error(Utility::Middleware::RestrictHostnames::AddressNotAllowed) end end shared_examples_for 'an allowed call' do it '' do expect(subject.call(env)).to eq('it worked') end end context 'by default' do let(:hosts) { [] } context 'localhost ip4' do let(:env) { localhost_ipv4_url } it_behaves_like 'a blocked call' end context 'localhost ipv6' do let(:env) { localhost_ipv6_url } it_behaves_like 'a blocked call' end context 'localhost domain' do let(:env) { localhost_url } it_behaves_like 'a blocked call' end context 'external domain' do let(:env) { external_url } it_behaves_like 'a blocked call' end end context 'when localhost URLs are allowed by hostname' do let(:hosts) { ['localhost'] } context 'localhost ipv4' do let(:env) { localhost_ipv4_url } it_behaves_like 'an allowed call' end context 'localhost ipv6' do let(:env) { localhost_ipv6_url } skip 'jenkins docker does not bind localhost to ipv6' do it_behaves_like 'an allowed call' end end context 'localhost domain' do let(:env) { localhost_url } it_behaves_like 'an allowed call' end context 'external domain' do let(:env) { external_url } it_behaves_like 'a blocked call' end end context 'when localhost URLs are allowed by IP' do let(:hosts) { ['127.0.0.1'] } context 'localhost ipv4' do let(:env) { localhost_ipv4_url } it_behaves_like 'an allowed call' end context 'localhost ipv6' do let(:env) { localhost_ipv6_url } it_behaves_like 'a blocked call' end context 'localhost domain' do let(:env) { localhost_url } it_behaves_like 'an allowed call' end context 'external domain' do let(:env) { external_url } it_behaves_like 'a blocked call' end end context 'when localhost IPV6 URLs are allowed' do let(:hosts) { ['::1'] } context 'localhost ipv4' do let(:env) { localhost_ipv4_url } it_behaves_like 'a blocked call' end context 'localhost ipv6' do let(:env) { localhost_ipv6_url } it_behaves_like 'an allowed call' end context 'localhost domain' do let(:env) { localhost_url } skip 'jenkins docker does not bind localhost to ipv6' do it_behaves_like 'an allowed call' end end context 'external domain' do let(:env) { external_url } it_behaves_like 'a blocked call' end end context 'when external URLs are allowed by URL' do let(:hosts) { ['https://google.com'] } context 'localhost ipv4' do let(:env) { localhost_ipv4_url } it_behaves_like 'a blocked call' end context 'localhost ipv6' do let(:env) { localhost_ipv6_url } it_behaves_like 'a blocked call' end context 'localhost domain' do let(:env) { localhost_url } it_behaves_like 'a blocked call' end context 'external domain' do let(:actual_ip) { double('good ip', :ip_address => '203.0.113.1') } let(:env) { external_url } before(:each) do allow(Addrinfo).to receive(:getaddrinfo).with(external_url[:url].host, nil, :UNSPEC, :STREAM).and_return( [actual_ip], [actual_ip] ) end it_behaves_like 'an allowed call' context 'when DNS lookup changes between initialization and request' do let(:fake_ip) { double('bad ip', :ip_address => '203.0.113.0') } # TEST-NET-3 let(:actual_ip) { double('good ip', :ip_address => '203.0.113.1') } before(:each) do allow(Addrinfo).to receive(:getaddrinfo).with(external_url[:url].host, nil, :UNSPEC, :STREAM).and_return( [fake_ip], [actual_ip] ) expect(Utility::Logger).to receive(:warn).with('Requested url https://google.com with resolved ip addresses [#<IPAddr: IPv4:203.0.113.1/255.255.255.255>] does not match allowed hosts ["https://google.com"] with resolved ip addresses [#<IPAddr: IPv4:203.0.113.0/255.255.255.255>]. Retrying.').and_call_original expect(Utility::Logger).to_not receive(:error) end it_behaves_like 'an allowed call' end end end end