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>2022-07-27 22:03:37 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2022-07-27 22:03:37 +0300
commit42c5548470596a37ed1a071e3bb72af2c9c35c0e (patch)
tree460370c3d31272184a5d7f74a5a52e8a25297186
parente154e2735d9832994b0499d69732966abe2e3f5d (diff)
Add latest changes from gitlab-org/security/gitlab@15-0-stable-ee
-rw-r--r--GITLAB_PAGES_VERSION2
-rw-r--r--app/controllers/autocomplete_controller.rb5
-rw-r--r--app/finders/issuable_finder.rb20
-rw-r--r--app/models/hooks/web_hook_log.rb7
-rw-r--r--app/models/project.rb1
-rw-r--r--app/models/repository.rb10
-rw-r--r--app/models/snippet.rb2
-rw-r--r--app/serializers/build_details_entity.rb2
-rw-r--r--app/services/concerns/update_visibility_level.rb2
-rw-r--r--config/feature_flags/development/ci_yaml_limit_size.yml8
-rw-r--r--config/gitlab_loose_foreign_keys.yml3
-rw-r--r--db/migrate/20220329130330_add_author_to_ci_subscriptions_projects.rb20
-rw-r--r--db/schema_migrations/202203291303301
-rw-r--r--db/structure.sql5
-rw-r--r--doc/administration/instance_limits.md6
-rw-r--r--lib/gitlab/config/loader/yaml.rb2
-rw-r--r--lib/gitlab/visibility_level.rb6
-rwxr-xr-xscripts/changed-feature-flags4
-rw-r--r--spec/controllers/autocomplete_controller_spec.rb73
-rw-r--r--spec/finders/issues_finder_spec.rb75
-rw-r--r--spec/graphql/resolvers/issues_resolver_spec.rb11
-rw-r--r--spec/lib/gitlab/config/loader/yaml_spec.rb6
-rw-r--r--spec/lib/gitlab/import_export/all_models.yml1
-rw-r--r--spec/lib/gitlab/visibility_level_spec.rb44
-rw-r--r--spec/models/hooks/web_hook_log_spec.rb35
-rw-r--r--spec/models/project_spec.rb1
-rw-r--r--spec/models/repository_spec.rb58
-rw-r--r--spec/models/snippet_spec.rb2
-rw-r--r--spec/serializers/build_details_entity_spec.rb18
-rw-r--r--spec/services/groups/update_service_spec.rb63
-rw-r--r--spec/services/projects/update_service_spec.rb59
31 files changed, 444 insertions, 108 deletions
diff --git a/GITLAB_PAGES_VERSION b/GITLAB_PAGES_VERSION
index 79f82f6b8e0..69478d187bd 100644
--- a/GITLAB_PAGES_VERSION
+++ b/GITLAB_PAGES_VERSION
@@ -1 +1 @@
-1.58.0
+1.58.1
diff --git a/app/controllers/autocomplete_controller.rb b/app/controllers/autocomplete_controller.rb
index f84d2ed320d..22d7ccbd069 100644
--- a/app/controllers/autocomplete_controller.rb
+++ b/app/controllers/autocomplete_controller.rb
@@ -5,6 +5,7 @@ class AutocompleteController < ApplicationController
skip_before_action :authenticate_user!, only: [:users, :award_emojis, :merge_request_target_branches]
before_action :check_search_rate_limit!, only: [:users, :projects]
+ before_action :authorize_admin_project, only: :deploy_keys_with_owners
feature_category :users, [:users, :user]
feature_category :projects, [:projects]
@@ -67,6 +68,10 @@ class AutocompleteController < ApplicationController
private
+ def authorize_admin_project
+ render_403 unless Ability.allowed?(current_user, :admin_project, project)
+ end
+
def project
@project ||= Autocomplete::ProjectFinder
.new(current_user, params)
diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb
index fe07a52cbf0..6e927b661c6 100644
--- a/app/finders/issuable_finder.rb
+++ b/app/finders/issuable_finder.rb
@@ -480,10 +480,14 @@ class IssuableFinder
end
def by_crm_contact(items)
+ return items unless can_filter_by_crm_contact?
+
Issuables::CrmContactFilter.new(params: original_params).filter(items)
end
def by_crm_organization(items)
+ return items unless can_filter_by_crm_organization?
+
Issuables::CrmOrganizationFilter.new(params: original_params).filter(items)
end
@@ -496,4 +500,20 @@ class IssuableFinder
def feature_flag_scope
params.group || params.project
end
+
+ def can_filter_by_crm_contact?
+ current_user&.can?(:read_crm_contact, root_group)
+ end
+
+ def can_filter_by_crm_organization?
+ current_user&.can?(:read_crm_organization, root_group)
+ end
+
+ def root_group
+ strong_memoize(:root_group) do
+ base_group = params.group || params.project&.group
+
+ base_group&.root_ancestor
+ end
+ end
end
diff --git a/app/models/hooks/web_hook_log.rb b/app/models/hooks/web_hook_log.rb
index 8c0565e4a38..04d6d1ebd5c 100644
--- a/app/models/hooks/web_hook_log.rb
+++ b/app/models/hooks/web_hook_log.rb
@@ -20,6 +20,7 @@ class WebHookLog < ApplicationRecord
validates :web_hook, presence: true
before_save :obfuscate_basic_auth
+ before_save :redact_author_email
def self.recent
where('created_at >= ?', 2.days.ago.beginning_of_day)
@@ -39,4 +40,10 @@ class WebHookLog < ApplicationRecord
def obfuscate_basic_auth
self.url = safe_url
end
+
+ def redact_author_email
+ return unless self.request_data.dig('commit', 'author', 'email').present?
+
+ self.request_data['commit']['author']['email'] = _('[REDACTED]')
+ end
end
diff --git a/app/models/project.rb b/app/models/project.rb
index b66ec28b659..cf0dfc33490 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -2018,6 +2018,7 @@ class Project < ApplicationRecord
end
def after_import
+ repository.remove_prohibited_branches
repository.expire_content_cache
wiki.repository.expire_content_cache
diff --git a/app/models/repository.rb b/app/models/repository.rb
index dc0b5b54fb0..d10f4089061 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -1167,6 +1167,16 @@ class Repository
@cache ||= Gitlab::RepositoryCache.new(self)
end
+ def remove_prohibited_branches
+ return unless exists?
+
+ prohibited_branches = raw_repository.branch_names.select { |name| name.match(/\A\h{40}\z/) }
+
+ return if prohibited_branches.blank?
+
+ prohibited_branches.each { |name| raw_repository.delete_branch(name) }
+ end
+
private
# TODO Genericize finder, later split this on finders by Ref or Oid
diff --git a/app/models/snippet.rb b/app/models/snippet.rb
index cf4b83d44c2..2001577c88b 100644
--- a/app/models/snippet.rb
+++ b/app/models/snippet.rb
@@ -70,8 +70,6 @@ class Snippet < ApplicationRecord
},
if: :content_changed?
- validates :visibility_level, inclusion: { in: Gitlab::VisibilityLevel.values }
-
after_create :create_statistics
# Scopes
diff --git a/app/serializers/build_details_entity.rb b/app/serializers/build_details_entity.rb
index 5f72259f34a..dc7b5e95361 100644
--- a/app/serializers/build_details_entity.rb
+++ b/app/serializers/build_details_entity.rb
@@ -151,7 +151,7 @@ class BuildDetailsEntity < Ci::JobEntity
# We do not return the invalid_dependencies for all scenarios see https://gitlab.com/gitlab-org/gitlab/-/issues/287772#note_914406387
punctuation = invalid_dependencies.empty? ? '.' : ': '
_("This job could not start because it could not retrieve the needed artifacts%{punctuation}%{invalid_dependencies}") %
- { invalid_dependencies: invalid_dependencies, punctuation: punctuation }
+ { invalid_dependencies: html_escape(invalid_dependencies), punctuation: punctuation }
end
def help_message(docs_url)
diff --git a/app/services/concerns/update_visibility_level.rb b/app/services/concerns/update_visibility_level.rb
index 4cd14a2fb53..debcff0295c 100644
--- a/app/services/concerns/update_visibility_level.rb
+++ b/app/services/concerns/update_visibility_level.rb
@@ -5,7 +5,7 @@ module UpdateVisibilityLevel
def valid_visibility_level_change?(target, new_visibility)
return true unless new_visibility
- new_visibility_level = Gitlab::VisibilityLevel.level_value(new_visibility)
+ new_visibility_level = Gitlab::VisibilityLevel.level_value(new_visibility, fallback_value: nil)
if new_visibility_level != target.visibility_level_value
unless can?(current_user, :change_visibility_level, target) &&
diff --git a/config/feature_flags/development/ci_yaml_limit_size.yml b/config/feature_flags/development/ci_yaml_limit_size.yml
deleted file mode 100644
index 222dc409c45..00000000000
--- a/config/feature_flags/development/ci_yaml_limit_size.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: ci_yaml_limit_size
-introduced_by_url: https://dev.gitlab.org/gitlab/gitlabhq/-/merge_requests/3126
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/29875
-milestone: '12.0'
-type: development
-group: group::pipeline authoring
-default_enabled: true
diff --git a/config/gitlab_loose_foreign_keys.yml b/config/gitlab_loose_foreign_keys.yml
index ae6c2bde081..24166535ca8 100644
--- a/config/gitlab_loose_foreign_keys.yml
+++ b/config/gitlab_loose_foreign_keys.yml
@@ -144,6 +144,9 @@ ci_subscriptions_projects:
- table: projects
column: upstream_project_id
on_delete: async_delete
+ - table: users
+ column: author_id
+ on_delete: async_delete
ci_triggers:
- table: users
column: owner_id
diff --git a/db/migrate/20220329130330_add_author_to_ci_subscriptions_projects.rb b/db/migrate/20220329130330_add_author_to_ci_subscriptions_projects.rb
new file mode 100644
index 00000000000..b1d0ac64d42
--- /dev/null
+++ b/db/migrate/20220329130330_add_author_to_ci_subscriptions_projects.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+class AddAuthorToCiSubscriptionsProjects < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ INDEX_NAME = 'index_ci_subscriptions_projects_author_id'
+
+ def up
+ unless column_exists?(:ci_subscriptions_projects, :author_id)
+ add_column :ci_subscriptions_projects, :author_id, :bigint
+ end
+
+ add_concurrent_index :ci_subscriptions_projects, :author_id, name: INDEX_NAME
+ end
+
+ def down
+ remove_concurrent_index_by_name :ci_subscriptions_projects, INDEX_NAME
+ remove_column :ci_subscriptions_projects, :author_id
+ end
+end
diff --git a/db/schema_migrations/20220329130330 b/db/schema_migrations/20220329130330
new file mode 100644
index 00000000000..367d43a89a2
--- /dev/null
+++ b/db/schema_migrations/20220329130330
@@ -0,0 +1 @@
+8726707f9f4bb8d256886c592b6a11ba8487de24f5340c837800f5ce0c32df9d \ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index f5d926d9ba7..0ebc4e4e587 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -13162,7 +13162,8 @@ ALTER SEQUENCE ci_stages_id_seq OWNED BY ci_stages.id;
CREATE TABLE ci_subscriptions_projects (
id bigint NOT NULL,
downstream_project_id bigint NOT NULL,
- upstream_project_id bigint NOT NULL
+ upstream_project_id bigint NOT NULL,
+ author_id bigint
);
CREATE SEQUENCE ci_subscriptions_projects_id_seq
@@ -27304,6 +27305,8 @@ CREATE INDEX index_ci_stages_on_pipeline_id_and_position ON ci_stages USING btre
CREATE INDEX index_ci_stages_on_project_id ON ci_stages USING btree (project_id);
+CREATE INDEX index_ci_subscriptions_projects_author_id ON ci_subscriptions_projects USING btree (author_id);
+
CREATE INDEX index_ci_subscriptions_projects_on_upstream_project_id ON ci_subscriptions_projects USING btree (upstream_project_id);
CREATE UNIQUE INDEX index_ci_subscriptions_projects_unique_subscription ON ci_subscriptions_projects USING btree (downstream_project_id, upstream_project_id);
diff --git a/doc/administration/instance_limits.md b/doc/administration/instance_limits.md
index 8d3fec3b19e..4353a5f36b0 100644
--- a/doc/administration/instance_limits.md
+++ b/doc/administration/instance_limits.md
@@ -686,12 +686,6 @@ You can change these limits in the [GitLab Rails console](operations/rails_conso
ApplicationSetting.update!(max_yaml_depth: 125)
```
-To disable this limitation entirely, disable the feature flag in the console:
-
-```ruby
-Feature.disable(:ci_yaml_limit_size)
-```
-
### Limit dotenv variables
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/321552) in GitLab 14.5.
diff --git a/lib/gitlab/config/loader/yaml.rb b/lib/gitlab/config/loader/yaml.rb
index 0559c85647d..7b87b5b8f97 100644
--- a/lib/gitlab/config/loader/yaml.rb
+++ b/lib/gitlab/config/loader/yaml.rb
@@ -41,8 +41,6 @@ module Gitlab
end
def too_big?
- return false unless Feature.enabled?(:ci_yaml_limit_size)
-
!deep_size.valid?
end
diff --git a/lib/gitlab/visibility_level.rb b/lib/gitlab/visibility_level.rb
index d378e558b8a..049e3befe64 100644
--- a/lib/gitlab/visibility_level.rb
+++ b/lib/gitlab/visibility_level.rb
@@ -18,6 +18,8 @@ module Gitlab
scope :public_to_user, -> (user = nil) do
where(visibility_level: VisibilityLevel.levels_for_user(user))
end
+
+ alias_method :visibility_level=, :visibility=
end
PRIVATE = 0 unless const_defined?(:PRIVATE)
@@ -108,10 +110,10 @@ module Gitlab
options.key(level.to_i) || s_('VisibilityLevel|Unknown')
end
- def level_value(level)
+ def level_value(level, fallback_value: PRIVATE)
return level.to_i if level.to_i.to_s == level.to_s && string_options.key(level.to_i)
- string_options[level] || PRIVATE
+ string_options[level] || fallback_value
end
def string_level(level)
diff --git a/scripts/changed-feature-flags b/scripts/changed-feature-flags
index 86214e86788..ded6156bfa8 100755
--- a/scripts/changed-feature-flags
+++ b/scripts/changed-feature-flags
@@ -10,8 +10,8 @@ require_relative 'api/default_options'
# Each desired feature flag state is specified as 'feature-flag=state'. This allows us to run package-and-qa with the
# feature flag set to the desired state.
#
-# For example, if the specified files included `config/feature_flags/development/ci_yaml_limit_size.yml` and the desired
-# state as specified by the second argument was enabled, the value returned would be `ci_yaml_limit_size=enabled`
+# For example, if the specified files included `config/feature_flags/development/ci_awesome_feature.yml` and the desired
+# state as specified by the second argument was enabled, the value returned would be `ci_awesome_feature=enabled`
class GetFeatureFlagsFromFiles
def initialize(options)
diff --git a/spec/controllers/autocomplete_controller_spec.rb b/spec/controllers/autocomplete_controller_spec.rb
index 0a809e80fcd..1df685e3e5a 100644
--- a/spec/controllers/autocomplete_controller_spec.rb
+++ b/spec/controllers/autocomplete_controller_spec.rb
@@ -378,61 +378,72 @@ RSpec.describe AutocompleteController do
end
context 'GET deploy_keys_with_owners' do
- let!(:deploy_key) { create(:deploy_key, user: user) }
- let!(:deploy_keys_project) { create(:deploy_keys_project, :write_access, project: project, deploy_key: deploy_key) }
+ let_it_be(:public_project) { create(:project, :public) }
+ let_it_be(:user) { create(:user) }
+ let_it_be(:deploy_key) { create(:deploy_key, user: user) }
+ let_it_be(:deploy_keys_project) do
+ create(:deploy_keys_project, :write_access, project: public_project, deploy_key: deploy_key)
+ end
context 'unauthorized user' do
it 'returns a not found response' do
- get(:deploy_keys_with_owners, params: { project_id: project.id })
+ get(:deploy_keys_with_owners, params: { project_id: public_project.id })
expect(response).to have_gitlab_http_status(:redirect)
end
end
- context 'when the user who can read the project is logged in' do
+ context 'when the user is logged in' do
before do
sign_in(user)
end
- context 'and they cannot read the project' do
+ context 'with a non-existing project' do
it 'returns a not found response' do
- allow(Ability).to receive(:allowed?).and_call_original
- allow(Ability).to receive(:allowed?).with(user, :read_project, project).and_return(false)
-
- get(:deploy_keys_with_owners, params: { project_id: project.id })
+ get(:deploy_keys_with_owners, params: { project_id: 9999 })
expect(response).to have_gitlab_http_status(:not_found)
end
end
- it 'renders the deploy key in a json payload, with its owner' do
- get(:deploy_keys_with_owners, params: { project_id: project.id })
+ context 'with an existing project' do
+ context 'when user cannot admin project' do
+ it 'returns a forbidden response' do
+ get(:deploy_keys_with_owners, params: { project_id: public_project.id })
- expect(json_response.count).to eq(1)
- expect(json_response.first['title']).to eq(deploy_key.title)
- expect(json_response.first['owner']['id']).to eq(deploy_key.user.id)
- end
+ expect(response).to have_gitlab_http_status(:forbidden)
+ end
+ end
- context 'with an unknown project' do
- it 'returns a not found response' do
- get(:deploy_keys_with_owners, params: { project_id: 9999 })
+ context 'when user can admin project' do
+ before do
+ public_project.add_maintainer(user)
+ end
- expect(response).to have_gitlab_http_status(:not_found)
- end
- end
+ context 'and user can read owner of key' do
+ it 'renders the deploy keys in a json payload, with owner' do
+ get(:deploy_keys_with_owners, params: { project_id: public_project.id })
- context 'and the user cannot read the owner of the key' do
- before do
- allow(Ability).to receive(:allowed?).and_call_original
- allow(Ability).to receive(:allowed?).with(user, :read_user, deploy_key.user).and_return(false)
- end
+ expect(json_response.count).to eq(1)
+ expect(json_response.first['title']).to eq(deploy_key.title)
+ expect(json_response.first['owner']['id']).to eq(deploy_key.user.id)
+ end
+ end
+
+ context 'and user cannot read owner of key' do
+ before do
+ allow(Ability).to receive(:allowed?).and_call_original
+ allow(Ability).to receive(:allowed?).with(user, :read_user, deploy_key.user).and_return(false)
+ end
- it 'returns a payload without owner' do
- get(:deploy_keys_with_owners, params: { project_id: project.id })
+ it 'returns a payload without owner' do
+ get(:deploy_keys_with_owners, params: { project_id: public_project.id })
- expect(json_response.count).to eq(1)
- expect(json_response.first['title']).to eq(deploy_key.title)
- expect(json_response.first['owner']).to be_nil
+ expect(json_response.count).to eq(1)
+ expect(json_response.first['title']).to eq(deploy_key.title)
+ expect(json_response.first['owner']).to be_nil
+ end
+ end
end
end
end
diff --git a/spec/finders/issues_finder_spec.rb b/spec/finders/issues_finder_spec.rb
index 3f5a55410d2..be779129971 100644
--- a/spec/finders/issues_finder_spec.rb
+++ b/spec/finders/issues_finder_spec.rb
@@ -893,42 +893,65 @@ RSpec.describe IssuesFinder do
end
end
- context 'filtering by crm contact' do
- let_it_be(:contact1) { create(:contact, group: group) }
- let_it_be(:contact2) { create(:contact, group: group) }
+ context 'crm filtering' do
+ let_it_be(:root_group) { create(:group) }
+ let_it_be(:group) { create(:group, parent: root_group) }
+ let_it_be(:project_crm) { create(:project, :public, group: group) }
+ let_it_be(:organization) { create(:organization, group: root_group) }
+ let_it_be(:contact1) { create(:contact, group: root_group, organization: organization) }
+ let_it_be(:contact2) { create(:contact, group: root_group, organization: organization) }
- let_it_be(:contact1_issue1) { create(:issue, project: project1) }
- let_it_be(:contact1_issue2) { create(:issue, project: project1) }
- let_it_be(:contact2_issue1) { create(:issue, project: project1) }
+ let_it_be(:contact1_item1) { create(:issue, project: project_crm) }
+ let_it_be(:contact1_item2) { create(:issue, project: project_crm) }
+ let_it_be(:contact2_item1) { create(:issue, project: project_crm) }
+ let_it_be(:item_no_contact) { create(:issue, project: project_crm) }
- let(:params) { { crm_contact_id: contact1.id } }
+ let_it_be(:all_project_issues) do
+ [contact1_item1, contact1_item2, contact2_item1, item_no_contact]
+ end
- it 'returns for that contact' do
- create(:issue_customer_relations_contact, issue: contact1_issue1, contact: contact1)
- create(:issue_customer_relations_contact, issue: contact1_issue2, contact: contact1)
- create(:issue_customer_relations_contact, issue: contact2_issue1, contact: contact2)
+ before do
+ create(:crm_settings, group: root_group, enabled: true)
- expect(issues).to contain_exactly(contact1_issue1, contact1_issue2)
+ create(:issue_customer_relations_contact, issue: contact1_item1, contact: contact1)
+ create(:issue_customer_relations_contact, issue: contact1_item2, contact: contact1)
+ create(:issue_customer_relations_contact, issue: contact2_item1, contact: contact2)
end
- end
- context 'filtering by crm organization' do
- let_it_be(:organization) { create(:organization, group: group) }
- let_it_be(:contact1) { create(:contact, group: group, organization: organization) }
- let_it_be(:contact2) { create(:contact, group: group, organization: organization) }
+ context 'filtering by crm contact' do
+ let(:params) { { project_id: project_crm.id, crm_contact_id: contact1.id } }
- let_it_be(:contact1_issue1) { create(:issue, project: project1) }
- let_it_be(:contact1_issue2) { create(:issue, project: project1) }
- let_it_be(:contact2_issue1) { create(:issue, project: project1) }
+ context 'when the user can read crm contacts' do
+ it 'returns for that contact' do
+ root_group.add_reporter(user)
- let(:params) { { crm_organization_id: organization.id } }
+ expect(issues).to contain_exactly(contact1_item1, contact1_item2)
+ end
+ end
- it 'returns for that contact' do
- create(:issue_customer_relations_contact, issue: contact1_issue1, contact: contact1)
- create(:issue_customer_relations_contact, issue: contact1_issue2, contact: contact1)
- create(:issue_customer_relations_contact, issue: contact2_issue1, contact: contact2)
+ context 'when the user can not read crm contacts' do
+ it 'does not filter by contact' do
+ expect(issues).to match_array(all_project_issues)
+ end
+ end
+ end
- expect(issues).to contain_exactly(contact1_issue1, contact1_issue2, contact2_issue1)
+ context 'filtering by crm organization' do
+ let(:params) { { project_id: project_crm.id, crm_organization_id: organization.id } }
+
+ context 'when the user can read crm organization' do
+ it 'returns for that organization' do
+ root_group.add_reporter(user)
+
+ expect(issues).to contain_exactly(contact1_item1, contact1_item2, contact2_item1)
+ end
+ end
+
+ context 'when the user can not read crm organization' do
+ it 'does not filter by organization' do
+ expect(issues).to match_array(all_project_issues)
+ end
+ end
end
end
diff --git a/spec/graphql/resolvers/issues_resolver_spec.rb b/spec/graphql/resolvers/issues_resolver_spec.rb
index e6ec9d8c895..609484639cb 100644
--- a/spec/graphql/resolvers/issues_resolver_spec.rb
+++ b/spec/graphql/resolvers/issues_resolver_spec.rb
@@ -30,6 +30,9 @@ RSpec.describe Resolvers::IssuesResolver do
before_all do
project.add_developer(current_user)
project.add_reporter(reporter)
+
+ create(:crm_settings, group: group, enabled: true)
+
create(:label_link, label: label1, target: issue1)
create(:label_link, label: label1, target: issue2)
create(:label_link, label: label2, target: issue2)
@@ -399,6 +402,8 @@ RSpec.describe Resolvers::IssuesResolver do
let_it_be(:crm_issue3) { create(:issue, project: project) }
before_all do
+ group.add_developer(current_user)
+
create(:issue_customer_relations_contact, issue: crm_issue1, contact: contact1)
create(:issue_customer_relations_contact, issue: crm_issue2, contact: contact2)
create(:issue_customer_relations_contact, issue: crm_issue3, contact: contact3)
@@ -631,13 +636,13 @@ RSpec.describe Resolvers::IssuesResolver do
end
it 'finds a specific issue with iid', :request_store do
- result = batch_sync(max_queries: 7) { resolve_issues(iid: issue1.iid).to_a }
+ result = batch_sync(max_queries: 8) { resolve_issues(iid: issue1.iid).to_a }
expect(result).to contain_exactly(issue1)
end
it 'batches queries that only include IIDs', :request_store do
- result = batch_sync(max_queries: 7) do
+ result = batch_sync(max_queries: 8) do
[issue1, issue2]
.map { |issue| resolve_issues(iid: issue.iid.to_s) }
.flat_map(&:to_a)
@@ -647,7 +652,7 @@ RSpec.describe Resolvers::IssuesResolver do
end
it 'finds a specific issue with iids', :request_store do
- result = batch_sync(max_queries: 7) do
+ result = batch_sync(max_queries: 8) do
resolve_issues(iids: [issue1.iid]).to_a
end
diff --git a/spec/lib/gitlab/config/loader/yaml_spec.rb b/spec/lib/gitlab/config/loader/yaml_spec.rb
index 66ea931a42c..c7f84cd583c 100644
--- a/spec/lib/gitlab/config/loader/yaml_spec.rb
+++ b/spec/lib/gitlab/config/loader/yaml_spec.rb
@@ -120,12 +120,6 @@ RSpec.describe Gitlab::Config::Loader::Yaml do
it 'returns false' do
expect(loader).not_to be_valid
end
-
- it 'returns true if "ci_yaml_limit_size" feature flag is disabled' do
- stub_feature_flags(ci_yaml_limit_size: false)
-
- expect(loader).to be_valid
- end
end
describe '#load!' do
diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml
index 1546b6a26c8..a342d1f6c1b 100644
--- a/spec/lib/gitlab/import_export/all_models.yml
+++ b/spec/lib/gitlab/import_export/all_models.yml
@@ -565,7 +565,6 @@ project:
- remove_source_branch_after_merge
- deleting_user
- upstream_projects
-- downstream_projects
- upstream_project_subscriptions
- downstream_project_subscriptions
- service_desk_setting
diff --git a/spec/lib/gitlab/visibility_level_spec.rb b/spec/lib/gitlab/visibility_level_spec.rb
index 0d34d22cbbe..9555c0afba2 100644
--- a/spec/lib/gitlab/visibility_level_spec.rb
+++ b/spec/lib/gitlab/visibility_level_spec.rb
@@ -4,20 +4,52 @@ require 'spec_helper'
RSpec.describe Gitlab::VisibilityLevel do
describe '.level_value' do
- it 'converts "public" to integer value' do
- expect(described_class.level_value('public')).to eq(Gitlab::VisibilityLevel::PUBLIC)
+ where(:string_value, :integer_value) do
+ [
+ ['private', described_class::PRIVATE],
+ ['internal', described_class::INTERNAL],
+ ['public', described_class::PUBLIC]
+ ]
end
- it 'converts string integer to integer value' do
- expect(described_class.level_value('20')).to eq(20)
+ with_them do
+ it "converts '#{params[:string_value]}' to integer value #{params[:integer_value]}" do
+ expect(described_class.level_value(string_value)).to eq(integer_value)
+ end
+
+ it "converts string integer '#{params[:integer_value]}' to integer value #{params[:integer_value]}" do
+ expect(described_class.level_value(integer_value.to_s)).to eq(integer_value)
+ end
+
+ it 'defaults to PRIVATE when string integer value is not valid' do
+ expect(described_class.level_value(integer_value.to_s + 'r')).to eq(described_class::PRIVATE)
+ expect(described_class.level_value(integer_value.to_s + ' ')).to eq(described_class::PRIVATE)
+ expect(described_class.level_value('r' + integer_value.to_s)).to eq(described_class::PRIVATE)
+ end
+
+ it 'defaults to PRIVATE when string value is not valid' do
+ expect(described_class.level_value(string_value.capitalize)).to eq(described_class::PRIVATE)
+ expect(described_class.level_value(string_value + ' ')).to eq(described_class::PRIVATE)
+ expect(described_class.level_value(string_value + 'r')).to eq(described_class::PRIVATE)
+ end
end
it 'defaults to PRIVATE when string value is not valid' do
- expect(described_class.level_value('invalid')).to eq(Gitlab::VisibilityLevel::PRIVATE)
+ expect(described_class.level_value('invalid')).to eq(described_class::PRIVATE)
end
it 'defaults to PRIVATE when integer value is not valid' do
- expect(described_class.level_value(100)).to eq(Gitlab::VisibilityLevel::PRIVATE)
+ expect(described_class.level_value(100)).to eq(described_class::PRIVATE)
+ end
+
+ context 'when `fallback_value` is set to `nil`' do
+ it 'returns `nil` when string value is not valid' do
+ expect(described_class.level_value('invalid', fallback_value: nil)).to be_nil
+ end
+
+ it 'returns `nil` when integer value is not valid' do
+ expect(described_class.level_value(100, fallback_value: nil)).to be_nil
+ end
end
end
diff --git a/spec/models/hooks/web_hook_log_spec.rb b/spec/models/hooks/web_hook_log_spec.rb
index 9cfbb14e087..25df569c461 100644
--- a/spec/models/hooks/web_hook_log_spec.rb
+++ b/spec/models/hooks/web_hook_log_spec.rb
@@ -30,15 +30,12 @@ RSpec.describe WebHookLog do
end
describe '#save' do
- let(:web_hook_log) { build(:web_hook_log, url: url) }
- let(:url) { 'http://example.com' }
-
- subject { web_hook_log.save! }
+ context 'with basic auth credentials' do
+ let(:web_hook_log) { build(:web_hook_log, url: 'http://test:123@example.com') }
- it { is_expected.to eq(true) }
+ subject { web_hook_log.save! }
- context 'with basic auth credentials' do
- let(:url) { 'http://test:123@example.com'}
+ it { is_expected.to eq(true) }
it 'obfuscates the basic auth credentials' do
subject
@@ -46,6 +43,30 @@ RSpec.describe WebHookLog do
expect(web_hook_log.url).to eq('http://*****:*****@example.com')
end
end
+
+ context 'with author email' do
+ let(:author) { create(:user) }
+ let(:web_hook_log) { create(:web_hook_log, request_data: data) }
+ let(:data) do
+ {
+ commit: {
+ author: {
+ name: author.name,
+ email: author.email
+ }
+ }
+ }.deep_stringify_keys
+ end
+
+ it "redacts author's email" do
+ expect(web_hook_log.request_data['commit']).to match a_hash_including(
+ 'author' => {
+ 'name' => author.name,
+ 'email' => _('[REDACTED]')
+ }
+ )
+ end
+ end
end
describe '#success?' do
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index ed5b3d4e0be..990189a10a1 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -5623,6 +5623,7 @@ RSpec.describe Project, factory_default: :keep do
let(:import_state) { create(:import_state, project: project) }
it 'runs the correct hooks' do
+ expect(project.repository).to receive(:remove_prohibited_branches)
expect(project.repository).to receive(:expire_content_cache)
expect(project.wiki.repository).to receive(:expire_content_cache)
expect(import_state).to receive(:finish)
diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb
index 215f83adf5d..e24adc20143 100644
--- a/spec/models/repository_spec.rb
+++ b/spec/models/repository_spec.rb
@@ -3375,4 +3375,62 @@ RSpec.describe Repository do
end
end
end
+
+ describe '#remove_prohibited_branches' do
+ let(:branch_name) { '37fd3601be4c25497a39fa2e6a206e09e759d597' }
+
+ before do
+ allow(repository.raw_repository).to receive(:branch_names).and_return([branch_name])
+ end
+
+ context 'when prohibited branch exists' do
+ it 'deletes prohibited branch' do
+ expect(repository.raw_repository).to receive(:delete_branch).with(branch_name)
+
+ repository.remove_prohibited_branches
+ end
+ end
+
+ shared_examples 'does not delete branch' do
+ it 'returns without removing the branch' do
+ expect(repository.raw_repository).not_to receive(:delete_branch)
+
+ repository.remove_prohibited_branches
+ end
+ end
+
+ context 'when branch name is 40-characters long but not hexadecimal' do
+ let(:branch_name) { '37fd3601be4c25497a39fa2e6a206e09e759d59s' }
+
+ include_examples 'does not delete branch'
+ end
+
+ context 'when branch name is hexadecimal' do
+ context 'when branch name is less than 40-characters long' do
+ let(:branch_name) { '37fd3601be4c25497a39fa2e6a206e09e759d' }
+
+ include_examples 'does not delete branch'
+ end
+
+ context 'when branch name is more than 40-characters long' do
+ let(:branch_name) { '37fd3601be4c25497a39fa2e6a206e09e759dfdfd' }
+
+ include_examples 'does not delete branch'
+ end
+ end
+
+ context 'when prohibited branch does not exist' do
+ let(:branch_name) { 'main' }
+
+ include_examples 'does not delete branch'
+ end
+
+ context 'when raw repository does not exist' do
+ before do
+ allow(repository).to receive(:exists?).and_return(false)
+ end
+
+ include_examples 'does not delete branch'
+ end
+ end
end
diff --git a/spec/models/snippet_spec.rb b/spec/models/snippet_spec.rb
index 70afafce132..a54edc8510e 100644
--- a/spec/models/snippet_spec.rb
+++ b/spec/models/snippet_spec.rb
@@ -36,8 +36,6 @@ RSpec.describe Snippet do
it { is_expected.to validate_presence_of(:content) }
- it { is_expected.to validate_inclusion_of(:visibility_level).in_array(Gitlab::VisibilityLevel.values) }
-
it do
allow(Gitlab::CurrentSettings).to receive(:snippet_size_limit).and_return(1)
diff --git a/spec/serializers/build_details_entity_spec.rb b/spec/serializers/build_details_entity_spec.rb
index dd8238456aa..916798c669c 100644
--- a/spec/serializers/build_details_entity_spec.rb
+++ b/spec/serializers/build_details_entity_spec.rb
@@ -170,6 +170,24 @@ RSpec.describe BuildDetailsEntity do
expect(message).to include('could not retrieve the needed artifacts.')
end
end
+
+ context 'when dependency contains invalid dependency names' do
+ invalid_name = 'XSS<a href=# data-disable-with="<img src=x onerror=alert(document.domain)>">'
+ let!(:test1) { create(:ci_build, :success, :expired, pipeline: pipeline, name: invalid_name, stage_idx: 0) }
+ let!(:build) { create(:ci_build, :pending, pipeline: pipeline, stage_idx: 1, options: { dependencies: [invalid_name] }) }
+
+ before do
+ build.pipeline.unlocked!
+ build.drop!(:missing_dependency_failure)
+ end
+
+ it { is_expected.to include(failure_reason: 'missing_dependency_failure') }
+
+ it 'escapes the invalid dependency names' do
+ escaped_name = html_escape(invalid_name)
+ expect(message).to include(escaped_name)
+ end
+ end
end
context 'when a build has environment with latest deployment' do
diff --git a/spec/services/groups/update_service_spec.rb b/spec/services/groups/update_service_spec.rb
index 46c5e2a9818..c9f247be04c 100644
--- a/spec/services/groups/update_service_spec.rb
+++ b/spec/services/groups/update_service_spec.rb
@@ -242,6 +242,69 @@ RSpec.describe Groups::UpdateService do
end
end
+ context 'when user is not group owner' do
+ context 'when group is private' do
+ before do
+ private_group.add_maintainer(user)
+ end
+
+ it 'does not update the group to public' do
+ result = described_class.new(private_group, user, visibility_level: Gitlab::VisibilityLevel::PUBLIC).execute
+
+ expect(result).to eq(false)
+ expect(private_group.errors.count).to eq(1)
+ expect(private_group).to be_private
+ end
+
+ it 'does not update the group to public with tricky value' do
+ result = described_class.new(private_group, user, visibility_level: Gitlab::VisibilityLevel::PUBLIC.to_s + 'r').execute
+
+ expect(result).to eq(false)
+ expect(private_group.errors.count).to eq(1)
+ expect(private_group).to be_private
+ end
+ end
+
+ context 'when group is public' do
+ before do
+ public_group.add_maintainer(user)
+ end
+
+ it 'does not update the group to private' do
+ result = described_class.new(public_group, user, visibility_level: Gitlab::VisibilityLevel::PRIVATE).execute
+
+ expect(result).to eq(false)
+ expect(public_group.errors.count).to eq(1)
+ expect(public_group).to be_public
+ end
+
+ it 'does not update the group to private with invalid string value' do
+ result = described_class.new(public_group, user, visibility_level: 'invalid').execute
+
+ expect(result).to eq(false)
+ expect(public_group.errors.count).to eq(1)
+ expect(public_group).to be_public
+ end
+
+ it 'does not update the group to private with valid string value' do
+ result = described_class.new(public_group, user, visibility_level: 'private').execute
+
+ expect(result).to eq(false)
+ expect(public_group.errors.count).to eq(1)
+ expect(public_group).to be_public
+ end
+
+ # See https://gitlab.com/gitlab-org/gitlab/-/issues/359910
+ it 'does not update the group to private because of Active Record typecasting' do
+ result = described_class.new(public_group, user, visibility_level: 'public').execute
+
+ expect(result).to eq(true)
+ expect(public_group.errors.count).to eq(0)
+ expect(public_group).to be_public
+ end
+ end
+ end
+
context 'when updating #emails_disabled' do
let(:service) { described_class.new(internal_group, user, emails_disabled: true) }
diff --git a/spec/services/projects/update_service_spec.rb b/spec/services/projects/update_service_spec.rb
index 7b5bf1db030..7e2c7c89276 100644
--- a/spec/services/projects/update_service_spec.rb
+++ b/spec/services/projects/update_service_spec.rb
@@ -120,6 +120,65 @@ RSpec.describe Projects::UpdateService do
end
end
+ context 'when user is not project owner' do
+ let_it_be(:maintainer) { create(:user) }
+
+ before do
+ project.add_maintainer(maintainer)
+ end
+
+ context 'when project is private' do
+ it 'does not update the project to public' do
+ result = update_project(project, maintainer, visibility_level: Gitlab::VisibilityLevel::PUBLIC)
+
+ expect(result).to eq({ status: :error, message: 'New visibility level not allowed!' })
+ expect(project).to be_private
+ end
+
+ it 'does not update the project to public with tricky value' do
+ result = update_project(project, maintainer, visibility_level: Gitlab::VisibilityLevel::PUBLIC.to_s + 'r')
+
+ expect(result).to eq({ status: :error, message: 'New visibility level not allowed!' })
+ expect(project).to be_private
+ end
+ end
+
+ context 'when project is public' do
+ before do
+ project.update!(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
+ end
+
+ it 'does not update the project to private' do
+ result = update_project(project, maintainer, visibility_level: Gitlab::VisibilityLevel::PRIVATE)
+
+ expect(result).to eq({ status: :error, message: 'New visibility level not allowed!' })
+ expect(project).to be_public
+ end
+
+ it 'does not update the project to private with invalid string value' do
+ result = update_project(project, maintainer, visibility_level: 'invalid')
+
+ expect(result).to eq({ status: :error, message: 'New visibility level not allowed!' })
+ expect(project).to be_public
+ end
+
+ it 'does not update the project to private with valid string value' do
+ result = update_project(project, maintainer, visibility_level: 'private')
+
+ expect(result).to eq({ status: :error, message: 'New visibility level not allowed!' })
+ expect(project).to be_public
+ end
+
+ # See https://gitlab.com/gitlab-org/gitlab/-/issues/359910
+ it 'does not update the project to private because of Active Record typecasting' do
+ result = update_project(project, maintainer, visibility_level: 'public')
+
+ expect(result).to eq({ status: :success })
+ expect(project).to be_public
+ end
+ end
+ end
+
context 'when updating shared runners' do
context 'can enable shared runners' do
let(:group) { create(:group, shared_runners_enabled: true) }