diff options
Diffstat (limited to 'spec/lib/gitlab/import_export/remote_stream_upload_spec.rb')
-rw-r--r-- | spec/lib/gitlab/import_export/remote_stream_upload_spec.rb | 232 |
1 files changed, 232 insertions, 0 deletions
diff --git a/spec/lib/gitlab/import_export/remote_stream_upload_spec.rb b/spec/lib/gitlab/import_export/remote_stream_upload_spec.rb new file mode 100644 index 00000000000..b1bc6b7eeaf --- /dev/null +++ b/spec/lib/gitlab/import_export/remote_stream_upload_spec.rb @@ -0,0 +1,232 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::ImportExport::RemoteStreamUpload do + include StubRequests + + subject do + described_class.new( + download_url: download_url, + upload_url: upload_url, + options: { + upload_method: upload_method, + upload_content_type: upload_content_type + } + ) + end + + let(:download_url) { 'http://object-storage/file.txt' } + let(:upload_url) { 'http://example.com/file.txt' } + let(:upload_method) { :post } + let(:upload_content_type) { 'text/plain' } + + describe '#execute' do + context 'when download request and upload request return 200' do + it 'uploads the downloaded content' do + stub_request(:get, download_url).to_return(status: 200, body: 'ABC', headers: { 'Content-Length' => 3 }) + stub_request(:post, upload_url) + + subject.execute + + expect( + a_request(:post, upload_url).with( + body: 'ABC', headers: { 'Content-Length' => 3, 'Content-Type' => 'text/plain' } + ) + ).to have_been_made + end + end + + context 'when upload method is put' do + let(:upload_method) { :put } + + it 'uploads using the put method' do + stub_request(:get, download_url).to_return(status: 200, body: 'ABC', headers: { 'Content-Length' => 3 }) + stub_request(:put, upload_url) + + subject.execute + + expect( + a_request(:put, upload_url).with( + body: 'ABC', headers: { 'Content-Length' => 3, 'Content-Type' => 'text/plain' } + ) + ).to have_been_made + end + end + + context 'when download request does not return 200' do + it do + stub_request(:get, download_url).to_return(status: 404) + + expect { subject.execute }.to raise_error( + Gitlab::ImportExport::RemoteStreamUpload::StreamError, + "Invalid response code while downloading file. Code: 404" + ) + end + end + + context 'when upload request does not returns 200' do + it do + stub_request(:get, download_url).to_return(status: 200, body: 'ABC', headers: { 'Content-Length' => 3 }) + stub_request(:post, upload_url).to_return(status: 403) + + expect { subject.execute }.to raise_error( + Gitlab::ImportExport::RemoteStreamUpload::StreamError, + "Invalid response code while uploading file. Code: 403" + ) + end + end + + context 'when download URL is a local address' do + let(:download_url) { 'http://127.0.0.1/file.txt' } + + before do + stub_request(:get, download_url) + stub_request(:post, upload_url) + end + + it 'raises error' do + expect { subject.execute }.to raise_error( + Gitlab::HTTP::BlockedUrlError, + "URL 'http://127.0.0.1/file.txt' is blocked: Requests to localhost are not allowed" + ) + end + + context 'when local requests are allowed' do + before do + stub_application_setting(allow_local_requests_from_web_hooks_and_services: true) + end + + it 'raises does not error' do + expect { subject.execute }.not_to raise_error + end + end + end + + context 'when download URL is a local network' do + let(:download_url) { 'http://172.16.0.0/file.txt' } + + before do + stub_request(:get, download_url) + stub_request(:post, upload_url) + end + + it 'raises error' do + expect { subject.execute }.to raise_error( + Gitlab::HTTP::BlockedUrlError, + "URL 'http://172.16.0.0/file.txt' is blocked: Requests to the local network are not allowed" + ) + end + + context 'when local network requests are allowed' do + before do + stub_application_setting(allow_local_requests_from_web_hooks_and_services: true) + end + + it 'raises does not error' do + expect { subject.execute }.not_to raise_error + end + end + end + + context 'when upload URL is a local address' do + let(:upload_url) { 'http://127.0.0.1/file.txt' } + + before do + stub_request(:get, download_url) + stub_request(:post, upload_url) + end + + it 'raises error' do + stub_request(:get, download_url) + + expect { subject.execute }.to raise_error( + Gitlab::HTTP::BlockedUrlError, + "URL 'http://127.0.0.1/file.txt' is blocked: Requests to localhost are not allowed" + ) + end + + context 'when local requests are allowed' do + before do + stub_application_setting(allow_local_requests_from_web_hooks_and_services: true) + end + + it 'raises does not error' do + expect { subject.execute }.not_to raise_error + end + end + end + + context 'when upload URL it is a request to local network' do + let(:upload_url) { 'http://172.16.0.0/file.txt' } + + before do + stub_request(:get, download_url) + stub_request(:post, upload_url) + end + + it 'raises error' do + expect { subject.execute }.to raise_error( + Gitlab::HTTP::BlockedUrlError, + "URL 'http://172.16.0.0/file.txt' is blocked: Requests to the local network are not allowed" + ) + end + + context 'when local network requests are allowed' do + before do + stub_application_setting(allow_local_requests_from_web_hooks_and_services: true) + end + + it 'raises does not error' do + expect { subject.execute }.not_to raise_error + end + end + end + + context 'when upload URL resolves to a local address' do + let(:upload_url) { 'http://example.com/file.txt' } + + it 'raises error' do + stub_request(:get, download_url) + stub_full_request(upload_url, ip_address: '127.0.0.1', method: upload_method) + + expect { subject.execute }.to raise_error( + Gitlab::HTTP::BlockedUrlError, + "URL 'http://example.com/file.txt' is blocked: Requests to localhost are not allowed" + ) + end + end + end + + describe Gitlab::ImportExport::RemoteStreamUpload::ChunkStream do + describe 'StringIO#copy_stream compatibility' do + it 'copies all chunks' do + chunks = %w[ABC EFD].to_enum + chunk_stream = described_class.new(chunks) + new_stream = StringIO.new + + IO.copy_stream(chunk_stream, new_stream) + new_stream.rewind + + expect(new_stream.read).to eq('ABCEFD') + end + + context 'with chunks smaller and bigger than buffer size' do + before do + stub_const('Gitlab::ImportExport::RemoteStreamUpload::ChunkStream::DEFAULT_BUFFER_SIZE', 4) + end + + it 'copies all chunks' do + chunks = %w[A BC DEF GHIJ KLMNOPQ RSTUVWXYZ].to_enum + chunk_stream = described_class.new(chunks) + new_stream = StringIO.new + + IO.copy_stream(chunk_stream, new_stream) + new_stream.rewind + + expect(new_stream.read).to eq('ABCDEFGHIJKLMNOPQRSTUVWXYZ') + end + end + end + end +end |