summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Release Tools Bot <delivery-team+release-tools@gitlab.com>2022-11-30 18:46:14 +0000
committerGitLab Release Tools Bot <delivery-team+release-tools@gitlab.com>2022-11-30 18:46:14 +0000
commitb28d1361dd1ec63053dbfae475097928c6d01116 (patch)
treec77e0d33f4b7733391fb5550f5514a35296b71f8
parentf981f6691d5395e04ee2858593135c448c10757d (diff)
parent779fe6c4b74b73e2db8ab7cb8d304fcbbd73a704 (diff)
downloadgitlab-ce-b28d1361dd1ec63053dbfae475097928c6d01116.tar.gz
Merge remote-tracking branch 'dev/15-6-stable' into 15-6-stable
-rw-r--r--CHANGELOG.md21
-rw-r--r--GITALY_SERVER_VERSION2
-rw-r--r--Gemfile2
-rw-r--r--Gemfile.checksum2
-rw-r--r--Gemfile.lock4
-rw-r--r--VERSION2
-rw-r--r--app/models/ci/build_runner_session.rb20
-rw-r--r--app/models/hooks/web_hook.rb6
-rw-r--r--app/models/hooks/web_hook_log.rb7
-rw-r--r--app/models/integrations/jira.rb5
-rw-r--r--app/models/project.rb2
-rw-r--r--app/models/repository.rb8
-rw-r--r--app/models/user.rb2
-rw-r--r--app/policies/packages/policies/group_policy.rb2
-rw-r--r--app/services/markup/rendering_service.rb8
-rw-r--r--app/services/packages/nuget/metadata_extraction_service.rb10
-rw-r--r--app/services/projects/import_service.rb32
-rw-r--r--app/services/web_hooks/log_execution_service.rb2
-rw-r--r--app/views/projects/tags/_release_link.html.haml9
-rw-r--r--app/views/projects/tags/show.html.haml13
-rw-r--r--doc/user/admin_area/settings/external_authorization.md3
-rw-r--r--doc/user/packages/package_registry/index.md1
-rw-r--r--doc/user/project/deploy_keys/index.md2
-rw-r--r--doc/user/project/deploy_tokens/index.md2
-rw-r--r--lib/gitlab/api_authentication/token_resolver.rb2
-rw-r--r--lib/gitlab/auth/auth_finders.rb1
-rw-r--r--lib/gitlab/ci/pipeline/chain/populate_metadata.rb4
-rw-r--r--lib/gitlab/git/repository.rb13
-rw-r--r--lib/gitlab/git_access.rb4
-rw-r--r--lib/gitlab/gitaly_client/repository_service.rb10
-rw-r--r--lib/gitlab/hook_data/project_builder.rb2
-rw-r--r--lib/gitlab/hook_data/project_member_builder.rb2
-rw-r--r--spec/features/tags/developer_views_tags_spec.rb2
-rw-r--r--spec/fixtures/packages/nuget/corrupted_package.nupkgbin0 -> 3513 bytes
-rw-r--r--spec/lib/gitlab/api_authentication/token_resolver_spec.rb12
-rw-r--r--spec/lib/gitlab/auth/auth_finders_spec.rb9
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/populate_metadata_spec.rb14
-rw-r--r--spec/lib/gitlab/git/repository_spec.rb7
-rw-r--r--spec/lib/gitlab/git_access_spec.rb28
-rw-r--r--spec/lib/gitlab/gitaly_client/repository_service_spec.rb63
-rw-r--r--spec/lib/gitlab/hook_data/project_builder_spec.rb4
-rw-r--r--spec/lib/gitlab/hook_data/project_member_builder_spec.rb2
-rw-r--r--spec/models/ci/build_runner_session_spec.rb51
-rw-r--r--spec/models/hooks/web_hook_log_spec.rb18
-rw-r--r--spec/models/hooks/web_hook_spec.rb30
-rw-r--r--spec/models/integrations/jira_spec.rb13
-rw-r--r--spec/models/project_import_state_spec.rb2
-rw-r--r--spec/models/project_spec.rb4
-rw-r--r--spec/models/repository_spec.rb13
-rw-r--r--spec/requests/api/ci/runner/jobs_request_post_spec.rb35
-rw-r--r--spec/requests/jira_connect/users_controller_spec.rb11
-rw-r--r--spec/services/markup/rendering_service_spec.rb5
-rw-r--r--spec/services/packages/nuget/metadata_extraction_service_spec.rb11
-rw-r--r--spec/services/projects/import_service_spec.rb163
-rw-r--r--spec/services/web_hooks/log_execution_service_spec.rb15
-rw-r--r--spec/support/shared_examples/features/user_views_tag_shared_examples.rb47
56 files changed, 658 insertions, 106 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 19d2707531f..881670144b5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,27 @@
documentation](doc/development/changelog.md) for instructions on adding your own
entry.
+## 15.6.1 (2022-11-30)
+
+### Fixed (1 change)
+
+- [Do not save PipelineMetadata if name is blank [15.6]](gitlab-org/security/gitlab@340cd5f74dbe8318105574303d49d6cda54b43bf) ([merge request](gitlab-org/security/gitlab!2947))
+
+### Security (12 changes)
+
+- [Send resolved_address param to gitaly during repository import](gitlab-org/security/gitlab@5b3540629cb8d113d96d721549be77ef35060c15) ([merge request](gitlab-org/security/gitlab!2938))
+- [Add size validation during nuspec file extraction](gitlab-org/security/gitlab@d7048d0bf20574a5b1c926ac25b8c15504723da3) ([merge request](gitlab-org/security/gitlab!2935))
+- [Cross-site scripting in Jira Integration](gitlab-org/security/gitlab@1419e9d1513d481472b89d36e9e22b7b20c3a5c5) ([merge request](gitlab-org/security/gitlab!2930))
+- [Protect web-hook secret tokens after changing URL](gitlab-org/security/gitlab@d3df2d08f7ec59d2e4ebba64770c6b7309733d9b) ([merge request](gitlab-org/security/gitlab!2920))
+- [HTML content injection in README file](gitlab-org/security/gitlab@c64a283ee09115d1edefb5fcd81a9766658757e4) ([merge request](gitlab-org/security/gitlab!2928))
+- [Redact secret tokens from web-hook logs](gitlab-org/security/gitlab@bca8656f7a04759acec00170f9e3cabbdda45558) ([merge request](gitlab-org/security/gitlab!2916))
+- [Prevent unauthorized users from seeing Release information on tag pages](gitlab-org/security/gitlab@f04b3cf159f40e98ea0d24df0ff168ae91522813) ([merge request](gitlab-org/security/gitlab!2927))
+- [Update after_import to expire cache before removing prohibited branches](gitlab-org/security/gitlab@49de4ce145d00adecf33c19c8413a87e6bb0c904) ([merge request](gitlab-org/security/gitlab!2905))
+- [Deny all package permissions when group access is restricted by IP](gitlab-org/security/gitlab@cca110162915b2cdca64181305bfed2044df2bba) ([merge request](gitlab-org/security/gitlab!2902))
+- [Redact user emails from project webhook data](gitlab-org/security/gitlab@9148dd7f77cab086d696d56907d2cbbc921e0e6d) ([merge request](gitlab-org/security/gitlab!2934))
+- [Disallow local URls for build_runner_session if dictated by app setting](gitlab-org/security/gitlab@1c98ba9dbe78e5969213e1a66f7b3922557e67ec) ([merge request](gitlab-org/security/gitlab!2924))
+- [Prevent token bypass for extenal authorisation](gitlab-org/security/gitlab@95eb5d2f641d7a5329aca37f92792de02f115eb8) ([merge request](gitlab-org/security/gitlab!2929))
+
## 15.6.0 (2022-11-21)
### Added (150 changes)
diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION
index b75517323b7..0ed900b4221 100644
--- a/GITALY_SERVER_VERSION
+++ b/GITALY_SERVER_VERSION
@@ -1 +1 @@
-15.6.0 \ No newline at end of file
+15.6.1 \ No newline at end of file
diff --git a/Gemfile b/Gemfile
index 87869f6f1e4..4dd695e69d1 100644
--- a/Gemfile
+++ b/Gemfile
@@ -500,7 +500,7 @@ gem 'ssh_data', '~> 1.3'
gem 'spamcheck', '~> 1.0.0'
# Gitaly GRPC protocol definitions
-gem 'gitaly', '~> 15.5.0'
+gem 'gitaly', '~> 15.5.2'
# KAS GRPC protocol definitions
gem 'kas-grpc', '~> 0.0.2'
diff --git a/Gemfile.checksum b/Gemfile.checksum
index f8202c49abe..a8ae702878f 100644
--- a/Gemfile.checksum
+++ b/Gemfile.checksum
@@ -199,7 +199,7 @@
{"name":"gettext_i18n_rails","version":"1.8.0","platform":"ruby","checksum":"95e5cf8440b1e08705b27f2bccb56143272c5a7a0dabcf54ea1bd701140a496f"},
{"name":"gettext_i18n_rails_js","version":"1.3.0","platform":"ruby","checksum":"5d10afe4be3639bff78c50a56768c20f39aecdabc580c08aa45573911c2bd687"},
{"name":"git","version":"1.11.0","platform":"ruby","checksum":"7e95ba4da8298a0373ef1a6862aa22007d761f3c8274b675aa787966fecea0f1"},
-{"name":"gitaly","version":"15.5.0","platform":"ruby","checksum":"d85dd4890a1f0fd95f935c848bcedf03f19b78872f20f04b9811e602bea4ef42"},
+{"name":"gitaly","version":"15.5.2","platform":"ruby","checksum":"62babe0596a4505bf95051ea50f17160055e6cf6cacf209273691542120d7881"},
{"name":"gitlab","version":"4.16.1","platform":"ruby","checksum":"13fd7059cbdad5a1a21b15fa2cf9070b97d92e27f8c688581fe3d84dc038074f"},
{"name":"gitlab-chronic","version":"0.10.5","platform":"ruby","checksum":"f80f18dc699b708870a80685243331290bc10cfeedb6b99c92219722f729c875"},
{"name":"gitlab-dangerfiles","version":"3.6.2","platform":"ruby","checksum":"88585532bbb5c0e862ad0776b3804a32129eab06c6a8a7bc96b577baa7aac6c5"},
diff --git a/Gemfile.lock b/Gemfile.lock
index c6345cb7f5d..183f99e66fa 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -554,7 +554,7 @@ GEM
rails (>= 3.2.0)
git (1.11.0)
rchardet (~> 1.8)
- gitaly (15.5.0)
+ gitaly (15.5.2)
grpc (~> 1.0)
gitlab (4.16.1)
httparty (~> 0.14, >= 0.14.0)
@@ -1663,7 +1663,7 @@ DEPENDENCIES
gettext (~> 3.3)
gettext_i18n_rails (~> 1.8.0)
gettext_i18n_rails_js (~> 1.3)
- gitaly (~> 15.5.0)
+ gitaly (~> 15.5.2)
gitlab-chronic (~> 0.10.5)
gitlab-dangerfiles (~> 3.6.2)
gitlab-experiment (~> 0.7.1)
diff --git a/VERSION b/VERSION
index b75517323b7..0ed900b4221 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-15.6.0 \ No newline at end of file
+15.6.1 \ No newline at end of file
diff --git a/app/models/ci/build_runner_session.rb b/app/models/ci/build_runner_session.rb
index c6dbb5d0a43..0f37ce70964 100644
--- a/app/models/ci/build_runner_session.rb
+++ b/app/models/ci/build_runner_session.rb
@@ -13,14 +13,15 @@ module Ci
belongs_to :build, class_name: 'Ci::Build', inverse_of: :runner_session
validates :build, presence: true
- validates :url, addressable_url: { schemes: %w(https) }
+ validates :url, public_url: { schemes: %w(https) }
def terminal_specification
- wss_url = Gitlab::UrlHelpers.as_wss(self.url)
+ wss_url = Gitlab::UrlHelpers.as_wss(Addressable::URI.escape(self.url))
return {} unless wss_url.present?
- wss_url = "#{wss_url}/exec"
- channel_specification(wss_url, TERMINAL_SUBPROTOCOL)
+ parsed_wss_url = URI.parse(wss_url)
+ parsed_wss_url.path += '/exec'
+ channel_specification(parsed_wss_url, TERMINAL_SUBPROTOCOL)
end
def service_specification(service: nil, path: nil, port: nil, subprotocols: nil)
@@ -28,20 +29,21 @@ module Ci
port = port.presence || DEFAULT_PORT_NAME
service = service.presence || DEFAULT_SERVICE_NAME
- url = "#{self.url}/proxy/#{service}/#{port}/#{path}"
+ parsed_url = URI.parse(Addressable::URI.escape(self.url))
+ parsed_url.path += "/proxy/#{service}/#{port}/#{path}"
subprotocols = subprotocols.presence || ::Ci::BuildRunnerSession::TERMINAL_SUBPROTOCOL
- channel_specification(url, subprotocols)
+ channel_specification(parsed_url, subprotocols)
end
private
- def channel_specification(url, subprotocol)
- return {} if subprotocol.blank? || url.blank?
+ def channel_specification(parsed_url, subprotocol)
+ return {} if subprotocol.blank? || parsed_url.blank?
{
subprotocols: Array(subprotocol),
- url: url,
+ url: Addressable::URI.unescape(parsed_url.to_s),
headers: { Authorization: [authorization.presence] }.compact,
ca_pem: certificate.presence
}
diff --git a/app/models/hooks/web_hook.rb b/app/models/hooks/web_hook.rb
index 05e50c17988..946cdda2e75 100644
--- a/app/models/hooks/web_hook.rb
+++ b/app/models/hooks/web_hook.rb
@@ -39,6 +39,8 @@ class WebHook < ApplicationRecord
validates :token, format: { without: /\n/ }
after_initialize :initialize_url_variables
+
+ before_validation :reset_token
before_validation :set_branch_filter_nil, \
if: -> { branch_filter_strategy_all_branches? && enhanced_webhook_support_regex? }
validates :push_events_branch_filter, \
@@ -218,6 +220,10 @@ class WebHook < ApplicationRecord
private
+ def reset_token
+ self.token = nil if url_changed? && !encrypted_token_changed?
+ end
+
def next_failure_count
recent_failures.succ.clamp(1, MAX_FAILURES)
end
diff --git a/app/models/hooks/web_hook_log.rb b/app/models/hooks/web_hook_log.rb
index 2b26147b494..9de6f2a1b57 100644
--- a/app/models/hooks/web_hook_log.rb
+++ b/app/models/hooks/web_hook_log.rb
@@ -48,6 +48,13 @@ class WebHookLog < ApplicationRecord
request_data == OVERSIZE_REQUEST_DATA
end
+ def request_headers
+ super unless web_hook.token?
+ super if self[:request_headers]['X-Gitlab-Token'] == _('[REDACTED]')
+
+ self[:request_headers].merge('X-Gitlab-Token' => _('[REDACTED]'))
+ end
+
private
def obfuscate_basic_auth
diff --git a/app/models/integrations/jira.rb b/app/models/integrations/jira.rb
index 30497c0110e..65492bfd9c2 100644
--- a/app/models/integrations/jira.rb
+++ b/app/models/integrations/jira.rb
@@ -97,7 +97,10 @@ module Integrations
def self.valid_jira_cloud_url?(url)
return false unless url.present?
- !!URI(url).hostname&.end_with?(JIRA_CLOUD_HOST)
+ uri = URI.parse(url)
+ uri.is_a?(URI::HTTPS) && !!uri.hostname&.end_with?(JIRA_CLOUD_HOST)
+ rescue URI::InvalidURIError
+ false
end
def data_fields
diff --git a/app/models/project.rb b/app/models/project.rb
index a07d4147228..0c4f76fb2b9 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -2152,8 +2152,8 @@ class Project < ApplicationRecord
end
def after_import
- repository.remove_prohibited_branches
repository.expire_content_cache
+ repository.remove_prohibited_branches
wiki.repository.expire_content_cache
DetectRepositoryLanguagesWorker.perform_async(id)
diff --git a/app/models/repository.rb b/app/models/repository.rb
index 95d1b815e74..90e87de4a5b 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -984,12 +984,12 @@ class Repository
end
end
- def clone_as_mirror(url, http_authorization_header: "")
- import_repository(url, http_authorization_header: http_authorization_header, mirror: true)
+ def clone_as_mirror(url, http_authorization_header: "", resolved_address: "")
+ import_repository(url, http_authorization_header: http_authorization_header, mirror: true, resolved_address: resolved_address)
end
- def fetch_as_mirror(url, forced: false, refmap: :all_refs, prune: true, http_authorization_header: "")
- fetch_remote(url, refmap: refmap, forced: forced, prune: prune, http_authorization_header: http_authorization_header)
+ def fetch_as_mirror(url, forced: false, refmap: :all_refs, prune: true, http_authorization_header: "", resolved_address: "")
+ fetch_remote(url, refmap: refmap, forced: forced, prune: prune, http_authorization_header: http_authorization_header, resolved_address: resolved_address)
end
def fetch_source_branch!(source_repository, source_branch, local_ref)
diff --git a/app/models/user.rb b/app/models/user.rb
index 24f947183a2..b4b8a7ef7ad 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -1556,7 +1556,7 @@ class User < ApplicationRecord
name: name,
username: username,
avatar_url: avatar_url(only_path: false),
- email: public_email.presence || _('[REDACTED]')
+ email: webhook_email
}
end
diff --git a/app/policies/packages/policies/group_policy.rb b/app/policies/packages/policies/group_policy.rb
index 32dbcb1b65b..d8c20c7a90a 100644
--- a/app/policies/packages/policies/group_policy.rb
+++ b/app/policies/packages/policies/group_policy.rb
@@ -25,3 +25,5 @@ module Packages
end
end
end
+
+Packages::Policies::GroupPolicy.prepend_mod_with('Packages::Policies::GroupPolicy')
diff --git a/app/services/markup/rendering_service.rb b/app/services/markup/rendering_service.rb
index 0142d600522..c4abbb6b5b0 100644
--- a/app/services/markup/rendering_service.rb
+++ b/app/services/markup/rendering_service.rb
@@ -2,8 +2,6 @@
module Markup
class RenderingService
- include ActionView::Helpers::TextHelper
-
# Let's increase the render timeout
# For a smaller one, a test that renders the blob content statically fails
# We can consider removing this custom timeout when markup_rendering_timeout FF is removed:
@@ -51,7 +49,7 @@ module Markup
rescue StandardError => e
Gitlab::ErrorTracking.track_exception(e, project_id: context[:project]&.id, file_name: file_name)
- simple_format(text)
+ ActionController::Base.helpers.simple_format(text)
end
def markdown_unsafe
@@ -63,7 +61,9 @@ module Markup
end
def plain_unsafe
- "<pre class=\"plain-readme\">#{text}</pre>"
+ ActionController::Base.helpers.content_tag :pre, class: 'plain-readme' do
+ text
+ end
end
def other_markup_unsafe
diff --git a/app/services/packages/nuget/metadata_extraction_service.rb b/app/services/packages/nuget/metadata_extraction_service.rb
index 66abd189153..02086b2a282 100644
--- a/app/services/packages/nuget/metadata_extraction_service.rb
+++ b/app/services/packages/nuget/metadata_extraction_service.rb
@@ -104,9 +104,15 @@ module Packages
entry = zip_file.glob('*.nuspec').first
raise ExtractionError, 'nuspec file not found' unless entry
- raise ExtractionError, 'nuspec file too big' if entry.size > MAX_FILE_SIZE
+ raise ExtractionError, 'nuspec file too big' if MAX_FILE_SIZE < entry.size
- entry.get_input_stream.read
+ Tempfile.open("nuget_extraction_package_file_#{@package_file_id}") do |file|
+ entry.extract(file.path) { true } # allow #extract to overwrite the file
+ file.unlink
+ file.read
+ end
+ rescue Zip::EntrySizeError => e
+ raise ExtractionError, "nuspec file has the wrong entry size: #{e.message}"
end
end
diff --git a/app/services/projects/import_service.rb b/app/services/projects/import_service.rb
index de7ede4eabf..6a13b8e38c1 100644
--- a/app/services/projects/import_service.rb
+++ b/app/services/projects/import_service.rb
@@ -53,6 +53,8 @@ module Projects
private
+ attr_reader :resolved_address
+
def after_execute_hook
# Defined in EE::Projects::ImportService
end
@@ -64,11 +66,7 @@ module Projects
def add_repository_to_project
if project.external_import? && !unknown_url?
begin
- Gitlab::UrlBlocker.validate!(
- project.import_url,
- schemes: Project::VALID_IMPORT_PROTOCOLS,
- ports: Project::VALID_IMPORT_PORTS
- )
+ @resolved_address = get_resolved_address
rescue Gitlab::UrlBlocker::BlockedUrlError => e
raise e, s_("ImportProjects|Blocked import URL: %{message}") % { message: e.message }
end
@@ -97,9 +95,9 @@ module Projects
if refmap
project.ensure_repository
- project.repository.fetch_as_mirror(project.import_url, refmap: refmap)
+ project.repository.fetch_as_mirror(project.import_url, refmap: refmap, resolved_address: resolved_address)
else
- project.repository.import_repository(project.import_url)
+ project.repository.import_repository(project.import_url, resolved_address: resolved_address)
end
rescue ::Gitlab::Git::CommandError => e
# Expire cache to prevent scenarios such as:
@@ -157,6 +155,26 @@ module Projects
def importer_imports_repository?
has_importer? && importer_class.try(:imports_repository?)
end
+
+ def get_resolved_address
+ Gitlab::UrlBlocker
+ .validate!(
+ project.import_url,
+ schemes: Project::VALID_IMPORT_PROTOCOLS,
+ ports: Project::VALID_IMPORT_PORTS,
+ dns_rebind_protection: dns_rebind_protection?)
+ .then do |(import_url, resolved_host)|
+ next '' if resolved_host.nil? || !import_url.scheme.in?(%w[http https])
+
+ import_url.host.to_s
+ end
+ end
+
+ def dns_rebind_protection?
+ return false if Gitlab.http_proxy_env?
+
+ Gitlab::CurrentSettings.dns_rebinding_protection_enabled?
+ end
end
end
diff --git a/app/services/web_hooks/log_execution_service.rb b/app/services/web_hooks/log_execution_service.rb
index 1a40c877bda..448bb7d4097 100644
--- a/app/services/web_hooks/log_execution_service.rb
+++ b/app/services/web_hooks/log_execution_service.rb
@@ -24,6 +24,8 @@ module WebHooks
private
def log_execution
+ log_data[:request_headers]['X-Gitlab-Token'] = _('[REDACTED]') if hook.token?
+
WebHookLog.create!(web_hook: hook, **log_data)
end
diff --git a/app/views/projects/tags/_release_link.html.haml b/app/views/projects/tags/_release_link.html.haml
index c942d122a58..6c79b13f438 100644
--- a/app/views/projects/tags/_release_link.html.haml
+++ b/app/views/projects/tags/_release_link.html.haml
@@ -1,4 +1,5 @@
-.gl-text-secondary
- = sprite_icon("rocket", size: 12)
- = _("Release")
- = link_to release.name, project_release_path(project, release), class: "gl-text-blue-600!"
+- if can?(current_user, :read_release, release)
+ .gl-text-secondary
+ = sprite_icon("rocket", size: 12)
+ = _("Release")
+ = link_to release.name, project_release_path(project, release), class: "gl-text-blue-600!"
diff --git a/app/views/projects/tags/show.html.haml b/app/views/projects/tags/show.html.haml
index cb7751ecf2e..a9c3309e38c 100644
--- a/app/views/projects/tags/show.html.haml
+++ b/app/views/projects/tags/show.html.haml
@@ -57,12 +57,13 @@
%pre.wrap{ data: { qa_selector: 'tag_message_content' } }
= strip_signature(@tag.message)
-.gl-mb-3.gl-mt-3
- - if @release&.description.present?
- .description.md{ data: { qa_selector: 'tag_release_notes_content' } }
- = markdown_field(@release, :description)
- - else
- = s_('TagsPage|This tag has no release notes.')
+- if can?(current_user, :read_release, @release)
+ .gl-mb-3.gl-mt-3
+ - if @release&.description.present?
+ .description.md{ data: { qa_selector: 'tag_release_notes_content' } }
+ = markdown_field(@release, :description)
+ - else
+ = s_('TagsPage|This tag has no release notes.')
- if can?(current_user, :admin_tag, @project)
.js-delete-tag-modal
diff --git a/doc/user/admin_area/settings/external_authorization.md b/doc/user/admin_area/settings/external_authorization.md
index a34ceac0d95..09ac477b062 100644
--- a/doc/user/admin_area/settings/external_authorization.md
+++ b/doc/user/admin_area/settings/external_authorization.md
@@ -43,6 +43,9 @@ using Omnibus, learn to install a custom CA in the
Alternatively, learn where to install custom certificates by using
`openssl version -d`.
+When external authorization is enabled, [deploy tokens](../../project/deploy_tokens/index.md)
+ and [deploy keys](../../project/deploy_keys/index.md) can't be used for Git operations.
+
## Configuration
The external authorization service can be enabled by an administrator:
diff --git a/doc/user/packages/package_registry/index.md b/doc/user/packages/package_registry/index.md
index 8e160cbb195..1aeb98fd48a 100644
--- a/doc/user/packages/package_registry/index.md
+++ b/doc/user/packages/package_registry/index.md
@@ -62,6 +62,7 @@ For most package types, the following credential types are valid:
NOTE:
If you have not activated the "Packages" feature for your project at **Settings > General > Project features**, you will receive a 403 Forbidden response.
+Accessing package registry via deploy token is not available when external authorization is enabled.
## Use GitLab CI/CD to build packages
diff --git a/doc/user/project/deploy_keys/index.md b/doc/user/project/deploy_keys/index.md
index 58f7d3198b2..56bb899c233 100644
--- a/doc/user/project/deploy_keys/index.md
+++ b/doc/user/project/deploy_keys/index.md
@@ -18,6 +18,8 @@ Depending on your needs, you might want to use a [deploy token](../deploy_tokens
| Validity | Valid as long as it's registered and enabled. | Can be given an expiration date. |
| Registry access | Cannot access a package registry. | Can read from and write to a package registry. |
+Deploy keys can't be used for Git operations if [external authorization](../../admin_area/settings/external_authorization.md) is enabled.
+
## Scope
A deploy key has a defined scope when it is created:
diff --git a/doc/user/project/deploy_tokens/index.md b/doc/user/project/deploy_tokens/index.md
index aab72d4859e..3dd6f14ea70 100644
--- a/doc/user/project/deploy_tokens/index.md
+++ b/doc/user/project/deploy_tokens/index.md
@@ -41,6 +41,8 @@ You can create deploy tokens at either the project or group level:
By default, a deploy token does not expire. You can optionally set an expiry date when you create
it. Expiry occurs at midnight UTC on that date.
+Deploy tokens can't be used for Git operations and Package Registry operations if [external authorization](../../admin_area/settings/external_authorization.md) is enabled.
+
## Scope
A deploy token's scope determines the actions it can perform.
diff --git a/lib/gitlab/api_authentication/token_resolver.rb b/lib/gitlab/api_authentication/token_resolver.rb
index dd9039e37f6..afada055928 100644
--- a/lib/gitlab/api_authentication/token_resolver.rb
+++ b/lib/gitlab/api_authentication/token_resolver.rb
@@ -165,6 +165,8 @@ module Gitlab
end
def with_deploy_token(raw, &block)
+ raise ::Gitlab::Auth::UnauthorizedError if Gitlab::ExternalAuthorization.enabled?
+
token = ::DeployToken.active.find_by_token(raw.password)
return unless token
diff --git a/lib/gitlab/auth/auth_finders.rb b/lib/gitlab/auth/auth_finders.rb
index c994f179b66..16bee187c87 100644
--- a/lib/gitlab/auth/auth_finders.rb
+++ b/lib/gitlab/auth/auth_finders.rb
@@ -147,6 +147,7 @@ module Gitlab
# deploy tokens are accepted with deploy token headers and basic auth headers
def deploy_token_from_request
return unless route_authentication_setting[:deploy_token_allowed]
+ return if Gitlab::ExternalAuthorization.enabled?
token = current_request.env[DEPLOY_TOKEN_HEADER].presence || parsed_oauth_token
diff --git a/lib/gitlab/ci/pipeline/chain/populate_metadata.rb b/lib/gitlab/ci/pipeline/chain/populate_metadata.rb
index 35b907b669c..89befb2a65b 100644
--- a/lib/gitlab/ci/pipeline/chain/populate_metadata.rb
+++ b/lib/gitlab/ci/pipeline/chain/populate_metadata.rb
@@ -28,7 +28,9 @@ module Gitlab
name = @command.yaml_processor_result.workflow_name
name = ExpandVariables.expand(name, -> { global_context.variables.sort_and_expand_all })
- pipeline.build_pipeline_metadata(project: pipeline.project, name: name)
+ return if name.blank?
+
+ pipeline.build_pipeline_metadata(project: pipeline.project, name: name.strip)
end
def global_context
diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb
index b8f4ff0e9c4..3b5151ef4f2 100644
--- a/lib/gitlab/git/repository.rb
+++ b/lib/gitlab/git/repository.rb
@@ -861,7 +861,11 @@ module Gitlab
# no_tags - should we use --no-tags flag?
# prune - should we use --prune flag?
# check_tags_changed - should we ask gitaly to calculate whether any tags changed?
- def fetch_remote(url, refmap: nil, ssh_auth: nil, forced: false, no_tags: false, prune: true, check_tags_changed: false, http_authorization_header: "")
+ # resolved_address - resolved IP address for provided URL
+ def fetch_remote( # rubocop:disable Metrics/ParameterLists
+ url,
+ refmap: nil, ssh_auth: nil, forced: false, no_tags: false, prune: true,
+ check_tags_changed: false, http_authorization_header: "", resolved_address: "")
wrapped_gitaly_errors do
gitaly_repository_client.fetch_remote(
url,
@@ -872,16 +876,17 @@ module Gitlab
prune: prune,
check_tags_changed: check_tags_changed,
timeout: GITLAB_PROJECTS_TIMEOUT,
- http_authorization_header: http_authorization_header
+ http_authorization_header: http_authorization_header,
+ resolved_address: resolved_address
)
end
end
- def import_repository(url, http_authorization_header: '', mirror: false)
+ def import_repository(url, http_authorization_header: '', mirror: false, resolved_address: '')
raise ArgumentError, "don't use disk paths with import_repository: #{url.inspect}" if url.start_with?('.', '/')
wrapped_gitaly_errors do
- gitaly_repository_client.import_repository(url, http_authorization_header: http_authorization_header, mirror: mirror)
+ gitaly_repository_client.import_repository(url, http_authorization_header: http_authorization_header, mirror: mirror, resolved_address: resolved_address)
end
end
diff --git a/lib/gitlab/git_access.rb b/lib/gitlab/git_access.rb
index 9a3f5fb844b..da2a81983ec 100644
--- a/lib/gitlab/git_access.rb
+++ b/lib/gitlab/git_access.rb
@@ -367,7 +367,7 @@ module Gitlab
end
def deploy_key?
- actor.is_a?(DeployKey)
+ actor.is_a?(DeployKey) && !Gitlab::ExternalAuthorization.enabled?
end
def deploy_token
@@ -375,7 +375,7 @@ module Gitlab
end
def deploy_token?
- actor.is_a?(DeployToken)
+ actor.is_a?(DeployToken) && !Gitlab::ExternalAuthorization.enabled?
end
def ci?
diff --git a/lib/gitlab/gitaly_client/repository_service.rb b/lib/gitlab/gitaly_client/repository_service.rb
index e6565bd33c2..daaf18c711d 100644
--- a/lib/gitlab/gitaly_client/repository_service.rb
+++ b/lib/gitlab/gitaly_client/repository_service.rb
@@ -81,7 +81,7 @@ module Gitlab
# rubocop: disable Metrics/ParameterLists
# The `remote` parameter is going away soonish anyway, at which point the
# Rubocop warning can be enabled again.
- def fetch_remote(url, refmap:, ssh_auth:, forced:, no_tags:, timeout:, prune: true, check_tags_changed: false, http_authorization_header: "")
+ def fetch_remote(url, refmap:, ssh_auth:, forced:, no_tags:, timeout:, prune: true, check_tags_changed: false, http_authorization_header: "", resolved_address: "")
request = Gitaly::FetchRemoteRequest.new(
repository: @gitaly_repo,
force: forced,
@@ -92,7 +92,8 @@ module Gitlab
remote_params: Gitaly::Remote.new(
url: url,
mirror_refmaps: Array.wrap(refmap).map(&:to_s),
- http_authorization_header: http_authorization_header
+ http_authorization_header: http_authorization_header,
+ resolved_address: resolved_address
)
)
@@ -148,12 +149,13 @@ module Gitlab
)
end
- def import_repository(source, http_authorization_header: '', mirror: false)
+ def import_repository(source, http_authorization_header: '', mirror: false, resolved_address: '')
request = Gitaly::CreateRepositoryFromURLRequest.new(
repository: @gitaly_repo,
url: source,
http_authorization_header: http_authorization_header,
- mirror: mirror
+ mirror: mirror,
+ resolved_address: resolved_address
)
gitaly_client_call(
diff --git a/lib/gitlab/hook_data/project_builder.rb b/lib/gitlab/hook_data/project_builder.rb
index ebd97d3ab1b..aec842e061f 100644
--- a/lib/gitlab/hook_data/project_builder.rb
+++ b/lib/gitlab/hook_data/project_builder.rb
@@ -57,7 +57,7 @@ module Gitlab
end
def user_email(user)
- user.respond_to?(:email) ? user.email : ""
+ user.respond_to?(:webhook_email) ? user.webhook_email : ""
end
def event_specific_project_data(event)
diff --git a/lib/gitlab/hook_data/project_member_builder.rb b/lib/gitlab/hook_data/project_member_builder.rb
index 2ee61705ec1..be5bde356de 100644
--- a/lib/gitlab/hook_data/project_member_builder.rb
+++ b/lib/gitlab/hook_data/project_member_builder.rb
@@ -43,7 +43,7 @@ module Gitlab
project_id: project.id,
user_username: project_member.user.username,
user_name: project_member.user.name,
- user_email: project_member.user.email,
+ user_email: project_member.user.webhook_email,
user_id: project_member.user.id,
access_level: project_member.human_access,
project_visibility: project.visibility
diff --git a/spec/features/tags/developer_views_tags_spec.rb b/spec/features/tags/developer_views_tags_spec.rb
index 57e1f7da04e..e2399dd9978 100644
--- a/spec/features/tags/developer_views_tags_spec.rb
+++ b/spec/features/tags/developer_views_tags_spec.rb
@@ -53,6 +53,8 @@ RSpec.describe 'Developer views tags' do
end
it 'views a specific tag page' do
+ create(:release, project: project, tag: 'v1.0.0', name: 'v1.0.0', description: nil)
+
click_on 'v1.0.0'
expect(page).to have_current_path(
diff --git a/spec/fixtures/packages/nuget/corrupted_package.nupkg b/spec/fixtures/packages/nuget/corrupted_package.nupkg
new file mode 100644
index 00000000000..54f05c62aea
--- /dev/null
+++ b/spec/fixtures/packages/nuget/corrupted_package.nupkg
Binary files differ
diff --git a/spec/lib/gitlab/api_authentication/token_resolver_spec.rb b/spec/lib/gitlab/api_authentication/token_resolver_spec.rb
index bbc6bf0d481..9f86b95651a 100644
--- a/spec/lib/gitlab/api_authentication/token_resolver_spec.rb
+++ b/spec/lib/gitlab/api_authentication/token_resolver_spec.rb
@@ -114,6 +114,18 @@ RSpec.describe Gitlab::APIAuthentication::TokenResolver do
it_behaves_like 'an unauthorized request'
end
+
+ context 'when the external_authorization_service is enabled' do
+ before do
+ stub_application_setting(external_authorization_service_enabled: true)
+ end
+
+ context 'with a valid deploy token' do
+ let(:raw) { username_and_password(token.username, token.token) }
+
+ it_behaves_like 'an unauthorized request'
+ end
+ end
end
context 'with :personal_access_token' do
diff --git a/spec/lib/gitlab/auth/auth_finders_spec.rb b/spec/lib/gitlab/auth/auth_finders_spec.rb
index 05eca4cf70f..9283c31a207 100644
--- a/spec/lib/gitlab/auth/auth_finders_spec.rb
+++ b/spec/lib/gitlab/auth/auth_finders_spec.rb
@@ -389,6 +389,15 @@ RSpec.describe Gitlab::Auth::AuthFinders do
it { is_expected.to be_nil }
end
end
+
+ context 'when the external_authorization_service is enabled' do
+ before do
+ stub_application_setting(external_authorization_service_enabled: true)
+ set_header(described_class::DEPLOY_TOKEN_HEADER, deploy_token.token)
+ end
+
+ it { is_expected.to be_nil }
+ end
end
describe '#find_user_from_access_token' do
diff --git a/spec/lib/gitlab/ci/pipeline/chain/populate_metadata_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/populate_metadata_spec.rb
index ce1ee2fcda0..35e1c48a942 100644
--- a/spec/lib/gitlab/ci/pipeline/chain/populate_metadata_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/chain/populate_metadata_spec.rb
@@ -89,6 +89,18 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::PopulateMetadata do
expect(pipeline.pipeline_metadata).to be_nil
end
+
+ context 'with empty name after variable substitution' do
+ let(:config) do
+ { workflow: { name: '$VAR1' }, rspec: { script: 'rspec' } }
+ end
+
+ it 'does not save empty name' do
+ run_chain
+
+ expect(pipeline.pipeline_metadata).to be_nil
+ end
+ end
end
context 'with variables' do
@@ -106,7 +118,7 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::PopulateMetadata do
it 'substitutes variables' do
run_chain
- expect(pipeline.pipeline_metadata.name).to eq('Pipeline value value1 value2 ')
+ expect(pipeline.pipeline_metadata.name).to eq('Pipeline value value1 value2')
end
end
diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb
index 5e27979cbf3..197662943a0 100644
--- a/spec/lib/gitlab/git/repository_spec.rb
+++ b/spec/lib/gitlab/git/repository_spec.rb
@@ -524,12 +524,13 @@ RSpec.describe Gitlab::Git::Repository do
prune: false,
check_tags_changed: false,
refmap: nil,
- http_authorization_header: ""
+ http_authorization_header: "",
+ resolved_address: '172.16.123.1'
}
expect(repository.gitaly_repository_client).to receive(:fetch_remote).with(url, expected_opts)
- repository.fetch_remote(url, ssh_auth: ssh_auth, forced: true, no_tags: true, prune: false, check_tags_changed: false)
+ repository.fetch_remote(url, ssh_auth: ssh_auth, forced: true, no_tags: true, prune: false, check_tags_changed: false, resolved_address: '172.16.123.1')
end
it_behaves_like 'wrapping gRPC errors', Gitlab::GitalyClient::RepositoryService, :fetch_remote do
@@ -2448,7 +2449,7 @@ RSpec.describe Gitlab::Git::Repository do
it 'delegates to Gitaly' do
expect_next_instance_of(Gitlab::GitalyClient::RepositoryService) do |svc|
- expect(svc).to receive(:import_repository).with(url, http_authorization_header: '', mirror: false).and_return(nil)
+ expect(svc).to receive(:import_repository).with(url, http_authorization_header: '', mirror: false, resolved_address: '').and_return(nil)
end
repository.import_repository(url)
diff --git a/spec/lib/gitlab/git_access_spec.rb b/spec/lib/gitlab/git_access_spec.rb
index 7e3a1bf61bc..10a099af4f0 100644
--- a/spec/lib/gitlab/git_access_spec.rb
+++ b/spec/lib/gitlab/git_access_spec.rb
@@ -5,6 +5,7 @@ require 'spec_helper'
RSpec.describe Gitlab::GitAccess, :aggregate_failures do
include TermsHelper
include AdminModeHelper
+ include ExternalAuthorizationServiceHelpers
let(:user) { create(:user) }
let(:actor) { user }
@@ -111,6 +112,19 @@ RSpec.describe Gitlab::GitAccess, :aggregate_failures do
end
end
end
+
+ context 'when the external_authorization_service is enabled' do
+ before do
+ stub_application_setting(external_authorization_service_enabled: true)
+ end
+
+ it 'blocks push and pull with "not found"' do
+ aggregate_failures do
+ expect { push_access_check }.to raise_not_found
+ expect { pull_access_check }.to raise_not_found
+ end
+ end
+ end
end
context 'when actor is a User' do
@@ -176,6 +190,20 @@ RSpec.describe Gitlab::GitAccess, :aggregate_failures do
expect { push_access_check }.to raise_not_found
end
end
+
+ context 'when the external_authorization_service is enabled' do
+ before do
+ stub_application_setting(external_authorization_service_enabled: true)
+ end
+
+ it 'blocks pull access' do
+ expect { pull_access_check }.to raise_not_found
+ end
+
+ it 'blocks the push' do
+ expect { push_access_check }.to raise_not_found
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/gitaly_client/repository_service_spec.rb b/spec/lib/gitlab/gitaly_client/repository_service_spec.rb
index 58ace05b0d3..5aef250afac 100644
--- a/spec/lib/gitlab/gitaly_client/repository_service_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/repository_service_spec.rb
@@ -133,6 +133,40 @@ RSpec.describe Gitlab::GitalyClient::RepositoryService do
end
end
+ describe '#import_repository' do
+ let(:source) { 'https://example.com/git/repo.git' }
+
+ it 'sends a create_repository_from_url message' do
+ expected_request = gitaly_request_with_params(
+ url: source,
+ resolved_address: ''
+ )
+
+ expect_any_instance_of(Gitaly::RepositoryService::Stub)
+ .to receive(:create_repository_from_url)
+ .with(expected_request, kind_of(Hash))
+ .and_return(double(value: true))
+
+ client.import_repository(source)
+ end
+
+ context 'when http_host is provided' do
+ it 'sends a create_repository_from_url message with http_host provided in the request' do
+ expected_request = gitaly_request_with_params(
+ url: source,
+ resolved_address: '172.16.123.1'
+ )
+
+ expect_any_instance_of(Gitaly::RepositoryService::Stub)
+ .to receive(:create_repository_from_url)
+ .with(expected_request, kind_of(Hash))
+ .and_return(double(value: true))
+
+ client.import_repository(source, resolved_address: '172.16.123.1')
+ end
+ end
+ end
+
describe '#fetch_remote' do
let(:url) { 'https://example.com/git/repo.git' }
@@ -141,7 +175,8 @@ RSpec.describe Gitlab::GitalyClient::RepositoryService do
remote_params: Gitaly::Remote.new(
url: url,
http_authorization_header: "",
- mirror_refmaps: []
+ mirror_refmaps: [],
+ resolved_address: ''
),
ssh_key: '',
known_hosts: '',
@@ -159,6 +194,32 @@ RSpec.describe Gitlab::GitalyClient::RepositoryService do
client.fetch_remote(url, refmap: nil, ssh_auth: nil, forced: false, no_tags: false, timeout: 1, check_tags_changed: false)
end
+ context 'with resolved address' do
+ it 'sends a fetch_remote_request message' do
+ expected_request = gitaly_request_with_params(
+ remote_params: Gitaly::Remote.new(
+ url: url,
+ http_authorization_header: "",
+ mirror_refmaps: [],
+ resolved_address: '172.16.123.1'
+ ),
+ ssh_key: '',
+ known_hosts: '',
+ force: false,
+ no_tags: false,
+ no_prune: false,
+ check_tags_changed: false
+ )
+
+ expect_any_instance_of(Gitaly::RepositoryService::Stub)
+ .to receive(:fetch_remote)
+ .with(expected_request, kind_of(Hash))
+ .and_return(double(value: true))
+
+ client.fetch_remote(url, refmap: nil, ssh_auth: nil, forced: false, no_tags: false, timeout: 1, check_tags_changed: false, resolved_address: '172.16.123.1')
+ end
+ end
+
context 'SSH auth' do
where(:ssh_mirror_url, :ssh_key_auth, :ssh_private_key, :ssh_known_hosts, :expected_params) do
false | false | 'key' | 'known_hosts' | {}
diff --git a/spec/lib/gitlab/hook_data/project_builder_spec.rb b/spec/lib/gitlab/hook_data/project_builder_spec.rb
index 729712510ea..f80faac563d 100644
--- a/spec/lib/gitlab/hook_data/project_builder_spec.rb
+++ b/spec/lib/gitlab/hook_data/project_builder_spec.rb
@@ -29,8 +29,8 @@ RSpec.describe Gitlab::HookData::ProjectBuilder do
expect(data[:path_with_namespace]).to eq(project.full_path)
expect(data[:project_id]).to eq(project.id)
expect(data[:owner_name]).to eq('John')
- expect(data[:owner_email]).to eq('john@example.com')
- expect(data[:owners]).to contain_exactly({ name: 'John', email: 'john@example.com' })
+ expect(data[:owner_email]).to eq(_('[REDACTED]'))
+ expect(data[:owners]).to contain_exactly({ name: 'John', email: _('[REDACTED]') })
expect(data[:project_visibility]).to eq('internal')
end
end
diff --git a/spec/lib/gitlab/hook_data/project_member_builder_spec.rb b/spec/lib/gitlab/hook_data/project_member_builder_spec.rb
index 76446adf7b7..ea71c5442f4 100644
--- a/spec/lib/gitlab/hook_data/project_member_builder_spec.rb
+++ b/spec/lib/gitlab/hook_data/project_member_builder_spec.rb
@@ -27,7 +27,7 @@ RSpec.describe Gitlab::HookData::ProjectMemberBuilder do
expect(data[:user_username]).to eq('johndoe')
expect(data[:user_name]).to eq('John Doe')
expect(data[:user_id]).to eq(user.id)
- expect(data[:user_email]).to eq('john@example.com')
+ expect(data[:user_email]).to eq(_('[REDACTED]'))
expect(data[:access_level]).to eq('Developer')
expect(data[:project_visibility]).to eq('internal')
end
diff --git a/spec/models/ci/build_runner_session_spec.rb b/spec/models/ci/build_runner_session_spec.rb
index 9bb8a1bd626..8dfe854511c 100644
--- a/spec/models/ci/build_runner_session_spec.rb
+++ b/spec/models/ci/build_runner_session_spec.rb
@@ -13,6 +13,45 @@ RSpec.describe Ci::BuildRunnerSession, model: true do
it { is_expected.to validate_presence_of(:build) }
it { is_expected.to validate_presence_of(:url).with_message('must be a valid URL') }
+ context 'url validation of local web hook address' do
+ let(:url) { 'https://127.0.0.1:7777' }
+
+ subject(:build_with_local_runner_session_url) do
+ create(:ci_build).tap { |b| b.update!(runner_session_attributes: { url: url }) }
+ end
+
+ context 'with allow_local_requests_from_web_hooks_and_services? stubbed' do
+ before do
+ allow(ApplicationSetting).to receive(:current).and_return(ApplicationSetting.new)
+ stub_application_setting(allow_local_requests_from_web_hooks_and_services: allow_local_requests)
+ end
+
+ context 'as returning true' do
+ let(:allow_local_requests) { true }
+
+ it 'creates a new session', :aggregate_failures do
+ session = build_with_local_runner_session_url.reload.runner_session
+
+ expect(session.errors).to be_empty
+ expect(session).to be_a(Ci::BuildRunnerSession)
+ expect(session.url).to eq(url)
+ end
+ end
+
+ context 'as returning false' do
+ let(:allow_local_requests) { false }
+
+ it 'does not create a new session' do
+ expect { build_with_local_runner_session_url }.to raise_error(ActiveRecord::RecordInvalid) do |err|
+ expect(err.record.errors.full_messages).to include(
+ 'Runner session url is blocked: Requests to localhost are not allowed'
+ )
+ end
+ end
+ end
+ end
+ end
+
context 'nested attribute assignment' do
it 'creates a new session' do
simple_build = create(:ci_build)
@@ -49,6 +88,12 @@ RSpec.describe Ci::BuildRunnerSession, model: true do
expect(specification).to be_empty
end
+ it 'returns url with appended query if url has query' do
+ subject.url = 'https://new.example.com:7777/some_path?dummy='
+
+ expect(specification[:url]).to eq('wss://new.example.com:7777/some_path/exec?dummy=')
+ end
+
context 'when url is present' do
it 'returns ca_pem nil if empty certificate' do
subject.certificate = ''
@@ -85,6 +130,12 @@ RSpec.describe Ci::BuildRunnerSession, model: true do
expect(specification).to be_empty
end
+ it 'returns url with appended query if url has query' do
+ subject.url = 'https://new.example.com:7777/some_path?dummy='
+
+ expect(specification[:url]).to eq("https://new.example.com:7777/some_path/proxy/#{service}/#{port}/#{path}?dummy=")
+ end
+
context 'when port is not present' do
let(:port) { nil }
diff --git a/spec/models/hooks/web_hook_log_spec.rb b/spec/models/hooks/web_hook_log_spec.rb
index fafca144cae..2f0bfbd4fed 100644
--- a/spec/models/hooks/web_hook_log_spec.rb
+++ b/spec/models/hooks/web_hook_log_spec.rb
@@ -188,4 +188,22 @@ RSpec.describe WebHookLog do
it { expect(web_hook_log.internal_error?).to be_truthy }
end
end
+
+ describe '#request_headers' do
+ let(:hook) { build(:project_hook, :token) }
+ let(:web_hook_log) { build(:web_hook_log, request_headers: request_headers) }
+ let(:expected_headers) { { 'X-Gitlab-Token' => _('[REDACTED]') } }
+
+ context 'with redacted headers token' do
+ let(:request_headers) { { 'X-Gitlab-Token' => _('[REDACTED]') } }
+
+ it { expect(web_hook_log.request_headers).to eq(expected_headers) }
+ end
+
+ context 'with exposed headers token' do
+ let(:request_headers) { { 'X-Gitlab-Token' => hook.token } }
+
+ it { expect(web_hook_log.request_headers).to eq(expected_headers) }
+ end
+ end
end
diff --git a/spec/models/hooks/web_hook_spec.rb b/spec/models/hooks/web_hook_spec.rb
index db854670cc3..9b55db15f3b 100644
--- a/spec/models/hooks/web_hook_spec.rb
+++ b/spec/models/hooks/web_hook_spec.rb
@@ -195,6 +195,36 @@ RSpec.describe WebHook do
end
end
+ describe 'before_validation :reset_token' do
+ subject(:hook) { build_stubbed(:project_hook, :token, project: project) }
+
+ it 'resets token if url changed' do
+ hook.url = 'https://webhook.example.com/new-hook'
+
+ expect(hook).to be_valid
+ expect(hook.token).to be_nil
+ end
+
+ it 'does not reset token if new url is set together with the same token' do
+ hook.url = 'https://webhook.example.com/new-hook'
+ current_token = hook.token
+ hook.token = current_token
+
+ expect(hook).to be_valid
+ expect(hook.token).to eq(current_token)
+ expect(hook.url).to eq('https://webhook.example.com/new-hook')
+ end
+
+ it 'does not reset token if new url is set together with a new token' do
+ hook.url = 'https://webhook.example.com/new-hook'
+ hook.token = 'token'
+
+ expect(hook).to be_valid
+ expect(hook.token).to eq('token')
+ expect(hook.url).to eq('https://webhook.example.com/new-hook')
+ end
+ end
+
it "only consider these branch filter strategies are valid" do
expected_valid_types = %w[all_branches regex wildcard]
expect(described_class.branch_filter_strategies.keys).to contain_exactly(*expected_valid_types)
diff --git a/spec/models/integrations/jira_spec.rb b/spec/models/integrations/jira_spec.rb
index 819dad9d46d..af1112cf50d 100644
--- a/spec/models/integrations/jira_spec.rb
+++ b/spec/models/integrations/jira_spec.rb
@@ -230,9 +230,12 @@ RSpec.describe Integrations::Jira do
where(:url, :result) do
'https://abc.atlassian.net' | true
+ 'http://abc.atlassian.net' | false
'abc.atlassian.net' | false # This is how it behaves currently, but we may need to consider adding scheme if missing
'https://somethingelse.com' | false
- nil | false
+ 'javascript://test.atlassian.net/%250dalert(document.domain)' | false
+ 'https://example.com".atlassian.net' | false
+ nil | false
end
with_them do
@@ -289,7 +292,7 @@ RSpec.describe Integrations::Jira do
let(:server_info_results) { { 'deploymentType' => 'FutureCloud' } }
context 'and URL ends in .atlassian.net' do
- let(:api_url) { 'http://example-api.atlassian.net' }
+ let(:api_url) { 'https://example-api.atlassian.net' }
it 'deployment_type is set to cloud' do
expect(integration.jira_tracker_data).to be_deployment_cloud
@@ -297,7 +300,7 @@ RSpec.describe Integrations::Jira do
end
context 'and URL is something else' do
- let(:api_url) { 'http://my-jira-api.someserver.com' }
+ let(:api_url) { 'https://my-jira-api.someserver.com' }
it 'deployment_type is set to server' do
expect(integration.jira_tracker_data).to be_deployment_server
@@ -309,7 +312,7 @@ RSpec.describe Integrations::Jira do
let(:server_info_results) { {} }
context 'and URL ends in .atlassian.net' do
- let(:api_url) { 'http://example-api.atlassian.net' }
+ let(:api_url) { 'https://example-api.atlassian.net' }
it 'deployment_type is set to cloud' do
expect(Gitlab::AppLogger).to receive(:warn).with(message: "Jira API returned no ServerInfo, setting deployment_type from URL", server_info: server_info_results, url: api_url)
@@ -318,7 +321,7 @@ RSpec.describe Integrations::Jira do
end
context 'and URL is something else' do
- let(:api_url) { 'http://my-jira-api.someserver.com' }
+ let(:api_url) { 'https://my-jira-api.someserver.com' }
it 'deployment_type is set to server' do
expect(Gitlab::AppLogger).to receive(:warn).with(message: "Jira API returned no ServerInfo, setting deployment_type from URL", server_info: server_info_results, url: api_url)
diff --git a/spec/models/project_import_state_spec.rb b/spec/models/project_import_state_spec.rb
index db79185d759..ba1a29a8b27 100644
--- a/spec/models/project_import_state_spec.rb
+++ b/spec/models/project_import_state_spec.rb
@@ -22,7 +22,7 @@ RSpec.describe ProjectImportState, type: :model do
before do
allow_any_instance_of(Gitlab::GitalyClient::RepositoryService).to receive(:import_repository)
- .with(project.import_url, http_authorization_header: '', mirror: false).and_return(true)
+ .with(project.import_url, http_authorization_header: '', mirror: false, resolved_address: '').and_return(true)
# Works around https://github.com/rspec/rspec-mocks/issues/910
allow(Project).to receive(:find).with(project.id).and_return(project)
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index 8cccc9ad83e..1cae03ae2ae 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -5512,8 +5512,8 @@ 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.repository).to receive(:expire_content_cache).ordered
+ expect(project.repository).to receive(:remove_prohibited_branches).ordered
expect(project.wiki.repository).to receive(:expire_content_cache)
expect(import_state).to receive(:finish)
expect(project).to receive(:update_project_counter_caches)
diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb
index 93872bcd827..c17e180f282 100644
--- a/spec/models/repository_spec.rb
+++ b/spec/models/repository_spec.rb
@@ -1223,11 +1223,22 @@ RSpec.describe Repository do
it 'fetches the URL without creating a remote' do
expect(repository)
.to receive(:fetch_remote)
- .with(url, forced: false, prune: true, refmap: :all_refs, http_authorization_header: "")
+ .with(url, forced: false, prune: true, refmap: :all_refs, http_authorization_header: "", resolved_address: '')
.and_return(nil)
repository.fetch_as_mirror(url)
end
+
+ context 'with http_host provided' do
+ it 'fetches the URL with resolved_address value' do
+ expect(repository)
+ .to receive(:fetch_remote)
+ .with(url, forced: false, prune: true, refmap: :all_refs, http_authorization_header: "", resolved_address: '172.16.123.1')
+ .and_return(nil)
+
+ repository.fetch_as_mirror(url, resolved_address: '172.16.123.1')
+ end
+ end
end
describe '#fetch_ref' do
diff --git a/spec/requests/api/ci/runner/jobs_request_post_spec.rb b/spec/requests/api/ci/runner/jobs_request_post_spec.rb
index 1cb4cc93ea5..d69a3f5a980 100644
--- a/spec/requests/api/ci/runner/jobs_request_post_spec.rb
+++ b/spec/requests/api/ci/runner/jobs_request_post_spec.rb
@@ -949,6 +949,41 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
end
end
+ context 'with session url set to local URL' do
+ let(:job_params) { { session: { url: 'https://127.0.0.1:7777' } } }
+
+ context 'with allow_local_requests_from_web_hooks_and_services? stubbed' do
+ before do
+ allow(ApplicationSetting).to receive(:current).and_return(ApplicationSetting.new)
+ stub_application_setting(allow_local_requests_from_web_hooks_and_services: allow_local_requests)
+ ci_build
+ end
+
+ let(:ci_build) { create(:ci_build, :pending, :queued, pipeline: pipeline) }
+
+ context 'as returning true' do
+ let(:allow_local_requests) { true }
+
+ it 'creates a new session' do
+ request_job(**job_params)
+
+ expect(response).to have_gitlab_http_status(:created)
+ end
+ end
+
+ context 'as returning false' do
+ let(:allow_local_requests) { false }
+
+ it 'returns :unprocessable_entity status code', :aggregate_failures do
+ request_job(**job_params)
+
+ expect(response).to have_gitlab_http_status(:conflict)
+ expect(response.body).to include('409 Conflict')
+ end
+ end
+ end
+ end
+
def request_job(token = runner.token, **params)
new_params = params.merge(token: token, last_update: last_update)
post api('/jobs/request'), params: new_params.to_json, headers: { 'User-Agent' => user_agent, 'Content-Type': 'application/json' }
diff --git a/spec/requests/jira_connect/users_controller_spec.rb b/spec/requests/jira_connect/users_controller_spec.rb
index c648d28c1bc..6e927aaba91 100644
--- a/spec/requests/jira_connect/users_controller_spec.rb
+++ b/spec/requests/jira_connect/users_controller_spec.rb
@@ -31,5 +31,16 @@ RSpec.describe JiraConnect::UsersController do
expect(response.body).not_to include('Return to GitLab')
end
end
+
+ context 'with a script injected' do
+ let(:return_to) { 'javascript://test.atlassian.net/%250dalert(document.domain)' }
+
+ it 'does not include a return url' do
+ get '/-/jira_connect/users', params: { return_to: return_to }
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response.body).not_to include('Return to GitLab')
+ end
+ end
end
end
diff --git a/spec/services/markup/rendering_service_spec.rb b/spec/services/markup/rendering_service_spec.rb
index a5711a8cbc4..d54bc71f0a4 100644
--- a/spec/services/markup/rendering_service_spec.rb
+++ b/spec/services/markup/rendering_service_spec.rb
@@ -110,9 +110,12 @@ RSpec.describe Markup::RenderingService do
context 'when file is a regular text file' do
let(:file_name) { 'foo.txt' }
+ let(:text) { 'Noël <form>' }
it 'returns html (rendered by ActionView::TagHelper)' do
- is_expected.to eq('<pre class="plain-readme">Noël</pre>')
+ expect(ActionController::Base.helpers).to receive(:content_tag).and_call_original
+
+ is_expected.to eq('<pre class="plain-readme">Noël &lt;form&gt;</pre>')
end
end
diff --git a/spec/services/packages/nuget/metadata_extraction_service_spec.rb b/spec/services/packages/nuget/metadata_extraction_service_spec.rb
index fc21cfd502e..12bab30b4a7 100644
--- a/spec/services/packages/nuget/metadata_extraction_service_spec.rb
+++ b/spec/services/packages/nuget/metadata_extraction_service_spec.rb
@@ -114,5 +114,16 @@ RSpec.describe Packages::Nuget::MetadataExtractionService do
it { expect { subject }.to raise_error(::Packages::Nuget::MetadataExtractionService::ExtractionError, 'nuspec file too big') }
end
+
+ context 'with a corrupted nupkg file with a wrong entry size' do
+ let(:nupkg_fixture_path) { expand_fixture_path('packages/nuget/corrupted_package.nupkg') }
+ let(:expected_error) { "nuspec file has the wrong entry size: entry 'DummyProject.DummyPackage.nuspec' should be 255B, but is larger when inflated." }
+
+ before do
+ allow(Zip::File).to receive(:new).and_return(Zip::File.new(nupkg_fixture_path, false, false))
+ end
+
+ it { expect { subject }.to raise_error(::Packages::Nuget::MetadataExtractionService::ExtractionError, expected_error) }
+ end
end
end
diff --git a/spec/services/projects/import_service_spec.rb b/spec/services/projects/import_service_spec.rb
index 6dc72948541..b3f8980a7bd 100644
--- a/spec/services/projects/import_service_spec.rb
+++ b/spec/services/projects/import_service_spec.rb
@@ -127,30 +127,67 @@ RSpec.describe Projects::ImportService do
project.import_type = 'bitbucket'
end
- it 'succeeds if repository import is successful' do
- expect(project.repository).to receive(:import_repository).and_return(true)
- expect_next_instance_of(Gitlab::BitbucketImport::Importer) do |importer|
- expect(importer).to receive(:execute).and_return(true)
+ context 'when importer supports refmap' do
+ before do
+ project.import_type = 'gitea'
end
- expect_next_instance_of(Projects::LfsPointers::LfsImportService) do |service|
- expect(service).to receive(:execute).and_return(status: :success)
+ it 'succeeds if repository fetch as mirror is successful' do
+ expect(project).to receive(:ensure_repository)
+ expect(project.repository).to receive(:fetch_as_mirror).with('https://bitbucket.org/vim/vim.git', refmap: Gitlab::LegacyGithubImport::Importer.refmap, resolved_address: '').and_return(true)
+ expect_next_instance_of(Gitlab::LegacyGithubImport::Importer) do |importer|
+ expect(importer).to receive(:execute).and_return(true)
+ end
+
+ expect_next_instance_of(Projects::LfsPointers::LfsImportService) do |service|
+ expect(service).to receive(:execute).and_return(status: :success)
+ end
+
+ result = subject.execute
+
+ expect(result[:status]).to eq :success
end
- result = subject.execute
+ it 'fails if repository fetch as mirror fails' do
+ expect(project).to receive(:ensure_repository)
+ expect(project.repository)
+ .to receive(:fetch_as_mirror)
+ .and_raise(Gitlab::Git::CommandError, 'Failed to import the repository /a/b/c')
- expect(result[:status]).to eq :success
+ result = subject.execute
+
+ expect(result[:status]).to eq :error
+ expect(result[:message]).to eq "Error importing repository #{project.safe_import_url} into #{project.full_path} - Failed to import the repository [FILTERED]"
+ end
end
- it 'fails if repository import fails' do
- expect(project.repository)
- .to receive(:import_repository)
- .and_raise(Gitlab::Git::CommandError, 'Failed to import the repository /a/b/c')
+ context 'when importer does not support refmap' do
+ it 'succeeds if repository import is successful' do
+ expect(project.repository).to receive(:import_repository).and_return(true)
+ expect_next_instance_of(Gitlab::BitbucketImport::Importer) do |importer|
+ expect(importer).to receive(:execute).and_return(true)
+ end
- result = subject.execute
+ expect_next_instance_of(Projects::LfsPointers::LfsImportService) do |service|
+ expect(service).to receive(:execute).and_return(status: :success)
+ end
- expect(result[:status]).to eq :error
- expect(result[:message]).to eq "Error importing repository #{project.safe_import_url} into #{project.full_path} - Failed to import the repository [FILTERED]"
+ result = subject.execute
+
+ expect(result[:status]).to eq :success
+ end
+
+ it 'fails if repository import fails' do
+ expect(project.repository)
+ .to receive(:import_repository)
+ .with('https://bitbucket.org/vim/vim.git', resolved_address: '')
+ .and_raise(Gitlab::Git::CommandError, 'Failed to import the repository /a/b/c')
+
+ result = subject.execute
+
+ expect(result[:status]).to eq :error
+ expect(result[:message]).to eq "Error importing repository #{project.safe_import_url} into #{project.full_path} - Failed to import the repository [FILTERED]"
+ end
end
context 'when lfs import fails' do
@@ -287,6 +324,102 @@ RSpec.describe Projects::ImportService do
end
end
+ context 'when DNS rebind protection is disabled' do
+ before do
+ allow(Gitlab::CurrentSettings).to receive(:dns_rebinding_protection_enabled?).and_return(false)
+ project.import_url = "https://example.com/group/project"
+
+ allow(Gitlab::UrlBlocker).to receive(:validate!)
+ .with(project.import_url, ports: Project::VALID_IMPORT_PORTS, schemes: Project::VALID_IMPORT_PROTOCOLS, dns_rebind_protection: false)
+ .and_return([Addressable::URI.parse("https://example.com/group/project"), nil])
+ end
+
+ it 'imports repository with url without additional resolved address' do
+ expect(project.repository).to receive(:import_repository).with('https://example.com/group/project', resolved_address: '').and_return(true)
+
+ expect_next_instance_of(Projects::LfsPointers::LfsImportService) do |service|
+ expect(service).to receive(:execute).and_return(status: :success)
+ end
+
+ result = subject.execute
+
+ expect(result[:status]).to eq(:success)
+ end
+ end
+
+ context 'when DNS rebind protection is enabled' do
+ before do
+ allow(Gitlab::CurrentSettings).to receive(:http_proxy_env?).and_return(false)
+ allow(Gitlab::CurrentSettings).to receive(:dns_rebinding_protection_enabled?).and_return(true)
+ end
+
+ context 'when https url is provided' do
+ before do
+ project.import_url = "https://example.com/group/project"
+
+ allow(Gitlab::UrlBlocker).to receive(:validate!)
+ .with(project.import_url, ports: Project::VALID_IMPORT_PORTS, schemes: Project::VALID_IMPORT_PROTOCOLS, dns_rebind_protection: true)
+ .and_return([Addressable::URI.parse("https://172.16.123.1/group/project"), 'example.com'])
+ end
+
+ it 'imports repository with url and additional resolved address' do
+ expect(project.repository).to receive(:import_repository).with('https://example.com/group/project', resolved_address: '172.16.123.1').and_return(true)
+
+ expect_next_instance_of(Projects::LfsPointers::LfsImportService) do |service|
+ expect(service).to receive(:execute).and_return(status: :success)
+ end
+
+ result = subject.execute
+
+ expect(result[:status]).to eq(:success)
+ end
+ end
+
+ context 'when http url is provided' do
+ before do
+ project.import_url = "http://example.com/group/project"
+
+ allow(Gitlab::UrlBlocker).to receive(:validate!)
+ .with(project.import_url, ports: Project::VALID_IMPORT_PORTS, schemes: Project::VALID_IMPORT_PROTOCOLS, dns_rebind_protection: true)
+ .and_return([Addressable::URI.parse("http://172.16.123.1/group/project"), 'example.com'])
+ end
+
+ it 'imports repository with url and additional resolved address' do
+ expect(project.repository).to receive(:import_repository).with('http://example.com/group/project', resolved_address: '172.16.123.1').and_return(true)
+
+ expect_next_instance_of(Projects::LfsPointers::LfsImportService) do |service|
+ expect(service).to receive(:execute).and_return(status: :success)
+ end
+
+ result = subject.execute
+
+ expect(result[:status]).to eq(:success)
+ end
+ end
+
+ context 'when git address is provided' do
+ before do
+ project.import_url = "git://example.com/group/project.git"
+
+ allow(Gitlab::UrlBlocker).to receive(:validate!)
+ .with(project.import_url, ports: Project::VALID_IMPORT_PORTS, schemes: Project::VALID_IMPORT_PROTOCOLS, dns_rebind_protection: true)
+ .and_return([Addressable::URI.parse("git://172.16.123.1/group/project"), 'example.com'])
+ end
+
+ it 'imports repository with url and without resolved address' do
+ expect(project.repository).to receive(:import_repository).with('git://example.com/group/project.git', resolved_address: '').and_return(true)
+
+ expect_next_instance_of(Projects::LfsPointers::LfsImportService) do |service|
+ expect(service).to receive(:execute).and_return(status: :success)
+ end
+
+ result = subject.execute
+
+ expect(result[:status]).to eq(:success)
+ end
+ end
+ end
+
it_behaves_like 'measurable service' do
let(:base_log_data) do
{
diff --git a/spec/services/web_hooks/log_execution_service_spec.rb b/spec/services/web_hooks/log_execution_service_spec.rb
index 1b8ff9f2a05..fd97d01fa9f 100644
--- a/spec/services/web_hooks/log_execution_service_spec.rb
+++ b/spec/services/web_hooks/log_execution_service_spec.rb
@@ -11,14 +11,15 @@ RSpec.describe WebHooks::LogExecutionService do
travel_to(Time.current) { example.run }
end
- let_it_be_with_reload(:project_hook) { create(:project_hook) }
+ let_it_be_with_reload(:project_hook) { create(:project_hook, :token) }
let(:response_category) { :ok }
+ let(:request_headers) { { 'Header' => 'header value' } }
let(:data) do
{
trigger: 'trigger_name',
url: 'https://example.com',
- request_headers: { 'Header' => 'header value' },
+ request_headers: request_headers,
request_data: { 'Request Data' => 'request data value' },
response_body: 'Response body',
response_status: '200',
@@ -163,5 +164,15 @@ RSpec.describe WebHooks::LogExecutionService do
service.execute
end
end
+
+ context 'with X-Gitlab-Token' do
+ let(:request_headers) { { 'X-Gitlab-Token' => project_hook.token } }
+
+ it 'redacts the token' do
+ service.execute
+
+ expect(WebHookLog.recent.first.request_headers).to include('X-Gitlab-Token' => '[REDACTED]')
+ end
+ end
end
end
diff --git a/spec/support/shared_examples/features/user_views_tag_shared_examples.rb b/spec/support/shared_examples/features/user_views_tag_shared_examples.rb
index 989de1dbfbb..702964a2610 100644
--- a/spec/support/shared_examples/features/user_views_tag_shared_examples.rb
+++ b/spec/support/shared_examples/features/user_views_tag_shared_examples.rb
@@ -2,33 +2,54 @@
RSpec.shared_examples 'user views tag' do
context 'when user views with the tag' do
- let(:project) { create(:project, :repository) }
+ let(:project) { create(:project, :repository, :public) }
let(:user) { create(:user) }
let(:tag_name) { "stable" }
- let!(:release) { create(:release, project: project, tag: tag_name, name: "ReleaseName") }
+ let(:release_name) { 'ReleaseName' }
+ let(:release_notes) { 'Release notes' }
+ let!(:release) do
+ create(:release, project: project, tag: tag_name, name: release_name, description: release_notes)
+ end
before do
- project.add_developer(user)
project.repository.add_tag(user, tag_name, project.default_branch_or_main)
-
sign_in(user)
end
- shared_examples 'shows tag' do
- it do
- visit tag_page
+ context 'and user is authorized to read release' do
+ before do
+ project.add_developer(user)
+ end
+
+ shared_examples 'shows tag' do
+ it do
+ visit tag_page
+
+ expect(page).to have_content tag_name
+ expect(page).to have_link(release_name, href: project_release_path(project, release))
+ end
+ end
- expect(page).to have_content tag_name
- expect(page).to have_link("ReleaseName", href: project_release_path(project, release))
+ it_behaves_like 'shows tag'
+
+ context 'when tag name contains a slash' do
+ let(:tag_name) { "stable/v0.1" }
+
+ it_behaves_like 'shows tag'
end
end
- it_behaves_like 'shows tag'
+ context 'and user is not authorized to read release' do
+ before do
+ project.project_feature.update!(releases_access_level: Featurable::PRIVATE)
+ end
- context 'when tag name contains a slash' do
- let(:tag_name) { "stable/v0.1" }
+ it 'hides release link and notes', :aggregate_failures do
+ visit tag_page
- it_behaves_like 'shows tag'
+ expect(page).not_to have_link(release_name, href: project_release_path(project, release))
+ expect(page).not_to have_text(release_notes)
+ end
end
end
end