diff options
-rw-r--r-- | app/models/project_services/slash_commands_service.rb | 2 | ||||
-rw-r--r-- | app/policies/global_policy.rb | 3 | ||||
-rw-r--r-- | app/serializers/issue_entity.rb | 7 | ||||
-rw-r--r-- | app/services/merge_requests/build_service.rb | 28 | ||||
-rw-r--r-- | changelogs/unreleased/security-2873-blocked-user-slash-command-bypass-master.yml | 5 | ||||
-rw-r--r-- | changelogs/unreleased/security-bvl-filter-mr-params.yml | 5 | ||||
-rw-r--r-- | changelogs/unreleased/security-hide_moved_issue_id.yml | 5 | ||||
-rw-r--r-- | spec/features/merge_request/user_tries_to_access_private_project_info_through_new_mr_spec.rb (renamed from spec/features/merge_request/user_tries_to_access_private_repository_through_new_mr_spec.rb) | 21 | ||||
-rw-r--r-- | spec/policies/global_policy_spec.rb | 28 | ||||
-rw-r--r-- | spec/serializers/issue_entity_spec.rb | 33 | ||||
-rw-r--r-- | spec/services/merge_requests/build_service_spec.rb | 37 | ||||
-rw-r--r-- | spec/support/shared_examples/chat_slash_commands_shared_examples.rb | 13 | ||||
-rw-r--r-- | spec/uploaders/file_mover_spec.rb | 3 |
13 files changed, 179 insertions, 11 deletions
diff --git a/app/models/project_services/slash_commands_service.rb b/app/models/project_services/slash_commands_service.rb index bfabc6d262c..46925f6704d 100644 --- a/app/models/project_services/slash_commands_service.rb +++ b/app/models/project_services/slash_commands_service.rb @@ -35,6 +35,8 @@ class SlashCommandsService < Service chat_user = find_chat_user(params) if chat_user&.user + return Gitlab::SlashCommands::Presenters::Access.new.access_denied unless chat_user.user.can?(:use_slash_commands) + Gitlab::SlashCommands::Command.new(project, chat_user, params).execute else url = authorize_chat_name_url(params) diff --git a/app/policies/global_policy.rb b/app/policies/global_policy.rb index e85397422e6..1807b741edb 100644 --- a/app/policies/global_policy.rb +++ b/app/policies/global_policy.rb @@ -37,6 +37,7 @@ class GlobalPolicy < BasePolicy enable :access_git enable :receive_notifications enable :use_quick_actions + enable :use_slash_commands end rule { blocked | internal }.policy do @@ -44,6 +45,7 @@ class GlobalPolicy < BasePolicy prevent :access_api prevent :access_git prevent :receive_notifications + prevent :use_slash_commands end rule { required_terms_not_accepted }.policy do @@ -61,6 +63,7 @@ class GlobalPolicy < BasePolicy rule { access_locked }.policy do prevent :log_in + prevent :use_slash_commands end rule { ~(anonymous & restricted_public_level) }.policy do diff --git a/app/serializers/issue_entity.rb b/app/serializers/issue_entity.rb index 914ad628a99..2e994f4c3f1 100644 --- a/app/serializers/issue_entity.rb +++ b/app/serializers/issue_entity.rb @@ -16,9 +16,14 @@ class IssueEntity < IssuableEntity expose :discussion_locked expose :assignees, using: API::Entities::UserBasic expose :due_date - expose :moved_to_id expose :project_id + expose :moved_to_id do |issue| + if issue.moved_to_id.present? && can?(request.current_user, :read_issue, issue.moved_to) + issue.moved_to_id + end + end + expose :web_url do |issue| project_issue_path(issue.project, issue) end diff --git a/app/services/merge_requests/build_service.rb b/app/services/merge_requests/build_service.rb index 109c964e577..b28f80939ae 100644 --- a/app/services/merge_requests/build_service.rb +++ b/app/services/merge_requests/build_service.rb @@ -11,15 +11,18 @@ module MergeRequests # https://gitlab.com/gitlab-org/gitlab-ce/issues/53658 merge_quick_actions_into_params!(merge_request, only: [:target_branch]) merge_request.merge_params['force_remove_source_branch'] = params.delete(:force_remove_source_branch) if params.has_key?(:force_remove_source_branch) - merge_request.assign_attributes(params) + # Assign the projects first so we can use policies for `filter_params` merge_request.author = current_user + merge_request.source_project = find_source_project + merge_request.target_project = find_target_project + + filter_params(merge_request) + merge_request.assign_attributes(params.to_h.compact) + merge_request.compare_commits = [] - merge_request.source_project = find_source_project - merge_request.target_project = find_target_project - merge_request.target_branch = find_target_branch - merge_request.can_be_created = projects_and_branches_valid? - ensure_milestone_available(merge_request) + merge_request.target_branch = find_target_branch + merge_request.can_be_created = projects_and_branches_valid? # compare branches only if branches are valid, otherwise # compare_branches may raise an error @@ -50,12 +53,14 @@ module MergeRequests to: :merge_request def find_source_project + source_project = project_from_params(:source_project) return source_project if source_project.present? && can?(current_user, :create_merge_request_from, source_project) project end def find_target_project + target_project = project_from_params(:target_project) return target_project if target_project.present? && can?(current_user, :create_merge_request_in, target_project) target_project = project.default_merge_request_target @@ -65,6 +70,17 @@ module MergeRequests project end + def project_from_params(param_name) + project_from_params = params.delete(param_name) + + id_param_name = :"#{param_name}_id" + if project_from_params.nil? && params[id_param_name] + project_from_params = Project.find_by_id(params.delete(id_param_name)) + end + + project_from_params + end + def find_target_branch target_branch || target_project.default_branch end diff --git a/changelogs/unreleased/security-2873-blocked-user-slash-command-bypass-master.yml b/changelogs/unreleased/security-2873-blocked-user-slash-command-bypass-master.yml new file mode 100644 index 00000000000..cd31fe0f35c --- /dev/null +++ b/changelogs/unreleased/security-2873-blocked-user-slash-command-bypass-master.yml @@ -0,0 +1,5 @@ +--- +title: Restrict slash commands to users who can log in +merge_request: +author: +type: security diff --git a/changelogs/unreleased/security-bvl-filter-mr-params.yml b/changelogs/unreleased/security-bvl-filter-mr-params.yml new file mode 100644 index 00000000000..4433ec73b7c --- /dev/null +++ b/changelogs/unreleased/security-bvl-filter-mr-params.yml @@ -0,0 +1,5 @@ +--- +title: Filter merge request params on the new merge request page +merge_request: +author: +type: security diff --git a/changelogs/unreleased/security-hide_moved_issue_id.yml b/changelogs/unreleased/security-hide_moved_issue_id.yml new file mode 100644 index 00000000000..24353d797c9 --- /dev/null +++ b/changelogs/unreleased/security-hide_moved_issue_id.yml @@ -0,0 +1,5 @@ +--- +title: Do not show moved issue id for users that cannot read issue +merge_request: +author: +type: security diff --git a/spec/features/merge_request/user_tries_to_access_private_repository_through_new_mr_spec.rb b/spec/features/merge_request/user_tries_to_access_private_project_info_through_new_mr_spec.rb index 9318b5f1ebb..1ebe9e2e409 100644 --- a/spec/features/merge_request/user_tries_to_access_private_repository_through_new_mr_spec.rb +++ b/spec/features/merge_request/user_tries_to_access_private_project_info_through_new_mr_spec.rb @@ -1,6 +1,8 @@ +# frozen_string_literal: true + require 'spec_helper' -describe 'Merge Request > Tries to access private repo of public project' do +describe 'Merge Request > User tries to access private project information through the new mr page' do let(:current_user) { create(:user) } let(:private_project) do create(:project, :public, :repository, @@ -33,5 +35,22 @@ describe 'Merge Request > Tries to access private repo of public project' do it "does not mention the project the user can't see the repo of" do expect(page).not_to have_content('nothing-to-see-here') end + + context 'when the user enters label information from the private project in the querystring' do + let(:inaccessible_label) { create(:label, project: private_project) } + let(:mr_path) do + project_new_merge_request_path( + owned_project, + merge_request: { + label_ids: [inaccessible_label.id], + source_branch: 'feature' + } + ) + end + + it 'does not expose the label name' do + expect(page).not_to have_content(inaccessible_label.name) + end + end end end diff --git a/spec/policies/global_policy_spec.rb b/spec/policies/global_policy_spec.rb index 12be3927e18..df6cc526eb0 100644 --- a/spec/policies/global_policy_spec.rb +++ b/spec/policies/global_policy_spec.rb @@ -226,4 +226,32 @@ describe GlobalPolicy do it { is_expected.not_to be_allowed(:read_instance_statistics) } end end + + describe 'slash commands' do + context 'regular user' do + it { is_expected.to be_allowed(:use_slash_commands) } + end + + context 'when internal' do + let(:current_user) { User.ghost } + + it { is_expected.not_to be_allowed(:use_slash_commands) } + end + + context 'when blocked' do + before do + current_user.block + end + + it { is_expected.not_to be_allowed(:use_slash_commands) } + end + + context 'when access locked' do + before do + current_user.lock_access! + end + + it { is_expected.not_to be_allowed(:use_slash_commands) } + end + end end diff --git a/spec/serializers/issue_entity_spec.rb b/spec/serializers/issue_entity_spec.rb index caa3e41402b..0e05b3c84f4 100644 --- a/spec/serializers/issue_entity_spec.rb +++ b/spec/serializers/issue_entity_spec.rb @@ -17,4 +17,37 @@ describe IssueEntity do it 'has time estimation attributes' do expect(subject).to include(:time_estimate, :total_time_spent, :human_time_estimate, :human_total_time_spent) end + + context 'when issue got moved' do + let(:public_project) { create(:project, :public) } + let(:member) { create(:user) } + let(:non_member) { create(:user) } + let(:issue) { create(:issue, project: public_project) } + + before do + project.add_developer(member) + public_project.add_developer(member) + Issues::MoveService.new(public_project, member).execute(issue, project) + end + + context 'when user cannot read target project' do + it 'does not return moved_to_id' do + request = double('request', current_user: non_member) + + response = described_class.new(issue, request: request).as_json + + expect(response[:moved_to_id]).to be_nil + end + end + + context 'when user can read target project' do + it 'returns moved moved_to_id' do + request = double('request', current_user: member) + + response = described_class.new(issue, request: request).as_json + + expect(response[:moved_to_id]).to eq(issue.moved_to_id) + end + end + end end diff --git a/spec/services/merge_requests/build_service_spec.rb b/spec/services/merge_requests/build_service_spec.rb index 5c3b209086c..f18239f6d39 100644 --- a/spec/services/merge_requests/build_service_spec.rb +++ b/spec/services/merge_requests/build_service_spec.rb @@ -1,5 +1,4 @@ # frozen_string_literal: true - require 'spec_helper' describe MergeRequests::BuildService do @@ -225,6 +224,11 @@ describe MergeRequests::BuildService do let(:label_ids) { [label2.id] } let(:milestone_id) { milestone2.id } + before do + # Guests are not able to assign labels or milestones to an issue + project.add_developer(user) + end + it 'assigns milestone_id and label_ids instead of issue labels and milestone' do expect(merge_request.milestone).to eq(milestone2) expect(merge_request.labels).to match_array([label2]) @@ -479,4 +483,35 @@ describe MergeRequests::BuildService do end end end + + context 'when assigning labels' do + let(:label_ids) { [create(:label, project: project).id] } + + context 'for members with less than developer access' do + it 'is not allowed' do + expect(merge_request.label_ids).to be_empty + end + end + + context 'for users allowed to assign labels' do + before do + project.add_developer(user) + end + + context 'for labels in the project' do + it 'is allowed for developers' do + expect(merge_request.label_ids).to contain_exactly(*label_ids) + end + end + + context 'for unrelated labels' do + let(:project_label) { create(:label, project: project) } + let(:label_ids) { [create(:label).id, project_label.id] } + + it 'only assigns related labels' do + expect(merge_request.label_ids).to contain_exactly(project_label.id) + end + end + end + end end diff --git a/spec/support/shared_examples/chat_slash_commands_shared_examples.rb b/spec/support/shared_examples/chat_slash_commands_shared_examples.rb index dc97a39f051..ef40287fd6e 100644 --- a/spec/support/shared_examples/chat_slash_commands_shared_examples.rb +++ b/spec/support/shared_examples/chat_slash_commands_shared_examples.rb @@ -91,6 +91,19 @@ RSpec.shared_examples 'chat slash commands service' do subject.trigger(params) end + + context 'when user is blocked' do + before do + chat_name.user.block + end + + it 'blocks command execution' do + expect_any_instance_of(Gitlab::SlashCommands::Command).not_to receive(:execute) + + result = subject.trigger(params) + expect(result).to include(text: /^Whoops! This action is not allowed/) + end + end end end end diff --git a/spec/uploaders/file_mover_spec.rb b/spec/uploaders/file_mover_spec.rb index a9e03f3d4e5..5ee0a10f38d 100644 --- a/spec/uploaders/file_mover_spec.rb +++ b/spec/uploaders/file_mover_spec.rb @@ -85,8 +85,7 @@ describe FileMover do context 'when tmp uploader is not local storage' do before do - allow(PersonalFileUploader).to receive(:object_store_enabled?) { true } - tmp_uploader.object_store = ObjectStorage::Store::REMOTE + stub_uploads_object_storage(uploader: PersonalFileUploader) allow_any_instance_of(PersonalFileUploader).to receive(:file_storage?) { false } end |