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 Release Tools Bot <delivery-team+release-tools@gitlab.com>2023-08-31 19:19:03 +0300
committerGitLab Release Tools Bot <delivery-team+release-tools@gitlab.com>2023-08-31 19:19:03 +0300
commitdd6f4bfcef3cca46c0b1962f52f7a729f97cb6a7 (patch)
tree9a89227ef75fe65e03cc2ff661a900cae88fcd21
parent2fa10931183f6d699f77575f084770b1e4b5470d (diff)
parentea817127f2a4fdc28a2b19cdea0a1cec3e7f812c (diff)
Merge remote-tracking branch 'dev/16-3-stable' into 16-3-stable
-rw-r--r--CHANGELOG.md20
-rw-r--r--GITALY_SERVER_VERSION2
-rw-r--r--GITLAB_PAGES_VERSION2
-rw-r--r--Gemfile2
-rw-r--r--Gemfile.checksum2
-rw-r--r--Gemfile.lock4
-rw-r--r--VERSION2
-rw-r--r--app/controllers/groups/labels_controller.rb9
-rw-r--r--app/controllers/projects/refs_controller.rb4
-rw-r--r--app/graphql/resolvers/group_issues_resolver.rb5
-rw-r--r--app/graphql/resolvers/issues_resolver.rb1
-rw-r--r--app/models/bulk_imports/entity.rb19
-rw-r--r--app/policies/project_policy.rb1
-rw-r--r--app/services/error_tracking/list_projects_service.rb8
-rw-r--r--doc/user/application_security/dast/proxy-based.md3
-rw-r--r--lib/api/entities/project_import_status.rb11
-rw-r--r--lib/api/project_import.rb8
-rw-r--r--lib/api/projects.rb7
-rw-r--r--lib/api/validations/validators/bulk_imports.rb8
-rw-r--r--lib/gitlab/pagination/gitaly_keyset_pager.rb6
-rw-r--r--lib/gitlab/regex/bulk_imports.rb22
-rw-r--r--locale/gitlab.pot6
-rw-r--r--spec/controllers/groups/labels_controller_spec.rb66
-rw-r--r--spec/controllers/projects/refs_controller_spec.rb90
-rw-r--r--spec/factories/bulk_import/entities.rb1
-rw-r--r--spec/helpers/nav/new_dropdown_helper_spec.rb1
-rw-r--r--spec/helpers/projects_helper_spec.rb1
-rw-r--r--spec/lib/api/entities/project_import_status_spec.rb38
-rw-r--r--spec/lib/api/validations/validators/bulk_imports/destination_namespace_path_spec.rb43
-rw-r--r--spec/lib/api/validations/validators/bulk_imports/source_full_path_validator_spec.rb168
-rw-r--r--spec/lib/bulk_imports/common/pipelines/lfs_objects_pipeline_spec.rb4
-rw-r--r--spec/lib/bulk_imports/common/pipelines/uploads_pipeline_spec.rb6
-rw-r--r--spec/lib/bulk_imports/projects/pipelines/design_bundle_pipeline_spec.rb5
-rw-r--r--spec/lib/bulk_imports/projects/pipelines/repository_bundle_pipeline_spec.rb5
-rw-r--r--spec/lib/gitlab/pagination/gitaly_keyset_pager_spec.rb24
-rw-r--r--spec/lib/gitlab/regex_spec.rb68
-rw-r--r--spec/lib/gitlab/usage/metrics/instrumentations/count_bulk_imports_entities_metric_spec.rb10
-rw-r--r--spec/lib/gitlab/usage/metrics/instrumentations/count_imported_projects_total_metric_spec.rb6
-rw-r--r--spec/models/bulk_imports/entity_spec.rb11
-rw-r--r--spec/policies/project_policy_spec.rb11
-rw-r--r--spec/requests/api/bulk_imports_spec.rb14
-rw-r--r--spec/requests/api/projects_spec.rb41
-rw-r--r--spec/services/error_tracking/list_projects_service_spec.rb16
-rw-r--r--spec/support/shared_examples/features/search/redacted_search_results_shared_examples.rb2
44 files changed, 575 insertions, 208 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index cf2f5dc4c44..7ad9803a8eb 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,26 @@
documentation](doc/development/changelog.md) for instructions on adding your own
entry.
+## 16.3.1 (2023-08-31)
+
+### Fixed (1 change)
+
+- [Geo: Resync direct upload object stored artifacts](gitlab-org/security/gitlab@5db060ebf8829d027d61403fe9e30f5dc04e17ae) **GitLab Enterprise Edition**
+
+### Security (11 changes)
+
+- [Add authorization checks to import status endpoint](gitlab-org/security/gitlab@4ab4a1e5a3388f222154a3c4d71487303bc15387) ([merge request](gitlab-org/security/gitlab!3513))
+- [Update commonmarker to 0.23.10](gitlab-org/security/gitlab@58c3c6e164f7c4f20a711f996b9816d767cafc8c) ([merge request](gitlab-org/security/gitlab!3507))
+- [Remove DAST secret variables when URL is updated](gitlab-org/security/gitlab@038471d42bfe5a15df95645b487c9c6872528a33) ([merge request](gitlab-org/security/gitlab!3498))
+- [Maintainer can leak sentry token by changing the configured URL](gitlab-org/security/gitlab@62ac5e0cb77f61c107c4b91a442927ab666056e8) ([merge request](gitlab-org/security/gitlab!3516))
+- [Service account users are external by default](gitlab-org/security/gitlab@8c6892ba0144d202a346d9cc83488fa5d9ae044b) ([merge request](gitlab-org/security/gitlab!3501))
+- [Additional permission check when editing label](gitlab-org/security/gitlab@ec01380d19929259a95bf2d198cdffde3351806f) ([merge request](gitlab-org/security/gitlab!3504))
+- [Fix ReDOS in bulk_imports endpoint params](gitlab-org/security/gitlab@6e043d181b1c4968777efd0ab06bc9879a5a2076) ([merge request](gitlab-org/security/gitlab!3510))
+- [Prevent namespace level banned users from accessing API](gitlab-org/security/gitlab@ca3180b89ee5601b3a535058ae0a51def2b78a0f) ([merge request](gitlab-org/security/gitlab!3519))
+- [Check prohibit_outer_forks in fork relationship api](gitlab-org/security/gitlab@95f74317906732a0f8c6db830dcd9ac1113f69d3) ([merge request](gitlab-org/security/gitlab!3479))
+- [Prevent traversal for `path` parameter in refs/switch endpoint](gitlab-org/security/gitlab@12b3b42cf27750b6b7c2c4b62352b444b4b4ae34) ([merge request](gitlab-org/security/gitlab!3491))
+- [Gitaly keyset pager when pagination none only with tree view](gitlab-org/security/gitlab@39cbdd7aae475cee11bc720ef25e02518dd54b99) ([merge request](gitlab-org/security/gitlab!3495))
+
## 16.3.0 (2023-08-21)
### Added (169 changes)
diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION
index 377126f01ca..f15237f6974 100644
--- a/GITALY_SERVER_VERSION
+++ b/GITALY_SERVER_VERSION
@@ -1 +1 @@
-16.3.0 \ No newline at end of file
+16.3.1 \ No newline at end of file
diff --git a/GITLAB_PAGES_VERSION b/GITLAB_PAGES_VERSION
index 377126f01ca..f15237f6974 100644
--- a/GITLAB_PAGES_VERSION
+++ b/GITLAB_PAGES_VERSION
@@ -1 +1 @@
-16.3.0 \ No newline at end of file
+16.3.1 \ No newline at end of file
diff --git a/Gemfile b/Gemfile
index a8b1e14a494..a31ae24ecbc 100644
--- a/Gemfile
+++ b/Gemfile
@@ -197,7 +197,7 @@ gem 'typhoeus', '~> 1.4.0' # Used with Elasticsearch to support http keep-alive
gem 'html-pipeline', '~> 2.14.3'
gem 'deckar01-task_list', '2.3.2'
gem 'gitlab-markup', '~> 1.9.0', require: 'github/markup'
-gem 'commonmarker', '~> 0.23.9'
+gem 'commonmarker', '~> 0.23.10'
gem 'kramdown', '~> 2.3.1'
gem 'RedCloth', '~> 4.3.2'
gem 'rdoc', '~> 6.3.2'
diff --git a/Gemfile.checksum b/Gemfile.checksum
index eebf8fed592..d87b57c74a6 100644
--- a/Gemfile.checksum
+++ b/Gemfile.checksum
@@ -84,7 +84,7 @@
{"name":"coderay","version":"1.1.3","platform":"ruby","checksum":"dc530018a4684512f8f38143cd2a096c9f02a1fc2459edcfe534787a7fc77d4b"},
{"name":"coercible","version":"1.0.0","platform":"ruby","checksum":"5081ad24352cc8435ce5472bc2faa30260c7ea7f2102cc6a9f167c4d9bffaadc"},
{"name":"colored2","version":"3.1.2","platform":"ruby","checksum":"b13c2bd7eeae2cf7356a62501d398e72fde78780bd26aec6a979578293c28b4a"},
-{"name":"commonmarker","version":"0.23.9","platform":"ruby","checksum":"2e739c85a6961531cb6f5ba5169f2c7f64471b7e700c64b048ec22a5b230811c"},
+{"name":"commonmarker","version":"0.23.10","platform":"ruby","checksum":"fdd312ae2bb4071b2f3085d4d7533cb9f8d9057a2eaa0760228a65bc3ed565d1"},
{"name":"concurrent-ruby","version":"1.2.2","platform":"ruby","checksum":"3879119b8b75e3b62616acc256c64a134d0b0a7a9a3fcba5a233025bcde22c4f"},
{"name":"connection_pool","version":"2.3.0","platform":"ruby","checksum":"677985be912f33c90f98f229aaa0c0ddb2ef8776f21929a36eeeb25251c944da"},
{"name":"cork","version":"0.3.0","platform":"ruby","checksum":"a0a0ac50e262f8514d1abe0a14e95e71c98b24e3378690e5d044daf0013ad4bc"},
diff --git a/Gemfile.lock b/Gemfile.lock
index 04fe6459575..cf94510e923 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -369,7 +369,7 @@ GEM
coercible (1.0.0)
descendants_tracker (~> 0.0.1)
colored2 (3.1.2)
- commonmarker (0.23.9)
+ commonmarker (0.23.10)
concurrent-ruby (1.2.2)
connection_pool (2.3.0)
cork (0.3.0)
@@ -1759,7 +1759,7 @@ DEPENDENCIES
circuitbox (= 2.0.0)
click_house-client!
cloud_profiler_agent (~> 0.0.0)!
- commonmarker (~> 0.23.9)
+ commonmarker (~> 0.23.10)
concurrent-ruby (~> 1.1)
connection_pool (~> 2.0)
countries (~> 4.0.0)
diff --git a/VERSION b/VERSION
index 377126f01ca..f15237f6974 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-16.3.0 \ No newline at end of file
+16.3.1 \ No newline at end of file
diff --git a/app/controllers/groups/labels_controller.rb b/app/controllers/groups/labels_controller.rb
index 57bca5ebc52..f927cae90b1 100644
--- a/app/controllers/groups/labels_controller.rb
+++ b/app/controllers/groups/labels_controller.rb
@@ -4,7 +4,8 @@ class Groups::LabelsController < Groups::ApplicationController
include ToggleSubscriptionAction
before_action :label, only: [:edit, :update, :destroy]
- before_action :authorize_admin_labels!, only: [:new, :create, :edit, :update, :destroy]
+ before_action :authorize_group_for_admin_labels!, only: [:new, :create, :edit, :update, :destroy]
+ before_action :authorize_label_for_admin_label!, only: [:edit, :update, :destroy]
before_action :save_previous_label_path, only: [:edit]
respond_to :html
@@ -75,10 +76,14 @@ class Groups::LabelsController < Groups::ApplicationController
protected
- def authorize_admin_labels!
+ def authorize_group_for_admin_labels!
return render_404 unless can?(current_user, :admin_label, @group)
end
+ def authorize_label_for_admin_label!
+ return render_404 unless can?(current_user, :admin_label, @label)
+ end
+
def authorize_read_labels!
return render_404 unless can?(current_user, :read_label, @group)
end
diff --git a/app/controllers/projects/refs_controller.rb b/app/controllers/projects/refs_controller.rb
index 4c2bd2a9d42..278d306301a 100644
--- a/app/controllers/projects/refs_controller.rb
+++ b/app/controllers/projects/refs_controller.rb
@@ -15,6 +15,8 @@ class Projects::RefsController < Projects::ApplicationController
urgency :low, [:switch, :logs_tree]
def switch
+ Gitlab::PathTraversal.check_path_traversal!(@id)
+
respond_to do |format|
format.html do
new_path =
@@ -40,6 +42,8 @@ class Projects::RefsController < Projects::ApplicationController
redirect_to new_path
end
end
+ rescue Gitlab::PathTraversal::PathTraversalAttackError
+ head :bad_request
end
def logs_tree
diff --git a/app/graphql/resolvers/group_issues_resolver.rb b/app/graphql/resolvers/group_issues_resolver.rb
index 43f01395896..7bbc662c6c8 100644
--- a/app/graphql/resolvers/group_issues_resolver.rb
+++ b/app/graphql/resolvers/group_issues_resolver.rb
@@ -9,6 +9,11 @@ module Resolvers
include GroupIssuableResolver
+ before_connection_authorization do |nodes, _|
+ projects = nodes.map(&:project)
+ ActiveRecord::Associations::Preloader.new(records: projects, associations: :namespace).call
+ end
+
def ready?(**args)
if args.dig(:not, :release_tag).present?
raise ::Gitlab::Graphql::Errors::ArgumentError, 'releaseTag filter is not allowed when parent is a group.'
diff --git a/app/graphql/resolvers/issues_resolver.rb b/app/graphql/resolvers/issues_resolver.rb
index 17e3e159a5b..589366ba26d 100644
--- a/app/graphql/resolvers/issues_resolver.rb
+++ b/app/graphql/resolvers/issues_resolver.rb
@@ -23,6 +23,7 @@ module Resolvers
projects = nodes.map(&:project)
::Preloaders::UserMaxAccessLevelInProjectsPreloader.new(projects, current_user).execute
::Preloaders::GroupPolicyPreloader.new(projects.filter_map(&:group), current_user).execute
+ ActiveRecord::Associations::Preloader.new(records: projects, associations: :namespace).call
end
def ready?(**args)
diff --git a/app/models/bulk_imports/entity.rb b/app/models/bulk_imports/entity.rb
index 4f50a112141..644673e249e 100644
--- a/app/models/bulk_imports/entity.rb
+++ b/app/models/bulk_imports/entity.rb
@@ -41,19 +41,15 @@ class BulkImports::Entity < ApplicationRecord
validates :project, absence: true, if: :group
validates :group, absence: true, if: :project
validates :source_type, presence: true
- validates :source_full_path, presence: true, format: {
- with: Gitlab::Regex.bulk_import_source_full_path_regex,
- message: Gitlab::Regex.bulk_import_source_full_path_regex_message
- }
-
+ validates :source_full_path, presence: true
validates :destination_name, presence: true, if: -> { group || project }
validates :destination_namespace, exclusion: [nil], if: :group
validates :destination_namespace, presence: true, if: :project?
validate :validate_parent_is_a_group, if: :parent
validate :validate_imported_entity_type
-
validate :validate_destination_namespace_ascendency, if: :group_entity?
+ validate :validate_source_full_path_format
enum source_type: { group_entity: 0, project_entity: 1 }
@@ -236,4 +232,15 @@ class BulkImports::Entity < ApplicationRecord
)
end
end
+
+ def validate_source_full_path_format
+ validator = group? ? NamespacePathValidator : ProjectPathValidator
+
+ return if validator.valid_path?(source_full_path)
+
+ errors.add(
+ :source_full_path,
+ Gitlab::Regex.bulk_import_source_full_path_regex_message
+ )
+ end
end
diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb
index 564215f6e50..25495bb0221 100644
--- a/app/policies/project_policy.rb
+++ b/app/policies/project_policy.rb
@@ -567,6 +567,7 @@ class ProjectPolicy < BasePolicy
enable :destroy_upload
enable :admin_incident_management_timeline_event_tag
enable :stop_environment
+ enable :read_import_error
end
rule { public_project & metrics_dashboard_allowed }.policy do
diff --git a/app/services/error_tracking/list_projects_service.rb b/app/services/error_tracking/list_projects_service.rb
index 35a8179d54d..1539e24df9d 100644
--- a/app/services/error_tracking/list_projects_service.rb
+++ b/app/services/error_tracking/list_projects_service.rb
@@ -20,22 +20,20 @@ module ErrorTracking
def project_error_tracking_setting
(super || project.build_error_tracking_setting).tap do |setting|
- url_changed = !setting.api_url&.start_with?(params[:api_host])
-
setting.api_url = ErrorTracking::ProjectErrorTrackingSetting.build_api_url_from(
api_host: params[:api_host],
organization_slug: 'org',
project_slug: 'proj'
)
- setting.token = token(setting, url_changed)
+ setting.token = token(setting)
setting.enabled = true
end
end
strong_memoize_attr :project_error_tracking_setting
- def token(setting, url_changed)
- return if url_changed && masked_token?
+ def token(setting)
+ return if setting.api_url_changed? && masked_token?
# Use param token if not masked, otherwise use database token
return params[:token] unless masked_token?
diff --git a/doc/user/application_security/dast/proxy-based.md b/doc/user/application_security/dast/proxy-based.md
index 3052fd3a72d..7538bd38d9f 100644
--- a/doc/user/application_security/dast/proxy-based.md
+++ b/doc/user/application_security/dast/proxy-based.md
@@ -646,6 +646,9 @@ NOTE:
If a site profile is linked to a security policy, you cannot edit the profile from this page. See
[Scan execution policies](../policies/scan-execution-policies.md) for more information.
+NOTE:
+If a site profile's Target URL or Authenticated URL is updated, the request headers and password fields associated with that profile are cleared.
+
When a validated site profile's file, header, or meta tag is edited, the site's
[validation status](#site-profile-validation) is revoked.
diff --git a/lib/api/entities/project_import_status.rb b/lib/api/entities/project_import_status.rb
index 59388aacafd..a7e7cd9ff73 100644
--- a/lib/api/entities/project_import_status.rb
+++ b/lib/api/entities/project_import_status.rb
@@ -17,8 +17,15 @@ module API
project.import_state&.relation_hard_failures(limit: 100) || []
end
- expose :import_error, documentation: { type: 'string', example: 'Error message' } do |project, _options|
- project.import_state&.last_error
+ expose :import_error, documentation: { type: 'string', example: 'Error message' } do |project, options|
+ next unless options[:current_user]
+ next unless project.import_state&.last_error
+
+ if Ability.allowed?(options[:current_user], :read_import_error, project)
+ project.import_state&.last_error
+ else
+ _("Ask a maintainer to check the import status for more details.")
+ end
end
expose :stats, documentation: { type: 'object' } do |project, _options|
diff --git a/lib/api/project_import.rb b/lib/api/project_import.rb
index 6639b3ec346..c28d0ae2def 100644
--- a/lib/api/project_import.rb
+++ b/lib/api/project_import.rb
@@ -111,7 +111,7 @@ module API
).execute
if response.success?
- present(response.payload, with: Entities::ProjectImportStatus)
+ present(response.payload, with: Entities::ProjectImportStatus, current_user: current_user)
else
render_api_error!(response.message, response.http_status)
end
@@ -134,7 +134,7 @@ module API
end
route_setting :skip_authentication, true
get ':id/import' do
- present user_project, with: Entities::ProjectImportStatus
+ present user_project, with: Entities::ProjectImportStatus, current_user: current_user
end
params do
@@ -182,7 +182,7 @@ module API
).execute
if response.success?
- present(response.payload, with: Entities::ProjectImportStatus)
+ present(response.payload, with: Entities::ProjectImportStatus, current_user: current_user)
else
render_api_error!(response.message, response.http_status)
end
@@ -241,7 +241,7 @@ module API
).execute
if response.success?
- present(response.payload, with: Entities::ProjectImportStatus)
+ present(response.payload, with: Entities::ProjectImportStatus, current_user: current_user)
else
render_api_error!(response.message, response.http_status)
end
diff --git a/lib/api/projects.rb b/lib/api/projects.rb
index f6a2ce0f829..6d13512aad6 100644
--- a/lib/api/projects.rb
+++ b/lib/api/projects.rb
@@ -691,6 +691,7 @@ module API
desc 'Mark this project as forked from another' do
success code: 201, model: Entities::Project
failure [
+ { code: 401, message: 'Unauthorized' },
{ code: 403, message: 'Unauthenticated' },
{ code: 404, message: 'Not found' }
]
@@ -708,7 +709,11 @@ module API
authorize! :fork_project, fork_from_project
- result = ::Projects::ForkService.new(fork_from_project, current_user).execute(user_project)
+ service = ::Projects::ForkService.new(fork_from_project, current_user)
+
+ unauthorized!('Target Namespace') unless service.valid_fork_target?(user_project.namespace)
+
+ result = service.execute(user_project)
if result
present_project user_project.reset, with: Entities::Project, current_user: current_user
diff --git a/lib/api/validations/validators/bulk_imports.rb b/lib/api/validations/validators/bulk_imports.rb
index f8ad5ed6d14..67dc084cc12 100644
--- a/lib/api/validations/validators/bulk_imports.rb
+++ b/lib/api/validations/validators/bulk_imports.rb
@@ -32,8 +32,7 @@ module API
class DestinationNamespacePath < Grape::Validations::Validators::Base
def validate_param!(attr_name, params)
return if params[attr_name].blank?
-
- return if params[attr_name] =~ Gitlab::Regex.bulk_import_destination_namespace_path_regex
+ return if NamespacePathValidator.valid_path?(params[attr_name])
raise Grape::Exceptions::Validation.new(
params: [@scope.full_name(attr_name)],
@@ -44,7 +43,10 @@ module API
class SourceFullPath < Grape::Validations::Validators::Base
def validate_param!(attr_name, params)
- return if params[attr_name] =~ Gitlab::Regex.bulk_import_source_full_path_regex
+ full_path = params[attr_name]
+
+ return if params['source_type'] == 'group_entity' && NamespacePathValidator.valid_path?(full_path)
+ return if params['source_type'] == 'project_entity' && ProjectPathValidator.valid_path?(full_path)
raise Grape::Exceptions::Validation.new(
params: [@scope.full_name(attr_name)],
diff --git a/lib/gitlab/pagination/gitaly_keyset_pager.rb b/lib/gitlab/pagination/gitaly_keyset_pager.rb
index 6235874132f..82d6fc64d89 100644
--- a/lib/gitlab/pagination/gitaly_keyset_pager.rb
+++ b/lib/gitlab/pagination/gitaly_keyset_pager.rb
@@ -15,7 +15,7 @@ module Gitlab
# It is expected that the given finder will respond to `execute` method with `gitaly_pagination:` option
# and supports pagination via gitaly.
def paginate(finder)
- return finder.execute(gitaly_pagination: false) if no_pagination?
+ return finder.execute(gitaly_pagination: false) if no_pagination?(finder)
return paginate_via_gitaly(finder) if keyset_pagination_enabled?(finder)
return paginate_first_page_via_gitaly(finder) if paginate_first_page?(finder)
@@ -28,8 +28,8 @@ module Gitlab
private
- def no_pagination?
- params[:pagination] == 'none'
+ def no_pagination?(finder)
+ params[:pagination] == 'none' && finder.is_a?(::Repositories::TreeFinder)
end
def keyset_pagination_enabled?(finder)
diff --git a/lib/gitlab/regex/bulk_imports.rb b/lib/gitlab/regex/bulk_imports.rb
index e9ec24b831f..65c23b9d2e6 100644
--- a/lib/gitlab/regex/bulk_imports.rb
+++ b/lib/gitlab/regex/bulk_imports.rb
@@ -3,28 +3,6 @@
module Gitlab
module Regex
module BulkImports
- def bulk_import_destination_namespace_path_regex
- # This regexp validates the string conforms to rules for a destination_namespace path:
- # i.e does not start with a non-alphanumeric character,
- # contains only alphanumeric characters, forward slashes, periods, and underscores,
- # does not end with a period or forward slash, and has a relative path structure
- # with no http protocol chars or leading or trailing forward slashes
- # eg 'source/full/path' or 'destination_namespace' not 'https://example.com/destination/namespace/path'
- # the regex also allows for an empty string ('') to be accepted as this is allowed in
- # a bulk_import POST request
- @bulk_import_destination_namespace_path_regex ||= %r/((\A\z)|(\A[0-9a-z]*(-_.)?[0-9a-z])(\/?[0-9a-z]*[-_.]?[0-9a-z])+\z)/i
- end
-
- def bulk_import_source_full_path_regex
- # This regexp validates the string conforms to rules for a source_full_path path:
- # i.e does not start with a non-alphanumeric character except for periods or underscores,
- # contains only alphanumeric characters, forward slashes, periods, and underscores,
- # does not end with a period or forward slash, and has a relative path structure
- # with no http protocol chars or leading or trailing forward slashes
- # eg 'source/full/path' or 'destination_namespace' not 'https://example.com/source/full/path'
- @bulk_import_source_full_path_regex ||= %r/\A([.]?)[^\W](\/?([-_.+]*)*[0-9a-z][-_]*)+\z/i
- end
-
def bulk_import_source_full_path_regex_message
bulk_import_destination_namespace_path_regex_message
end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 45493cfb068..226085ef41a 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -6437,6 +6437,9 @@ msgstr ""
msgid "AsanaService|User Personal Access Token. User must have access to the task. All comments are attributed to this user."
msgstr ""
+msgid "Ask a maintainer to check the import status for more details."
+msgstr ""
+
msgid "Ask again later"
msgstr ""
@@ -14964,6 +14967,9 @@ msgstr ""
msgid "DastProfiles|Minimum = 1 second, Maximum = 3600 seconds"
msgstr ""
+msgid "DastProfiles|Modifying the URL will clear any previously entered values for the additional request headers and password fields."
+msgstr ""
+
msgid "DastProfiles|Monitors all HTTP requests sent to the target to find potential vulnerabilities."
msgstr ""
diff --git a/spec/controllers/groups/labels_controller_spec.rb b/spec/controllers/groups/labels_controller_spec.rb
index ebe379d948c..f9f1fc21538 100644
--- a/spec/controllers/groups/labels_controller_spec.rb
+++ b/spec/controllers/groups/labels_controller_spec.rb
@@ -5,6 +5,7 @@ require 'spec_helper'
RSpec.describe Groups::LabelsController, feature_category: :team_planning do
let_it_be(:group) { create(:group) }
let_it_be(:user) { create(:user) }
+ let_it_be(:another_user) { create(:user) }
let_it_be(:project) { create(:project, namespace: group) }
before do
@@ -66,6 +67,46 @@ RSpec.describe Groups::LabelsController, feature_category: :team_planning do
end
end
+ shared_examples 'when current_user does not have ability to modify the label' do
+ before do
+ sign_in(another_user)
+ end
+
+ it 'responds with status 404' do
+ group_request
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+
+ # No matter what permissions you have in a sub-group, you need the proper
+ # permissions in the group in order to modify a group label
+ # See https://gitlab.com/gitlab-org/gitlab/-/issues/387531
+ context 'when trying to edit a parent group label from inside a subgroup' do
+ it 'responds with status 404' do
+ sub_group.add_owner(another_user)
+ sub_group_request
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+ end
+
+ describe 'GET #edit' do
+ let_it_be(:label) { create(:group_label, group: group) }
+
+ it 'shows the edit page' do
+ get :edit, params: { group_id: group.to_param, id: label.to_param }
+
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+
+ it_behaves_like 'when current_user does not have ability to modify the label' do
+ let_it_be(:sub_group) { create(:group, parent: group) }
+ let(:group_request) { get :edit, params: { group_id: group.to_param, id: label.to_param } }
+ let(:sub_group_request) { get :edit, params: { group_id: sub_group.to_param, id: label.to_param } }
+ end
+ end
+
describe 'POST #toggle_subscription' do
it 'allows user to toggle subscription on group labels' do
label = create(:group_label, group: group)
@@ -106,19 +147,20 @@ RSpec.describe Groups::LabelsController, feature_category: :team_planning do
end
end
- context 'when current_user does not have ability to destroy the label' do
- let(:another_user) { create(:user) }
-
- before do
- sign_in(another_user)
- end
-
- it 'responds with status 404' do
- label = create(:group_label, group: group)
- delete :destroy, params: { group_id: group.to_param, id: label.to_param }
+ it_behaves_like 'when current_user does not have ability to modify the label' do
+ let_it_be(:label) { create(:group_label, group: group) }
+ let_it_be(:sub_group) { create(:group, parent: group) }
+ let(:group_request) { delete :destroy, params: { group_id: group.to_param, id: label.to_param } }
+ let(:sub_group_request) { delete :destroy, params: { group_id: sub_group.to_param, id: label.to_param } }
+ end
+ end
- expect(response).to have_gitlab_http_status(:not_found)
- end
+ describe 'PUT #update' do
+ it_behaves_like 'when current_user does not have ability to modify the label' do
+ let_it_be(:label) { create(:group_label, group: group) }
+ let_it_be(:sub_group) { create(:group, parent: group) }
+ let(:group_request) { put :update, params: { group_id: group.to_param, id: label.to_param, label: { title: 'Test' } } }
+ let(:sub_group_request) { put :update, params: { group_id: sub_group.to_param, id: label.to_param, label: { title: 'Test' } } }
end
end
end
diff --git a/spec/controllers/projects/refs_controller_spec.rb b/spec/controllers/projects/refs_controller_spec.rb
index 0b1d0b75de7..7ea0e678a41 100644
--- a/spec/controllers/projects/refs_controller_spec.rb
+++ b/spec/controllers/projects/refs_controller_spec.rb
@@ -12,40 +12,70 @@ RSpec.describe Projects::RefsController, feature_category: :source_code_manageme
end
describe 'GET #switch' do
- using RSpec::Parameterized::TableSyntax
+ context 'with normal parameters' do
+ using RSpec::Parameterized::TableSyntax
- let(:id) { 'master' }
- let(:params) do
- { destination: destination, namespace_id: project.namespace.to_param, project_id: project, id: id,
- ref_type: ref_type }
- end
+ let(:id) { 'master' }
+ let(:id_and_path) { "#{id}/#{path}" }
+
+ let(:params) do
+ { destination: destination, namespace_id: project.namespace.to_param, project_id: project, id: id,
+ ref_type: ref_type, path: path }
+ end
+
+ subject { get :switch, params: params }
+
+ where(:destination, :ref_type, :path, :redirected_to) do
+ 'tree' | nil | nil | lazy { project_tree_path(project, id) }
+ 'tree' | 'heads' | nil | lazy { project_tree_path(project, id) }
+ 'tree' | nil | 'foo/bar' | lazy { project_tree_path(project, id_and_path) }
+ 'blob' | nil | nil | lazy { project_blob_path(project, id) }
+ 'blob' | 'heads' | nil | lazy { project_blob_path(project, id) }
+ 'blob' | nil | 'foo/bar' | lazy { project_blob_path(project, id_and_path) }
+ 'graph' | nil | nil | lazy { project_network_path(project, id) }
+ 'graph' | 'heads' | nil | lazy { project_network_path(project, id, ref_type: 'heads') }
+ 'graph' | nil | 'foo/bar' | lazy { project_network_path(project, id_and_path) }
+ 'graphs' | nil | nil | lazy { project_graph_path(project, id) }
+ 'graphs' | 'heads' | nil | lazy { project_graph_path(project, id, ref_type: 'heads') }
+ 'graphs' | nil | 'foo/bar' | lazy { project_graph_path(project, id_and_path) }
+ 'find_file' | nil | nil | lazy { project_find_file_path(project, id) }
+ 'find_file' | 'heads' | nil | lazy { project_find_file_path(project, id) }
+ 'find_file' | nil | 'foo/bar' | lazy { project_find_file_path(project, id_and_path) }
+ 'graphs_commits' | nil | nil | lazy { commits_project_graph_path(project, id) }
+ 'graphs_commits' | 'heads' | nil | lazy { commits_project_graph_path(project, id) }
+ 'graphs_commits' | nil | 'foo/bar' | lazy { commits_project_graph_path(project, id_and_path) }
+ 'badges' | nil | nil | lazy { project_settings_ci_cd_path(project, ref: id) }
+ 'badges' | 'heads' | nil | lazy { project_settings_ci_cd_path(project, ref: id) }
+ 'badges' | nil | 'foo/bar' | lazy { project_settings_ci_cd_path(project, ref: id_and_path) }
+ 'commits' | nil | nil | lazy { project_commits_path(project, id) }
+ 'commits' | 'heads' | nil | lazy { project_commits_path(project, id, ref_type: 'heads') }
+ 'commits' | nil | 'foo/bar' | lazy { project_commits_path(project, id_and_path) }
+ nil | nil | nil | lazy { project_commits_path(project, id) }
+ nil | 'heads' | nil | lazy { project_commits_path(project, id, ref_type: 'heads') }
+ nil | nil | 'foo/bar' | lazy { project_commits_path(project, id_and_path) }
+ end
- subject { get :switch, params: params }
-
- where(:destination, :ref_type, :redirected_to) do
- 'tree' | nil | lazy { project_tree_path(project, id) }
- 'tree' | 'heads' | lazy { project_tree_path(project, id) }
- 'blob' | nil | lazy { project_blob_path(project, id) }
- 'blob' | 'heads' | lazy { project_blob_path(project, id) }
- 'graph' | nil | lazy { project_network_path(project, id) }
- 'graph' | 'heads' | lazy { project_network_path(project, id, ref_type: 'heads') }
- 'graphs' | nil | lazy { project_graph_path(project, id) }
- 'graphs' | 'heads' | lazy { project_graph_path(project, id, ref_type: 'heads') }
- 'find_file' | nil | lazy { project_find_file_path(project, id) }
- 'find_file' | 'heads' | lazy { project_find_file_path(project, id) }
- 'graphs_commits' | nil | lazy { commits_project_graph_path(project, id) }
- 'graphs_commits' | 'heads' | lazy { commits_project_graph_path(project, id) }
- 'badges' | nil | lazy { project_settings_ci_cd_path(project, ref: id) }
- 'badges' | 'heads' | lazy { project_settings_ci_cd_path(project, ref: id) }
- 'commits' | nil | lazy { project_commits_path(project, id) }
- 'commits' | 'heads' | lazy { project_commits_path(project, id, ref_type: 'heads') }
- nil | nil | lazy { project_commits_path(project, id) }
- nil | 'heads' | lazy { project_commits_path(project, id, ref_type: 'heads') }
+ with_them do
+ it 'redirects to destination' do
+ expect(subject).to redirect_to(redirected_to)
+ end
+ end
end
- with_them do
- it 'redirects to destination' do
- expect(subject).to redirect_to(redirected_to)
+ context 'with bad path parameter' do
+ it 'returns 400 bad request' do
+ params = {
+ destination: 'tree',
+ namespace_id: project.namespace.to_param,
+ project_id: project,
+ id: 'master',
+ ref_type: nil,
+ path: '../bad_path_redirect'
+ }
+
+ get :switch, params: params
+
+ expect(response).to have_gitlab_http_status(:bad_request)
end
end
end
diff --git a/spec/factories/bulk_import/entities.rb b/spec/factories/bulk_import/entities.rb
index a662d42c7c9..04407947f14 100644
--- a/spec/factories/bulk_import/entities.rb
+++ b/spec/factories/bulk_import/entities.rb
@@ -18,6 +18,7 @@ FactoryBot.define do
trait(:project_entity) do
source_type { :project_entity }
+ sequence(:source_full_path) { |n| "root/source-path-#{n}" }
end
trait :created do
diff --git a/spec/helpers/nav/new_dropdown_helper_spec.rb b/spec/helpers/nav/new_dropdown_helper_spec.rb
index 26dadd3b4f1..4ec120d152b 100644
--- a/spec/helpers/nav/new_dropdown_helper_spec.rb
+++ b/spec/helpers/nav/new_dropdown_helper_spec.rb
@@ -22,6 +22,7 @@ RSpec.describe Nav::NewDropdownHelper, feature_category: :navigation do
allow(helper).to receive(:can?).and_return(false)
allow(user).to receive(:can_create_project?) { with_can_create_project }
allow(user).to receive(:can_create_group?) { with_can_create_group }
+ allow(user).to receive(:can?).and_call_original
allow(user).to receive(:can?).with(:create_snippet) { with_can_create_snippet }
end
diff --git a/spec/helpers/projects_helper_spec.rb b/spec/helpers/projects_helper_spec.rb
index aa064a26ec4..e1537c7b287 100644
--- a/spec/helpers/projects_helper_spec.rb
+++ b/spec/helpers/projects_helper_spec.rb
@@ -1170,6 +1170,7 @@ RSpec.describe ProjectsHelper, feature_category: :source_code_management do
current_user = user if has_user
allow(helper).to receive(:current_user).and_return(current_user)
+ allow(user).to receive(:can?).and_call_original
allow(user).to receive(:can?).with(:fork_project, project).and_return(true)
allow(user).to receive(:can?).with(:create_fork).and_return(true)
allow(user).to receive(:can?).with(:create_projects, anything).and_return(true)
diff --git a/spec/lib/api/entities/project_import_status_spec.rb b/spec/lib/api/entities/project_import_status_spec.rb
index 37a18718950..5d7f06dc78e 100644
--- a/spec/lib/api/entities/project_import_status_spec.rb
+++ b/spec/lib/api/entities/project_import_status_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Entities::ProjectImportStatus, :aggregate_failures do
+RSpec.describe API::Entities::ProjectImportStatus, :aggregate_failures, feature_category: :importers do
describe '#as_json' do
subject { entity.as_json }
@@ -67,14 +67,36 @@ RSpec.describe API::Entities::ProjectImportStatus, :aggregate_failures do
context 'when import has failed' do
let(:project) { create(:project, :import_failed, import_type: 'import_type', import_correlation_id: correlation_id, import_last_error: 'error') }
- let(:entity) { described_class.new(project) }
+ let(:current_user) { create(:user) }
+ let(:options) { { current_user: current_user } }
+ let(:entity) { described_class.new(project, options) }
+
+ context 'when user has access to read import status' do
+ before do
+ project.add_maintainer(current_user)
+ end
+
+ it 'includes basic fields with import error' do
+ expect(subject[:import_status]).to eq('failed')
+ expect(subject[:import_type]).to eq('import_type')
+ expect(subject[:correlation_id]).to eq(correlation_id)
+ expect(subject[:import_error]).to eq('error')
+ expect(subject[:failed_relations]).to eq([])
+ end
+ end
- it 'includes basic fields with import error' do
- expect(subject[:import_status]).to eq('failed')
- expect(subject[:import_type]).to eq('import_type')
- expect(subject[:correlation_id]).to eq(correlation_id)
- expect(subject[:import_error]).to eq('error')
- expect(subject[:failed_relations]).to eq([])
+ context 'when user does not have access to read import status' do
+ before do
+ project.add_reporter(current_user)
+ end
+
+ it 'includes basic fields with import error' do
+ expect(subject[:import_status]).to eq('failed')
+ expect(subject[:import_type]).to eq('import_type')
+ expect(subject[:correlation_id]).to eq(correlation_id)
+ expect(subject[:import_error]).to eq('Ask a maintainer to check the import status for more details.')
+ expect(subject[:failed_relations]).to eq([])
+ end
end
end
diff --git a/spec/lib/api/validations/validators/bulk_imports/destination_namespace_path_spec.rb b/spec/lib/api/validations/validators/bulk_imports/destination_namespace_path_spec.rb
new file mode 100644
index 00000000000..39432c724a0
--- /dev/null
+++ b/spec/lib/api/validations/validators/bulk_imports/destination_namespace_path_spec.rb
@@ -0,0 +1,43 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe API::Validations::Validators::BulkImports::DestinationNamespacePath, feature_category: :importers do
+ include ApiValidatorsHelpers
+
+ subject do
+ described_class.new(['test'], {}, false, scope.new)
+ end
+
+ context 'when destination namespace param is valid' do
+ it 'raises a validation error', :aggregate_failures do
+ expect_validation_error('test' => '?gitlab')
+ expect_validation_error('test' => "Users's something")
+ expect_validation_error('test' => '/source')
+ expect_validation_error('test' => 'http:')
+ expect_validation_error('test' => 'https:')
+ expect_validation_error('test' => 'example.com/?stuff=true')
+ expect_validation_error('test' => 'example.com:5000/?stuff=true')
+ expect_validation_error('test' => 'http://gitlab.example/gitlab-org/manage/import/gitlab-migration-test')
+ expect_validation_error('test' => 'good_for_me!')
+ expect_validation_error('test' => 'good_for+you')
+ expect_validation_error('test' => 'source/')
+ expect_validation_error('test' => '.source/full./path')
+ end
+ end
+
+ context 'when destination namespace param is invalid' do
+ it 'does not raise a validation error', :aggregate_failures do
+ expect_no_validation_error('')
+ expect_no_validation_error('test' => '')
+ expect_no_validation_error('test' => 'source')
+ expect_no_validation_error('test' => 'source/full')
+ expect_no_validation_error('test' => 'source/full/path')
+ expect_no_validation_error('test' => 'sou_rce/fu-ll/pa.th')
+ expect_no_validation_error('test' => 'domain_namespace')
+ expect_no_validation_error('test' => 'gitlab-migration-test')
+ expect_no_validation_error('test' => '1-project-path')
+ expect_no_validation_error('test' => 'e-project-path')
+ end
+ end
+end
diff --git a/spec/lib/api/validations/validators/bulk_imports/source_full_path_validator_spec.rb b/spec/lib/api/validations/validators/bulk_imports/source_full_path_validator_spec.rb
new file mode 100644
index 00000000000..f0d2121b0d6
--- /dev/null
+++ b/spec/lib/api/validations/validators/bulk_imports/source_full_path_validator_spec.rb
@@ -0,0 +1,168 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe API::Validations::Validators::BulkImports::SourceFullPath, feature_category: :importers do
+ include ApiValidatorsHelpers
+ using RSpec::Parameterized::TableSyntax
+
+ subject do
+ described_class.new(['test'], {}, false, scope.new)
+ end
+
+ let(:source_type_params) { { 'source_type' => source_type } }
+
+ context 'when source_type is group_entity' do
+ let(:source_type) { 'group_entity' }
+
+ context 'when source_full_path param is invalid' do
+ where(:invalid_param) do
+ [
+ '',
+ '?gitlab',
+ "Users's something",
+ '/source',
+ 'http:',
+ 'https:',
+ 'example.com/?stuff=true',
+ 'example.com:5000/?stuff=true',
+ 'http://gitlab.example/gitlab-org/manage/import/gitlab-migration-test',
+ 'good_for_me!',
+ 'good_for+you',
+ 'source/',
+ '.source/full/path.'
+ ]
+ end
+
+ with_them do
+ it 'raises a validation error' do
+ params = source_type_params.merge('test' => invalid_param)
+
+ expect_validation_error(params)
+ end
+ end
+ end
+
+ context 'when source_full_path param is valid' do
+ where(:valid_param) do
+ [
+ 'source',
+ 'source/full',
+ 'source/full/path',
+ 'sou_rce/fu-ll/pa.th',
+ 'source/full/path---',
+ 'source/full/..path',
+ 'domain_namespace',
+ 'gitlab-migration-test',
+ '1-project-path',
+ 'e-project-path'
+ ]
+ end
+
+ with_them do
+ it 'does not raise a validation error' do
+ params = source_type_params.merge('test' => valid_param)
+
+ expect_no_validation_error(params)
+ end
+ end
+ end
+ end
+
+ context 'when source_type is project_entity' do
+ let(:source_type) { 'project_entity' }
+
+ context 'when source_full_path param is invalid' do
+ where(:invalid_param) do
+ [
+ '',
+ '?gitlab',
+ "Users's something",
+ '/source',
+ 'http:',
+ 'https:',
+ 'example.com/?stuff=true',
+ 'example.com:5000/?stuff=true',
+ 'http://gitlab.example/gitlab-org/manage/import/gitlab-migration-test',
+ 'good_for_me!',
+ 'good_for+you',
+ 'source/',
+ 'source',
+ '.source/full./path',
+ 'domain_namespace',
+ 'gitlab-migration-test',
+ '1-project-path',
+ 'e-project-path'
+ ]
+ end
+
+ with_them do
+ it 'raises a validation error' do
+ params = source_type_params.merge('test' => invalid_param)
+
+ expect_validation_error(params)
+ end
+ end
+ end
+
+ context 'when source_full_path param is valid' do
+ where(:valid_param) do
+ [
+ 'source/full',
+ 'source/full/path',
+ 'sou_rce/fu-ll/pa.th',
+ 'source/full/path---',
+ 'source/full/..path'
+ ]
+ end
+
+ with_them do
+ it 'does not raise a validation error' do
+ params = source_type_params.merge('test' => valid_param)
+
+ expect_no_validation_error(params)
+ end
+ end
+ end
+ end
+
+ context 'when source_type is invalid' do
+ let(:source_type) { '' }
+
+ context 'when source_full_path param is invalid' do
+ where(:invalid_param) do
+ [
+ '',
+ '?gitlab',
+ "Users's something",
+ '/source',
+ 'http:',
+ 'https:',
+ 'example.com/?stuff=true',
+ 'example.com:5000/?stuff=true',
+ 'http://gitlab.example/gitlab-org/manage/import/gitlab-migration-test',
+ 'good_for_me!',
+ 'good_for+you',
+ 'source/',
+ '.source/full./path',
+ 'source',
+ 'source/full',
+ 'source/full/path',
+ 'sou_rce/fu-ll/pa.th',
+ 'domain_namespace',
+ 'gitlab-migration-test',
+ '1-project-path',
+ 'e-project-path'
+ ]
+ end
+
+ with_them do
+ it 'raises a validation error' do
+ params = source_type_params.merge('test' => invalid_param)
+
+ expect_validation_error(params)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/bulk_imports/common/pipelines/lfs_objects_pipeline_spec.rb b/spec/lib/bulk_imports/common/pipelines/lfs_objects_pipeline_spec.rb
index 50640e1e4ba..c91b031de30 100644
--- a/spec/lib/bulk_imports/common/pipelines/lfs_objects_pipeline_spec.rb
+++ b/spec/lib/bulk_imports/common/pipelines/lfs_objects_pipeline_spec.rb
@@ -7,7 +7,7 @@ RSpec.describe BulkImports::Common::Pipelines::LfsObjectsPipeline, feature_categ
let_it_be(:oid) { 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855' }
let(:tmpdir) { Dir.mktmpdir }
- let(:entity) { create(:bulk_import_entity, :project_entity, project: portable, source_full_path: 'test', source_xid: nil) }
+ let(:entity) { create(:bulk_import_entity, :project_entity, project: portable, source_xid: nil) }
let(:tracker) { create(:bulk_import_tracker, entity: entity) }
let(:context) { BulkImports::Pipeline::Context.new(tracker) }
let(:lfs_dir_path) { tmpdir }
@@ -53,7 +53,7 @@ RSpec.describe BulkImports::Common::Pipelines::LfsObjectsPipeline, feature_categ
.to receive(:new)
.with(
configuration: context.configuration,
- relative_url: "/#{entity.pluralized_name}/test/export_relations/download?relation=lfs_objects",
+ relative_url: "/#{entity.pluralized_name}/#{CGI.escape(entity.source_full_path)}/export_relations/download?relation=lfs_objects",
tmpdir: tmpdir,
filename: 'lfs_objects.tar.gz')
.and_return(download_service)
diff --git a/spec/lib/bulk_imports/common/pipelines/uploads_pipeline_spec.rb b/spec/lib/bulk_imports/common/pipelines/uploads_pipeline_spec.rb
index 09c8c7b92c2..675ae935c1c 100644
--- a/spec/lib/bulk_imports/common/pipelines/uploads_pipeline_spec.rb
+++ b/spec/lib/bulk_imports/common/pipelines/uploads_pipeline_spec.rb
@@ -79,7 +79,7 @@ RSpec.describe BulkImports::Common::Pipelines::UploadsPipeline, feature_category
.to receive(:new)
.with(
configuration: context.configuration,
- relative_url: "/#{entity.pluralized_name}/test/export_relations/download?relation=uploads",
+ relative_url: "/#{entity.pluralized_name}/#{CGI.escape(entity.source_full_path)}/export_relations/download?relation=uploads",
tmpdir: tmpdir,
filename: 'uploads.tar.gz')
.and_return(download_service)
@@ -178,14 +178,14 @@ RSpec.describe BulkImports::Common::Pipelines::UploadsPipeline, feature_category
context 'when importing to group' do
let(:portable) { group }
- let(:entity) { create(:bulk_import_entity, :group_entity, group: group, source_full_path: 'test', source_xid: nil) }
+ let(:entity) { create(:bulk_import_entity, :group_entity, group: group, source_xid: nil) }
include_examples 'uploads import'
end
context 'when importing to project' do
let(:portable) { project }
- let(:entity) { create(:bulk_import_entity, :project_entity, project: project, source_full_path: 'test', source_xid: nil) }
+ let(:entity) { create(:bulk_import_entity, :project_entity, project: project, source_xid: nil) }
include_examples 'uploads import'
end
diff --git a/spec/lib/bulk_imports/projects/pipelines/design_bundle_pipeline_spec.rb b/spec/lib/bulk_imports/projects/pipelines/design_bundle_pipeline_spec.rb
index 87efad92131..e65339ffdd0 100644
--- a/spec/lib/bulk_imports/projects/pipelines/design_bundle_pipeline_spec.rb
+++ b/spec/lib/bulk_imports/projects/pipelines/design_bundle_pipeline_spec.rb
@@ -9,7 +9,7 @@ RSpec.describe BulkImports::Projects::Pipelines::DesignBundlePipeline, feature_c
let(:tmpdir) { Dir.mktmpdir }
let(:design_bundle_path) { File.join(tmpdir, 'design.bundle') }
let(:entity) do
- create(:bulk_import_entity, :project_entity, project: portable, source_full_path: 'test', source_xid: nil)
+ create(:bulk_import_entity, :project_entity, project: portable, source_xid: nil)
end
let(:tracker) { create(:bulk_import_tracker, entity: entity) }
@@ -52,7 +52,8 @@ RSpec.describe BulkImports::Projects::Pipelines::DesignBundlePipeline, feature_c
.to receive(:new)
.with(
configuration: context.configuration,
- relative_url: "/#{entity.pluralized_name}/test/export_relations/download?relation=design",
+ relative_url: "/#{entity.pluralized_name}/#{CGI.escape(entity.source_full_path)}" \
+ '/export_relations/download?relation=design',
tmpdir: tmpdir,
filename: 'design.tar.gz')
.and_return(download_service)
diff --git a/spec/lib/bulk_imports/projects/pipelines/repository_bundle_pipeline_spec.rb b/spec/lib/bulk_imports/projects/pipelines/repository_bundle_pipeline_spec.rb
index 5aae8c959fa..2865215823a 100644
--- a/spec/lib/bulk_imports/projects/pipelines/repository_bundle_pipeline_spec.rb
+++ b/spec/lib/bulk_imports/projects/pipelines/repository_bundle_pipeline_spec.rb
@@ -9,7 +9,7 @@ RSpec.describe BulkImports::Projects::Pipelines::RepositoryBundlePipeline, featu
let(:tmpdir) { Dir.mktmpdir }
let(:bundle_path) { File.join(tmpdir, 'repository.bundle') }
let(:entity) do
- create(:bulk_import_entity, :project_entity, project: portable, source_full_path: 'test', source_xid: nil)
+ create(:bulk_import_entity, :project_entity, project: portable, source_xid: nil)
end
let(:tracker) { create(:bulk_import_tracker, entity: entity) }
@@ -62,7 +62,8 @@ RSpec.describe BulkImports::Projects::Pipelines::RepositoryBundlePipeline, featu
.to receive(:new)
.with(
configuration: context.configuration,
- relative_url: "/#{entity.pluralized_name}/test/export_relations/download?relation=repository",
+ relative_url: "/#{entity.pluralized_name}/#{CGI.escape(entity.source_full_path)}" \
+ '/export_relations/download?relation=repository',
tmpdir: tmpdir,
filename: 'repository.tar.gz')
.and_return(download_service)
diff --git a/spec/lib/gitlab/pagination/gitaly_keyset_pager_spec.rb b/spec/lib/gitlab/pagination/gitaly_keyset_pager_spec.rb
index 2e87c582040..cb3f1fe86dc 100644
--- a/spec/lib/gitlab/pagination/gitaly_keyset_pager_spec.rb
+++ b/spec/lib/gitlab/pagination/gitaly_keyset_pager_spec.rb
@@ -127,17 +127,27 @@ RSpec.describe Gitlab::Pagination::GitalyKeysetPager do
end
end
- context 'with "none" pagination option' do
+ context "with 'none' pagination option" do
let(:expected_result) { double(:result) }
let(:query) { { pagination: 'none' } }
- it 'uses offset pagination' do
- expect(finder).to receive(:execute).with(gitaly_pagination: false).and_return(expected_result)
- expect(Kaminari).not_to receive(:paginate_array)
- expect(Gitlab::Pagination::OffsetPagination).not_to receive(:new)
+ context "with a finder that is not a TreeFinder" do
+ it_behaves_like 'offset pagination'
+ end
+
+ context "with a finder that is a TreeFinder" do
+ before do
+ allow(finder).to receive(:is_a?).with(::Repositories::TreeFinder).and_return(true)
+ end
- actual_result = pager.paginate(finder)
- expect(actual_result).to eq(expected_result)
+ it "doesn't uses offset pagination" do
+ expect(finder).to receive(:execute).with(gitaly_pagination: false).and_return(expected_result)
+ expect(Kaminari).not_to receive(:paginate_array)
+ expect(Gitlab::Pagination::OffsetPagination).not_to receive(:new)
+
+ actual_result = pager.paginate(finder)
+ expect(actual_result).to eq(expected_result)
+ end
end
end
end
diff --git a/spec/lib/gitlab/regex_spec.rb b/spec/lib/gitlab/regex_spec.rb
index c91b99caba2..df123ef638f 100644
--- a/spec/lib/gitlab/regex_spec.rb
+++ b/spec/lib/gitlab/regex_spec.rb
@@ -98,36 +98,6 @@ RSpec.describe Gitlab::Regex, feature_category: :tooling do
}
end
- describe '.bulk_import_destination_namespace_path_regex' do
- subject { described_class.bulk_import_destination_namespace_path_regex }
-
- it { is_expected.not_to match('?gitlab') }
- it { is_expected.not_to match("Users's something") }
- it { is_expected.not_to match('/source') }
- it { is_expected.not_to match('http:') }
- it { is_expected.not_to match('https:') }
- it { is_expected.not_to match('example.com/?stuff=true') }
- it { is_expected.not_to match('example.com:5000/?stuff=true') }
- it { is_expected.not_to match('http://gitlab.example/gitlab-org/manage/import/gitlab-migration-test') }
- it { is_expected.not_to match('_good_for_me!') }
- it { is_expected.not_to match('good_for+you') }
- it { is_expected.not_to match('source/') }
- it { is_expected.not_to match('.source/full./path') }
- it { is_expected.not_to match('.source/.full/.path') }
- it { is_expected.not_to match('_source') }
- it { is_expected.not_to match('.source') }
-
- it { is_expected.to match('source') }
- it { is_expected.to match('source/full') }
- it { is_expected.to match('source/full/path') }
- it { is_expected.to match('sou_rce/fu-ll/pa.th') }
- it { is_expected.to match('domain_namespace') }
- it { is_expected.to match('gitlab-migration-test') }
- it { is_expected.to match('1-project-path') }
- it { is_expected.to match('e-project-path') }
- it { is_expected.to match('') } # it is possible to pass an empty string for destination_namespace in bulk_import POST request
- end
-
describe '.bulk_import_source_full_path_regex_message' do
subject { described_class.bulk_import_source_full_path_regex_message }
@@ -141,44 +111,6 @@ RSpec.describe Gitlab::Regex, feature_category: :tooling do
}
end
- describe '.bulk_import_source_full_path_regex' do
- subject { described_class.bulk_import_source_full_path_regex }
-
- it { is_expected.not_to match("Users's something") }
- it { is_expected.not_to match('/source') }
- it { is_expected.not_to match('http:') }
- it { is_expected.not_to match('https:') }
- it { is_expected.not_to match('example.com/?stuff=true') }
- it { is_expected.not_to match('example.com:5000/?stuff=true') }
- it { is_expected.not_to match('http://gitlab.example/gitlab-org/manage/import/gitlab-migration-test') }
- it { is_expected.not_to match('source/') }
- it { is_expected.not_to match('') }
- it { is_expected.not_to match('.source/full./path') }
- it { is_expected.not_to match('?gitlab') }
- it { is_expected.not_to match('_good_for_me!') }
- it { is_expected.not_to match('group/@*%_my_other-project-----') }
- it { is_expected.not_to match('_foog-for-me!') }
- it { is_expected.not_to match('.source/full/path.') }
-
- it { is_expected.to match('good_for+you') }
- it { is_expected.to match('source') }
- it { is_expected.to match('.source') }
- it { is_expected.to match('_source') }
- it { is_expected.to match('source/full') }
- it { is_expected.to match('source/full/path') }
- it { is_expected.to match('domain_namespace') }
- it { is_expected.to match('gitlab-migration-test') }
- it { is_expected.to match('source/full/path-') }
- it { is_expected.to match('.source/full/path') }
- it { is_expected.to match('.source/.full/.path') }
- it { is_expected.to match('source/full/.path') }
- it { is_expected.to match('source/full/..path') }
- it { is_expected.to match('source/full/---1path') }
- it { is_expected.to match('source/full/-___path') }
- it { is_expected.to match('source/full/path---') }
- it { is_expected.to match('group/__my_other-project-----') }
- end
-
describe '.group_path_regex' do
subject { described_class.group_path_regex }
diff --git a/spec/lib/gitlab/usage/metrics/instrumentations/count_bulk_imports_entities_metric_spec.rb b/spec/lib/gitlab/usage/metrics/instrumentations/count_bulk_imports_entities_metric_spec.rb
index 0deb586d488..f19e83935f8 100644
--- a/spec/lib/gitlab/usage/metrics/instrumentations/count_bulk_imports_entities_metric_spec.rb
+++ b/spec/lib/gitlab/usage/metrics/instrumentations/count_bulk_imports_entities_metric_spec.rb
@@ -5,17 +5,17 @@ require 'spec_helper'
RSpec.describe Gitlab::Usage::Metrics::Instrumentations::CountBulkImportsEntitiesMetric, feature_category: :importers do
let_it_be(:user) { create(:user) }
let_it_be(:bulk_import_projects) do
- create_list(:bulk_import_entity, 2, source_type: 'project_entity', created_at: 3.weeks.ago, status: 2)
- create(:bulk_import_entity, source_type: 'project_entity', created_at: 3.weeks.ago, status: 0)
+ create_list(:bulk_import_entity, 2, :project_entity, created_at: 3.weeks.ago, status: 2)
+ create(:bulk_import_entity, :project_entity, created_at: 3.weeks.ago, status: 0)
end
let_it_be(:bulk_import_groups) do
- create_list(:bulk_import_entity, 2, source_type: 'group_entity', created_at: 3.weeks.ago, status: 2)
- create(:bulk_import_entity, source_type: 'group_entity', created_at: 3.weeks.ago, status: 0)
+ create_list(:bulk_import_entity, 2, :group_entity, created_at: 3.weeks.ago, status: 2)
+ create(:bulk_import_entity, :group_entity, created_at: 3.weeks.ago, status: 0)
end
let_it_be(:old_bulk_import_project) do
- create(:bulk_import_entity, source_type: 'project_entity', created_at: 2.months.ago, status: 2)
+ create(:bulk_import_entity, :project_entity, created_at: 2.months.ago, status: 2)
end
context 'with no source_type' do
diff --git a/spec/lib/gitlab/usage/metrics/instrumentations/count_imported_projects_total_metric_spec.rb b/spec/lib/gitlab/usage/metrics/instrumentations/count_imported_projects_total_metric_spec.rb
index bd432b614e7..bf3cd810e23 100644
--- a/spec/lib/gitlab/usage/metrics/instrumentations/count_imported_projects_total_metric_spec.rb
+++ b/spec/lib/gitlab/usage/metrics/instrumentations/count_imported_projects_total_metric_spec.rb
@@ -15,15 +15,15 @@ RSpec.describe Gitlab::Usage::Metrics::Instrumentations::CountImportedProjectsTo
let_it_be(:old_import) { create(:project, import_type: 'gitea', creator_id: user.id, created_at: 2.months.ago) }
let_it_be(:bulk_import_projects) do
- create_list(:bulk_import_entity, 3, source_type: 'project_entity', created_at: 3.weeks.ago)
+ create_list(:bulk_import_entity, 3, :project_entity, created_at: 3.weeks.ago)
end
let_it_be(:bulk_import_groups) do
- create_list(:bulk_import_entity, 3, source_type: 'group_entity', created_at: 3.weeks.ago)
+ create_list(:bulk_import_entity, 3, :group_entity, created_at: 3.weeks.ago)
end
let_it_be(:old_bulk_import_project) do
- create(:bulk_import_entity, source_type: 'project_entity', created_at: 2.months.ago)
+ create(:bulk_import_entity, :project_entity, created_at: 2.months.ago)
end
before do
diff --git a/spec/models/bulk_imports/entity_spec.rb b/spec/models/bulk_imports/entity_spec.rb
index 7179ed7cb42..da1eb12e9b8 100644
--- a/spec/models/bulk_imports/entity_spec.rb
+++ b/spec/models/bulk_imports/entity_spec.rb
@@ -24,11 +24,20 @@ RSpec.describe BulkImports::Entity, type: :model, feature_category: :importers d
it { is_expected.to define_enum_for(:source_type).with_values(%i[group_entity project_entity]) }
- context 'when formatting with regexes' do
+ context 'when source_type is group_entity' do
+ subject { build(:bulk_import_entity, :group_entity) }
+
it { is_expected.to allow_values('source', 'source/path', 'source/full/path').for(:source_full_path) }
it { is_expected.not_to allow_values('/source', 'http://source/path', 'sou rce/full/path', '').for(:source_full_path) }
end
+ context 'when source_type is project_entity' do
+ subject { build(:bulk_import_entity, :project_entity) }
+
+ it { is_expected.to allow_values('source/path', 'source/full/path').for(:source_full_path) }
+ it { is_expected.not_to allow_values('/source', 'source', 'http://source/path', 'sou rce/full/path', '').for(:source_full_path) }
+ end
+
context 'when associated with a group and project' do
it 'is invalid' do
entity = build(:bulk_import_entity, group: build(:group), project: build(:project))
diff --git a/spec/policies/project_policy_spec.rb b/spec/policies/project_policy_spec.rb
index 2854d6daece..350d834e63e 100644
--- a/spec/policies/project_policy_spec.rb
+++ b/spec/policies/project_policy_spec.rb
@@ -578,6 +578,11 @@ RSpec.describe ProjectPolicy, feature_category: :system_access do
expect(described_class.new(maintainer, project)).to be_allowed(:admin_incident_management_timeline_event_tag)
expect(described_class.new(owner, project)).to be_allowed(:admin_incident_management_timeline_event_tag)
end
+
+ it 'allows to read import error' do
+ expect(described_class.new(maintainer, project)).to be_allowed(:read_import_error)
+ expect(described_class.new(owner, project)).to be_allowed(:read_import_error)
+ end
end
context 'when user is a developer/guest/reporter' do
@@ -586,6 +591,12 @@ RSpec.describe ProjectPolicy, feature_category: :system_access do
expect(described_class.new(guest, project)).to be_disallowed(:admin_incident_management_timeline_event_tag)
expect(described_class.new(reporter, project)).to be_disallowed(:admin_incident_management_timeline_event_tag)
end
+
+ it 'disallows reading the import error' do
+ expect(described_class.new(developer, project)).to be_disallowed(:read_import_error)
+ expect(described_class.new(guest, project)).to be_disallowed(:read_import_error)
+ expect(described_class.new(reporter, project)).to be_disallowed(:read_import_error)
+ end
end
context 'when user is not a member of the project' do
diff --git a/spec/requests/api/bulk_imports_spec.rb b/spec/requests/api/bulk_imports_spec.rb
index b159d4ad445..fdbfbf052d0 100644
--- a/spec/requests/api/bulk_imports_spec.rb
+++ b/spec/requests/api/bulk_imports_spec.rb
@@ -248,6 +248,20 @@ RSpec.describe API::BulkImports, feature_category: :importers do
end
end
+ context 'when the destination_namespace is invalid' do
+ it 'returns invalid error' do
+ params[:entities][0][:destination_namespace] = 'dest?nation-namespace'
+
+ request
+ expect(response).to have_gitlab_http_status(:bad_request)
+ expect(json_response['error']).to include('entities[0][destination_namespace] must have a relative path ' \
+ 'structure with no HTTP protocol characters, or leading or ' \
+ 'trailing forward slashes. Path segments must not start or end ' \
+ 'with a special character, and must not contain consecutive ' \
+ 'special characters.')
+ end
+ end
+
context 'when the destination_slug is invalid' do
it 'returns invalid error when restricting special characters is disabled' do
Feature.disable(:restrict_special_characters_in_namespace_path)
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index 26132215404..601e8cb3081 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -3311,16 +3311,41 @@ RSpec.describe API::Projects, :aggregate_failures, feature_category: :groups_and
project_fork_target.add_maintainer(user)
end
- it 'allows project to be forked from an existing project' do
- expect(project_fork_target).not_to be_forked
+ context 'and user is a reporter of target group' do
+ let_it_be_with_reload(:target_group) { create(:group, project_creation_level: ::Gitlab::Access::DEVELOPER_MAINTAINER_PROJECT_ACCESS) }
+ let_it_be_with_reload(:project_fork_target) { create(:project, namespace: target_group) }
- post api(path, user)
- project_fork_target.reload
+ before do
+ target_group.add_reporter(user)
+ end
- expect(response).to have_gitlab_http_status(:created)
- expect(project_fork_target.forked_from_project.id).to eq(project_fork_source.id)
- expect(project_fork_target.fork_network_member).to be_present
- expect(project_fork_target).to be_forked
+ it 'fails as target namespace is unauthorized' do
+ post api(path, user)
+
+ expect(response).to have_gitlab_http_status(:unauthorized)
+ expect(json_response['message']).to eq "401 Unauthorized - Target Namespace"
+ end
+ end
+
+ context 'and user is a developer of target group' do
+ let_it_be_with_reload(:target_group) { create(:group, project_creation_level: ::Gitlab::Access::DEVELOPER_MAINTAINER_PROJECT_ACCESS) }
+ let_it_be_with_reload(:project_fork_target) { create(:project, namespace: target_group) }
+
+ before do
+ target_group.add_developer(user)
+ end
+
+ it 'allows project to be forked from an existing project' do
+ expect(project_fork_target).not_to be_forked
+
+ post api(path, user)
+ project_fork_target.reload
+
+ expect(response).to have_gitlab_http_status(:created)
+ expect(project_fork_target.forked_from_project.id).to eq(project_fork_source.id)
+ expect(project_fork_target.fork_network_member).to be_present
+ expect(project_fork_target).to be_forked
+ end
end
it 'fails without permission from forked_from project' do
diff --git a/spec/services/error_tracking/list_projects_service_spec.rb b/spec/services/error_tracking/list_projects_service_spec.rb
index 8408adcc21d..d91808edc8d 100644
--- a/spec/services/error_tracking/list_projects_service_spec.rb
+++ b/spec/services/error_tracking/list_projects_service_spec.rb
@@ -6,7 +6,7 @@ RSpec.describe ErrorTracking::ListProjectsService, feature_category: :integratio
let_it_be(:user) { create(:user) }
let_it_be(:project, reload: true) { create(:project) }
- let(:sentry_url) { 'https://sentrytest.gitlab.com/api/0/projects/sentry-org/sentry-project' }
+ let(:sentry_url) { 'https://sentrytest.gitlab.com/api/0/projects/org/proj/' }
let(:token) { 'test-token' }
let(:new_api_host) { 'https://gitlab.com/' }
let(:new_token) { 'new-token' }
@@ -66,6 +66,20 @@ RSpec.describe ErrorTracking::ListProjectsService, feature_category: :integratio
end
end
+ context 'with the similar api host' do
+ let(:api_host) { 'https://sentrytest.gitlab.co' }
+
+ it 'returns an error' do
+ expect(result[:message]).to start_with('Token is a required field')
+ expect(error_tracking_setting).not_to be_valid
+ expect(error_tracking_setting).not_to receive(:list_sentry_projects)
+ end
+
+ it 'resets the token' do
+ expect { subject.execute }.to change { error_tracking_setting.token }.from(token).to(nil)
+ end
+ end
+
context 'with a new api host' do
let(:api_host) { new_api_host }
diff --git a/spec/support/shared_examples/features/search/redacted_search_results_shared_examples.rb b/spec/support/shared_examples/features/search/redacted_search_results_shared_examples.rb
index cbd0ffbab21..f2052f4202d 100644
--- a/spec/support/shared_examples/features/search/redacted_search_results_shared_examples.rb
+++ b/spec/support/shared_examples/features/search/redacted_search_results_shared_examples.rb
@@ -254,7 +254,7 @@ RSpec.shared_examples 'a redacted search results' do
end
context 'with :with_api_entity_associations' do
- it_behaves_like "redaction limits N+1 queries", limit: 14
+ it_behaves_like "redaction limits N+1 queries", limit: 15
end
end