diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-09-04 00:08:18 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-09-04 00:08:18 +0300 |
commit | 692f4b734f1976b690dccb5458c198b5205c51b5 (patch) | |
tree | c6af56b7127850615b9dc5626cefbe665fd96ea9 /spec/lib | |
parent | 592223823c8ebf6e32d98e4b12620ba8ff043cca (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec/lib')
-rw-r--r-- | spec/lib/gitlab/group_search_results_spec.rb | 17 | ||||
-rw-r--r-- | spec/lib/gitlab/middleware/multipart/handler_spec.rb | 53 | ||||
-rw-r--r-- | spec/lib/gitlab/middleware/multipart_spec.rb | 347 | ||||
-rw-r--r-- | spec/lib/gitlab/project_search_results_spec.rb | 25 | ||||
-rw-r--r-- | spec/lib/gitlab/search_results_spec.rb | 24 | ||||
-rw-r--r-- | spec/lib/uploaded_file_spec.rb | 2 |
6 files changed, 194 insertions, 274 deletions
diff --git a/spec/lib/gitlab/group_search_results_spec.rb b/spec/lib/gitlab/group_search_results_spec.rb index 3c3410c41bf..726df37e3aa 100644 --- a/spec/lib/gitlab/group_search_results_spec.rb +++ b/spec/lib/gitlab/group_search_results_spec.rb @@ -6,9 +6,22 @@ RSpec.describe Gitlab::GroupSearchResults do # group creation calls GroupFinder, so need to create the group # before so expect(GroupsFinder) check works let_it_be(:group) { create(:group) } - let(:user) { create(:user) } + let_it_be(:user) { create(:user) } + let(:filters) { {} } + let(:limit_projects) { Project.all } + let(:query) { 'gob' } - subject(:results) { described_class.new(user, 'gob', anything, group: group) } + subject(:results) { described_class.new(user, query, limit_projects, group: group, filters: filters) } + + describe 'issues search' do + let_it_be(:project) { create(:project, :public, group: group) } + let_it_be(:opened_issue) { create(:issue, :opened, project: project, title: 'foo opened') } + let_it_be(:closed_issue) { create(:issue, :closed, project: project, title: 'foo closed') } + let(:query) { 'foo' } + let(:filters) { { state: 'opened' } } + + include_examples 'search issues scope filters by state' + end describe 'user search' do subject(:objects) { results.objects('users') } diff --git a/spec/lib/gitlab/middleware/multipart/handler_spec.rb b/spec/lib/gitlab/middleware/multipart/handler_spec.rb new file mode 100644 index 00000000000..aac3f00defe --- /dev/null +++ b/spec/lib/gitlab/middleware/multipart/handler_spec.rb @@ -0,0 +1,53 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::Middleware::Multipart::Handler do + using RSpec::Parameterized::TableSyntax + + let_it_be(:env) { Rack::MockRequest.env_for('/', method: 'post', params: {}) } + let_it_be(:message) { { 'rewritten_fields' => {} } } + + describe '#allowed_paths' do + let_it_be(:expected_allowed_paths) do + [ + Dir.tmpdir, + ::FileUploader.root, + ::Gitlab.config.uploads.storage_path, + ::JobArtifactUploader.workhorse_upload_path, + ::LfsObjectUploader.workhorse_upload_path, + File.join(Rails.root, 'public/uploads/tmp') + ] + end + + let_it_be(:expected_with_packages_path) { expected_allowed_paths + [::Packages::PackageFileUploader.workhorse_upload_path] } + + subject { described_class.new(env, message).send(:allowed_paths) } + + where(:package_features_enabled, :object_storage_enabled, :direct_upload_enabled, :expected_paths) do + false | false | true | :expected_allowed_paths + false | false | false | :expected_allowed_paths + false | true | true | :expected_allowed_paths + false | true | false | :expected_allowed_paths + true | false | true | :expected_with_packages_path + true | false | false | :expected_with_packages_path + true | true | true | :expected_allowed_paths + true | true | false | :expected_with_packages_path + end + + with_them do + before do + stub_config(packages: { + enabled: package_features_enabled, + object_store: { + enabled: object_storage_enabled, + direct_upload: direct_upload_enabled + }, + storage_path: '/any/dir' + }) + end + + it { is_expected.to eq(send(expected_paths)) } + end + end +end diff --git a/spec/lib/gitlab/middleware/multipart_spec.rb b/spec/lib/gitlab/middleware/multipart_spec.rb index 3b64fe335e8..781f3e0289b 100644 --- a/spec/lib/gitlab/middleware/multipart_spec.rb +++ b/spec/lib/gitlab/middleware/multipart_spec.rb @@ -2,311 +2,138 @@ require 'spec_helper' -require 'tempfile' - RSpec.describe Gitlab::Middleware::Multipart do - include_context 'multipart middleware context' - - RSpec.shared_examples_for 'multipart upload files' do - it 'opens top-level files' do - Tempfile.open('top-level') do |tempfile| - rewritten = { 'file' => tempfile.path } - in_params = { 'file.name' => original_filename, 'file.path' => file_path, 'file.remote_id' => remote_id, 'file.size' => file_size } - env = post_env(rewritten, in_params, Gitlab::Workhorse.secret, 'gitlab-workhorse') - - expect_uploaded_file(tempfile, %w(file)) + include MultipartHelpers - middleware.call(env) - end + describe '#call' do + let(:app) { double(:app) } + let(:middleware) { described_class.new(app) } + let(:secret) { Gitlab::Workhorse.secret } + let(:issuer) { 'gitlab-workhorse' } + + subject do + env = post_env( + rewritten_fields: rewritten_fields, + params: params, + secret: secret, + issuer: issuer + ) + middleware.call(env) end - it 'opens files one level deep' do - Tempfile.open('one-level') do |tempfile| - rewritten = { 'user[avatar]' => tempfile.path } - in_params = { 'user' => { 'avatar' => { '.name' => original_filename, '.path' => file_path, '.remote_id' => remote_id, '.size' => file_size } } } - env = post_env(rewritten, in_params, Gitlab::Workhorse.secret, 'gitlab-workhorse') + context 'with remote file mode params' do + let(:mode) { :remote } - expect_uploaded_file(tempfile, %w(user avatar)) + it_behaves_like 'handling all upload parameters conditions' - middleware.call(env) - end - end + context 'and a path set' do + include_context 'with one temporary file for multipart' - it 'opens files two levels deep' do - Tempfile.open('two-levels') do |tempfile| - in_params = { 'project' => { 'milestone' => { 'themesong' => { '.name' => original_filename, '.path' => file_path, '.remote_id' => remote_id, '.size' => file_size } } } } - rewritten = { 'project[milestone][themesong]' => tempfile.path } - env = post_env(rewritten, in_params, Gitlab::Workhorse.secret, 'gitlab-workhorse') + let(:rewritten_fields) { rewritten_fields_hash('file' => uploaded_filepath) } + let(:params) { upload_parameters_for(key: 'file', filename: filename, remote_id: remote_id).merge('file.path' => '/should/not/be/read') } - expect_uploaded_file(tempfile, %w(project milestone themesong)) - - middleware.call(env) - end - end + it 'builds an UploadedFile' do + expect_uploaded_files(original_filename: filename, remote_id: remote_id, size: uploaded_file.size, params_path: %w(file)) - def expect_uploaded_file(tempfile, path) - expect(app).to receive(:call) do |env| - file = get_params(env).dig(*path) - expect(file).to be_a(::UploadedFile) - expect(file.original_filename).to eq(original_filename) - - if remote_id - expect(file.remote_id).to eq(remote_id) - expect(file.path).to be_nil - else - expect(file.path).to eq(File.realpath(tempfile.path)) - expect(file.remote_id).to be_nil + subject end end end - end - RSpec.shared_examples_for 'handling CI artifact upload' do - it 'uploads both file and metadata' do - Tempfile.open('file') do |file| - Tempfile.open('metadata') do |metadata| - rewritten = { 'file' => file.path, 'metadata' => metadata.path } - in_params = { 'file.name' => 'file.txt', 'file.path' => file_path, 'file.remote_id' => file_remote_id, 'file.size' => file_size, 'metadata.name' => 'metadata.gz' } - env = post_env(rewritten, in_params, Gitlab::Workhorse.secret, 'gitlab-workhorse') - - with_expected_uploaded_artifact_files(file, metadata) do |uploaded_file, uploaded_metadata| - expect(uploaded_file).to be_a(::UploadedFile) - expect(uploaded_file.original_filename).to eq('file.txt') - - if file_remote_id - expect(uploaded_file.remote_id).to eq(file_remote_id) - expect(uploaded_file.size).to eq(file_size) - expect(uploaded_file.path).to be_nil - else - expect(uploaded_file.path).to eq(File.realpath(file.path)) - expect(uploaded_file.remote_id).to be_nil - end - - expect(uploaded_metadata).to be_a(::UploadedFile) - expect(uploaded_metadata.original_filename).to eq('metadata.gz') - expect(uploaded_metadata.path).to eq(File.realpath(metadata.path)) - expect(uploaded_metadata.remote_id).to be_nil - end - - middleware.call(env) - end - end - end + context 'local file mode' do + let(:mode) { :local } - def with_expected_uploaded_artifact_files(file, metadata) - expect(app).to receive(:call) do |env| - file = get_params(env).dig('file') - metadata = get_params(env).dig('metadata') + it_behaves_like 'handling all upload parameters conditions' - yield file, metadata - end - end - end + context 'when file is' do + include_context 'with one temporary file for multipart' - it 'rejects headers signed with the wrong secret' do - env = post_env({ 'file' => '/var/empty/nonesuch' }, {}, 'x' * 32, 'gitlab-workhorse') + let(:allowed_paths) { [Dir.tmpdir] } - expect { middleware.call(env) }.to raise_error(JWT::VerificationError) - end - - it 'rejects headers signed with the wrong issuer' do - env = post_env({ 'file' => '/var/empty/nonesuch' }, {}, Gitlab::Workhorse.secret, 'acme-inc') - - expect { middleware.call(env) }.to raise_error(JWT::InvalidIssuerError) - end - - context 'with invalid rewritten field' do - invalid_field_names = [ - '[file]', - ';file', - 'file]', - ';file]', - 'file]]', - 'file;;' - ] - - invalid_field_names.each do |invalid_field_name| - it "rejects invalid rewritten field name #{invalid_field_name}" do - env = post_env({ invalid_field_name => nil }, {}, Gitlab::Workhorse.secret, 'gitlab-workhorse') - - expect { middleware.call(env) }.to raise_error(RuntimeError, "invalid field: \"#{invalid_field_name}\"") - end - end - end - - context 'with remote file' do - let(:remote_id) { 'someid' } - let(:file_size) { 300 } - let(:file_path) { '' } - - it_behaves_like 'multipart upload files' - end - - context 'with remote file and a file path set' do - let(:remote_id) { 'someid' } - let(:file_size) { 300 } - let(:file_path) { 'not_a_valid_file_path' } # file path will come from the rewritten_fields - - it_behaves_like 'multipart upload files' - end + before do + expect_next_instance_of(::Gitlab::Middleware::Multipart::Handler) do |handler| + expect(handler).to receive(:allowed_paths).and_return(allowed_paths) + end + end - context 'with local file' do - let(:remote_id) { nil } - let(:file_size) { nil } - let(:file_path) { 'not_a_valid_file_path' } # file path will come from the rewritten_fields + context 'in allowed paths' do + let(:rewritten_fields) { rewritten_fields_hash('file' => uploaded_filepath) } + let(:params) { upload_parameters_for(filepath: uploaded_filepath, key: 'file', filename: filename, remote_id: remote_id) } - it_behaves_like 'multipart upload files' - end + it 'builds an UploadedFile' do + expect_uploaded_files(filepath: uploaded_filepath, original_filename: filename, remote_id: remote_id, size: uploaded_file.size, params_path: %w(file)) - context 'with remote CI artifact upload' do - let(:file_remote_id) { 'someid' } - let(:file_size) { 300 } - let(:file_path) { 'not_a_valid_file_path' } # file path will come from the rewritten_fields + subject + end + end - it_behaves_like 'handling CI artifact upload' - end + context 'not in allowed paths' do + let(:allowed_paths) { [] } - context 'with local CI artifact upload' do - let(:file_remote_id) { nil } - let(:file_size) { nil } - let(:file_path) { 'not_a_valid_file_path' } # file path will come from the rewritten_fields + let(:rewritten_fields) { rewritten_fields_hash('file' => uploaded_filepath) } + let(:params) { upload_parameters_for(filepath: uploaded_filepath, key: 'file') } - it_behaves_like 'handling CI artifact upload' - end + it 'returns an error' do + result = subject - it 'allows files in uploads/tmp directory' do - with_tmp_dir('public/uploads/tmp') do |dir, env| - expect(app).to receive(:call) do |env| - expect(get_params(env)['file']).to be_a(::UploadedFile) + expect(result[0]).to eq(400) + expect(result[2]).to include('insecure path used') + end + end end - - middleware.call(env) end - end - it 'allows files in the job artifact upload path' do - with_tmp_dir('artifacts') do |dir, env| - expect(JobArtifactUploader).to receive(:workhorse_upload_path).and_return(File.join(dir, 'artifacts')) - expect(app).to receive(:call) do |env| - expect(get_params(env)['file']).to be_a(::UploadedFile) - end + context 'with dummy params in remote mode' do + let(:rewritten_fields) { { 'file' => 'should/not/be/read' } } + let(:params) { upload_parameters_for(key: 'file') } + let(:mode) { :remote } - middleware.call(env) - end - end + context 'with an invalid secret' do + let(:secret) { 'INVALID_SECRET' } - it 'allows files in the lfs upload path' do - with_tmp_dir('lfs-objects') do |dir, env| - expect(LfsObjectUploader).to receive(:workhorse_upload_path).and_return(File.join(dir, 'lfs-objects')) - expect(app).to receive(:call) do |env| - expect(get_params(env)['file']).to be_a(::UploadedFile) + it { expect { subject }.to raise_error(JWT::VerificationError) } end - middleware.call(env) - end - end + context 'with an invalid issuer' do + let(:issuer) { 'INVALID_ISSUER' } - it 'allows symlinks for uploads dir' do - Tempfile.open('two-levels') do |tempfile| - symlinked_dir = '/some/dir/uploads' - symlinked_path = File.join(symlinked_dir, File.basename(tempfile.path)) - env = post_env({ 'file' => symlinked_path }, { 'file.name' => original_filename, 'file.path' => symlinked_path }, Gitlab::Workhorse.secret, 'gitlab-workhorse') - - allow(FileUploader).to receive(:root).and_return(symlinked_dir) - allow(UploadedFile).to receive(:allowed_paths).and_return([symlinked_dir, Gitlab.config.uploads.storage_path]) - allow(File).to receive(:realpath).and_call_original - allow(File).to receive(:realpath).with(symlinked_dir).and_return(Dir.tmpdir) - allow(File).to receive(:realpath).with(symlinked_path).and_return(tempfile.path) - allow(File).to receive(:exist?).and_call_original - allow(File).to receive(:exist?).with(symlinked_dir).and_return(true) - - # override Dir.tmpdir because this dir is in the list of allowed paths - # and it would match FileUploader.root path (which in this test is linked - # to /tmp too) - allow(Dir).to receive(:tmpdir).and_return(File.join(Dir.tmpdir, 'tmpsubdir')) - - expect(app).to receive(:call) do |env| - expect(get_params(env)['file']).to be_a(::UploadedFile) + it { expect { subject }.to raise_error(JWT::InvalidIssuerError) } end - middleware.call(env) - end - end - - describe '#call' do - context 'with packages storage' do - using RSpec::Parameterized::TableSyntax - - let(:storage_path) { 'shared/packages' } - - RSpec.shared_examples 'allowing the multipart upload' do - it 'allows files to be uploaded' do - with_tmp_dir('tmp/uploads', storage_path) do |dir, env| - allow(Packages::PackageFileUploader).to receive(:root).and_return(File.join(dir, storage_path)) + context 'with invalid rewritten field key' do + invalid_keys = [ + '[file]', + ';file', + 'file]', + ';file]', + 'file]]', + 'file;;' + ] - expect(app).to receive(:call) do |env| - expect(get_params(env)['file']).to be_a(::UploadedFile) - end + invalid_keys.each do |invalid_key| + context invalid_key do + let(:rewritten_fields) { { invalid_key => 'should/not/be/read' } } - middleware.call(env) + it { expect { subject }.to raise_error(RuntimeError, "invalid field: \"#{invalid_key}\"") } end end end - RSpec.shared_examples 'not allowing the multipart upload when package upload path is used' do - it 'does not allow files to be uploaded' do - with_tmp_dir('tmp/uploads', storage_path) do |dir, env| - # with_tmp_dir sets the same workhorse_upload_path for all Uploaders, - # so we have to prevent JobArtifactUploader and LfsObjectUploader to - # allow the tested path - allow(JobArtifactUploader).to receive(:workhorse_upload_path).and_return(Dir.tmpdir) - allow(LfsObjectUploader).to receive(:workhorse_upload_path).and_return(Dir.tmpdir) + context 'with invalid key in parameters' do + include_context 'with one temporary file for multipart' - status, headers, body = middleware.call(env) + let(:rewritten_fields) { rewritten_fields_hash('file' => uploaded_filepath) } + let(:params) { upload_parameters_for(filepath: uploaded_filepath, key: 'wrong_key', filename: filename, remote_id: remote_id) } - expect(status).to eq(400) - expect(headers).to eq({ 'Content-Type' => 'text/plain' }) - expect(body).to start_with('insecure path used') + it 'builds no UploadedFile' do + expect(app).to receive(:call) do |env| + received_params = get_params(env) + expect(received_params['file']).to be_nil + expect(received_params['wrong_key']).to be_nil end - end - end - RSpec.shared_examples 'adding package storage to multipart allowed paths' do - before do - expect(::Packages::PackageFileUploader).to receive(:workhorse_upload_path).and_call_original + subject end - - it_behaves_like 'allowing the multipart upload' - end - - RSpec.shared_examples 'not adding package storage to multipart allowed paths' do - before do - expect(::Packages::PackageFileUploader).not_to receive(:workhorse_upload_path) - end - - it_behaves_like 'not allowing the multipart upload when package upload path is used' - end - - where(:object_storage_enabled, :direct_upload_enabled, :example_name) do - false | true | 'adding package storage to multipart allowed paths' - false | false | 'adding package storage to multipart allowed paths' - true | true | 'not adding package storage to multipart allowed paths' - true | false | 'adding package storage to multipart allowed paths' - end - - with_them do - before do - stub_config(packages: { - enabled: true, - object_store: { - enabled: object_storage_enabled, - direct_upload: direct_upload_enabled - }, - storage_path: storage_path - }) - end - - it_behaves_like params[:example_name] end end end diff --git a/spec/lib/gitlab/project_search_results_spec.rb b/spec/lib/gitlab/project_search_results_spec.rb index ea66363469a..22383cd993c 100644 --- a/spec/lib/gitlab/project_search_results_spec.rb +++ b/spec/lib/gitlab/project_search_results_spec.rb @@ -5,12 +5,13 @@ require 'spec_helper' RSpec.describe Gitlab::ProjectSearchResults do include SearchHelpers - let(:user) { create(:user) } - let(:project) { create(:project) } + let_it_be(:user) { create(:user) } + let_it_be(:project) { create(:project) } let(:query) { 'hello world' } let(:repository_ref) { nil } + let(:filters) { {} } - subject(:results) { described_class.new(user, query, project: project, repository_ref: repository_ref) } + subject(:results) { described_class.new(user, query, project: project, repository_ref: repository_ref, filters: filters) } context 'with a repository_ref' do context 'when empty' do @@ -258,6 +259,24 @@ RSpec.describe Gitlab::ProjectSearchResults do describe "confidential issues" do include_examples "access restricted confidential issues" end + + context 'filtering' do + let_it_be(:project) { create(:project, :public) } + let_it_be(:closed_issue) { create(:issue, :closed, project: project, title: 'foo closed') } + let_it_be(:opened_issue) { create(:issue, :opened, project: project, title: 'foo opened') } + let(:query) { 'foo' } + + include_examples 'search issues scope filters by state' + end + + it 'filters issues when state is provided', :aggregate_failures do + closed_issue = create(:issue, :closed, project: project, title: "Revert: #{issue.title}") + + results = described_class.new(project.creator, query, project: project, filters: { state: 'opened' }) + + expect(results.objects('issues')).not_to include closed_issue + expect(results.objects('issues')).to include issue + end end describe 'notes search' do diff --git a/spec/lib/gitlab/search_results_spec.rb b/spec/lib/gitlab/search_results_spec.rb index c5563027a84..13942493cc5 100644 --- a/spec/lib/gitlab/search_results_spec.rb +++ b/spec/lib/gitlab/search_results_spec.rb @@ -6,13 +6,14 @@ RSpec.describe Gitlab::SearchResults do include ProjectForksHelper include SearchHelpers - let(:user) { create(:user) } - let!(:project) { create(:project, name: 'foo') } - let!(:issue) { create(:issue, project: project, title: 'foo') } - let!(:merge_request) { create(:merge_request, source_project: project, title: 'foo') } - let!(:milestone) { create(:milestone, project: project, title: 'foo') } + let_it_be(:user) { create(:user) } + let_it_be(:project) { create(:project, name: 'foo') } + let_it_be(:issue) { create(:issue, project: project, title: 'foo') } + let_it_be(:milestone) { create(:milestone, project: project, title: 'foo') } + let(:merge_request) { create(:merge_request, source_project: project, title: 'foo') } + let(:filters) { {} } - subject(:results) { described_class.new(user, 'foo', Project.all) } + subject(:results) { described_class.new(user, 'foo', Project.all, filters: filters) } context 'as a user with access' do before do @@ -105,10 +106,10 @@ RSpec.describe Gitlab::SearchResults do describe '#limited_issues_count' do it 'runs single SQL query to get the limited amount of issues' do - create(:milestone, project: project, title: 'foo2') + create(:issue, project: project, title: 'foo2') expect(results).to receive(:issues).with(public_only: true).and_call_original - expect(results).not_to receive(:issues).with(no_args).and_call_original + expect(results).not_to receive(:issues).with(no_args) expect(results.limited_issues_count).to eq(1) end @@ -165,6 +166,13 @@ RSpec.describe Gitlab::SearchResults do results.objects('issues') end + + context 'filtering' do + let_it_be(:closed_issue) { create(:issue, :closed, project: project, title: 'foo closed') } + let_it_be(:opened_issue) { create(:issue, :opened, project: project, title: 'foo open') } + + include_examples 'search issues scope filters by state' + end end describe '#users' do diff --git a/spec/lib/uploaded_file_spec.rb b/spec/lib/uploaded_file_spec.rb index 5ff46193b4f..cf2ab04b457 100644 --- a/spec/lib/uploaded_file_spec.rb +++ b/spec/lib/uploaded_file_spec.rb @@ -23,7 +23,7 @@ RSpec.describe UploadedFile do end subject do - described_class.from_params(params, :file, upload_path, file_path_override) + described_class.from_params(params, :file, [upload_path, Dir.tmpdir], file_path_override) end context 'when valid file is specified' do |