diff options
Diffstat (limited to 'spec')
24 files changed, 581 insertions, 192 deletions
diff --git a/spec/controllers/import/github_controller_spec.rb b/spec/controllers/import/github_controller_spec.rb index 059354870b5..5675798ac33 100644 --- a/spec/controllers/import/github_controller_spec.rb +++ b/spec/controllers/import/github_controller_spec.rb @@ -33,6 +33,16 @@ describe Import::GithubController do expect(response).to have_http_status(200) end + + context 'when importing a CI/CD project' do + it 'always prompts for an access token' do + allow(controller).to receive(:github_import_configured?).and_return(true) + + get :new, params: { ci_cd_only: true } + + expect(response).to render_template(:new) + end + end end describe "GET callback" do diff --git a/spec/controllers/projects/raw_controller_spec.rb b/spec/controllers/projects/raw_controller_spec.rb index 97acd47b4da..8ee3168273f 100644 --- a/spec/controllers/projects/raw_controller_spec.rb +++ b/spec/controllers/projects/raw_controller_spec.rb @@ -3,6 +3,8 @@ require 'spec_helper' describe Projects::RawController do + include RepoHelpers + let(:project) { create(:project, :public, :repository) } describe 'GET #show' do @@ -46,5 +48,98 @@ describe Projects::RawController do let(:filename) { 'lfs_object.iso' } let(:filepath) { "be93687/files/lfs/#{filename}" } end + + context 'when the endpoint receives requests above the limit', :clean_gitlab_redis_cache do + let(:file_path) { 'master/README.md' } + + before do + stub_application_setting(raw_blob_request_limit: 5) + end + + it 'prevents from accessing the raw file' do + execute_raw_requests(requests: 6, project: project, file_path: file_path) + + expect(flash[:alert]).to eq('You cannot access the raw file. Please wait a minute.') + expect(response).to redirect_to(project_blob_path(project, file_path)) + end + + it 'logs the event on auth.log' do + attributes = { + message: 'Action_Rate_Limiter_Request', + env: :raw_blob_request_limit, + ip: '0.0.0.0', + request_method: 'GET', + fullpath: "/#{project.full_path}/raw/#{file_path}" + } + + expect(Gitlab::AuthLogger).to receive(:error).with(attributes).once + + execute_raw_requests(requests: 6, project: project, file_path: file_path) + end + + context 'when the request uses a different version of a commit' do + it 'prevents from accessing the raw file' do + # 3 times with the normal sha + commit_sha = project.repository.commit.sha + file_path = "#{commit_sha}/README.md" + + execute_raw_requests(requests: 3, project: project, file_path: file_path) + + # 3 times with the modified version + modified_sha = commit_sha.gsub(commit_sha[0..5], commit_sha[0..5].upcase) + modified_path = "#{modified_sha}/README.md" + + execute_raw_requests(requests: 3, project: project, file_path: modified_path) + + expect(flash[:alert]).to eq('You cannot access the raw file. Please wait a minute.') + expect(response).to redirect_to(project_blob_path(project, modified_path)) + end + end + + context 'when the throttling has been disabled' do + before do + stub_application_setting(raw_blob_request_limit: 0) + end + + it 'does not prevent from accessing the raw file' do + execute_raw_requests(requests: 10, project: project, file_path: file_path) + + expect(response).to have_gitlab_http_status(200) + end + end + + context 'with case-sensitive files' do + it 'prevents from accessing the specific file' do + create_file_in_repo(project, 'master', 'master', 'readme.md', 'Add readme.md') + create_file_in_repo(project, 'master', 'master', 'README.md', 'Add README.md') + + commit_sha = project.repository.commit.sha + file_path = "#{commit_sha}/readme.md" + + # Accessing downcase version of readme + execute_raw_requests(requests: 6, project: project, file_path: file_path) + + expect(flash[:alert]).to eq('You cannot access the raw file. Please wait a minute.') + expect(response).to redirect_to(project_blob_path(project, file_path)) + + # Accessing upcase version of readme + file_path = "#{commit_sha}/README.md" + + execute_raw_requests(requests: 1, project: project, file_path: file_path) + + expect(response).to have_gitlab_http_status(200) + end + end + end + end + + def execute_raw_requests(requests:, project:, file_path:) + requests.times do + get :show, params: { + namespace_id: project.namespace, + project_id: project, + id: file_path + } + end end end diff --git a/spec/controllers/user_callouts_controller_spec.rb b/spec/controllers/user_callouts_controller_spec.rb index babc93a83e5..07eaff2da09 100644 --- a/spec/controllers/user_callouts_controller_spec.rb +++ b/spec/controllers/user_callouts_controller_spec.rb @@ -13,7 +13,7 @@ describe UserCalloutsController do subject { post :create, params: { feature_name: feature_name }, format: :json } context 'with valid feature name' do - let(:feature_name) { UserCallout.feature_names.keys.first } + let(:feature_name) { UserCallout.feature_names.first.first } context 'when callout entry does not exist' do it 'creates a callout entry with dismissed state' do @@ -28,7 +28,7 @@ describe UserCalloutsController do end context 'when callout entry already exists' do - let!(:callout) { create(:user_callout, feature_name: UserCallout.feature_names.keys.first, user: user) } + let!(:callout) { create(:user_callout, feature_name: UserCallout.feature_names.first.first, user: user) } it 'returns success' do subject diff --git a/spec/frontend/lib/utils/text_utility_spec.js b/spec/frontend/lib/utils/text_utility_spec.js index dc886d0db3b..b6f1aef9ce4 100644 --- a/spec/frontend/lib/utils/text_utility_spec.js +++ b/spec/frontend/lib/utils/text_utility_spec.js @@ -29,20 +29,6 @@ describe('text_utility', () => { }); }); - describe('pluralize', () => { - it('should pluralize given string', () => { - expect(textUtils.pluralize('test', 2)).toBe('tests'); - }); - - it('should pluralize when count is 0', () => { - expect(textUtils.pluralize('test', 0)).toBe('tests'); - }); - - it('should not pluralize when count is 1', () => { - expect(textUtils.pluralize('test', 1)).toBe('test'); - }); - }); - describe('dasherize', () => { it('should replace underscores with dashes', () => { expect(textUtils.dasherize('foo_bar_foo')).toEqual('foo-bar-foo'); diff --git a/spec/helpers/labels_helper_spec.rb b/spec/helpers/labels_helper_spec.rb index 314305d7a8e..4f1cab38f34 100644 --- a/spec/helpers/labels_helper_spec.rb +++ b/spec/helpers/labels_helper_spec.rb @@ -3,10 +3,8 @@ require 'spec_helper' describe LabelsHelper do describe '#show_label_issuables_link?' do shared_examples 'a valid response to show_label_issuables_link?' do |issuables_type, when_enabled = true, when_disabled = false| - let(:context_project) { project } - context "when asking for a #{issuables_type} link" do - subject { show_label_issuables_link?(label.present(issuable_subject: nil), issuables_type, project: context_project) } + subject { show_label_issuables_link?(label.present(issuable_subject: nil), issuables_type) } context "when #{issuables_type} are enabled for the project" do let(:project) { create(:project, "#{issuables_type}_access_level": ProjectFeature::ENABLED) } @@ -39,27 +37,11 @@ describe LabelsHelper do let(:label) { create(:group_label, group: group, title: 'bug') } context 'when asking for an issue link' do - context 'in the context of a project' do - it_behaves_like 'a valid response to show_label_issuables_link?', :issues, true, true - end - - context 'in the context of a group' do - let(:context_project) { nil } - - it_behaves_like 'a valid response to show_label_issuables_link?', :issues, true, true - end + it_behaves_like 'a valid response to show_label_issuables_link?', :issues, true, true end context 'when asking for a merge requests link' do - context 'in the context of a project' do - it_behaves_like 'a valid response to show_label_issuables_link?', :merge_requests, true, true - end - - context 'in the context of a group' do - let(:context_project) { nil } - - it_behaves_like 'a valid response to show_label_issuables_link?', :merge_requests, true, true - end + it_behaves_like 'a valid response to show_label_issuables_link?', :merge_requests, true, true end end end diff --git a/spec/lib/gitlab/action_rate_limiter_spec.rb b/spec/lib/gitlab/action_rate_limiter_spec.rb index 542fc03e555..cf266a25819 100644 --- a/spec/lib/gitlab/action_rate_limiter_spec.rb +++ b/spec/lib/gitlab/action_rate_limiter_spec.rb @@ -1,11 +1,9 @@ require 'spec_helper' -describe Gitlab::ActionRateLimiter do +describe Gitlab::ActionRateLimiter, :clean_gitlab_redis_cache do let(:redis) { double('redis') } let(:user) { create(:user) } let(:project) { create(:project) } - let(:key) { [user, project] } - let(:cache_key) { "action_rate_limiter:test_action:user:#{user.id}:project:#{project.id}" } subject { described_class.new(action: :test_action, expiry_time: 100) } @@ -13,17 +11,98 @@ describe Gitlab::ActionRateLimiter do allow(Gitlab::Redis::Cache).to receive(:with).and_yield(redis) end - it 'increases the throttle count and sets the expire time' do - expect(redis).to receive(:incr).with(cache_key).and_return(1) - expect(redis).to receive(:expire).with(cache_key, 100) + shared_examples 'action rate limiter' do + it 'increases the throttle count and sets the expiration time' do + expect(redis).to receive(:incr).with(cache_key).and_return(1) + expect(redis).to receive(:expire).with(cache_key, 100) - expect(subject.throttled?(key, 1)).to be false + expect(subject.throttled?(key, 1)).to be_falsy + end + + it 'returns true if the key is throttled' do + expect(redis).to receive(:incr).with(cache_key).and_return(2) + expect(redis).not_to receive(:expire) + + expect(subject.throttled?(key, 1)).to be_truthy + end + + context 'when throttling is disabled' do + it 'returns false and does not set expiration time' do + expect(redis).not_to receive(:incr) + expect(redis).not_to receive(:expire) + + expect(subject.throttled?(key, 0)).to be_falsy + end + end + end + + context 'when the key is an array of only ActiveRecord models' do + let(:key) { [user, project] } + + let(:cache_key) do + "action_rate_limiter:test_action:user:#{user.id}:project:#{project.id}" + end + + it_behaves_like 'action rate limiter' + end + + context 'when they key a combination of ActiveRecord models and strings' do + let(:project) { create(:project, :public, :repository) } + let(:commit) { project.repository.commit } + let(:path) { 'app/controllers/groups_controller.rb' } + let(:key) { [project, commit, path] } + + let(:cache_key) do + "action_rate_limiter:test_action:project:#{project.id}:commit:#{commit.sha}:#{path}" + end + + it_behaves_like 'action rate limiter' end - it 'returns true if the key is throttled' do - expect(redis).to receive(:incr).with(cache_key).and_return(2) - expect(redis).not_to receive(:expire) + describe '#log_request' do + let(:file_path) { 'master/README.md' } + let(:type) { :raw_blob_request_limit } + let(:fullpath) { "/#{project.full_path}/raw/#{file_path}" } + + let(:request) do + double('request', ip: '127.0.0.1', request_method: 'GET', fullpath: fullpath) + end + + let(:base_attributes) do + { + message: 'Action_Rate_Limiter_Request', + env: type, + ip: '127.0.0.1', + request_method: 'GET', + fullpath: fullpath + } + end + + context 'without a current user' do + let(:current_user) { nil } + + it 'logs information to auth.log' do + expect(Gitlab::AuthLogger).to receive(:error).with(base_attributes).once + + subject.log_request(request, type, current_user) + end + end + + context 'with a current_user' do + let(:current_user) { create(:user) } + + let(:attributes) do + base_attributes.merge({ + user_id: current_user.id, + username: current_user.username + }) + end + + it 'logs information to auth.log' do + expect(Gitlab::AuthLogger).to receive(:error).with(attributes).once - expect(subject.throttled?(key, 1)).to be true + subject.log_request(request, type, current_user) + end + end end end diff --git a/spec/lib/gitlab/database/count/exact_count_strategy_spec.rb b/spec/lib/gitlab/database/count/exact_count_strategy_spec.rb index 3991c737a26..0c1be4b4610 100644 --- a/spec/lib/gitlab/database/count/exact_count_strategy_spec.rb +++ b/spec/lib/gitlab/database/count/exact_count_strategy_spec.rb @@ -23,18 +23,4 @@ describe Gitlab::Database::Count::ExactCountStrategy do expect(subject).to eq({}) end end - - describe '.enabled?' do - it 'is enabled for PostgreSQL' do - allow(Gitlab::Database).to receive(:postgresql?).and_return(true) - - expect(described_class.enabled?).to be_truthy - end - - it 'is enabled for MySQL' do - allow(Gitlab::Database).to receive(:postgresql?).and_return(false) - - expect(described_class.enabled?).to be_truthy - end - end end diff --git a/spec/lib/gitlab/database/count/reltuples_count_strategy_spec.rb b/spec/lib/gitlab/database/count/reltuples_count_strategy_spec.rb index bd3c66d0548..a528707c9dc 100644 --- a/spec/lib/gitlab/database/count/reltuples_count_strategy_spec.rb +++ b/spec/lib/gitlab/database/count/reltuples_count_strategy_spec.rb @@ -48,18 +48,4 @@ describe Gitlab::Database::Count::ReltuplesCountStrategy do end end end - - describe '.enabled?' do - it 'is enabled for PostgreSQL' do - allow(Gitlab::Database).to receive(:postgresql?).and_return(true) - - expect(described_class.enabled?).to be_truthy - end - - it 'is disabled for MySQL' do - allow(Gitlab::Database).to receive(:postgresql?).and_return(false) - - expect(described_class.enabled?).to be_falsey - end - end end diff --git a/spec/lib/gitlab/database/count/tablesample_count_strategy_spec.rb b/spec/lib/gitlab/database/count/tablesample_count_strategy_spec.rb index 40d810b195b..a57f033b5ed 100644 --- a/spec/lib/gitlab/database/count/tablesample_count_strategy_spec.rb +++ b/spec/lib/gitlab/database/count/tablesample_count_strategy_spec.rb @@ -56,22 +56,4 @@ describe Gitlab::Database::Count::TablesampleCountStrategy do end end end - - describe '.enabled?' do - before do - stub_feature_flags(tablesample_counts: true) - end - - it 'is enabled for PostgreSQL' do - allow(Gitlab::Database).to receive(:postgresql?).and_return(true) - - expect(described_class.enabled?).to be_truthy - end - - it 'is disabled for MySQL' do - allow(Gitlab::Database).to receive(:postgresql?).and_return(false) - - expect(described_class.enabled?).to be_falsey - end - end end diff --git a/spec/lib/gitlab/database/count_spec.rb b/spec/lib/gitlab/database/count_spec.rb index 1d096b8fa7c..71d6633f62f 100644 --- a/spec/lib/gitlab/database/count_spec.rb +++ b/spec/lib/gitlab/database/count_spec.rb @@ -9,24 +9,13 @@ describe Gitlab::Database::Count do let(:models) { [Project, Identity] } context '.approximate_counts' do - context 'selecting strategies' do - let(:strategies) { [double('s1', enabled?: true), double('s2', enabled?: false)] } - - it 'uses only enabled strategies' do - expect(strategies[0]).to receive(:new).and_return(double('strategy1', count: {})) - expect(strategies[1]).not_to receive(:new) - - described_class.approximate_counts(models, strategies: strategies) - end - end - context 'fallbacks' do subject { described_class.approximate_counts(models, strategies: strategies) } let(:strategies) do [ - double('s1', enabled?: true, new: first_strategy), - double('s2', enabled?: true, new: second_strategy) + double('s1', new: first_strategy), + double('s2', new: second_strategy) ] end diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb index 41b898df112..dccd50bc472 100644 --- a/spec/lib/gitlab/git/repository_spec.rb +++ b/spec/lib/gitlab/git/repository_spec.rb @@ -186,6 +186,12 @@ describe Gitlab::Git::Repository, :seed_helper do it { is_expected.to be < 2 } end + describe '#to_s' do + subject { repository.to_s } + + it { is_expected.to eq("<Gitlab::Git::Repository: group/project>") } + end + describe '#object_directory_size' do before do allow(repository.gitaly_repository_client) diff --git a/spec/lib/gitlab/git/rugged_impl/use_rugged_spec.rb b/spec/lib/gitlab/git/rugged_impl/use_rugged_spec.rb index e437647c258..1a4168f7317 100644 --- a/spec/lib/gitlab/git/rugged_impl/use_rugged_spec.rb +++ b/spec/lib/gitlab/git/rugged_impl/use_rugged_spec.rb @@ -16,7 +16,13 @@ describe Gitlab::Git::RuggedImpl::UseRugged, :seed_helper do end subject(:wrapper) do - klazz = Class.new { include Gitlab::Git::RuggedImpl::UseRugged } + klazz = Class.new do + include Gitlab::Git::RuggedImpl::UseRugged + + def rugged_test(ref, test_number) + end + end + klazz.new end @@ -25,6 +31,23 @@ describe Gitlab::Git::RuggedImpl::UseRugged, :seed_helper do Gitlab::GitalyClient.instance_variable_set(:@can_use_disk, {}) end + context '#execute_rugged_call', :request_store do + let(:args) { ['refs/heads/master', 1] } + + before do + allow(Gitlab::RuggedInstrumentation).to receive(:peek_enabled?).and_return(true) + end + + it 'instruments Rugged call' do + expect(subject).to receive(:rugged_test).with(args) + + subject.execute_rugged_call(:rugged_test, args) + + expect(Gitlab::RuggedInstrumentation.query_count).to eq(1) + expect(Gitlab::RuggedInstrumentation.list_call_details.count).to eq(1) + end + end + context 'when feature flag is not persisted' do before do allow(Feature).to receive(:persisted?).with(feature_flag).and_return(false) diff --git a/spec/lib/gitlab/import_export/project.group.json b/spec/lib/gitlab/import_export/project.group.json index 1a561e81e4a..66f5bb4c87b 100644 --- a/spec/lib/gitlab/import_export/project.group.json +++ b/spec/lib/gitlab/import_export/project.group.json @@ -19,7 +19,7 @@ "labels": [ { "id": 2, - "title": "project label", + "title": "A project label", "color": "#428bca", "project_id": 8, "created_at": "2016-07-22T08:55:44.161Z", @@ -105,7 +105,7 @@ "updated_at": "2017-08-15T18:37:40.795Z", "label": { "id": 6, - "title": "project label", + "title": "A project label", "color": "#A8D695", "project_id": null, "created_at": "2017-08-15T18:37:19.698Z", @@ -162,7 +162,7 @@ "updated_at": "2017-08-15T18:37:40.795Z", "label": { "id": 2, - "title": "project label", + "title": "A project label", "color": "#A8D695", "project_id": null, "created_at": "2017-08-15T18:37:19.698Z", diff --git a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb index 3b7de185cf1..b9f6595762b 100644 --- a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb +++ b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb @@ -272,7 +272,7 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do end it 'has label priorities' do - expect(project.labels.first.priorities).not_to be_empty + expect(project.labels.find_by(title: 'A project label').priorities).not_to be_empty end it 'has milestones' do @@ -325,7 +325,7 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do it_behaves_like 'restores project correctly', issues: 1, - labels: 1, + labels: 2, milestones: 1, first_issue_labels: 1, services: 1 @@ -402,7 +402,7 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do it_behaves_like 'restores project successfully' it_behaves_like 'restores project correctly', issues: 2, - labels: 1, + labels: 2, milestones: 2, first_issue_labels: 1 diff --git a/spec/lib/gitlab/url_blocker_spec.rb b/spec/lib/gitlab/url_blocker_spec.rb index f8b0cbfb6f6..93194de4a1b 100644 --- a/spec/lib/gitlab/url_blocker_spec.rb +++ b/spec/lib/gitlab/url_blocker_spec.rb @@ -220,53 +220,53 @@ describe Gitlab::UrlBlocker do end let(:fake_domain) { 'www.fakedomain.fake' } - context 'true (default)' do + shared_examples 'allows local requests' do |url_blocker_attributes| it 'does not block urls from private networks' do local_ips.each do |ip| - stub_domain_resolv(fake_domain, ip) - - expect(described_class).not_to be_blocked_url("http://#{fake_domain}") - - unstub_domain_resolv + stub_domain_resolv(fake_domain, ip) do + expect(described_class).not_to be_blocked_url("http://#{fake_domain}", url_blocker_attributes) + end - expect(described_class).not_to be_blocked_url("http://#{ip}") + expect(described_class).not_to be_blocked_url("http://#{ip}", url_blocker_attributes) end end it 'allows localhost endpoints' do - expect(described_class).not_to be_blocked_url('http://0.0.0.0', allow_localhost: true) - expect(described_class).not_to be_blocked_url('http://localhost', allow_localhost: true) - expect(described_class).not_to be_blocked_url('http://127.0.0.1', allow_localhost: true) + expect(described_class).not_to be_blocked_url('http://0.0.0.0', url_blocker_attributes) + expect(described_class).not_to be_blocked_url('http://localhost', url_blocker_attributes) + expect(described_class).not_to be_blocked_url('http://127.0.0.1', url_blocker_attributes) end it 'allows loopback endpoints' do - expect(described_class).not_to be_blocked_url('http://127.0.0.2', allow_localhost: true) + expect(described_class).not_to be_blocked_url('http://127.0.0.2', url_blocker_attributes) end it 'allows IPv4 link-local endpoints' do - expect(described_class).not_to be_blocked_url('http://169.254.169.254') - expect(described_class).not_to be_blocked_url('http://169.254.168.100') + expect(described_class).not_to be_blocked_url('http://169.254.169.254', url_blocker_attributes) + expect(described_class).not_to be_blocked_url('http://169.254.168.100', url_blocker_attributes) end it 'allows IPv6 link-local endpoints' do - expect(described_class).not_to be_blocked_url('http://[0:0:0:0:0:ffff:169.254.169.254]') - expect(described_class).not_to be_blocked_url('http://[::ffff:169.254.169.254]') - expect(described_class).not_to be_blocked_url('http://[::ffff:a9fe:a9fe]') - expect(described_class).not_to be_blocked_url('http://[0:0:0:0:0:ffff:169.254.168.100]') - expect(described_class).not_to be_blocked_url('http://[::ffff:169.254.168.100]') - expect(described_class).not_to be_blocked_url('http://[::ffff:a9fe:a864]') - expect(described_class).not_to be_blocked_url('http://[fe80::c800:eff:fe74:8]') + expect(described_class).not_to be_blocked_url('http://[0:0:0:0:0:ffff:169.254.169.254]', url_blocker_attributes) + expect(described_class).not_to be_blocked_url('http://[::ffff:169.254.169.254]', url_blocker_attributes) + expect(described_class).not_to be_blocked_url('http://[::ffff:a9fe:a9fe]', url_blocker_attributes) + expect(described_class).not_to be_blocked_url('http://[0:0:0:0:0:ffff:169.254.168.100]', url_blocker_attributes) + expect(described_class).not_to be_blocked_url('http://[::ffff:169.254.168.100]', url_blocker_attributes) + expect(described_class).not_to be_blocked_url('http://[::ffff:a9fe:a864]', url_blocker_attributes) + expect(described_class).not_to be_blocked_url('http://[fe80::c800:eff:fe74:8]', url_blocker_attributes) end end + context 'true (default)' do + it_behaves_like 'allows local requests', { allow_localhost: true, allow_local_network: true } + end + context 'false' do it 'blocks urls from private networks' do local_ips.each do |ip| - stub_domain_resolv(fake_domain, ip) - - expect(described_class).to be_blocked_url("http://#{fake_domain}", allow_local_network: false) - - unstub_domain_resolv + stub_domain_resolv(fake_domain, ip) do + expect(described_class).to be_blocked_url("http://#{fake_domain}", allow_local_network: false) + end expect(described_class).to be_blocked_url("http://#{ip}", allow_local_network: false) end @@ -286,15 +286,169 @@ describe Gitlab::UrlBlocker do expect(described_class).to be_blocked_url('http://[::ffff:a9fe:a864]', allow_local_network: false) expect(described_class).to be_blocked_url('http://[fe80::c800:eff:fe74:8]', allow_local_network: false) end + + context 'when local domain/IP is whitelisted' do + let(:url_blocker_attributes) do + { + allow_localhost: false, + allow_local_network: false + } + end + + before do + stub_application_setting(outbound_local_requests_whitelist: whitelist) + end + + context 'with IPs in whitelist' do + let(:whitelist) do + [ + '0.0.0.0', + '127.0.0.1', + '127.0.0.2', + '192.168.1.1', + '192.168.1.2', + '0:0:0:0:0:ffff:192.168.1.2', + '::ffff:c0a8:102', + '10.0.0.2', + '0:0:0:0:0:ffff:10.0.0.2', + '::ffff:a00:2', + '172.16.0.2', + '0:0:0:0:0:ffff:172.16.0.2', + '::ffff:ac10:20', + 'feef::1', + 'fee2::', + 'fc00:bf8b:e62c:abcd:abcd:aaaa:aaaa:aaaa', + '0:0:0:0:0:ffff:169.254.169.254', + '::ffff:a9fe:a9fe', + '::ffff:169.254.168.100', + '::ffff:a9fe:a864', + 'fe80::c800:eff:fe74:8', + + # garbage IPs + '45645632345', + 'garbage456:more345gar:bage' + ] + end + + it_behaves_like 'allows local requests', { allow_localhost: false, allow_local_network: false } + + it 'whitelists IP when dns_rebind_protection is disabled' do + stub_domain_resolv('example.com', '192.168.1.1') do + expect(described_class).not_to be_blocked_url("http://example.com", + url_blocker_attributes.merge(dns_rebind_protection: false)) + end + end + end + + context 'with domains in whitelist' do + let(:whitelist) do + [ + 'www.example.com', + 'example.com', + 'xn--itlab-j1a.com', + 'garbage$^$%#$^&$' + ] + end + + it 'allows domains present in whitelist' do + domain = 'example.com' + subdomain1 = 'www.example.com' + subdomain2 = 'subdomain.example.com' + + stub_domain_resolv(domain, '192.168.1.1') do + expect(described_class).not_to be_blocked_url("http://#{domain}", + url_blocker_attributes) + end + + stub_domain_resolv(subdomain1, '192.168.1.1') do + expect(described_class).not_to be_blocked_url("http://#{subdomain1}", + url_blocker_attributes) + end + + # subdomain2 is not part of the whitelist so it should be blocked + stub_domain_resolv(subdomain2, '192.168.1.1') do + expect(described_class).to be_blocked_url("http://#{subdomain2}", + url_blocker_attributes) + end + end + + it 'works with unicode and idna encoded domains' do + unicode_domain = 'ğitlab.com' + idna_encoded_domain = 'xn--itlab-j1a.com' + + stub_domain_resolv(unicode_domain, '192.168.1.1') do + expect(described_class).not_to be_blocked_url("http://#{unicode_domain}", + url_blocker_attributes) + end + + stub_domain_resolv(idna_encoded_domain, '192.168.1.1') do + expect(described_class).not_to be_blocked_url("http://#{idna_encoded_domain}", + url_blocker_attributes) + end + end + end + + context 'with ip ranges in whitelist' do + let(:ipv4_range) { '127.0.0.0/28' } + let(:ipv6_range) { 'fd84:6d02:f6d8:c89e::/124' } + + let(:whitelist) do + [ + ipv4_range, + ipv6_range + ] + end + + it 'blocks ipv4 range when not in whitelist' do + stub_application_setting(outbound_local_requests_whitelist: []) + + IPAddr.new(ipv4_range).to_range.to_a.each do |ip| + expect(described_class).to be_blocked_url("http://#{ip}", + url_blocker_attributes) + end + end + + it 'allows all ipv4s in the range when in whitelist' do + IPAddr.new(ipv4_range).to_range.to_a.each do |ip| + expect(described_class).not_to be_blocked_url("http://#{ip}", + url_blocker_attributes) + end + end + + it 'blocks ipv6 range when not in whitelist' do + stub_application_setting(outbound_local_requests_whitelist: []) + + IPAddr.new(ipv6_range).to_range.to_a.each do |ip| + expect(described_class).to be_blocked_url("http://[#{ip}]", + url_blocker_attributes) + end + end + + it 'allows all ipv6s in the range when in whitelist' do + IPAddr.new(ipv6_range).to_range.to_a.each do |ip| + expect(described_class).not_to be_blocked_url("http://[#{ip}]", + url_blocker_attributes) + end + end + + it 'blocks IPs outside the range' do + expect(described_class).to be_blocked_url("http://[fd84:6d02:f6d8:c89e:0:0:1:f]", + url_blocker_attributes) + + expect(described_class).to be_blocked_url("http://127.0.1.15", + url_blocker_attributes) + end + end + end end - def stub_domain_resolv(domain, ip) + def stub_domain_resolv(domain, ip, &block) address = double(ip_address: ip, ipv4_private?: true, ipv6_link_local?: false, ipv4_loopback?: false, ipv6_loopback?: false, ipv4?: false) allow(Addrinfo).to receive(:getaddrinfo).with(domain, any_args).and_return([address]) allow(address).to receive(:ipv6_v4mapped?).and_return(false) - end - def unstub_domain_resolv + yield + allow(Addrinfo).to receive(:getaddrinfo).and_call_original end end diff --git a/spec/lib/gitlab/utils_spec.rb b/spec/lib/gitlab/utils_spec.rb index 4645339f439..0c20b3aa4c8 100644 --- a/spec/lib/gitlab/utils_spec.rb +++ b/spec/lib/gitlab/utils_spec.rb @@ -231,4 +231,23 @@ describe Gitlab::Utils do end end end + + describe '.string_to_ip_object' do + it 'returns nil when string is nil' do + expect(described_class.string_to_ip_object(nil)).to eq(nil) + end + + it 'returns nil when string is invalid IP' do + expect(described_class.string_to_ip_object('invalid ip')).to eq(nil) + expect(described_class.string_to_ip_object('')).to eq(nil) + end + + it 'returns IP object when string is valid IP' do + expect(described_class.string_to_ip_object('192.168.1.1')).to eq(IPAddr.new('192.168.1.1')) + expect(described_class.string_to_ip_object('::ffff:a9fe:a864')).to eq(IPAddr.new('::ffff:a9fe:a864')) + expect(described_class.string_to_ip_object('[::ffff:a9fe:a864]')).to eq(IPAddr.new('::ffff:a9fe:a864')) + expect(described_class.string_to_ip_object('127.0.0.0/28')).to eq(IPAddr.new('127.0.0.0/28')) + expect(described_class.string_to_ip_object('1:0:0:0:0:0:0:0/124')).to eq(IPAddr.new('1:0:0:0:0:0:0:0/124')) + end + end end diff --git a/spec/lib/peek/views/rugged_spec.rb b/spec/lib/peek/views/rugged_spec.rb new file mode 100644 index 00000000000..8bf996fc6bc --- /dev/null +++ b/spec/lib/peek/views/rugged_spec.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Peek::Views::Rugged, :request_store do + subject { described_class.new } + + let(:project) { create(:project) } + + before do + allow(Gitlab::RuggedInstrumentation).to receive(:peek_enabled?).and_return(true) + end + + it 'returns no results' do + expect(subject.results).to eq({}) + end + + it 'returns aggregated results' do + ::Gitlab::RuggedInstrumentation.query_time += 1.234 + ::Gitlab::RuggedInstrumentation.increment_query_count + ::Gitlab::RuggedInstrumentation.increment_query_count + + ::Gitlab::RuggedInstrumentation.add_call_details(feature: :rugged_test, + args: [project.repository.raw, 'HEAD'], + duration: 0.123) + ::Gitlab::RuggedInstrumentation.add_call_details(feature: :rugged_test2, + args: [project.repository.raw, 'refs/heads/master'], + duration: 0.456) + + results = subject.results + expect(results[:calls]).to eq(2) + expect(results[:duration]).to eq('1234.00ms') + expect(results[:details].count).to eq(2) + + expect(results[:details][0][:args]).to eq([project.repository.raw.to_s, "refs/heads/master"]) + expect(results[:details][1][:args]).to eq([project.repository.raw.to_s, "HEAD"]) + end +end diff --git a/spec/lib/quality/test_level_spec.rb b/spec/lib/quality/test_level_spec.rb index 3465c3a050b..59870ce44a7 100644 --- a/spec/lib/quality/test_level_spec.rb +++ b/spec/lib/quality/test_level_spec.rb @@ -7,7 +7,7 @@ RSpec.describe Quality::TestLevel do context 'when level is unit' do it 'returns a pattern' do expect(subject.pattern(:unit)) - .to eq("spec/{bin,config,db,dependencies,factories,finders,frontend,graphql,helpers,initializers,javascripts,lib,migrations,models,policies,presenters,rack_servers,routing,rubocop,serializers,services,sidekiq,tasks,uploaders,validators,views,workers,elastic_integration}{,/**/}*_spec.rb") + .to eq("spec/{bin,config,db,dependencies,factories,finders,frontend,graphql,haml_lint,helpers,initializers,javascripts,lib,migrations,models,policies,presenters,rack_servers,routing,rubocop,serializers,services,sidekiq,tasks,uploaders,validators,views,workers,elastic_integration}{,/**/}*_spec.rb") end end @@ -47,7 +47,7 @@ RSpec.describe Quality::TestLevel do context 'when level is unit' do it 'returns a regexp' do expect(subject.regexp(:unit)) - .to eq(%r{spec/(bin|config|db|dependencies|factories|finders|frontend|graphql|helpers|initializers|javascripts|lib|migrations|models|policies|presenters|rack_servers|routing|rubocop|serializers|services|sidekiq|tasks|uploaders|validators|views|workers|elastic_integration)}) + .to eq(%r{spec/(bin|config|db|dependencies|factories|finders|frontend|graphql|haml_lint|helpers|initializers|javascripts|lib|migrations|models|policies|presenters|rack_servers|routing|rubocop|serializers|services|sidekiq|tasks|uploaders|validators|views|workers|elastic_integration)}) end end diff --git a/spec/models/application_setting_spec.rb b/spec/models/application_setting_spec.rb index ab6f6dfe720..bd87bbd8d68 100644 --- a/spec/models/application_setting_spec.rb +++ b/spec/models/application_setting_spec.rb @@ -37,6 +37,17 @@ describe ApplicationSetting do it { is_expected.not_to allow_value("myemail@example.com").for(:lets_encrypt_notification_email) } it { is_expected.to allow_value("myemail@test.example.com").for(:lets_encrypt_notification_email) } + it { is_expected.to allow_value(['192.168.1.1'] * 1_000).for(:outbound_local_requests_whitelist) } + it { is_expected.not_to allow_value(['192.168.1.1'] * 1_001).for(:outbound_local_requests_whitelist) } + it { is_expected.to allow_value(['1' * 255]).for(:outbound_local_requests_whitelist) } + it { is_expected.not_to allow_value(['1' * 256]).for(:outbound_local_requests_whitelist) } + it { is_expected.not_to allow_value(['ğitlab.com']).for(:outbound_local_requests_whitelist) } + it { is_expected.to allow_value(['xn--itlab-j1a.com']).for(:outbound_local_requests_whitelist) } + it { is_expected.not_to allow_value(['<h1></h1>']).for(:outbound_local_requests_whitelist) } + it { is_expected.to allow_value(['gitlab.com']).for(:outbound_local_requests_whitelist) } + it { is_expected.to allow_value(nil).for(:outbound_local_requests_whitelist) } + it { is_expected.to allow_value([]).for(:outbound_local_requests_whitelist) } + context "when user accepted let's encrypt terms of service" do before do setting.update(lets_encrypt_terms_of_service_accepted: true) diff --git a/spec/models/label_spec.rb b/spec/models/label_spec.rb index 5174c590a10..c2e2298823e 100644 --- a/spec/models/label_spec.rb +++ b/spec/models/label_spec.rb @@ -193,4 +193,17 @@ describe Label do expect(described_class.optionally_subscribed_by(nil)).to match_array([label, label2]) end end + + describe '#templates' do + context 'with invalid template labels' do + it 'returns only valid template labels' do + create(:label) + # Project labels should not have template set to true + create(:label, template: true) + valid_template_label = described_class.create!(title: 'test', template: true, type: nil) + + expect(described_class.templates).to eq([valid_template_label]) + end + end + end end diff --git a/spec/models/project_auto_devops_spec.rb b/spec/models/project_auto_devops_spec.rb index 7bdd2367a68..da9e56ef897 100644 --- a/spec/models/project_auto_devops_spec.rb +++ b/spec/models/project_auto_devops_spec.rb @@ -15,7 +15,7 @@ describe ProjectAutoDevops do it { is_expected.to respond_to(:updated_at) } describe '#predefined_variables' do - let(:auto_devops) { build_stubbed(:project_auto_devops, project: project, domain: domain) } + let(:auto_devops) { build_stubbed(:project_auto_devops, project: project) } context 'when deploy_strategy is manual' do let(:auto_devops) { build_stubbed(:project_auto_devops, :manual_deployment, project: project) } diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 9a083eee05e..7d458324c20 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -3117,11 +3117,8 @@ describe Project do let(:project) { create(:project) } it 'shows full error updating an invalid MR' do - error_message = 'Failed to replace merge_requests because one or more of the new records could not be saved.'\ - ' Validate fork Source project is not a fork of the target project' - expect { project.append_or_update_attribute(:merge_requests, [create(:merge_request)]) } - .to raise_error(ActiveRecord::RecordNotSaved, error_message) + .to raise_error(ActiveRecord::RecordInvalid, /Failed to set merge_requests:/) end it 'updates the project successfully' do diff --git a/spec/services/application_settings/update_service_spec.rb b/spec/services/application_settings/update_service_spec.rb index a641828faa5..33cd1f37ff6 100644 --- a/spec/services/application_settings/update_service_spec.rb +++ b/spec/services/application_settings/update_service_spec.rb @@ -180,4 +180,20 @@ describe ApplicationSettings::UpdateService do described_class.new(application_settings, admin, { home_page_url: 'http://foo.bar' }).execute end end + + context 'when raw_blob_request_limit is passsed' do + let(:params) do + { + raw_blob_request_limit: 600 + } + end + + it 'updates raw_blob_request_limit value' do + subject.execute + + application_settings.reload + + expect(application_settings.raw_blob_request_limit).to eq(600) + end + end end diff --git a/spec/support/shared_examples/application_setting_examples.rb b/spec/support/shared_examples/application_setting_examples.rb index e7ec24c5b7e..2c600785ad3 100644 --- a/spec/support/shared_examples/application_setting_examples.rb +++ b/spec/support/shared_examples/application_setting_examples.rb @@ -1,58 +1,54 @@ # frozen_string_literal: true -RSpec.shared_examples 'application settings examples' do - context 'restricted signup domains' do - it 'sets single domain' do - setting.domain_whitelist_raw = 'example.com' - expect(setting.domain_whitelist).to eq(['example.com']) - end +RSpec.shared_examples 'string of domains' do |attribute| + it 'sets single domain' do + setting.method("#{attribute}_raw=").call('example.com') + expect(setting.method(attribute).call).to eq(['example.com']) + end - it 'sets multiple domains with spaces' do - setting.domain_whitelist_raw = 'example.com *.example.com' - expect(setting.domain_whitelist).to eq(['example.com', '*.example.com']) - end + it 'sets multiple domains with spaces' do + setting.method("#{attribute}_raw=").call('example.com *.example.com') + expect(setting.method(attribute).call).to eq(['example.com', '*.example.com']) + end - it 'sets multiple domains with newlines and a space' do - setting.domain_whitelist_raw = "example.com\n *.example.com" - expect(setting.domain_whitelist).to eq(['example.com', '*.example.com']) - end + it 'sets multiple domains with newlines and a space' do + setting.method("#{attribute}_raw=").call("example.com\n *.example.com") + expect(setting.method(attribute).call).to eq(['example.com', '*.example.com']) + end - it 'sets multiple domains with commas' do - setting.domain_whitelist_raw = "example.com, *.example.com" - expect(setting.domain_whitelist).to eq(['example.com', '*.example.com']) - end + it 'sets multiple domains with commas' do + setting.method("#{attribute}_raw=").call("example.com, *.example.com") + expect(setting.method(attribute).call).to eq(['example.com', '*.example.com']) end - context 'blacklisted signup domains' do - it 'sets single domain' do - setting.domain_blacklist_raw = 'example.com' - expect(setting.domain_blacklist).to contain_exactly('example.com') - end + it 'sets multiple domains with semicolon' do + setting.method("#{attribute}_raw=").call("example.com; *.example.com") + expect(setting.method(attribute).call).to contain_exactly('example.com', '*.example.com') + end - it 'sets multiple domains with spaces' do - setting.domain_blacklist_raw = 'example.com *.example.com' - expect(setting.domain_blacklist).to contain_exactly('example.com', '*.example.com') - end + it 'sets multiple domains with mixture of everything' do + setting.method("#{attribute}_raw=").call("example.com; *.example.com\n test.com\sblock.com yes.com") + expect(setting.method(attribute).call).to contain_exactly('example.com', '*.example.com', 'test.com', 'block.com', 'yes.com') + end - it 'sets multiple domains with newlines and a space' do - setting.domain_blacklist_raw = "example.com\n *.example.com" - expect(setting.domain_blacklist).to contain_exactly('example.com', '*.example.com') - end + it 'removes duplicates' do + setting.method("#{attribute}_raw=").call("example.com; example.com; 127.0.0.1; 127.0.0.1") + expect(setting.method(attribute).call).to contain_exactly('example.com', '127.0.0.1') + end - it 'sets multiple domains with commas' do - setting.domain_blacklist_raw = "example.com, *.example.com" - expect(setting.domain_blacklist).to contain_exactly('example.com', '*.example.com') - end + it 'does not fail with garbage values' do + setting.method("#{attribute}_raw=").call("example;34543:garbage:fdh5654;") + expect(setting.method(attribute).call).to contain_exactly('example', '34543:garbage:fdh5654') + end +end - it 'sets multiple domains with semicolon' do - setting.domain_blacklist_raw = "example.com; *.example.com" - expect(setting.domain_blacklist).to contain_exactly('example.com', '*.example.com') - end +RSpec.shared_examples 'application settings examples' do + context 'restricted signup domains' do + it_behaves_like 'string of domains', :domain_whitelist + end - it 'sets multiple domains with mixture of everything' do - setting.domain_blacklist_raw = "example.com; *.example.com\n test.com\sblock.com yes.com" - expect(setting.domain_blacklist).to contain_exactly('example.com', '*.example.com', 'test.com', 'block.com', 'yes.com') - end + context 'blacklisted signup domains' do + it_behaves_like 'string of domains', :domain_blacklist it 'sets multiple domain with file' do setting.domain_blacklist_file = File.open(Rails.root.join('spec/fixtures/', 'domain_blacklist.txt')) @@ -60,6 +56,27 @@ RSpec.shared_examples 'application settings examples' do end end + context 'outbound_local_requests_whitelist' do + it_behaves_like 'string of domains', :outbound_local_requests_whitelist + end + + context 'outbound_local_requests_whitelist_arrays' do + it 'separates the IPs and domains' do + setting.outbound_local_requests_whitelist = [ + '192.168.1.1', '127.0.0.0/28', 'www.example.com', 'example.com', + '::ffff:a00:2', '1:0:0:0:0:0:0:0/124', 'subdomain.example.com' + ] + + ip_whitelist = [ + IPAddr.new('192.168.1.1'), IPAddr.new('127.0.0.0/8'), + IPAddr.new('::ffff:a00:2'), IPAddr.new('1:0:0:0:0:0:0:0/124') + ] + domain_whitelist = ['www.example.com', 'example.com', 'subdomain.example.com'] + + expect(setting.outbound_local_requests_whitelist_arrays).to contain_exactly(ip_whitelist, domain_whitelist) + end + end + describe 'usage ping settings' do context 'when the usage ping is disabled in gitlab.yml' do before do |