Merge branch 'security-6881-project-group-approvers-leaks-private-group-info-ce' into 'master'
[master] CE: Project group approvers leaks private group info See merge request gitlab/gitlabhq!2488
This commit is contained in:
commit
b93f1d3cf8
20 changed files with 160 additions and 45 deletions
|
@ -106,6 +106,10 @@ class Projects::MergeRequests::CreationsController < Projects::MergeRequests::Ap
|
||||||
@commits = set_commits_for_rendering(@merge_request.commits)
|
@commits = set_commits_for_rendering(@merge_request.commits)
|
||||||
@commit = @merge_request.diff_head_commit
|
@commit = @merge_request.diff_head_commit
|
||||||
|
|
||||||
|
# FIXME: We have to assign a presenter to another instance variable
|
||||||
|
# due to class_name checks being made with issuable classes
|
||||||
|
@mr_presenter = @merge_request.present(current_user: current_user)
|
||||||
|
|
||||||
@labels = LabelsFinder.new(current_user, project_id: @project.id).execute
|
@labels = LabelsFinder.new(current_user, project_id: @project.id).execute
|
||||||
|
|
||||||
set_pipeline_variables
|
set_pipeline_variables
|
||||||
|
|
|
@ -333,6 +333,10 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
|
||||||
@target_project = @merge_request.target_project
|
@target_project = @merge_request.target_project
|
||||||
@target_branches = @merge_request.target_project.repository.branch_names
|
@target_branches = @merge_request.target_project.repository.branch_names
|
||||||
@noteable = @merge_request
|
@noteable = @merge_request
|
||||||
|
|
||||||
|
# FIXME: We have to assign a presenter to another instance variable
|
||||||
|
# due to class_name checks being made with issuable classes
|
||||||
|
@mr_presenter = @merge_request.present(current_user: current_user)
|
||||||
end
|
end
|
||||||
|
|
||||||
def finder_type
|
def finder_type
|
||||||
|
|
|
@ -16,6 +16,7 @@ class ProjectsController < Projects::ApplicationController
|
||||||
before_action :tree, only: [:show], if: [:repo_exists?, :project_view_files?]
|
before_action :tree, only: [:show], if: [:repo_exists?, :project_view_files?]
|
||||||
before_action :lfs_blob_ids, only: [:show], if: [:repo_exists?, :project_view_files?]
|
before_action :lfs_blob_ids, only: [:show], if: [:repo_exists?, :project_view_files?]
|
||||||
before_action :project_export_enabled, only: [:export, :download_export, :remove_export, :generate_new_export]
|
before_action :project_export_enabled, only: [:export, :download_export, :remove_export, :generate_new_export]
|
||||||
|
before_action :present_project, only: [:edit]
|
||||||
|
|
||||||
# Authorize
|
# Authorize
|
||||||
before_action :authorize_admin_project!, only: [:edit, :update, :housekeeping, :download_export, :export, :remove_export, :generate_new_export]
|
before_action :authorize_admin_project!, only: [:edit, :update, :housekeeping, :download_export, :export, :remove_export, :generate_new_export]
|
||||||
|
@ -433,4 +434,8 @@ class ProjectsController < Projects::ApplicationController
|
||||||
def whitelist_query_limiting
|
def whitelist_query_limiting
|
||||||
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-ce/issues/42440')
|
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-ce/issues/42440')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def present_project
|
||||||
|
@project = @project.present(current_user: current_user)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class JoinedGroupsFinder < UnionFinder
|
class JoinedGroupsFinder
|
||||||
def initialize(user)
|
def initialize(user)
|
||||||
@user = user
|
@user = user
|
||||||
end
|
end
|
||||||
|
@ -8,19 +8,8 @@ class JoinedGroupsFinder < UnionFinder
|
||||||
# Finds the groups of the source user, optionally limited to those visible to
|
# Finds the groups of the source user, optionally limited to those visible to
|
||||||
# the current user.
|
# the current user.
|
||||||
def execute(current_user = nil)
|
def execute(current_user = nil)
|
||||||
segments = all_groups(current_user)
|
@user.authorized_groups
|
||||||
|
.public_or_visible_to_user(current_user)
|
||||||
find_union(segments, Group).order_id_desc
|
.order_id_desc
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def all_groups(current_user)
|
|
||||||
groups = []
|
|
||||||
|
|
||||||
groups << @user.authorized_groups.visible_to_user(current_user) if current_user
|
|
||||||
groups << @user.authorized_groups.public_to_user(current_user)
|
|
||||||
|
|
||||||
groups
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,13 +3,14 @@
|
||||||
module Emails
|
module Emails
|
||||||
module MergeRequests
|
module MergeRequests
|
||||||
def new_merge_request_email(recipient_id, merge_request_id, reason = nil)
|
def new_merge_request_email(recipient_id, merge_request_id, reason = nil)
|
||||||
setup_merge_request_mail(merge_request_id, recipient_id)
|
setup_merge_request_mail(merge_request_id, recipient_id, present: true)
|
||||||
|
|
||||||
mail_new_thread(@merge_request, merge_request_thread_options(@merge_request.author_id, recipient_id, reason))
|
mail_new_thread(@merge_request, merge_request_thread_options(@merge_request.author_id, recipient_id, reason))
|
||||||
end
|
end
|
||||||
|
|
||||||
def new_mention_in_merge_request_email(recipient_id, merge_request_id, updated_by_user_id, reason = nil)
|
def new_mention_in_merge_request_email(recipient_id, merge_request_id, updated_by_user_id, reason = nil)
|
||||||
setup_merge_request_mail(merge_request_id, recipient_id)
|
setup_merge_request_mail(merge_request_id, recipient_id, present: true)
|
||||||
|
|
||||||
mail_answer_thread(@merge_request, merge_request_thread_options(updated_by_user_id, recipient_id, reason))
|
mail_answer_thread(@merge_request, merge_request_thread_options(updated_by_user_id, recipient_id, reason))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -75,11 +76,16 @@ module Emails
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def setup_merge_request_mail(merge_request_id, recipient_id)
|
def setup_merge_request_mail(merge_request_id, recipient_id, present: false)
|
||||||
@merge_request = MergeRequest.find(merge_request_id)
|
@merge_request = MergeRequest.find(merge_request_id)
|
||||||
@project = @merge_request.project
|
@project = @merge_request.project
|
||||||
@target_url = project_merge_request_url(@project, @merge_request)
|
@target_url = project_merge_request_url(@project, @merge_request)
|
||||||
|
|
||||||
|
if present
|
||||||
|
recipient = User.find(recipient_id)
|
||||||
|
@mr_presenter = @merge_request.present(current_user: recipient)
|
||||||
|
end
|
||||||
|
|
||||||
@sent_notification = SentNotification.record(@merge_request, recipient_id, reply_key)
|
@sent_notification = SentNotification.record(@merge_request, recipient_id, reply_key)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -82,8 +82,17 @@ class Group < Namespace
|
||||||
User.reference_pattern
|
User.reference_pattern
|
||||||
end
|
end
|
||||||
|
|
||||||
def visible_to_user(user)
|
# WARNING: This method should never be used on its own
|
||||||
where(id: user.authorized_groups.select(:id).reorder(nil))
|
# please do make sure the number of rows you are filtering is small
|
||||||
|
# enough for this query
|
||||||
|
def public_or_visible_to_user(user)
|
||||||
|
return public_to_user unless user
|
||||||
|
|
||||||
|
public_for_user = public_to_user_arel(user)
|
||||||
|
visible_for_user = visible_to_user_arel(user)
|
||||||
|
public_or_visible = public_for_user.or(visible_for_user)
|
||||||
|
|
||||||
|
where(public_or_visible)
|
||||||
end
|
end
|
||||||
|
|
||||||
def select_for_project_authorization
|
def select_for_project_authorization
|
||||||
|
@ -95,6 +104,23 @@ class Group < Namespace
|
||||||
super
|
super
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def public_to_user_arel(user)
|
||||||
|
self.arel_table[:visibility_level]
|
||||||
|
.in(Gitlab::VisibilityLevel.levels_for_user(user))
|
||||||
|
end
|
||||||
|
|
||||||
|
def visible_to_user_arel(user)
|
||||||
|
groups_table = self.arel_table
|
||||||
|
authorized_groups = user.authorized_groups.as('authorized')
|
||||||
|
|
||||||
|
groups_table.project(1)
|
||||||
|
.from(authorized_groups)
|
||||||
|
.where(authorized_groups[:id].eq(groups_table[:id]))
|
||||||
|
.exists
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Overrides notification_settings has_many association
|
# Overrides notification_settings has_many association
|
||||||
|
|
|
@ -6,6 +6,7 @@ class MergeRequest < ActiveRecord::Base
|
||||||
include Issuable
|
include Issuable
|
||||||
include Noteable
|
include Noteable
|
||||||
include Referable
|
include Referable
|
||||||
|
include Presentable
|
||||||
include IgnorableColumn
|
include IgnorableColumn
|
||||||
include TimeTrackable
|
include TimeTrackable
|
||||||
include ManualInverseAssociation
|
include ManualInverseAssociation
|
||||||
|
|
|
@ -674,11 +674,13 @@ class User < ActiveRecord::Base
|
||||||
|
|
||||||
# Returns the groups a user has access to, either through a membership or a project authorization
|
# Returns the groups a user has access to, either through a membership or a project authorization
|
||||||
def authorized_groups
|
def authorized_groups
|
||||||
|
Group.unscoped do
|
||||||
Group.from_union([
|
Group.from_union([
|
||||||
groups,
|
groups,
|
||||||
authorized_projects.joins(:namespace).select('namespaces.*')
|
authorized_projects.joins(:namespace).select('namespaces.*')
|
||||||
])
|
])
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# Returns the groups a user is a member of, either directly or through a parent group
|
# Returns the groups a user is a member of, either directly or through a parent group
|
||||||
def membership_groups
|
def membership_groups
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
%p
|
%p
|
||||||
Assignee: #{@merge_request.assignee_name}
|
Assignee: #{@merge_request.assignee_name}
|
||||||
|
|
||||||
= render_if_exists 'notify/merge_request_approvers', merge_request: @merge_request
|
= render_if_exists 'notify/merge_request_approvers', presenter: @mr_presenter
|
||||||
|
|
||||||
- if @merge_request.description
|
- if @merge_request.description
|
||||||
%div
|
%div
|
||||||
|
|
|
@ -5,6 +5,6 @@ New Merge Request <%= @merge_request.to_reference %>
|
||||||
<%= merge_path_description(@merge_request, 'to') %>
|
<%= merge_path_description(@merge_request, 'to') %>
|
||||||
Author: <%= @merge_request.author_name %>
|
Author: <%= @merge_request.author_name %>
|
||||||
Assignee: <%= @merge_request.assignee_name %>
|
Assignee: <%= @merge_request.assignee_name %>
|
||||||
<%= render_if_exists 'notify/merge_request_approvers', merge_request: @merge_request %>
|
<%= render_if_exists 'notify/merge_request_approvers', presenter: @mr_presenter %>
|
||||||
|
|
||||||
<%= @merge_request.description %>
|
<%= @merge_request.description %>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
= form_for [@project.namespace.becomes(Namespace), @project, @merge_request],
|
= form_for [@project.namespace.becomes(Namespace), @project, @merge_request],
|
||||||
html: { class: 'merge-request-form common-note-form js-requires-input js-quick-submit' },
|
html: { class: 'merge-request-form common-note-form js-requires-input js-quick-submit' },
|
||||||
data: { markdown_version: @merge_request.cached_markdown_version } do |f|
|
data: { markdown_version: @merge_request.cached_markdown_version } do |f|
|
||||||
= render 'shared/issuable/form', f: f, issuable: @merge_request
|
= render 'shared/issuable/form', f: f, issuable: @merge_request, presenter: @mr_presenter
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
= link_to 'Change branches', mr_change_branches_path(@merge_request)
|
= link_to 'Change branches', mr_change_branches_path(@merge_request)
|
||||||
%hr
|
%hr
|
||||||
= form_for [@project.namespace.becomes(Namespace), @project, @merge_request], html: { class: 'merge-request-form common-note-form js-requires-input js-quick-submit' } do |f|
|
= form_for [@project.namespace.becomes(Namespace), @project, @merge_request], html: { class: 'merge-request-form common-note-form js-requires-input js-quick-submit' } do |f|
|
||||||
= render 'shared/issuable/form', f: f, issuable: @merge_request, commits: @commits
|
= render 'shared/issuable/form', f: f, issuable: @merge_request, commits: @commits, presenter: @mr_presenter
|
||||||
= f.hidden_field :source_project_id
|
= f.hidden_field :source_project_id
|
||||||
= f.hidden_field :source_branch
|
= f.hidden_field :source_branch
|
||||||
= f.hidden_field :target_project_id
|
= f.hidden_field :target_project_id
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
- form = local_assigns.fetch(:f)
|
- form = local_assigns.fetch(:f)
|
||||||
- commits = local_assigns[:commits]
|
- commits = local_assigns[:commits]
|
||||||
- project = @target_project || @project
|
- project = @target_project || @project
|
||||||
|
- presenter = local_assigns.fetch(:presenter, nil)
|
||||||
|
|
||||||
= form_errors(issuable)
|
= form_errors(issuable)
|
||||||
|
|
||||||
|
@ -29,7 +30,7 @@
|
||||||
|
|
||||||
= render 'shared/issuable/form/metadata', issuable: issuable, form: form
|
= render 'shared/issuable/form/metadata', issuable: issuable, form: form
|
||||||
|
|
||||||
= render_if_exists 'shared/issuable/approvals', issuable: issuable, form: form
|
= render_if_exists 'shared/issuable/approvals', issuable: issuable, presenter: presenter, form: form
|
||||||
|
|
||||||
= render 'shared/issuable/form/branch_chooser', issuable: issuable, form: form
|
= render 'shared/issuable/form/branch_chooser', issuable: issuable, form: form
|
||||||
|
|
||||||
|
|
22
rubocop/cop/group_public_or_visible_to_user.rb
Normal file
22
rubocop/cop/group_public_or_visible_to_user.rb
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
#
|
||||||
|
module RuboCop
|
||||||
|
module Cop
|
||||||
|
# Cop that blacklists the usage of Group.public_or_visible_to_user
|
||||||
|
class GroupPublicOrVisibleToUser < RuboCop::Cop::Cop
|
||||||
|
MSG = '`Group.public_or_visible_to_user` should be used with extreme care. ' \
|
||||||
|
'Please ensure that you are not using it on its own and that the amount ' \
|
||||||
|
'of rows being filtered is reasonable.'
|
||||||
|
|
||||||
|
def_node_matcher :public_or_visible_to_user?, <<~PATTERN
|
||||||
|
(send (const nil? :Group) :public_or_visible_to_user ...)
|
||||||
|
PATTERN
|
||||||
|
|
||||||
|
def on_send(node)
|
||||||
|
return unless public_or_visible_to_user?(node)
|
||||||
|
|
||||||
|
add_offense(node, location: :expression)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -38,3 +38,4 @@ require_relative 'cop/code_reuse/service_class'
|
||||||
require_relative 'cop/code_reuse/presenter'
|
require_relative 'cop/code_reuse/presenter'
|
||||||
require_relative 'cop/code_reuse/serializer'
|
require_relative 'cop/code_reuse/serializer'
|
||||||
require_relative 'cop/code_reuse/active_record'
|
require_relative 'cop/code_reuse/active_record'
|
||||||
|
require_relative 'cop/group_public_or_visible_to_user'
|
||||||
|
|
|
@ -169,22 +169,42 @@ describe Group do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '.visible_to_user' do
|
describe '.public_or_visible_to_user' do
|
||||||
let!(:group) { create(:group) }
|
let!(:private_group) { create(:group, :private) }
|
||||||
|
let!(:internal_group) { create(:group, :internal) }
|
||||||
|
|
||||||
|
subject { described_class.public_or_visible_to_user(user) }
|
||||||
|
|
||||||
|
context 'when user is nil' do
|
||||||
|
let!(:user) { nil }
|
||||||
|
|
||||||
|
it { is_expected.to match_array([group]) }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when user' do
|
||||||
let!(:user) { create(:user) }
|
let!(:user) { create(:user) }
|
||||||
|
|
||||||
subject { described_class.visible_to_user(user) }
|
context 'when user does not have access to any private group' do
|
||||||
|
it { is_expected.to match_array([internal_group, group]) }
|
||||||
|
end
|
||||||
|
|
||||||
describe 'when the user has access to a group' do
|
context 'when user is a member of private group' do
|
||||||
before do
|
before do
|
||||||
group.add_user(user, Gitlab::Access::MAINTAINER)
|
private_group.add_user(user, Gitlab::Access::DEVELOPER)
|
||||||
end
|
end
|
||||||
|
|
||||||
it { is_expected.to eq([group]) }
|
it { is_expected.to match_array([private_group, internal_group, group]) }
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'when the user does not have access to any groups' do
|
context 'when user is a member of private subgroup', :postgresql do
|
||||||
it { is_expected.to eq([]) }
|
let!(:private_subgroup) { create(:group, :private, parent: private_group) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
private_subgroup.add_user(user, Gitlab::Access::DEVELOPER)
|
||||||
|
end
|
||||||
|
|
||||||
|
it { is_expected.to match_array([private_subgroup, internal_group, group]) }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -155,7 +155,7 @@ describe API::Groups do
|
||||||
expect(response).to have_gitlab_http_status(200)
|
expect(response).to have_gitlab_http_status(200)
|
||||||
expect(response).to include_pagination_headers
|
expect(response).to include_pagination_headers
|
||||||
expect(json_response).to be_an Array
|
expect(json_response).to be_an Array
|
||||||
expect(response_groups).to eq(Group.visible_to_user(user1).order(:name).pluck(:name))
|
expect(response_groups).to eq(groups_visible_to_user(user1).order(:name).pluck(:name))
|
||||||
end
|
end
|
||||||
|
|
||||||
it "sorts in descending order when passed" do
|
it "sorts in descending order when passed" do
|
||||||
|
@ -164,7 +164,7 @@ describe API::Groups do
|
||||||
expect(response).to have_gitlab_http_status(200)
|
expect(response).to have_gitlab_http_status(200)
|
||||||
expect(response).to include_pagination_headers
|
expect(response).to include_pagination_headers
|
||||||
expect(json_response).to be_an Array
|
expect(json_response).to be_an Array
|
||||||
expect(response_groups).to eq(Group.visible_to_user(user1).order(name: :desc).pluck(:name))
|
expect(response_groups).to eq(groups_visible_to_user(user1).order(name: :desc).pluck(:name))
|
||||||
end
|
end
|
||||||
|
|
||||||
it "sorts by path in order_by param" do
|
it "sorts by path in order_by param" do
|
||||||
|
@ -173,7 +173,7 @@ describe API::Groups do
|
||||||
expect(response).to have_gitlab_http_status(200)
|
expect(response).to have_gitlab_http_status(200)
|
||||||
expect(response).to include_pagination_headers
|
expect(response).to include_pagination_headers
|
||||||
expect(json_response).to be_an Array
|
expect(json_response).to be_an Array
|
||||||
expect(response_groups).to eq(Group.visible_to_user(user1).order(:path).pluck(:name))
|
expect(response_groups).to eq(groups_visible_to_user(user1).order(:path).pluck(:name))
|
||||||
end
|
end
|
||||||
|
|
||||||
it "sorts by id in the order_by param" do
|
it "sorts by id in the order_by param" do
|
||||||
|
@ -182,7 +182,7 @@ describe API::Groups do
|
||||||
expect(response).to have_gitlab_http_status(200)
|
expect(response).to have_gitlab_http_status(200)
|
||||||
expect(response).to include_pagination_headers
|
expect(response).to include_pagination_headers
|
||||||
expect(json_response).to be_an Array
|
expect(json_response).to be_an Array
|
||||||
expect(response_groups).to eq(Group.visible_to_user(user1).order(:id).pluck(:name))
|
expect(response_groups).to eq(groups_visible_to_user(user1).order(:id).pluck(:name))
|
||||||
end
|
end
|
||||||
|
|
||||||
it "sorts also by descending id with pagination fix" do
|
it "sorts also by descending id with pagination fix" do
|
||||||
|
@ -191,7 +191,7 @@ describe API::Groups do
|
||||||
expect(response).to have_gitlab_http_status(200)
|
expect(response).to have_gitlab_http_status(200)
|
||||||
expect(response).to include_pagination_headers
|
expect(response).to include_pagination_headers
|
||||||
expect(json_response).to be_an Array
|
expect(json_response).to be_an Array
|
||||||
expect(response_groups).to eq(Group.visible_to_user(user1).order(id: :desc).pluck(:name))
|
expect(response_groups).to eq(groups_visible_to_user(user1).order(id: :desc).pluck(:name))
|
||||||
end
|
end
|
||||||
|
|
||||||
it "sorts identical keys by id for good pagination" do
|
it "sorts identical keys by id for good pagination" do
|
||||||
|
@ -211,6 +211,10 @@ describe API::Groups do
|
||||||
expect(json_response).to be_an Array
|
expect(json_response).to be_an Array
|
||||||
expect(response_groups_ids).to eq(Group.select { |group| group['name'] == 'same-name' }.map { |group| group['id'] }.sort)
|
expect(response_groups_ids).to eq(Group.select { |group| group['name'] == 'same-name' }.map { |group| group['id'] }.sort)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def groups_visible_to_user(user)
|
||||||
|
Group.where(id: user.authorized_groups.select(:id).reorder(nil))
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when using owned in the request' do
|
context 'when using owned in the request' do
|
||||||
|
|
28
spec/rubocop/cop/group_public_or_visible_to_user_spec.rb
Normal file
28
spec/rubocop/cop/group_public_or_visible_to_user_spec.rb
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
require 'rubocop'
|
||||||
|
require 'rubocop/rspec/support'
|
||||||
|
require_relative '../../../rubocop/cop/group_public_or_visible_to_user'
|
||||||
|
|
||||||
|
describe RuboCop::Cop::GroupPublicOrVisibleToUser do
|
||||||
|
include CopHelper
|
||||||
|
|
||||||
|
subject(:cop) { described_class.new }
|
||||||
|
|
||||||
|
it 'flags the use of Group.public_or_visible_to_user with a constant receiver' do
|
||||||
|
inspect_source('Group.public_or_visible_to_user')
|
||||||
|
|
||||||
|
expect(cop.offenses.size).to eq(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not flat the use of public_or_visible_to_user with a constant that is not Group' do
|
||||||
|
inspect_source('Project.public_or_visible_to_user')
|
||||||
|
|
||||||
|
expect(cop.offenses.size).to eq(0)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not flag the use of Group.public_or_visible_to_user with a send receiver' do
|
||||||
|
inspect_source('foo.public_or_visible_to_user')
|
||||||
|
|
||||||
|
expect(cop.offenses.size).to eq(0)
|
||||||
|
end
|
||||||
|
end
|
|
@ -12,6 +12,7 @@ describe 'projects/merge_requests/creations/_new_submit.html.haml' do
|
||||||
assign(:hidden_commit_count, 0)
|
assign(:hidden_commit_count, 0)
|
||||||
assign(:total_commit_count, merge_request.commits.count)
|
assign(:total_commit_count, merge_request.commits.count)
|
||||||
assign(:project, merge_request.target_project)
|
assign(:project, merge_request.target_project)
|
||||||
|
assign(:mr_presenter, merge_request.present(current_user: merge_request.author))
|
||||||
|
|
||||||
allow(view).to receive(:can?).and_return(true)
|
allow(view).to receive(:can?).and_return(true)
|
||||||
allow(view).to receive(:url_for).and_return('#')
|
allow(view).to receive(:url_for).and_return('#')
|
||||||
|
|
|
@ -24,6 +24,7 @@ describe 'projects/merge_requests/edit.html.haml' do
|
||||||
before do
|
before do
|
||||||
assign(:project, project)
|
assign(:project, project)
|
||||||
assign(:merge_request, closed_merge_request)
|
assign(:merge_request, closed_merge_request)
|
||||||
|
assign(:mr_presenter, closed_merge_request.present(current_user: user))
|
||||||
|
|
||||||
allow(view).to receive(:can?).and_return(true)
|
allow(view).to receive(:can?).and_return(true)
|
||||||
allow(view).to receive(:current_user)
|
allow(view).to receive(:current_user)
|
||||||
|
|
Loading…
Reference in a new issue