Skip to content
Snippets Groups Projects
Commit 686ae219 authored by GitLab Release Tools Bot's avatar GitLab Release Tools Bot
Browse files

Merge remote-tracking branch 'dev/12-5-stable' into 12-5-stable

parents 64b66e0c 36107ed3
No related branches found
No related tags found
No related merge requests found
Showing
with 151 additions and 23 deletions
Loading
@@ -2,9 +2,19 @@
Loading
@@ -2,9 +2,19 @@
documentation](doc/development/changelog.md) for instructions on adding your own documentation](doc/development/changelog.md) for instructions on adding your own
entry. entry.
   
## 12.5.6
### Security (5 changes)
- GraphQL: Add timeout to all queries.
- Return only runners from groups where user is owner for user CI owned runners.
- Filter out notification settings for projects that a user does not have at least read access.
- Hide project name and path when unsusbcribing from an issue or merge request.
- Fix 500 error caused by invalid byte sequences in uploads links.
## 12.5.5 ## 12.5.5
   
- No changes.
### Security (1 change) ### Security (1 change)
   
- Upgrade Akismet gem to v3.0.0. !21786 - Upgrade Akismet gem to v3.0.0. !21786
Loading
Loading
12.5.5 12.5.6
Loading
@@ -11,6 +11,7 @@ class Profiles::NotificationsController < Profiles::ApplicationController
Loading
@@ -11,6 +11,7 @@ class Profiles::NotificationsController < Profiles::ApplicationController
exclude_group_ids: @group_notifications.select(:source_id) exclude_group_ids: @group_notifications.select(:source_id)
).execute.map { |group| current_user.notification_settings_for(group, inherit: true) } ).execute.map { |group| current_user.notification_settings_for(group, inherit: true) }
@project_notifications = current_user.notification_settings.for_projects.order(:id) @project_notifications = current_user.notification_settings.for_projects.order(:id)
.select { |notification| current_user.can?(:read_project, notification.source) }
@global_notification_setting = current_user.global_notification_setting @global_notification_setting = current_user.global_notification_setting
end end
# rubocop: enable CodeReuse/ActiveRecord # rubocop: enable CodeReuse/ActiveRecord
Loading
Loading
Loading
@@ -116,4 +116,8 @@ module NotificationsHelper
Loading
@@ -116,4 +116,8 @@ module NotificationsHelper
def show_unsubscribe_title?(noteable) def show_unsubscribe_title?(noteable)
can?(current_user, "read_#{noteable.to_ability_name}".to_sym, noteable) can?(current_user, "read_#{noteable.to_ability_name}".to_sym, noteable)
end end
def can_read_project?(project)
can?(current_user, :read_project, project)
end
end end
Loading
@@ -1307,7 +1307,7 @@ class User < ApplicationRecord
Loading
@@ -1307,7 +1307,7 @@ class User < ApplicationRecord
.select('ci_runners.*') .select('ci_runners.*')
   
group_runners = Ci::RunnerNamespace group_runners = Ci::RunnerNamespace
.where(namespace_id: owned_or_maintainers_groups.select(:id)) .where(namespace_id: owned_groups.select(:id))
.joins(:runner) .joins(:runner)
.select('ci_runners.*') .select('ci_runners.*')
   
