Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-12-04 19:53:44 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2020-12-04 19:53:44 +0300
commit4e3a54f835daa49bf784d6e6ad91e90116a24dc8 (patch)
tree8e1f7be7a80da2de02b2da0ed88f81b2f6b6de8c
parentaefe6486cf0d193067112b90145083d73b96bfef (diff)
Add latest changes from gitlab-org/security/gitlab@13-6-stable-ee
-rw-r--r--app/controllers/users_controller.rb2
-rw-r--r--app/finders/starred_projects_finder.rb11
-rw-r--r--app/services/todos/destroy/entity_leave_service.rb6
-rw-r--r--app/validators/zoom_url_validator.rb7
-rw-r--r--app/views/devise/confirmations/new.html.haml2
-rw-r--r--changelogs/unreleased/security-hide-email-in-confirmation-page.yml5
-rw-r--r--changelogs/unreleased/security-project-import-zoom-xss.yml5
-rw-r--r--changelogs/unreleased/security-starred-projects-api-fix.yml5
-rw-r--r--changelogs/unreleased/security-starred-projects-private-profile.yml5
-rw-r--r--config/feature_categories.yml1
-rw-r--r--db/post_migrate/20201109114603_schedule_remove_inaccessible_epic_todos.rb29
-rw-r--r--db/schema_migrations/202011091146031
-rw-r--r--doc/user/todos.md2
-rw-r--r--lib/gitlab/background_migration/remove_inaccessible_epic_todos.rb13
-rw-r--r--spec/controllers/confirmations_controller_spec.rb80
-rw-r--r--spec/controllers/users_controller_spec.rb89
-rw-r--r--spec/finders/starred_projects_finder_spec.rb59
-rw-r--r--spec/requests/api/graphql/user/starred_projects_query_spec.rb27
-rw-r--r--spec/requests/api/projects_spec.rb45
-rw-r--r--spec/validators/zoom_url_validator_spec.rb36
-rwxr-xr-x[-rw-r--r--]vendor/gitignore/C++.gitignore0
-rwxr-xr-x[-rw-r--r--]vendor/gitignore/Java.gitignore0
22 files changed, 396 insertions, 34 deletions
diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb
index 672f36dedc0..05573255066 100644
--- a/app/controllers/users_controller.rb
+++ b/app/controllers/users_controller.rb
@@ -19,7 +19,7 @@ class UsersController < ApplicationController
prepend_before_action(only: [:show]) { authenticate_sessionless_user!(:rss) }
before_action :user, except: [:exists, :suggests]
before_action :authorize_read_user_profile!,
- only: [:calendar, :calendar_activities, :groups, :projects, :contributed_projects, :starred_projects, :snippets]
+ only: [:calendar, :calendar_activities, :groups, :projects, :contributed, :starred, :snippets]
feature_category :users
diff --git a/app/finders/starred_projects_finder.rb b/app/finders/starred_projects_finder.rb
index fcb469d1d17..e209960c471 100644
--- a/app/finders/starred_projects_finder.rb
+++ b/app/finders/starred_projects_finder.rb
@@ -1,11 +1,22 @@
# frozen_string_literal: true
class StarredProjectsFinder < ProjectsFinder
+ include Gitlab::Allowable
+
def initialize(user, params: {}, current_user: nil)
+ @user = user
+
super(
params: params,
current_user: current_user,
project_ids_relation: user.starred_projects.select(:id)
)
end
+
+ def execute
+ # Do not show starred projects if the user has a private profile.
+ return Project.none unless can?(current_user, :read_user_profile, @user)
+
+ super
+ end
end
diff --git a/app/services/todos/destroy/entity_leave_service.rb b/app/services/todos/destroy/entity_leave_service.rb
index 97c56b84434..7cfedc2233a 100644
--- a/app/services/todos/destroy/entity_leave_service.rb
+++ b/app/services/todos/destroy/entity_leave_service.rb
@@ -22,7 +22,7 @@ module Todos
# if at least reporter, all entities including confidential issues can be accessed
return if user_has_reporter_access?
- remove_confidential_issue_todos
+ remove_confidential_resource_todos
if entity.private?
remove_project_todos
@@ -40,7 +40,7 @@ module Todos
end
end
- def remove_confidential_issue_todos
+ def remove_confidential_resource_todos
Todo
.for_target(confidential_issues.select(:id))
.for_type(Issue.name)
@@ -133,3 +133,5 @@ module Todos
end
end
end
+
+Todos::Destroy::EntityLeaveService.prepend_if_ee('EE::Todos::Destroy::EntityLeaveService')
diff --git a/app/validators/zoom_url_validator.rb b/app/validators/zoom_url_validator.rb
index dc4ca6b9501..e0f8e4e34a2 100644
--- a/app/validators/zoom_url_validator.rb
+++ b/app/validators/zoom_url_validator.rb
@@ -5,8 +5,13 @@
# Custom validator for zoom urls
#
class ZoomUrlValidator < ActiveModel::EachValidator
+ ALLOWED_SCHEMES = %w(https).freeze
+
def validate_each(record, attribute, value)
- return if Gitlab::ZoomLinkExtractor.new(value).links.size == 1
+ links_count = Gitlab::ZoomLinkExtractor.new(value).links.size
+ valid = Gitlab::UrlSanitizer.valid?(value, allowed_schemes: ALLOWED_SCHEMES)
+
+ return if links_count == 1 && valid
record.errors.add(:url, 'must contain one valid Zoom URL')
end
diff --git a/app/views/devise/confirmations/new.html.haml b/app/views/devise/confirmations/new.html.haml
index 49112ed6cd5..770a29a629e 100644
--- a/app/views/devise/confirmations/new.html.haml
+++ b/app/views/devise/confirmations/new.html.haml
@@ -6,7 +6,7 @@
= render "devise/shared/error_messages", resource: resource
.form-group
= f.label :email
- = f.email_field :email, class: "form-control", required: true, title: 'Please provide a valid email address.'
+ = f.email_field :email, class: "form-control", required: true, title: 'Please provide a valid email address.', value: nil
.clearfix
= f.submit "Resend", class: 'gl-button btn btn-success'
diff --git a/changelogs/unreleased/security-hide-email-in-confirmation-page.yml b/changelogs/unreleased/security-hide-email-in-confirmation-page.yml
new file mode 100644
index 00000000000..b8f448acfcd
--- /dev/null
+++ b/changelogs/unreleased/security-hide-email-in-confirmation-page.yml
@@ -0,0 +1,5 @@
+---
+title: Do not show emails of users in confirmation page
+merge_request:
+author:
+type: security
diff --git a/changelogs/unreleased/security-project-import-zoom-xss.yml b/changelogs/unreleased/security-project-import-zoom-xss.yml
new file mode 100644
index 00000000000..4f4d7f14b6b
--- /dev/null
+++ b/changelogs/unreleased/security-project-import-zoom-xss.yml
@@ -0,0 +1,5 @@
+---
+title: Validate zoom links to start with https only
+merge_request: 1055
+author:
+type: security
diff --git a/changelogs/unreleased/security-starred-projects-api-fix.yml b/changelogs/unreleased/security-starred-projects-api-fix.yml
new file mode 100644
index 00000000000..efb12998393
--- /dev/null
+++ b/changelogs/unreleased/security-starred-projects-api-fix.yml
@@ -0,0 +1,5 @@
+---
+title: Do not expose starred projects of users with private profile via API
+merge_request:
+author:
+type: security
diff --git a/changelogs/unreleased/security-starred-projects-private-profile.yml b/changelogs/unreleased/security-starred-projects-private-profile.yml
new file mode 100644
index 00000000000..1fb47dce518
--- /dev/null
+++ b/changelogs/unreleased/security-starred-projects-private-profile.yml
@@ -0,0 +1,5 @@
+---
+title: Do not show starred & contributed projects of users with private profile
+merge_request:
+author:
+type: security
diff --git a/config/feature_categories.yml b/config/feature_categories.yml
index fb261377532..205d3ee5851 100644
--- a/config/feature_categories.yml
+++ b/config/feature_categories.yml
@@ -45,6 +45,7 @@
- dynamic_application_security_testing
- editor_extension
- epics
+- epic_tracking
- error_tracking
- feature_flags
- foundations
diff --git a/db/post_migrate/20201109114603_schedule_remove_inaccessible_epic_todos.rb b/db/post_migrate/20201109114603_schedule_remove_inaccessible_epic_todos.rb
new file mode 100644
index 00000000000..13d12675a28
--- /dev/null
+++ b/db/post_migrate/20201109114603_schedule_remove_inaccessible_epic_todos.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+class ScheduleRemoveInaccessibleEpicTodos < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+ INTERVAL = 2.minutes
+ BATCH_SIZE = 10
+ MIGRATION = 'RemoveInaccessibleEpicTodos'
+
+ disable_ddl_transaction!
+
+ class Epic < ActiveRecord::Base
+ include EachBatch
+ end
+
+ def up
+ return unless Gitlab.ee?
+
+ relation = Epic.where(confidential: true)
+
+ queue_background_migration_jobs_by_range_at_intervals(
+ relation, MIGRATION, INTERVAL, batch_size: BATCH_SIZE)
+ end
+
+ def down
+ # no-op
+ end
+end
diff --git a/db/schema_migrations/20201109114603 b/db/schema_migrations/20201109114603
new file mode 100644
index 00000000000..c1df2223dbd
--- /dev/null
+++ b/db/schema_migrations/20201109114603
@@ -0,0 +1 @@
+ae8034ec52df47ce2ce3397715dd18347e4d297a963c17c7b26321f414dfa632 \ No newline at end of file
diff --git a/doc/user/todos.md b/doc/user/todos.md
index 061d81618a1..69c0736bdff 100644
--- a/doc/user/todos.md
+++ b/doc/user/todos.md
@@ -64,7 +64,7 @@ To-do item triggers aren't affected by [GitLab notification email settings](prof
NOTE: **Note:**
When a user no longer has access to a resource related to a to-do item (such as
-an issue, merge request, project, or group), for security reasons GitLab
+an issue, merge request, epic, project, or group), for security reasons GitLab
deletes any related to-do items within the next hour. Deletion is delayed to
prevent data loss, in the case where a user's access is accidentally revoked.
diff --git a/lib/gitlab/background_migration/remove_inaccessible_epic_todos.rb b/lib/gitlab/background_migration/remove_inaccessible_epic_todos.rb
new file mode 100644
index 00000000000..74c48b237cc
--- /dev/null
+++ b/lib/gitlab/background_migration/remove_inaccessible_epic_todos.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module BackgroundMigration
+ # rubocop:disable Style/Documentation
+ class RemoveInaccessibleEpicTodos
+ def perform(start_id, stop_id)
+ end
+ end
+ end
+end
+
+Gitlab::BackgroundMigration::RemoveInaccessibleEpicTodos.prepend_if_ee('EE::Gitlab::BackgroundMigration::RemoveInaccessibleEpicTodos')
diff --git a/spec/controllers/confirmations_controller_spec.rb b/spec/controllers/confirmations_controller_spec.rb
new file mode 100644
index 00000000000..49a39f257fe
--- /dev/null
+++ b/spec/controllers/confirmations_controller_spec.rb
@@ -0,0 +1,80 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe ConfirmationsController do
+ include DeviseHelpers
+
+ before do
+ set_devise_mapping(context: @request)
+ end
+
+ describe '#show' do
+ render_views
+
+ subject { get :show, params: { confirmation_token: confirmation_token } }
+
+ context 'user is already confirmed' do
+ let_it_be_with_reload(:user) { create(:user, :unconfirmed) }
+ let(:confirmation_token) { user.confirmation_token }
+
+ before do
+ user.confirm
+ subject
+ end
+
+ it 'renders `new`' do
+ expect(response).to render_template(:new)
+ end
+
+ it 'displays an error message' do
+ expect(response.body).to include('Email was already confirmed, please try signing in')
+ end
+
+ it 'does not display the email of the user' do
+ expect(response.body).not_to include(user.email)
+ end
+ end
+
+ context 'user accesses the link after the expiry of confirmation token has passed' do
+ let_it_be_with_reload(:user) { create(:user, :unconfirmed) }
+ let(:confirmation_token) { user.confirmation_token }
+
+ before do
+ allow(Devise).to receive(:confirm_within).and_return(1.day)
+
+ travel_to(3.days.from_now) do
+ subject
+ end
+ end
+
+ it 'renders `new`' do
+ expect(response).to render_template(:new)
+ end
+
+ it 'displays an error message' do
+ expect(response.body).to include('Email needs to be confirmed within 1 day, please request a new one below')
+ end
+
+ it 'does not display the email of the user' do
+ expect(response.body).not_to include(user.email)
+ end
+ end
+
+ context 'with an invalid confirmation token' do
+ let(:confirmation_token) { 'invalid_confirmation_token' }
+
+ before do
+ subject
+ end
+
+ it 'renders `new`' do
+ expect(response).to render_template(:new)
+ end
+
+ it 'displays an error message' do
+ expect(response.body).to include('Confirmation token is invalid')
+ end
+ end
+ end
+end
diff --git a/spec/controllers/users_controller_spec.rb b/spec/controllers/users_controller_spec.rb
index bec4b24484a..2e57a901319 100644
--- a/spec/controllers/users_controller_spec.rb
+++ b/spec/controllers/users_controller_spec.rb
@@ -247,32 +247,99 @@ RSpec.describe UsersController do
describe 'GET #contributed' do
let(:project) { create(:project, :public) }
- let(:current_user) { create(:user) }
+
+ subject do
+ get :contributed, params: { username: author.username }, format: format
+ end
before do
- sign_in(current_user)
+ sign_in(user)
project.add_developer(public_user)
project.add_developer(private_user)
+ create(:push_event, project: project, author: author)
+
+ subject
end
- context 'with public profile' do
+ shared_examples_for 'renders contributed projects' do
it 'renders contributed projects' do
- create(:push_event, project: project, author: public_user)
+ expect(assigns[:contributed_projects]).not_to be_empty
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+ end
- get :contributed, params: { username: public_user.username }
+ %i(html json).each do |format|
+ context "format: #{format}" do
+ let(:format) { format }
- expect(assigns[:contributed_projects]).not_to be_empty
+ context 'with public profile' do
+ let(:author) { public_user }
+
+ it_behaves_like 'renders contributed projects'
+ end
+
+ context 'with private profile' do
+ let(:author) { private_user }
+
+ it 'returns 404' do
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+
+ context 'with a user that has the ability to read private profiles', :enable_admin_mode do
+ let(:user) { create(:admin) }
+
+ it_behaves_like 'renders contributed projects'
+ end
+ end
+ end
+ end
+ end
+
+ describe 'GET #starred' do
+ let(:project) { create(:project, :public) }
+
+ subject do
+ get :starred, params: { username: author.username }, format: format
+ end
+
+ before do
+ author.toggle_star(project)
+
+ sign_in(user)
+ subject
+ end
+
+ shared_examples_for 'renders starred projects' do
+ it 'renders starred projects' do
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(assigns[:starred_projects]).not_to be_empty
end
end
- context 'with private profile' do
- it 'does not render contributed projects' do
- create(:push_event, project: project, author: private_user)
+ %i(html json).each do |format|
+ context "format: #{format}" do
+ let(:format) { format }
+
+ context 'with public profile' do
+ let(:author) { public_user }
+
+ it_behaves_like 'renders starred projects'
+ end
+
+ context 'with private profile' do
+ let(:author) { private_user }
+
+ it 'returns 404' do
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
- get :contributed, params: { username: private_user.username }
+ context 'with a user that has the ability to read private profiles', :enable_admin_mode do
+ let(:user) { create(:admin) }
- expect(assigns[:contributed_projects]).to be_empty
+ it_behaves_like 'renders starred projects'
+ end
+ end
end
end
end
diff --git a/spec/finders/starred_projects_finder_spec.rb b/spec/finders/starred_projects_finder_spec.rb
index 15d4ae52ddd..f5d3314021d 100644
--- a/spec/finders/starred_projects_finder_spec.rb
+++ b/spec/finders/starred_projects_finder_spec.rb
@@ -5,7 +5,7 @@ require 'spec_helper'
RSpec.describe StarredProjectsFinder do
let(:project1) { create(:project, :public, :empty_repo) }
let(:project2) { create(:project, :public, :empty_repo) }
- let(:other_project) { create(:project, :public, :empty_repo) }
+ let(:private_project) { create(:project, :private, :empty_repo) }
let(:user) { create(:user) }
let(:other_user) { create(:user) }
@@ -13,6 +13,9 @@ RSpec.describe StarredProjectsFinder do
before do
user.toggle_star(project1)
user.toggle_star(project2)
+
+ private_project.add_maintainer(user)
+ user.toggle_star(private_project)
end
describe '#execute' do
@@ -20,22 +23,56 @@ RSpec.describe StarredProjectsFinder do
subject { finder.execute }
- describe 'as same user' do
- let(:current_user) { user }
+ context 'user has a public profile' do
+ describe 'as same user' do
+ let(:current_user) { user }
- it { is_expected.to contain_exactly(project1, project2) }
- end
+ it { is_expected.to contain_exactly(project1, project2, private_project) }
+ end
+
+ describe 'as other user' do
+ let(:current_user) { other_user }
- describe 'as other user' do
- let(:current_user) { other_user }
+ it { is_expected.to contain_exactly(project1, project2) }
+ end
- it { is_expected.to contain_exactly(project1, project2) }
+ describe 'as no user' do
+ let(:current_user) { nil }
+
+ it { is_expected.to contain_exactly(project1, project2) }
+ end
end
- describe 'as no user' do
- let(:current_user) { nil }
+ context 'user has a private profile' do
+ before do
+ user.update!(private_profile: true)
+ end
+
+ describe 'as same user' do
+ let(:current_user) { user }
+
+ it { is_expected.to contain_exactly(project1, project2, private_project) }
+ end
+
+ describe 'as other user' do
+ context 'user does not have access to view the private profile' do
+ let(:current_user) { other_user }
+
+ it { is_expected.to be_empty }
+ end
+
+ context 'user has access to view the private profile', :enable_admin_mode do
+ let(:current_user) { create(:admin) }
+
+ it { is_expected.to contain_exactly(project1, project2, private_project) }
+ end
+ end
+
+ describe 'as no user' do
+ let(:current_user) { nil }
- it { is_expected.to contain_exactly(project1, project2) }
+ it { is_expected.to be_empty }
+ end
end
end
end
diff --git a/spec/requests/api/graphql/user/starred_projects_query_spec.rb b/spec/requests/api/graphql/user/starred_projects_query_spec.rb
index 8a1bd3d172f..b098058a735 100644
--- a/spec/requests/api/graphql/user/starred_projects_query_spec.rb
+++ b/spec/requests/api/graphql/user/starred_projects_query_spec.rb
@@ -70,4 +70,31 @@ RSpec.describe 'Getting starredProjects of the user' do
)
end
end
+
+ context 'the user has a private profile' do
+ before do
+ user.update!(private_profile: true)
+ post_graphql(query, current_user: current_user)
+ end
+
+ context 'the current user does not have access to view the private profile of the user' do
+ let(:current_user) { create(:user) }
+
+ it 'finds no projects' do
+ expect(starred_projects).to be_empty
+ end
+ end
+
+ context 'the current user has access to view the private profile of the user' do
+ let(:current_user) { create(:admin) }
+
+ it 'finds all projects starred by the user, which the current user has access to' do
+ expect(starred_projects).to contain_exactly(
+ a_hash_including('id' => global_id_of(project_a)),
+ a_hash_including('id' => global_id_of(project_b)),
+ a_hash_including('id' => global_id_of(project_c))
+ )
+ end
+ end
+ end
end
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index 4a792fc218d..234ac1778fd 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -1255,13 +1255,46 @@ RSpec.describe API::Projects do
expect(json_response['message']).to eq('404 User Not Found')
end
- it 'returns projects filtered by user' do
- get api("/users/#{user3.id}/starred_projects/", user)
+ context 'with a public profile' do
+ it 'returns projects filtered by user' do
+ get api("/users/#{user3.id}/starred_projects/", user)
- expect(response).to have_gitlab_http_status(:ok)
- expect(response).to include_pagination_headers
- expect(json_response).to be_an Array
- expect(json_response.map { |project| project['id'] }).to contain_exactly(project.id, project2.id, project3.id)
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to include_pagination_headers
+ expect(json_response).to be_an Array
+ expect(json_response.map { |project| project['id'] })
+ .to contain_exactly(project.id, project2.id, project3.id)
+ end
+ end
+
+ context 'with a private profile' do
+ before do
+ user3.update!(private_profile: true)
+ user3.reload
+ end
+
+ context 'user does not have access to view the private profile' do
+ it 'returns no projects' do
+ get api("/users/#{user3.id}/starred_projects/", user)
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to include_pagination_headers
+ expect(json_response).to be_an Array
+ expect(json_response).to be_empty
+ end
+ end
+
+ context 'user has access to view the private profile' do
+ it 'returns projects filtered by user' do
+ get api("/users/#{user3.id}/starred_projects/", admin)
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to include_pagination_headers
+ expect(json_response).to be_an Array
+ expect(json_response.map { |project| project['id'] })
+ .to contain_exactly(project.id, project2.id, project3.id)
+ end
+ end
end
end
diff --git a/spec/validators/zoom_url_validator_spec.rb b/spec/validators/zoom_url_validator_spec.rb
new file mode 100644
index 00000000000..7d5c94bc249
--- /dev/null
+++ b/spec/validators/zoom_url_validator_spec.rb
@@ -0,0 +1,36 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe ZoomUrlValidator do
+ let(:zoom_meeting) { build(:zoom_meeting) }
+
+ describe 'validations' do
+ context 'when zoom link starts with https' do
+ it 'passes validation' do
+ zoom_meeting.url = 'https://zoom.us/j/123456789'
+
+ expect(zoom_meeting.valid?).to eq(true)
+ expect(zoom_meeting.errors).to be_empty
+ end
+ end
+
+ shared_examples 'zoom link does not start with https' do |url|
+ it 'fails validation' do
+ zoom_meeting.url = url
+ expect(zoom_meeting.valid?).to eq(false)
+
+ expect(zoom_meeting.errors).to be_present
+ expect(zoom_meeting.errors.first[1]).to eq 'must contain one valid Zoom URL'
+ end
+ end
+
+ context 'when zoom link does not start with https' do
+ include_examples 'zoom link does not start with https', 'http://zoom.us/j/123456789'
+
+ context 'when zoom link does not start with a scheme' do
+ include_examples 'zoom link does not start with https', 'testinghttp://zoom.us/j/123456789'
+ end
+ end
+ end
+end
diff --git a/vendor/gitignore/C++.gitignore b/vendor/gitignore/C++.gitignore
index 259148fa18f..259148fa18f 100644..100755
--- a/vendor/gitignore/C++.gitignore
+++ b/vendor/gitignore/C++.gitignore
diff --git a/vendor/gitignore/Java.gitignore b/vendor/gitignore/Java.gitignore
index a1c2a238a96..a1c2a238a96 100644..100755
--- a/vendor/gitignore/Java.gitignore
+++ b/vendor/gitignore/Java.gitignore