summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Release Tools Bot <delivery-team+release-tools@gitlab.com>2022-06-01 17:33:49 +0000
committerGitLab Release Tools Bot <delivery-team+release-tools@gitlab.com>2022-06-01 17:33:49 +0000
commit9a8ae3b4e90e56f71bb770463b943512efdcd1d1 (patch)
tree4a791b23a3b7085fa89d2dc300ef1cf0228f01a5
parent8a186dedfc1da12270ea77f2673b59fa08f770c1 (diff)
parent39b217000ded81fdbd7ff509dd27895d1952ffc1 (diff)
downloadgitlab-ce-9a8ae3b4e90e56f71bb770463b943512efdcd1d1.tar.gz
Merge remote-tracking branch 'dev/15-0-stable' into 15-0-stable
-rw-r--r--CHANGELOG.md13
-rw-r--r--GITALY_SERVER_VERSION2
-rw-r--r--VERSION2
-rw-r--r--app/assets/javascripts/gfm_auto_complete.js2
-rw-r--r--app/controllers/groups/application_controller.rb6
-rw-r--r--app/controllers/groups/group_members_controller.rb1
-rw-r--r--app/policies/ci/build_policy.rb2
-rw-r--r--app/policies/group_policy.rb9
-rw-r--r--app/policies/project_policy.rb4
-rw-r--r--app/services/ci/pipeline_trigger_service.rb1
-rw-r--r--app/services/members/import_project_team_service.rb2
-rw-r--r--doc/api/scim.md14
-rw-r--r--doc/user/group/subgroups/index.md3
-rw-r--r--lib/api/helpers/members_helpers.rb4
-rw-r--r--lib/api/members.rb8
-rw-r--r--spec/controllers/projects/jobs_controller_spec.rb8
-rw-r--r--spec/features/security/group/private_access_spec.rb2
-rw-r--r--spec/frontend/gfm_auto_complete_spec.js15
-rw-r--r--spec/policies/ci/build_policy_spec.rb48
-rw-r--r--spec/policies/project_policy_spec.rb30
-rw-r--r--spec/requests/api/members_spec.rb15
-rw-r--r--spec/services/ci/pipeline_trigger_service_spec.rb9
22 files changed, 183 insertions, 17 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index dfa7003cf2f..97042e1c1b3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,19 @@
documentation](doc/development/changelog.md) for instructions on adding your own
entry.
+## 15.0.1 (2022-06-01)
+
+### Security (8 changes)
+
+- [Fix IP restrictions not applying to deploy tokens](gitlab-org/security/gitlab@3af76bc31e2d141e2262d65eb08fcab7f34844bf) ([merge request](gitlab-org/security/gitlab!2474))
+- [Trigger token should respect group IP restrictions](gitlab-org/security/gitlab@f9ba81383a97b014be3085524def6f01120d9e3e) ([merge request](gitlab-org/security/gitlab!2476))
+- [Fix content injection in Jira issue title](gitlab-org/security/gitlab@d0c449079ce8d680f3390e21ed08aced1bfaf17b) ([merge request](gitlab-org/security/gitlab!2463))
+- [Escape contact details correctly in quick actions](gitlab-org/security/gitlab@cbafec91630b1309354784040c572ba1f844f794) ([merge request](gitlab-org/security/gitlab!2459))
+- [Subgroup member can list members of parent group](gitlab-org/security/gitlab@93583b7fe97f59f746f719742230eadbfbdf5ce3) ([merge request](gitlab-org/security/gitlab!2479))
+- [Do not allow project member import when membership is locked](gitlab-org/security/gitlab@b6ca02c9bac46ebcc822bbeee9e75aaf184d9996) ([merge request](gitlab-org/security/gitlab!2457))
+- [Disable changing user attributes when updating SCIM provisioned user](gitlab-org/security/gitlab@660a0021e3df45893c1f4317d3aa86dd276c6071) ([merge request](gitlab-org/security/gitlab!2453))
+- [Allow only job owner to run interactive terminal](gitlab-org/security/gitlab@917c3e4e314b02da33d8b9aea07179bc74833053) ([merge request](gitlab-org/security/gitlab!2451))
+
## 15.0.0 (2022-05-20)
### Added (147 changes)
diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION
index 791fc94d82f..9dc738e691e 100644
--- a/GITALY_SERVER_VERSION
+++ b/GITALY_SERVER_VERSION
@@ -1 +1 @@
-15.0.0 \ No newline at end of file
+15.0.1 \ No newline at end of file
diff --git a/VERSION b/VERSION
index 791fc94d82f..9dc738e691e 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-15.0.0 \ No newline at end of file
+15.0.1 \ No newline at end of file
diff --git a/app/assets/javascripts/gfm_auto_complete.js b/app/assets/javascripts/gfm_auto_complete.js
index b1af1ad797b..146255df31f 100644
--- a/app/assets/javascripts/gfm_auto_complete.js
+++ b/app/assets/javascripts/gfm_auto_complete.js
@@ -955,7 +955,7 @@ GfmAutoComplete.Milestones = {
};
GfmAutoComplete.Contacts = {
templateFunction({ email, firstName, lastName }) {
- return `<li><small>${firstName} ${lastName}</small> ${escape(email)}</li>`;
+ return `<li><small>${escape(firstName)} ${escape(lastName)}</small> ${escape(email)}</li>`;
},
};
diff --git a/app/controllers/groups/application_controller.rb b/app/controllers/groups/application_controller.rb
index bf72ade32d0..aec3247f4b2 100644
--- a/app/controllers/groups/application_controller.rb
+++ b/app/controllers/groups/application_controller.rb
@@ -67,6 +67,12 @@ class Groups::ApplicationController < ApplicationController
end
end
+ def authorize_read_group_member!
+ unless can?(current_user, :read_group_member, group)
+ render_403
+ end
+ end
+
def build_canonical_path(group)
params[:group_id] = group.to_param
diff --git a/app/controllers/groups/group_members_controller.rb b/app/controllers/groups/group_members_controller.rb
index d325bb402e7..b95d8c87a4a 100644
--- a/app/controllers/groups/group_members_controller.rb
+++ b/app/controllers/groups/group_members_controller.rb
@@ -14,6 +14,7 @@ class Groups::GroupMembersController < Groups::ApplicationController
# Authorize
before_action :authorize_admin_group_member!, except: admin_not_required_endpoints
+ before_action :authorize_read_group_member!, only: :index
skip_before_action :check_two_factor_requirement, only: :leave
skip_cross_project_access_check :index, :update, :destroy, :request_access,
diff --git a/app/policies/ci/build_policy.rb b/app/policies/ci/build_policy.rb
index 6162a31c118..f377ff85b5e 100644
--- a/app/policies/ci/build_policy.rb
+++ b/app/policies/ci/build_policy.rb
@@ -84,7 +84,7 @@ module Ci
enable :update_commit_status
end
- rule { can?(:update_build) & terminal }.enable :create_build_terminal
+ rule { can?(:update_build) & terminal & owner_of_job }.enable :create_build_terminal
rule { can?(:update_build) }.enable :play_job
diff --git a/app/policies/group_policy.rb b/app/policies/group_policy.rb
index a4600c720a3..9aae295aea7 100644
--- a/app/policies/group_policy.rb
+++ b/app/policies/group_policy.rb
@@ -23,6 +23,7 @@ class GroupPolicy < Namespaces::GroupProjectNamespaceSharedPolicy
condition(:parent_share_with_group_locked, scope: :subject) { @subject.parent&.share_with_group_lock? }
condition(:can_change_parent_share_with_group_lock) { can?(:change_share_with_group_lock, @subject.parent) }
condition(:migration_bot, scope: :user) { @user.migration_bot? }
+ condition(:can_read_group_member) { can_read_group_member? }
desc "User is a project bot"
condition(:project_bot) { user.project_bot? && access_level >= GroupMember::GUEST }
@@ -128,6 +129,10 @@ class GroupPolicy < Namespaces::GroupProjectNamespaceSharedPolicy
rule { ~public_group & ~has_access }.prevent :read_counts
+ rule { ~can_read_group_member }.policy do
+ prevent :read_group_member
+ end
+
rule { ~can?(:read_group) }.policy do
prevent :read_design_activity
end
@@ -316,6 +321,10 @@ class GroupPolicy < Namespaces::GroupProjectNamespaceSharedPolicy
true
end
+ def can_read_group_member?
+ !(@subject.private? && access_level == GroupMember::NO_ACCESS)
+ end
+
def resource_access_token_creation_allowed?
resource_access_token_feature_available? && group.root_ancestor.namespace_settings.resource_access_token_creation_allowed?
end
diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb
index 60519dc346b..7c439fe8b29 100644
--- a/app/policies/project_policy.rb
+++ b/app/policies/project_policy.rb
@@ -748,6 +748,10 @@ class ProjectPolicy < BasePolicy
prevent :register_project_runners
end
+ rule { can?(:admin_project_member) }.policy do
+ enable :import_project_members_from_another_project
+ end
+
private
def user_is_user?
diff --git a/app/services/ci/pipeline_trigger_service.rb b/app/services/ci/pipeline_trigger_service.rb
index 06eb1aee8e6..39ac9bf33e9 100644
--- a/app/services/ci/pipeline_trigger_service.rb
+++ b/app/services/ci/pipeline_trigger_service.rb
@@ -27,6 +27,7 @@ module Ci
def create_pipeline_from_trigger(trigger)
# this check is to not leak the presence of the project if user cannot read it
return unless trigger.project == project
+ return unless can?(trigger.owner, :read_project, project)
response = Ci::CreatePipelineService
.new(project, trigger.owner, ref: params[:ref], variables_attributes: variables)
diff --git a/app/services/members/import_project_team_service.rb b/app/services/members/import_project_team_service.rb
index 5f4d5414cfa..6efd65e2575 100644
--- a/app/services/members/import_project_team_service.rb
+++ b/app/services/members/import_project_team_service.rb
@@ -29,7 +29,7 @@ module Members
def import_project_team
return false unless target_project.present? && source_project.present? && current_user.present?
return false unless can?(current_user, :read_project_member, source_project)
- return false unless can?(current_user, :admin_project_member, target_project)
+ return false unless can?(current_user, :import_project_members_from_another_project, target_project)
target_project.team.import(source_project, current_user)
end
diff --git a/doc/api/scim.md b/doc/api/scim.md
index ab3a181f5be..9c88997b94c 100644
--- a/doc/api/scim.md
+++ b/doc/api/scim.md
@@ -170,13 +170,13 @@ Returns a `201` status code if successful.
Fields that can be updated are:
-| SCIM/IdP field | GitLab field |
-|:---------------------------------|:---------------------------------------|
-| `id/externalId` | `extern_uid` |
-| `name.formatted` | `name` |
-| `emails\[type eq "work"\].value` | `email` |
-| `active` | Identity removal if `active` = `false` |
-| `userName` | `username` |
+| SCIM/IdP field | GitLab field |
+|:---------------------------------|:-----------------------------------------------------------------------------|
+| `id/externalId` | `extern_uid` |
+| `name.formatted` | `name` ([Removed](https://gitlab.com/gitlab-org/gitlab/-/issues/363058)) |
+| `emails\[type eq "work"\].value` | `email` ([Removed](https://gitlab.com/gitlab-org/gitlab/-/issues/363058)) |
+| `active` | Identity removal if `active` = `false` |
+| `userName` | `username` ([Removed](https://gitlab.com/gitlab-org/gitlab/-/issues/363058)) |
```plaintext
PATCH /api/scim/v2/groups/:group_path/Users/:id
diff --git a/doc/user/group/subgroups/index.md b/doc/user/group/subgroups/index.md
index 2cddbaa9723..5f3c859d15a 100644
--- a/doc/user/group/subgroups/index.md
+++ b/doc/user/group/subgroups/index.md
@@ -93,6 +93,9 @@ For more information, view the [permissions table](../../permissions.md#group-me
## Subgroup membership
+NOTE:
+There is a bug that causes some pages in the parent group to be accessible by subgroup members. For more details, see [this issue](https://gitlab.com/gitlab-org/gitlab/-/issues/340421).
+
When you add a member to a group, that member is also added to all subgroups. The user's permissions are inherited from
the group's parent.
diff --git a/lib/api/helpers/members_helpers.rb b/lib/api/helpers/members_helpers.rb
index c91e153c7b9..6a3cf5c87ae 100644
--- a/lib/api/helpers/members_helpers.rb
+++ b/lib/api/helpers/members_helpers.rb
@@ -15,6 +15,10 @@ module API
public_send("find_#{source_type}!", id) # rubocop:disable GitlabSecurity/PublicSend
end
+ def authorize_read_source_member!(source_type, source)
+ authorize! :"read_#{source_type}_member", source
+ end
+
def authorize_admin_source!(source_type, source)
authorize! :"admin_#{source_type}", source
end
diff --git a/lib/api/members.rb b/lib/api/members.rb
index e2045c6def7..b94f68f60b5 100644
--- a/lib/api/members.rb
+++ b/lib/api/members.rb
@@ -32,6 +32,8 @@ module API
get ":id/members", feature_category: feature_category do
source = find_source(source_type, params[:id])
+ authorize_read_source_member!(source_type, source)
+
members = paginate(retrieve_members(source, params: params))
present_members members
@@ -51,6 +53,8 @@ module API
get ":id/members/all", feature_category: feature_category do
source = find_source(source_type, params[:id])
+ authorize_read_source_member!(source_type, source)
+
members = paginate(retrieve_members(source, params: params, deep: true))
present_members members
@@ -66,6 +70,8 @@ module API
get ":id/members/:user_id", feature_category: feature_category do
source = find_source(source_type, params[:id])
+ authorize_read_source_member!(source_type, source)
+
members = source_members(source)
member = members.find_by!(user_id: params[:user_id])
@@ -83,6 +89,8 @@ module API
get ":id/members/all/:user_id", feature_category: feature_category do
source = find_source(source_type, params[:id])
+ authorize_read_source_member!(source_type, source)
+
members = find_all_members(source)
member = members.find_by!(user_id: params[:user_id])
diff --git a/spec/controllers/projects/jobs_controller_spec.rb b/spec/controllers/projects/jobs_controller_spec.rb
index 162c36f5069..f0fbbb65fa5 100644
--- a/spec/controllers/projects/jobs_controller_spec.rb
+++ b/spec/controllers/projects/jobs_controller_spec.rb
@@ -183,7 +183,7 @@ RSpec.describe Projects::JobsController, :clean_gitlab_redis_shared_state do
end
context 'with web terminal' do
- let(:job) { create(:ci_build, :running, :with_runner_session, pipeline: pipeline) }
+ let(:job) { create(:ci_build, :running, :with_runner_session, pipeline: pipeline, user: user) }
it 'exposes the terminal path' do
expect(response).to have_gitlab_http_status(:ok)
@@ -1285,7 +1285,7 @@ RSpec.describe Projects::JobsController, :clean_gitlab_redis_shared_state do
context 'when job exists' do
context 'and it has a terminal' do
- let!(:job) { create(:ci_build, :running, :with_runner_session, pipeline: pipeline) }
+ let!(:job) { create(:ci_build, :running, :with_runner_session, pipeline: pipeline, user: user) }
it 'has a job' do
get_terminal(id: job.id)
@@ -1296,7 +1296,7 @@ RSpec.describe Projects::JobsController, :clean_gitlab_redis_shared_state do
end
context 'and does not have a terminal' do
- let!(:job) { create(:ci_build, :running, pipeline: pipeline) }
+ let!(:job) { create(:ci_build, :running, pipeline: pipeline, user: user) }
it 'returns not_found' do
get_terminal(id: job.id)
@@ -1325,7 +1325,7 @@ RSpec.describe Projects::JobsController, :clean_gitlab_redis_shared_state do
end
describe 'GET #terminal_websocket_authorize' do
- let!(:job) { create(:ci_build, :running, :with_runner_session, pipeline: pipeline) }
+ let!(:job) { create(:ci_build, :running, :with_runner_session, pipeline: pipeline, user: user) }
before do
project.add_developer(user)
diff --git a/spec/features/security/group/private_access_spec.rb b/spec/features/security/group/private_access_spec.rb
index fc1fb3e3848..f733145b5e3 100644
--- a/spec/features/security/group/private_access_spec.rb
+++ b/spec/features/security/group/private_access_spec.rb
@@ -97,7 +97,7 @@ RSpec.describe 'Private Group access' do
it { is_expected.to be_allowed_for(:developer).of(group) }
it { is_expected.to be_allowed_for(:reporter).of(group) }
it { is_expected.to be_allowed_for(:guest).of(group) }
- it { is_expected.to be_allowed_for(project_guest) }
+ it { is_expected.to be_denied_for(project_guest) }
it { is_expected.to be_denied_for(:user) }
it { is_expected.to be_denied_for(:external) }
it { is_expected.to be_denied_for(:visitor) }
diff --git a/spec/frontend/gfm_auto_complete_spec.js b/spec/frontend/gfm_auto_complete_spec.js
index aa98b2774ea..552377e3381 100644
--- a/spec/frontend/gfm_auto_complete_spec.js
+++ b/spec/frontend/gfm_auto_complete_spec.js
@@ -868,4 +868,19 @@ describe('GfmAutoComplete', () => {
);
});
});
+
+ describe('Contacts', () => {
+ it('escapes name and email correct', () => {
+ const xssPayload = '<script>alert(1)</script>';
+ const escapedPayload = '&lt;script&gt;alert(1)&lt;/script&gt;';
+
+ expect(
+ GfmAutoComplete.Contacts.templateFunction({
+ email: xssPayload,
+ firstName: xssPayload,
+ lastName: xssPayload,
+ }),
+ ).toBe(`<li><small>${escapedPayload} ${escapedPayload}</small> ${escapedPayload}</li>`);
+ });
+ });
});
diff --git a/spec/policies/ci/build_policy_spec.rb b/spec/policies/ci/build_policy_spec.rb
index 1ec749fb394..fee4d76ca8f 100644
--- a/spec/policies/ci/build_policy_spec.rb
+++ b/spec/policies/ci/build_policy_spec.rb
@@ -405,4 +405,52 @@ RSpec.describe Ci::BuildPolicy do
end
end
end
+
+ describe 'ability :create_build_terminal' do
+ let(:project) { create(:project, :private) }
+
+ subject { described_class.new(user, build) }
+
+ context 'when user can update_build' do
+ before do
+ project.add_maintainer(user)
+ end
+
+ context 'when job has terminal' do
+ before do
+ allow(build).to receive(:has_terminal?).and_return(true)
+ end
+
+ context 'when current user is the job owner' do
+ before do
+ build.update!(user: user)
+ end
+
+ it { expect_allowed(:create_build_terminal) }
+ end
+
+ context 'when current user is not the job owner' do
+ it { expect_disallowed(:create_build_terminal) }
+ end
+ end
+
+ context 'when job does not have terminal' do
+ before do
+ allow(build).to receive(:has_terminal?).and_return(false)
+ build.update!(user: user)
+ end
+
+ it { expect_disallowed(:create_build_terminal) }
+ end
+ end
+
+ context 'when user cannot update build' do
+ before do
+ project.add_guest(user)
+ allow(build).to receive(:has_terminal?).and_return(true)
+ end
+
+ it { expect_disallowed(:create_build_terminal) }
+ end
+ end
end
diff --git a/spec/policies/project_policy_spec.rb b/spec/policies/project_policy_spec.rb
index ca4ca2eb7a0..b77ccb83509 100644
--- a/spec/policies/project_policy_spec.rb
+++ b/spec/policies/project_policy_spec.rb
@@ -396,6 +396,36 @@ RSpec.describe ProjectPolicy do
end
end
+ context 'importing members from another project' do
+ %w(maintainer owner).each do |role|
+ context "with #{role}" do
+ let(:current_user) { send(role) }
+
+ it { is_expected.to be_allowed(:import_project_members_from_another_project) }
+ end
+ end
+
+ %w(guest reporter developer anonymous).each do |role|
+ context "with #{role}" do
+ let(:current_user) { send(role) }
+
+ it { is_expected.to be_disallowed(:import_project_members_from_another_project) }
+ end
+ end
+
+ context 'with an admin' do
+ let(:current_user) { admin }
+
+ context 'when admin mode is enabled', :enable_admin_mode do
+ it { expect_allowed(:import_project_members_from_another_project) }
+ end
+
+ context 'when admin mode is disabled' do
+ it { expect_disallowed(:import_project_members_from_another_project) }
+ end
+ end
+ end
+
context 'reading usage quotas' do
%w(maintainer owner).each do |role|
context "with #{role}" do
diff --git a/spec/requests/api/members_spec.rb b/spec/requests/api/members_spec.rb
index 0db42e7439c..63ef8643088 100644
--- a/spec/requests/api/members_spec.rb
+++ b/spec/requests/api/members_spec.rb
@@ -184,6 +184,21 @@ RSpec.describe API::Members do
expect(json_response).to be_an Array
expect(json_response.map { |u| u['id'] }).to match_array [maintainer.id, developer.id, nested_user.id]
end
+
+ context 'with a subgroup' do
+ let(:group) { create(:group, :private)}
+ let(:subgroup) { create(:group, :private, parent: group)}
+ let(:project) { create(:project, group: subgroup) }
+
+ before do
+ subgroup.add_developer(developer)
+ end
+
+ it 'subgroup member cannot get parent group members list' do
+ get api("/groups/#{group.id}/members/all", developer)
+ expect(response).to have_gitlab_http_status(:forbidden)
+ end
+ end
end
shared_examples 'GET /:source_type/:id/members/(all/):user_id' do |source_type, all|
diff --git a/spec/services/ci/pipeline_trigger_service_spec.rb b/spec/services/ci/pipeline_trigger_service_spec.rb
index a794dedc658..4b3e774ff3c 100644
--- a/spec/services/ci/pipeline_trigger_service_spec.rb
+++ b/spec/services/ci/pipeline_trigger_service_spec.rb
@@ -56,6 +56,15 @@ RSpec.describe Ci::PipelineTriggerService do
end
end
+ context 'when trigger owner does not have a permission to read a project' do
+ let(:params) { { token: trigger.token, ref: 'master', variables: nil } }
+ let(:trigger) { create(:ci_trigger, project: project, owner: create(:user)) }
+
+ it 'does nothing' do
+ expect { result }.not_to change { Ci::Pipeline.count }
+ end
+ end
+
context 'when params have an existing trigger token' do
context 'when params have an existing ref' do
let(:params) { { token: trigger.token, ref: 'master', variables: nil } }