Loading
Loading
- noteable = @sent_notification.noteable - noteable = @sent_notification.noteable
- noteable_type = @sent_notification.noteable_type.titleize.downcase - noteable_type = @sent_notification.noteable_type.titleize.downcase
- noteable_text = show_unsubscribe_title?(noteable) ? %(#{noteable.title} (#{noteable.to_reference})) : %(#{noteable.to_reference}) - noteable_text = show_unsubscribe_title?(noteable) ? %(#{noteable.title} (#{noteable.to_reference})) : %(#{noteable.to_reference})
- page_title _("Unsubscribe"), noteable_text, noteable_type.pluralize, @sent_notification.project.full_name - show_project_path = can_read_project?(@sent_notification.project)
- project_path = show_project_path ? @sent_notification.project.full_name : _("GitLab / Unsubscribe")
- noteable_url = show_project_path ? url_for([@sent_notification.project.namespace.becomes(Namespace), @sent_notification.project, noteable]) : breadcrumb_title_link
- page_title _('Unsubscribe'), noteable_text, noteable_type.pluralize, project_path
   
%h3.page-title %h3.page-title
= _("Unsubscribe from %{type}") % { type: noteable_type } = _("Unsubscribe from %{type}") % { type: noteable_type }
   
%p %p
- link_to_noteable_text = link_to(noteable_text, url_for([@sent_notification.project.namespace.becomes(Namespace), @sent_notification.project, noteable])) - link_to_noteable_text = link_to(noteable_text, noteable_url)
= _("Are you sure you want to unsubscribe from the %{type}: %{link_to_noteable_text}?").html_safe % { type: noteable_type, link_to_noteable_text: link_to_noteable_text } = _("Are you sure you want to unsubscribe from the %{type}: %{link_to_noteable_text}?").html_safe % { type: noteable_type, link_to_noteable_text: link_to_noteable_text }
   
%p %p
Loading
Loading
Loading
@@ -5,3 +5,7 @@ GraphQL::Field.accepts_definitions(authorize: GraphQL::Define.assign_metadata_ke
Loading
@@ -5,3 +5,7 @@ GraphQL::Field.accepts_definitions(authorize: GraphQL::Define.assign_metadata_ke
   
GraphQL::Schema::Object.accepts_definition(:authorize) GraphQL::Schema::Object.accepts_definition(:authorize)
GraphQL::Schema::Field.accepts_definition(:authorize) GraphQL::Schema::Field.accepts_definition(:authorize)
GitlabSchema.middleware << GraphQL::Schema::TimeoutMiddleware.new(max_seconds: ENV.fetch('GITLAB_RAILS_GRAPHQL_TIMEOUT', 30).to_i) do |timeout_error, query|
Gitlab::GraphqlLogger.error(message: timeout_error.to_s, query: query.query_string, query_variables: query.provided_variables)
end
Loading
@@ -116,7 +116,7 @@ module Banzai
Loading
@@ -116,7 +116,7 @@ module Banzai
end end
   
def process_link_to_upload_attr(html_attr) def process_link_to_upload_attr(html_attr)
path_parts = [Addressable::URI.unescape(html_attr.value)] path_parts = [unescape_and_scrub_uri(html_attr.value)]
   
if project if project
path_parts.unshift(relative_url_root, project.full_path) path_parts.unshift(relative_url_root, project.full_path)
Loading
@@ -172,7 +172,7 @@ module Banzai
Loading
@@ -172,7 +172,7 @@ module Banzai
end end
   
def cleaned_file_path(uri) def cleaned_file_path(uri)
Addressable::URI.unescape(uri.path).scrub.delete("\0").chomp("/") unescape_and_scrub_uri(uri.path).delete("\0").chomp("/")
end end
   
def relative_file_path(uri) def relative_file_path(uri)
Loading
@@ -184,7 +184,7 @@ module Banzai
Loading
@@ -184,7 +184,7 @@ module Banzai
def request_path def request_path
return unless context[:requested_path] return unless context[:requested_path]
   
Addressable::URI.unescape(context[:requested_path]).chomp("/") unescape_and_scrub_uri(context[:requested_path]).chomp("/")
end end
   
# Convert a relative path into its correct location based on the currently # Convert a relative path into its correct location based on the currently
Loading
@@ -266,6 +266,12 @@ module Banzai
Loading
@@ -266,6 +266,12 @@ module Banzai
def repository def repository
@repository ||= project&.repository @repository ||= project&.repository
end end
private
def unescape_and_scrub_uri(uri)
Addressable::URI.unescape(uri).scrub
end
end end
end end
end end
Loading
@@ -8193,6 +8193,9 @@ msgstr ""
Loading
@@ -8193,6 +8193,9 @@ msgstr ""
msgid "GitHub import" msgid "GitHub import"
msgstr "" msgstr ""
   
msgid "GitLab / Unsubscribe"
msgstr ""
msgid "GitLab CI Linter has been moved" msgid "GitLab CI Linter has been moved"
msgstr "" msgstr ""
   
Loading
Loading
Loading
@@ -52,6 +52,35 @@ describe Profiles::NotificationsController do
Loading
@@ -52,6 +52,35 @@ describe Profiles::NotificationsController do
end.to exceed_query_limit(control) end.to exceed_query_limit(control)
end end
end end
context 'with project notifications' do
let!(:notification_setting) { create(:notification_setting, source: project, user: user, level: :watch) }
before do
sign_in(user)
get :show
end
context 'when project is public' do
let(:project) { create(:project, :public) }
it 'shows notification setting for project' do
expect(assigns(:project_notifications).map(&:source_id)).to include(project.id)
end
end
context 'when project is public' do
let(:project) { create(:project, :private) }
it 'shows notification setting for project' do
# notification settings for given project were created before project was set to private
expect(user.notification_settings.for_projects.map(&:source_id)).to include(project.id)
# check that notification settings for project where user does not have access are filtered
expect(assigns(:project_notifications)).to be_empty
end
end
end
end end
   
describe 'POST update' do describe 'POST update' do
Loading
Loading
Loading
@@ -56,7 +56,7 @@ describe SentNotificationsController do
Loading
@@ -56,7 +56,7 @@ describe SentNotificationsController do
get(:unsubscribe, params: { id: sent_notification.reply_key }) get(:unsubscribe, params: { id: sent_notification.reply_key })
end end
   
shared_examples 'unsubscribing as anonymous' do shared_examples 'unsubscribing as anonymous' do |project_visibility|
it 'does not unsubscribe the user' do it 'does not unsubscribe the user' do
expect(noteable.subscribed?(user, target_project)).to be_truthy expect(noteable.subscribed?(user, target_project)).to be_truthy
end end
Loading
@@ -69,6 +69,18 @@ describe SentNotificationsController do
Loading
@@ -69,6 +69,18 @@ describe SentNotificationsController do
expect(response.status).to eq(200) expect(response.status).to eq(200)
expect(response).to render_template :unsubscribe expect(response).to render_template :unsubscribe
end end
if project_visibility == :private
it 'does not show project name or path' do
expect(response.body).not_to include(noteable.project.name)
expect(response.body).not_to include(noteable.project.full_name)
end
else
it 'shows project name or path' do
expect(response.body).to include(noteable.project.name)
expect(response.body).to include(noteable.project.full_name)
end
end
end end
   
context 'when project is public' do context 'when project is public' do
Loading
@@ -79,7 +91,7 @@ describe SentNotificationsController do
Loading
@@ -79,7 +91,7 @@ describe SentNotificationsController do
expect(response.body).to include(issue.title) expect(response.body).to include(issue.title)
end end
   
it_behaves_like 'unsubscribing as anonymous' it_behaves_like 'unsubscribing as anonymous', :public
end end
   
context 'when unsubscribing from confidential issue' do context 'when unsubscribing from confidential issue' do
Loading
@@ -90,7 +102,7 @@ describe SentNotificationsController do
Loading
@@ -90,7 +102,7 @@ describe SentNotificationsController do
expect(response.body).to include(confidential_issue.to_reference) expect(response.body).to include(confidential_issue.to_reference)
end end
   
it_behaves_like 'unsubscribing as anonymous' it_behaves_like 'unsubscribing as anonymous', :public
end end
   
context 'when unsubscribing from merge request' do context 'when unsubscribing from merge request' do
Loading
@@ -100,7 +112,12 @@ describe SentNotificationsController do
Loading
@@ -100,7 +112,12 @@ describe SentNotificationsController do
expect(response.body).to include(merge_request.title) expect(response.body).to include(merge_request.title)
end end
   
it_behaves_like 'unsubscribing as anonymous' it 'shows project name or path' do
expect(response.body).to include(issue.project.name)
expect(response.body).to include(issue.project.full_name)
end
it_behaves_like 'unsubscribing as anonymous', :public
end end
end end
   
Loading
@@ -110,11 +127,11 @@ describe SentNotificationsController do
Loading
@@ -110,11 +127,11 @@ describe SentNotificationsController do
context 'when unsubscribing from issue' do context 'when unsubscribing from issue' do
let(:noteable) { issue } let(:noteable) { issue }
   
it 'shows issue title' do it 'does not show issue title' do
expect(response.body).not_to include(issue.title) expect(response.body).not_to include(issue.title)
end end
   
it_behaves_like 'unsubscribing as anonymous' it_behaves_like 'unsubscribing as anonymous', :private
end end
   
context 'when unsubscribing from confidential issue' do context 'when unsubscribing from confidential issue' do
Loading
@@ -125,17 +142,17 @@ describe SentNotificationsController do
Loading
@@ -125,17 +142,17 @@ describe SentNotificationsController do
expect(response.body).to include(confidential_issue.to_reference) expect(response.body).to include(confidential_issue.to_reference)
end end
   
it_behaves_like 'unsubscribing as anonymous' it_behaves_like 'unsubscribing as anonymous', :private
end end
   
context 'when unsubscribing from merge request' do context 'when unsubscribing from merge request' do
let(:noteable) { merge_request } let(:noteable) { merge_request }
   
it 'shows merge request title' do it 'dos not show merge request title' do
expect(response.body).not_to include(merge_request.title) expect(response.body).not_to include(merge_request.title)
end end
   
it_behaves_like 'unsubscribing as anonymous' it_behaves_like 'unsubscribing as anonymous', :private
end end
end end
end end
Loading
Loading
Loading
@@ -124,6 +124,15 @@ describe Banzai::Filter::RelativeLinkFilter do
Loading
@@ -124,6 +124,15 @@ describe Banzai::Filter::RelativeLinkFilter do
expect { filter(act) }.not_to raise_error expect { filter(act) }.not_to raise_error
end end
   
it 'does not raise an exception on URIs containing invalid utf-8 byte sequences in uploads' do
act = link("/uploads/%FF")
expect { filter(act) }.not_to raise_error
end
it 'does not raise an exception on URIs containing invalid utf-8 byte sequences in context requested path' do
expect { filter(link("files/test.md"), requested_path: '%FF') }.not_to raise_error
end
it 'does not raise an exception with a garbled path' do it 'does not raise an exception with a garbled path' do
act = link("open(/var/tmp/):%20/location%0Afrom:%20/test") act = link("open(/var/tmp/):%20/location%0Afrom:%20/test")
expect { filter(act) }.not_to raise_error expect { filter(act) }.not_to raise_error
Loading
Loading
Loading
@@ -2533,8 +2533,8 @@ describe User, :do_not_mock_admin_mode do
Loading
@@ -2533,8 +2533,8 @@ describe User, :do_not_mock_admin_mode do
add_user(:maintainer) add_user(:maintainer)
end end
   
it 'loads' do it 'does not load' do
expect(user.ci_owned_runners).to contain_exactly(runner) expect(user.ci_owned_runners).to be_empty
end end
end end
   
Loading
@@ -2549,6 +2549,20 @@ describe User, :do_not_mock_admin_mode do
Loading
@@ -2549,6 +2549,20 @@ describe User, :do_not_mock_admin_mode do
end end
end end
   
shared_examples :group_member do
context 'when the user is owner' do
before do
add_user(:owner)
end
it 'loads' do
expect(user.ci_owned_runners).to contain_exactly(runner)
end
end
it_behaves_like :member
end
context 'with groups projects runners' do context 'with groups projects runners' do
let(:group) { create(:group) } let(:group) { create(:group) }
let!(:project) { create(:project, group: group) } let!(:project) { create(:project, group: group) }
Loading
@@ -2557,7 +2571,7 @@ describe User, :do_not_mock_admin_mode do
Loading
@@ -2557,7 +2571,7 @@ describe User, :do_not_mock_admin_mode do
group.add_user(user, access) group.add_user(user, access)
end end
   
it_behaves_like :member it_behaves_like :group_member
end end
   
context 'with groups runners' do context 'with groups runners' do
Loading
@@ -2568,14 +2582,14 @@ describe User, :do_not_mock_admin_mode do
Loading
@@ -2568,14 +2582,14 @@ describe User, :do_not_mock_admin_mode do
group.add_user(user, access) group.add_user(user, access)
end end
   
it_behaves_like :member it_behaves_like :group_member
end end
   
context 'with other projects runners' do context 'with other projects runners' do
let!(:project) { create(:project) } let!(:project) { create(:project) }
   
def add_user(access) def add_user(access)
project.add_role(user, access) project.add_user(user, access)
end end
   
it_behaves_like :member it_behaves_like :member
Loading
@@ -2593,7 +2607,7 @@ describe User, :do_not_mock_admin_mode do
Loading
@@ -2593,7 +2607,7 @@ describe User, :do_not_mock_admin_mode do
subgroup.add_user(another_user, :owner) subgroup.add_user(another_user, :owner)
end end
   
it_behaves_like :member it_behaves_like :group_member
end end
end end
   
Loading
Loading
Loading
@@ -8,6 +8,18 @@ describe 'GitlabSchema configurations' do
Loading
@@ -8,6 +8,18 @@ describe 'GitlabSchema configurations' do
set(:project) { create(:project) } set(:project) { create(:project) }
   
shared_examples 'imposing query limits' do shared_examples 'imposing query limits' do
describe 'timeouts' do
context 'when timeout is reached' do
it 'shows an error' do
Timecop.scale(50000000) do # ludicrously large number because the timeout has to happen before the query even begins
subject
expect_graphql_errors_to_include /Timeout/
end
end
end
end
describe '#max_complexity' do describe '#max_complexity' do
context 'when complexity is too high' do context 'when complexity is too high' do
it 'shows an error' do it 'shows an error' do
Loading
Loading
Loading
@@ -6,6 +6,7 @@ describe API::Runners do
Loading
@@ -6,6 +6,7 @@ describe API::Runners do
let(:admin) { create(:user, :admin) } let(:admin) { create(:user, :admin) }
let(:user) { create(:user) } let(:user) { create(:user) }
let(:user2) { create(:user) } let(:user2) { create(:user) }
let(:group_maintainer) { create(:user) }
   
let(:project) { create(:project, creator_id: user.id) } let(:project) { create(:project, creator_id: user.id) }
let(:project2) { create(:project, creator_id: user.id) } let(:project2) { create(:project, creator_id: user.id) }
Loading
@@ -20,6 +21,7 @@ describe API::Runners do
Loading
@@ -20,6 +21,7 @@ describe API::Runners do
   
before do before do
# Set project access for users # Set project access for users
create(:group_member, :maintainer, user: group_maintainer, group: group)
create(:project_member, :maintainer, user: user, project: project) create(:project_member, :maintainer, user: user, project: project)
create(:project_member, :maintainer, user: user, project: project2) create(:project_member, :maintainer, user: user, project: project2)
create(:project_member, :reporter, user: user2, project: project) create(:project_member, :reporter, user: user2, project: project)
Loading
@@ -525,6 +527,20 @@ describe API::Runners do
Loading
@@ -525,6 +527,20 @@ describe API::Runners do
end.to change { Ci::Runner.project_type.count }.by(-1) end.to change { Ci::Runner.project_type.count }.by(-1)
end end
   
it 'does not delete group runner with maintainer access' do
delete api("/runners/#{group_runner.id}", group_maintainer)
expect(response).to have_http_status(403)
end
it 'deletes group runner with owner access' do
expect do
delete api("/runners/#{group_runner.id}", user)
expect(response).to have_http_status(204)
end.to change { Ci::Runner.group_type.count }.by(-1)
end
it_behaves_like '412 response' do it_behaves_like '412 response' do
let(:request) { api("/runners/#{project_runner.id}", user) } let(:request) { api("/runners/#{project_runner.id}", user) }
end end
Loading
Loading
File mode changed from 100755 to 100644
File mode changed from 100755 to 100644
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment