From 0e36c5a8a487dcc7cb92e68192b119f1d3d9781c Mon Sep 17 00:00:00 2001 From: Mohammed Bilal Date: Tue, 10 Jan 2023 11:22:00 +0530 Subject: [PATCH] New upstream version 15.5.7+ds1 --- CHANGELOG.md | 35 ++ GITALY_SERVER_VERSION | 2 +- GITLAB_PAGES_VERSION | 2 +- Gemfile | 2 +- Gemfile.checksum | 2 +- Gemfile.lock | 4 +- VERSION | 2 +- app/controllers/concerns/page_limiter.rb | 2 +- .../personal_access_tokens_controller.rb | 6 - .../projects/grafana_api_controller.rb | 2 + app/controllers/uploads_controller.rb | 2 + app/helpers/diff_helper.rb | 8 +- app/helpers/submodule_helper.rb | 74 ++- app/models/active_session.rb | 2 +- app/models/application_setting.rb | 4 - app/models/ci/build_runner_session.rb | 20 +- app/models/hooks/web_hook.rb | 10 + app/models/hooks/web_hook_log.rb | 7 + app/models/integrations/jira.rb | 5 +- app/models/project.rb | 2 +- app/models/repository.rb | 8 +- app/models/user.rb | 2 +- app/policies/group_policy.rb | 9 +- .../packages/policies/group_policy.rb | 2 + .../packages/policies/project_policy.rb | 2 + app/policies/project_policy.rb | 15 +- .../error_tracking/list_projects_service.rb | 16 +- .../nuget/metadata_extraction_service.rb | 10 +- app/services/projects/import_service.rb | 32 +- .../resource_access_tokens/create_service.rb | 9 +- app/services/users/update_service.rb | 8 + .../web_hooks/log_execution_service.rb | 2 + ...onfirmation_instructions_account.html.haml | 2 +- ...confirmation_instructions_account.text.erb | 2 +- .../layouts/nav/sidebar/_profile.html.haml | 23 +- .../projects/tags/_release_link.html.haml | 9 +- app/views/projects/tags/show.html.haml | 13 +- doc/operations/metrics/embed_grafana.md | 7 +- .../settings/external_authorization.md | 3 + .../group/settings/group_access_tokens.md | 3 - doc/user/packages/package_registry/index.md | 1 + doc/user/profile/personal_access_tokens.md | 3 - doc/user/project/deploy_keys/index.md | 2 + doc/user/project/deploy_tokens/index.md | 2 + .../project/settings/project_access_tokens.md | 3 - lib/api/ci/runner.rb | 3 +- .../api_authentication/token_resolver.rb | 2 + lib/gitlab/auth/auth_finders.rb | 1 + lib/gitlab/git/repository.rb | 13 +- lib/gitlab/git_access.rb | 4 +- .../gitaly_client/repository_service.rb | 10 +- lib/gitlab/hook_data/project_builder.rb | 2 +- .../hook_data/project_member_builder.rb | 2 +- lib/gitlab/safe_device_detector.rb | 16 + .../personal_access_tokens_controller_spec.rb | 16 - .../projects/grafana_api_controller_spec.rb | 82 ++- .../projects/jobs_controller_spec.rb | 5 +- spec/controllers/uploads_controller_spec.rb | 32 +- spec/factories/ci/builds.rb | 2 +- spec/factories/project_hooks.rb | 4 + spec/features/projects_spec.rb | 2 +- .../tags/developer_views_tags_spec.rb | 2 + spec/fixtures/clusters/chain_certificates.pem | 99 ++- .../clusters/intermediate_certificate.pem | 41 +- spec/fixtures/clusters/leaf_certificate.pem | 58 +- .../emails/valid_reply_signed_smime.eml | 588 +++++++++--------- .../packages/nuget/corrupted_package.nupkg | Bin 0 -> 3513 bytes spec/helpers/diff_helper_spec.rb | 13 + spec/helpers/submodule_helper_spec.rb | 2 +- .../api_authentication/token_resolver_spec.rb | 12 + spec/lib/gitlab/auth/auth_finders_spec.rb | 11 +- spec/lib/gitlab/git/repository_spec.rb | 7 +- spec/lib/gitlab/git_access_spec.rb | 28 + .../gitaly_client/repository_service_spec.rb | 63 +- .../gitlab/hook_data/project_builder_spec.rb | 4 +- .../hook_data/project_member_builder_spec.rb | 2 +- spec/lib/gitlab/safe_device_detector_spec.rb | 20 + .../hll_redis_counter_spec.rb | 41 +- spec/mailers/devise_mailer_spec.rb | 12 +- spec/models/application_setting_spec.rb | 6 - spec/models/ci/build_runner_session_spec.rb | 57 +- spec/models/hooks/web_hook_log_spec.rb | 18 + spec/models/hooks/web_hook_spec.rb | 60 +- spec/models/integrations/jira_spec.rb | 13 +- spec/models/project_import_state_spec.rb | 2 +- spec/models/project_spec.rb | 4 +- spec/models/repository_spec.rb | 13 +- spec/models/user_spec.rb | 28 + spec/policies/project_policy_spec.rb | 29 + .../impersonation_tokens_controller_spec.rb | 12 - .../api/ci/runner/jobs_request_post_spec.rb | 35 ++ .../jira_connect/users_controller_spec.rb | 11 + .../list_projects_service_spec.rb | 30 +- .../nuget/metadata_extraction_service_spec.rb | 11 + spec/services/projects/import_service_spec.rb | 163 ++++- spec/services/users/update_service_spec.rb | 43 ++ spec/services/web_hook_service_spec.rb | 5 +- .../web_hooks/log_execution_service_spec.rb | 15 +- spec/support/helpers/gpg_helpers.rb | 241 +++---- .../user_views_tag_shared_examples.rb | 47 +- .../resource_access_token_shared_examples.rb | 23 - .../nav/sidebar/_profile.html.haml_spec.rb | 16 - 102 files changed, 1639 insertions(+), 823 deletions(-) create mode 100644 lib/gitlab/safe_device_detector.rb create mode 100644 spec/fixtures/packages/nuget/corrupted_package.nupkg create mode 100644 spec/lib/gitlab/safe_device_detector_spec.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index cc95a92c45..d040d3bbdf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,41 @@ documentation](doc/development/changelog.md) for instructions on adding your own entry. +## 15.5.7 (2023-01-09) + +### Security (10 changes) + +- [Avoid regex with potential for poorly performing backtracking](gitlab-org/security/gitlab@c3f8d8c93e99ac3f226668086bfbf21739b02a0e) ([merge request](gitlab-org/security/gitlab!2989)) +- [Protect web-hook url variables after changing URL](gitlab-org/security/gitlab@8a18fea752a2759938b4c3d28516b6ed9386404f) ([merge request](gitlab-org/security/gitlab!2978)) +- [Limit the size of user agent to reduce ReDos attack](gitlab-org/security/gitlab@293db707009b7dd133a9a55b25892506013062fd) ([merge request](gitlab-org/security/gitlab!2991)) +- [Only allow safe params for diff helper](gitlab-org/security/gitlab@0c5de464c1d062103d6bc81cca45f7298929ca68) ([merge request](gitlab-org/security/gitlab!2951)) +- [Protect Sentry auth-token after changing URL](gitlab-org/security/gitlab@a2c3380748eb3aa36f23c74f1666c741fafec635) ([merge request](gitlab-org/security/gitlab!2986)) +- [Delete project specific licenses when license policy is deleted](gitlab-org/security/gitlab@312a28196df206b501861b6528b4b6fcaf7cc686) ([merge request](gitlab-org/security/gitlab!2896)) +- [Restrict user avatar availability based on visibility restrictions](gitlab-org/security/gitlab@f7b5c0a57b64c15edb0f555dd53c26b9d6147f0e) ([merge request](gitlab-org/security/gitlab!2973)) +- [Policy change to read and destroy token without license for .com](gitlab-org/security/gitlab@b51bc20ba07d8ef3d339aeacd1b0f904521f4158) ([merge request](gitlab-org/security/gitlab!2914)) +- [Restrict Grafana API access on public projects](gitlab-org/security/gitlab@d9798aa2d31ddef9ed6fedfc7b32bc8a8bac76bc) ([merge request](gitlab-org/security/gitlab!2959)) +- [Fix "Race condition enables verified email forgery"](gitlab-org/security/gitlab@95e65f637ed193b9c8b3c39af58a9bc0d552bad2) ([merge request](gitlab-org/security/gitlab!2962)) + +## 15.5.6 (2022-12-07) + +No changes. + +## 15.5.5 (2022-11-30) + +### Security (11 changes) + +- [Send resolved_address param to gitaly during repository import](gitlab-org/security/gitlab@768edcdca74fa09f7ba50c324aacd86fb71ed7e7) ([merge request](gitlab-org/security/gitlab!2939)) +- [Add size validation during nuspec file extraction](gitlab-org/security/gitlab@27f79d015684896b66e0418db253613e3efa1df7) ([merge request](gitlab-org/security/gitlab!2936)) +- [Cross-site scripting in Jira Integration](gitlab-org/security/gitlab@efcb2fc3110b7cf997b3e1a1e173e6462a54f208) ([merge request](gitlab-org/security/gitlab!2931)) +- [Protect web-hook secret tokens after changing URL](gitlab-org/security/gitlab@00b75ba0c52c10a578091ad89440e8ae78cbe066) ([merge request](gitlab-org/security/gitlab!2921)) +- [Redact secret tokens from web-hook logs](gitlab-org/security/gitlab@27699db7e44e7808f5ec415860ed03c55ae554b0) ([merge request](gitlab-org/security/gitlab!2917)) +- [Prevent unauthorized users from seeing Release information on tag pages](gitlab-org/security/gitlab@112d45bdba5e0d34f77eec1ffaf86443e28b2c8c) ([merge request](gitlab-org/security/gitlab!2926)) +- [Update after_import to expire cache before removing prohibited branches](gitlab-org/security/gitlab@5e84ca50689dceb7614e181ee7addbc3671dc935) ([merge request](gitlab-org/security/gitlab!2904)) +- [Deny all package permissions when group access is restricted by IP](gitlab-org/security/gitlab@23a8ba46641053317c45f58037499235438b5ad8) ([merge request](gitlab-org/security/gitlab!2901)) +- [Redact user emails from project webhook data](gitlab-org/security/gitlab@9f49c4d34fffd598af19d2db548281847855f987) ([merge request](gitlab-org/security/gitlab!2907)) +- [Disallow local URls for build_runner_session if dictated by app setting](gitlab-org/security/gitlab@087415cf7a780c97b1d4055590858a98c673c64b) ([merge request](gitlab-org/security/gitlab!2867)) +- [Prevent token bypass for extenal authorisation](gitlab-org/security/gitlab@96a6193a6e03bd1f76c2792cca404d2e672dfcf4) ([merge request](gitlab-org/security/gitlab!2884)) + ## 15.5.4 (2022-11-11) ### Fixed (3 changes) diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION index 63a3b0b00a..7a3ee10d51 100644 --- a/GITALY_SERVER_VERSION +++ b/GITALY_SERVER_VERSION @@ -1 +1 @@ -15.5.4 \ No newline at end of file +15.5.7 \ No newline at end of file diff --git a/GITLAB_PAGES_VERSION b/GITLAB_PAGES_VERSION index 76d0536205..7a3ee10d51 100644 --- a/GITLAB_PAGES_VERSION +++ b/GITLAB_PAGES_VERSION @@ -1 +1 @@ -1.62.0 +15.5.7 \ No newline at end of file diff --git a/Gemfile b/Gemfile index 8a8c0a444a..9ea2dd6c2c 100644 --- a/Gemfile +++ b/Gemfile @@ -503,7 +503,7 @@ gem 'ssh_data', '~> 1.3' gem 'spamcheck', '~> 1.0.0' # Gitaly GRPC protocol definitions -gem 'gitaly', '~> 15.4.0-rc2' +gem 'gitaly', '~> 15.5.2' # KAS GRPC protocol definitions gem 'kas-grpc', '~> 0.0.2' diff --git a/Gemfile.checksum b/Gemfile.checksum index afcbc5a6a6..c4ff7b9ff3 100644 --- a/Gemfile.checksum +++ b/Gemfile.checksum @@ -198,7 +198,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.4.0.pre.rc2","platform":"ruby","checksum":"48764528a730605a46f00cf86c7cfcb92d25f4f3d8cb9e09557baac3e9f3f8e3"}, +{"name":"gitaly","version":"15.5.2","platform":"ruby","checksum":"62babe0596a4505bf95051ea50f17160055e6cf6cacf209273691542120d7881"}, {"name":"github-markup","version":"1.7.0","platform":"ruby","checksum":"97eb27c70662d9cc1d5997cd6c99832026fae5d4913b5dce1ce6c9f65078e69d"}, {"name":"gitlab","version":"4.16.1","platform":"ruby","checksum":"13fd7059cbdad5a1a21b15fa2cf9070b97d92e27f8c688581fe3d84dc038074f"}, {"name":"gitlab-chronic","version":"0.10.5","platform":"ruby","checksum":"f80f18dc699b708870a80685243331290bc10cfeedb6b99c92219722f729c875"}, diff --git a/Gemfile.lock b/Gemfile.lock index 8bf67a41f4..b5ab43d16a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -547,7 +547,7 @@ GEM rails (>= 3.2.0) git (1.11.0) rchardet (~> 1.8) - gitaly (15.4.0.pre.rc2) + gitaly (15.5.2) grpc (~> 1.0) github-markup (1.7.0) gitlab (4.16.1) @@ -1622,7 +1622,7 @@ DEPENDENCIES gettext (~> 3.3) gettext_i18n_rails (~> 1.8.0) gettext_i18n_rails_js (~> 1.3) - gitaly (~> 15.4.0.pre.rc2) + gitaly (~> 15.5.2) github-markup (~> 1.7.0) gitlab-chronic (~> 0.10.5) gitlab-dangerfiles (~> 3.5.2) diff --git a/VERSION b/VERSION index 63a3b0b00a..7a3ee10d51 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -15.5.4 \ No newline at end of file +15.5.7 \ No newline at end of file diff --git a/app/controllers/concerns/page_limiter.rb b/app/controllers/concerns/page_limiter.rb index 362b02e585..3e75c428fd 100644 --- a/app/controllers/concerns/page_limiter.rb +++ b/app/controllers/concerns/page_limiter.rb @@ -57,7 +57,7 @@ module PageLimiter # Record the page limit being hit in Prometheus def record_page_limit_interception - dd = DeviceDetector.new(request.user_agent) + dd = Gitlab::SafeDeviceDetector.new(request.user_agent) Gitlab::Metrics.counter(:gitlab_page_out_of_bounds, controller: params[:controller], diff --git a/app/controllers/profiles/personal_access_tokens_controller.rb b/app/controllers/profiles/personal_access_tokens_controller.rb index 4cf26d3e1e..8ed67c26f1 100644 --- a/app/controllers/profiles/personal_access_tokens_controller.rb +++ b/app/controllers/profiles/personal_access_tokens_controller.rb @@ -3,8 +3,6 @@ class Profiles::PersonalAccessTokensController < Profiles::ApplicationController feature_category :authentication_and_authorization - before_action :check_personal_access_tokens_enabled - def index set_index_vars scopes = params[:scopes].split(',').map(&:squish).select(&:present?).map(&:to_sym) unless params[:scopes].nil? @@ -85,8 +83,4 @@ class Profiles::PersonalAccessTokensController < Profiles::ApplicationController def page (params[:page] || 1).to_i end - - def check_personal_access_tokens_enabled - render_404 if Gitlab::CurrentSettings.personal_access_tokens_disabled? - end end diff --git a/app/controllers/projects/grafana_api_controller.rb b/app/controllers/projects/grafana_api_controller.rb index d509936787..9cd511f6a1 100644 --- a/app/controllers/projects/grafana_api_controller.rb +++ b/app/controllers/projects/grafana_api_controller.rb @@ -4,6 +4,8 @@ class Projects::GrafanaApiController < Projects::ApplicationController include RenderServiceResults include MetricsDashboard + before_action :authorize_read_grafana!, only: :proxy + feature_category :metrics urgency :low diff --git a/app/controllers/uploads_controller.rb b/app/controllers/uploads_controller.rb index 09419a4589..66f715f32a 100644 --- a/app/controllers/uploads_controller.rb +++ b/app/controllers/uploads_controller.rb @@ -52,6 +52,8 @@ class UploadsController < ApplicationController # access to itself when a secret is given. # For instance, user avatars are readable by anyone, # while temporary, user snippet uploads are not. + return false if !current_user && public_visibility_restricted? + !secret? || can?(current_user, :update_user, model) when Appearance true diff --git a/app/helpers/diff_helper.rb b/app/helpers/diff_helper.rb index 5c3b9d4b5a..08678c21b7 100644 --- a/app/helpers/diff_helper.rb +++ b/app/helpers/diff_helper.rb @@ -244,6 +244,10 @@ module DiffHelper {} end + def params_with_whitespace + hide_whitespace? ? safe_params.except(:w) : safe_params.merge(w: 1) + end + private def diff_btn(title, name, selected) @@ -277,10 +281,6 @@ module DiffHelper params[:w] == '1' end - def params_with_whitespace - hide_whitespace? ? request.query_parameters.except(:w) : request.query_parameters.merge(w: 1) - end - def toggle_whitespace_link(url, options) options[:class] = [*options[:class], 'btn gl-button btn-default'].join(' ') link_to "#{hide_whitespace? ? 'Show' : 'Hide'} whitespace changes", url, class: options[:class] diff --git a/app/helpers/submodule_helper.rb b/app/helpers/submodule_helper.rb index 2942765a10..19a4052d52 100644 --- a/app/helpers/submodule_helper.rb +++ b/app/helpers/submodule_helper.rb @@ -17,40 +17,26 @@ module SubmoduleHelper url = File.join(Gitlab.config.gitlab.url, repository.project.full_path) end - if url =~ %r{([^/:]+)/([^/]+(?:\.git)?)\Z} - namespace = Regexp.last_match(1) - project = Regexp.last_match(2) - gitlab_hosts = [Gitlab.config.gitlab.url, - Gitlab.config.gitlab_shell.ssh_path_prefix] + namespace, project = extract_namespace_project(url) - gitlab_hosts.each do |host| - if url.start_with?(host) - namespace, _, project = url.sub(host, '').rpartition('/') - break - end - end + if namespace.blank? || project.blank? + return [sanitize_submodule_url(url), nil, nil] + end - namespace.delete_prefix!('/') - project.rstrip! - project.delete_suffix!('.git') - - if self_url?(url, namespace, project) - [ - url_helpers.namespace_project_path(namespace, project), - url_helpers.namespace_project_tree_path(namespace, project, submodule_item_id), - (url_helpers.namespace_project_compare_path(namespace, project, to: submodule_item_id, from: old_submodule_item_id) if old_submodule_item_id) - ] - elsif relative_self_url?(url) - relative_self_links(url, submodule_item_id, old_submodule_item_id, repository.project) - elsif gist_github_dot_com_url?(url) - gist_github_com_tree_links(namespace, project, submodule_item_id) - elsif github_dot_com_url?(url) - github_com_tree_links(namespace, project, submodule_item_id, old_submodule_item_id) - elsif gitlab_dot_com_url?(url) - gitlab_com_tree_links(namespace, project, submodule_item_id, old_submodule_item_id) - else - [sanitize_submodule_url(url), nil, nil] - end + if self_url?(url, namespace, project) + [ + url_helpers.namespace_project_path(namespace, project), + url_helpers.namespace_project_tree_path(namespace, project, submodule_item_id), + (url_helpers.namespace_project_compare_path(namespace, project, to: submodule_item_id, from: old_submodule_item_id) if old_submodule_item_id) + ] + elsif relative_self_url?(url) + relative_self_links(url, submodule_item_id, old_submodule_item_id, repository.project) + elsif gist_github_dot_com_url?(url) + gist_github_com_tree_links(namespace, project, submodule_item_id) + elsif github_dot_com_url?(url) + github_com_tree_links(namespace, project, submodule_item_id, old_submodule_item_id) + elsif gitlab_dot_com_url?(url) + gitlab_com_tree_links(namespace, project, submodule_item_id, old_submodule_item_id) else [sanitize_submodule_url(url), nil, nil] end @@ -58,6 +44,30 @@ module SubmoduleHelper protected + def extract_namespace_project(url) + namespace_fragment, _, project = url.rpartition('/') + namespace = namespace_fragment.rpartition(%r{[:/]}).last + + return [nil, nil] unless project.present? && namespace.present? + + gitlab_hosts = [Gitlab.config.gitlab.url, + Gitlab.config.gitlab_shell.ssh_path_prefix] + + matching_host = gitlab_hosts.find do |host| + url.start_with?(host) + end + + if matching_host + namespace, _, project = url.delete_prefix(matching_host).rpartition('/') + end + + namespace.delete_prefix!('/') + project.rstrip! + project.delete_suffix!('.git') + + [namespace, project] + end + def gist_github_dot_com_url?(url) url =~ %r{gist\.github\.com[/:][^/]+/[^/]+\Z} end diff --git a/app/models/active_session.rb b/app/models/active_session.rb index 7dbc95c251..0dcd8105b7 100644 --- a/app/models/active_session.rb +++ b/app/models/active_session.rb @@ -67,7 +67,7 @@ class ActiveSession def self.set(user, request) Gitlab::Redis::Sessions.with do |redis| session_private_id = request.session.id.private_id - client = DeviceDetector.new(request.user_agent) + client = Gitlab::SafeDeviceDetector.new(request.user_agent) timestamp = Time.current expiry = Settings.gitlab['session_expire_delay'] * 60 diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb index 361b1a8dca..3bb63542c2 100644 --- a/app/models/application_setting.rb +++ b/app/models/application_setting.rb @@ -799,10 +799,6 @@ class ApplicationSetting < ApplicationRecord ::AsciidoctorExtensions::Kroki::SUPPORTED_DIAGRAM_NAMES.include?(diagram_type) end - def personal_access_tokens_disabled? - false - end - private def parsed_grafana_url diff --git a/app/models/ci/build_runner_session.rb b/app/models/ci/build_runner_session.rb index c6dbb5d0a4..0f37ce7096 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 71794964c9..f761193a3a 100644 --- a/app/models/hooks/web_hook.rb +++ b/app/models/hooks/web_hook.rb @@ -41,6 +41,8 @@ class WebHook < ApplicationRecord validate :no_missing_url_variables after_initialize :initialize_url_variables + before_validation :reset_token + before_validation :reset_url_variables, unless: ->(hook) { hook.is_a?(ServiceHook) } scope :executable, -> do next all unless Feature.enabled?(:web_hooks_disable_failed) @@ -200,6 +202,14 @@ class WebHook < ApplicationRecord private + def reset_token + self.token = nil if url_changed? && !encrypted_token_changed? + end + + def reset_url_variables + self.url_variables = {} if url_changed? && !encrypted_url_variables_changed? + end + def web_hooks_disable_failed? self.class.web_hooks_disable_failed?(self) end diff --git a/app/models/hooks/web_hook_log.rb b/app/models/hooks/web_hook_log.rb index c32957fbef..5af01cae0f 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 3ca514ab1f..e2aefb3c11 100644 --- a/app/models/integrations/jira.rb +++ b/app/models/integrations/jira.rb @@ -98,7 +98,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 7b61010ab0..0f5e7f80e1 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -2145,8 +2145,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 3413b3e342..3f1f6fbb3e 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -980,12 +980,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 6d198fc755..ff34b00187 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1574,7 +1574,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/group_policy.rb b/app/policies/group_policy.rb index 7a0fb10928..fd8ebf4395 100644 --- a/app/policies/group_policy.rb +++ b/app/policies/group_policy.rb @@ -272,6 +272,9 @@ class GroupPolicy < Namespaces::GroupProjectNamespaceSharedPolicy rule { can?(:admin_group) & resource_access_token_feature_available }.policy do enable :read_resource_access_tokens enable :destroy_resource_access_tokens + end + + rule { can?(:admin_group) & resource_access_token_creation_allowed }.policy do enable :admin_setting_to_allow_project_access_token_creation end @@ -332,12 +335,16 @@ class GroupPolicy < Namespaces::GroupProjectNamespaceSharedPolicy true end + def resource_access_token_create_feature_available? + true + end + def can_read_group_member? !(@subject.private? && access_level == GroupMember::NO_ACCESS) end def resource_access_token_creation_allowed? - resource_access_token_feature_available? && group.root_ancestor.namespace_settings.resource_access_token_creation_allowed? + resource_access_token_create_feature_available? && group.root_ancestor.namespace_settings.resource_access_token_creation_allowed? end def valid_dependency_proxy_deploy_token diff --git a/app/policies/packages/policies/group_policy.rb b/app/policies/packages/policies/group_policy.rb index 32dbcb1b65..d8c20c7a90 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/policies/packages/policies/project_policy.rb b/app/policies/packages/policies/project_policy.rb index c754d24349..0fb5953f2a 100644 --- a/app/policies/packages/policies/project_policy.rb +++ b/app/policies/packages/policies/project_policy.rb @@ -52,3 +52,5 @@ module Packages end end end + +Packages::Policies::ProjectPolicy.prepend_mod_with('Packages::Policies::ProjectPolicy') diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb index 77bdf9d62f..ac59b4b640 100644 --- a/app/policies/project_policy.rb +++ b/app/policies/project_policy.rb @@ -157,7 +157,9 @@ class ProjectPolicy < BasePolicy condition(:service_desk_enabled) { @subject.service_desk_enabled? } with_scope :subject - condition(:resource_access_token_feature_available) { resource_access_token_feature_available? } + condition(:resource_access_token_feature_available) do + resource_access_token_feature_available? + end condition(:resource_access_token_creation_allowed) { resource_access_token_creation_allowed? } # We aren't checking `:read_issue` or `:read_merge_request` in this case @@ -310,6 +312,8 @@ class ProjectPolicy < BasePolicy rule { guest & can?(:download_code) }.enable :build_download_code rule { guest & can?(:read_container_image) }.enable :build_read_container_image + rule { guest & ~public_project }.enable :read_grafana + rule { can?(:reporter_access) }.policy do enable :admin_issue_board enable :download_code @@ -342,6 +346,7 @@ class ProjectPolicy < BasePolicy enable :read_package enable :read_product_analytics enable :read_ci_cd_analytics + enable :read_grafana end # We define `:public_user_access` separately because there are cases in gitlab-ee @@ -794,7 +799,7 @@ class ProjectPolicy < BasePolicy rule { project_bot }.enable :project_bot_access - rule { can?(:read_all_resources) & resource_access_token_feature_available }.enable :read_resource_access_tokens + rule { can?(:read_all_resources) }.enable :read_resource_access_tokens rule { can?(:admin_project) & resource_access_token_feature_available }.policy do enable :read_resource_access_tokens @@ -915,12 +920,16 @@ class ProjectPolicy < BasePolicy true end + def resource_access_token_create_feature_available? + true + end + def resource_access_token_creation_allowed? group = project.group return true unless group # always enable for projects in personal namespaces - resource_access_token_feature_available? && group.root_ancestor.namespace_settings.resource_access_token_creation_allowed? + resource_access_token_create_feature_available? && group.root_ancestor.namespace_settings.resource_access_token_creation_allowed? end def project diff --git a/app/services/error_tracking/list_projects_service.rb b/app/services/error_tracking/list_projects_service.rb index 625addaf91..4a47b09ae6 100644 --- a/app/services/error_tracking/list_projects_service.rb +++ b/app/services/error_tracking/list_projects_service.rb @@ -2,6 +2,8 @@ module ErrorTracking class ListProjectsService < ErrorTracking::BaseService + MASKED_TOKEN_REGEX = /\A\*+\z/.freeze + private def perform @@ -21,23 +23,31 @@ module ErrorTracking def project_error_tracking_setting @project_error_tracking_setting ||= begin (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) + setting.token = token(setting, url_changed) setting.enabled = true end end end - def token(setting) + def token(setting, url_changed) + return if url_changed && masked_token? + # Use param token if not masked, otherwise use database token - return params[:token] unless /\A\*+\z/.match?(params[:token]) + return params[:token] unless masked_token? setting.token end + + def masked_token? + MASKED_TOKEN_REGEX.match?(params[:token]) + end end end diff --git a/app/services/packages/nuget/metadata_extraction_service.rb b/app/services/packages/nuget/metadata_extraction_service.rb index 66abd18915..02086b2a28 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 de7ede4eab..6a13b8e38c 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/resource_access_tokens/create_service.rb b/app/services/resource_access_tokens/create_service.rb index b8a210c0a9..c694853605 100644 --- a/app/services/resource_access_tokens/create_service.rb +++ b/app/services/resource_access_tokens/create_service.rb @@ -13,6 +13,7 @@ module ResourceAccessTokens return error("User does not have permission to create #{resource_type} access token") unless has_permission_to_create? access_level = params[:access_level] || Gitlab::Access::MAINTAINER + return error("Could not provision owner access to project access token") if do_not_allow_owner_access_level_for_project_bot?(access_level) user = create_user @@ -107,7 +108,7 @@ module ResourceAccessTokens end def create_membership(resource, user, access_level) - resource.add_member(user, access_level, current_user: current_user, expires_at: params[:expires_at]) + resource.add_member(user, access_level, expires_at: params[:expires_at]) end def log_event(token) @@ -121,6 +122,12 @@ module ResourceAccessTokens def success(access_token) ServiceResponse.success(payload: { access_token: access_token }) end + + def do_not_allow_owner_access_level_for_project_bot?(access_level) + resource.is_a?(Project) && + access_level == Gitlab::Access::OWNER && + !current_user.can?(:manage_owners, resource) + end end end diff --git a/app/services/users/update_service.rb b/app/services/users/update_service.rb index cb2711b6fe..96018db597 100644 --- a/app/services/users/update_service.rb +++ b/app/services/users/update_service.rb @@ -31,6 +31,7 @@ module Users assign_identity build_canonical_email + reset_unconfirmed_email if @user.save(validate: validate) && update_status notify_success(user_exists) @@ -64,6 +65,13 @@ module Users Users::UpdateCanonicalEmailService.new(user: @user).execute end + def reset_unconfirmed_email + return unless @user.persisted? + return unless @user.email_changed? + + @user.update_column(:unconfirmed_email, nil) + end + def update_status return true unless @status_params diff --git a/app/services/web_hooks/log_execution_service.rb b/app/services/web_hooks/log_execution_service.rb index 1a40c877bd..448bb7d409 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/devise/mailer/_confirmation_instructions_account.html.haml b/app/views/devise/mailer/_confirmation_instructions_account.html.haml index 9d469ff6e7..c165581877 100644 --- a/app/views/devise/mailer/_confirmation_instructions_account.html.haml +++ b/app/views/devise/mailer/_confirmation_instructions_account.html.haml @@ -1,7 +1,7 @@ - confirmation_link = confirmation_url(@resource, confirmation_token: @token) - if @resource.unconfirmed_email.present? || !@resource.created_recently? #content - = email_default_heading(@resource.unconfirmed_email || @resource.email) + = email_default_heading(@email) %p= _('Click the link below to confirm your email address.') #cta = link_to _('Confirm your email address'), confirmation_link diff --git a/app/views/devise/mailer/_confirmation_instructions_account.text.erb b/app/views/devise/mailer/_confirmation_instructions_account.text.erb index e6da78e3a3..7e4f38885f 100644 --- a/app/views/devise/mailer/_confirmation_instructions_account.text.erb +++ b/app/views/devise/mailer/_confirmation_instructions_account.text.erb @@ -1,5 +1,5 @@ <% if @resource.unconfirmed_email.present? || !@resource.created_recently? %> -<%= @resource.unconfirmed_email || @resource.email %>, +<%= @email %>, <%= _('Use the link below to confirm your email address.') %> <% else %> <% if Gitlab.com? %> diff --git a/app/views/layouts/nav/sidebar/_profile.html.haml b/app/views/layouts/nav/sidebar/_profile.html.haml index 0e3327935c..a1393615e6 100644 --- a/app/views/layouts/nav/sidebar/_profile.html.haml +++ b/app/views/layouts/nav/sidebar/_profile.html.haml @@ -51,18 +51,17 @@ = link_to profile_chat_names_path do %strong.fly-out-top-item-name = _('Chat') - - unless Gitlab::CurrentSettings.personal_access_tokens_disabled? - = nav_link(controller: :personal_access_tokens) do - = link_to profile_personal_access_tokens_path do - .nav-icon-container - = sprite_icon('token') - %span.nav-item-name - = _('Access Tokens') - %ul.sidebar-sub-level-items.is-fly-out-only - = nav_link(controller: :personal_access_tokens, html_options: { class: "fly-out-top-item" } ) do - = link_to profile_personal_access_tokens_path do - %strong.fly-out-top-item-name - = _('Access Tokens') + = nav_link(controller: :personal_access_tokens) do + = link_to profile_personal_access_tokens_path do + .nav-icon-container + = sprite_icon('token') + %span.nav-item-name + = _('Access Tokens') + %ul.sidebar-sub-level-items.is-fly-out-only + = nav_link(controller: :personal_access_tokens, html_options: { class: "fly-out-top-item" } ) do + = link_to profile_personal_access_tokens_path do + %strong.fly-out-top-item-name + = _('Access Tokens') = nav_link(controller: :emails) do = link_to profile_emails_path, data: { qa_selector: 'profile_emails_link' } do .nav-icon-container diff --git a/app/views/projects/tags/_release_link.html.haml b/app/views/projects/tags/_release_link.html.haml index c942d122a5..6c79b13f43 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 cb7751ecf2..a9c3309e38 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/operations/metrics/embed_grafana.md b/doc/operations/metrics/embed_grafana.md index b307aa5fa3..43a7447a97 100644 --- a/doc/operations/metrics/embed_grafana.md +++ b/doc/operations/metrics/embed_grafana.md @@ -55,9 +55,10 @@ To set up the Grafana API in Grafana: 1. Select **Save Changes**. NOTE: -If the Grafana integration is enabled, any user with read access to the GitLab -project can query metrics from the Prometheus instance. All requests proxied -through GitLab are authenticated with the same Grafana Administrator API token. +If the Grafana integration is enabled, users with the Reporter role on public +projects and the Guest role on non-public projects can query metrics from the +Prometheus instance. All requests proxied through GitLab are authenticated with +the same Grafana Administrator API token. ### Generate a link to a panel diff --git a/doc/user/admin_area/settings/external_authorization.md b/doc/user/admin_area/settings/external_authorization.md index 62d3d71361..93021b3610 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/group/settings/group_access_tokens.md b/doc/user/group/settings/group_access_tokens.md index 158e1654c6..4806fcec4d 100644 --- a/doc/user/group/settings/group_access_tokens.md +++ b/doc/user/group/settings/group_access_tokens.md @@ -48,9 +48,6 @@ You cannot use group access tokens to create other group, project, or personal a Group access tokens inherit the [default prefix setting](../../admin_area/settings/account_and_limit_settings.md#personal-access-token-prefix) configured for personal access tokens. -NOTE: -Group access tokens are not FIPS compliant and creation and use are disabled when [FIPS mode](../../../development/fips_compliance.md) is enabled. - ## Create a group access token using UI > - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/214045) in GitLab 14.7. diff --git a/doc/user/packages/package_registry/index.md b/doc/user/packages/package_registry/index.md index 2d8cb46f93..bc2579820f 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/profile/personal_access_tokens.md b/doc/user/profile/personal_access_tokens.md index c7fe68c060..f6279262e6 100644 --- a/doc/user/profile/personal_access_tokens.md +++ b/doc/user/profile/personal_access_tokens.md @@ -45,9 +45,6 @@ For examples of how you can use a personal access token to authenticate with the Alternately, GitLab administrators can use the API to create [impersonation tokens](../../api/index.md#impersonation-tokens). Use impersonation tokens to automate authentication as a specific user. -NOTE: -Personal access tokens are not FIPS compliant and creation and use are disabled when [FIPS mode](../../development/fips_compliance.md) is enabled. - ## Create a personal access token > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/348660) in GitLab 15.3, default expiration of 30 days is populated in the UI. diff --git a/doc/user/project/deploy_keys/index.md b/doc/user/project/deploy_keys/index.md index 58f7d3198b..56bb899c23 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 aab72d4859..3dd6f14ea7 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/doc/user/project/settings/project_access_tokens.md b/doc/user/project/settings/project_access_tokens.md index f27672a1b0..6e312a448c 100644 --- a/doc/user/project/settings/project_access_tokens.md +++ b/doc/user/project/settings/project_access_tokens.md @@ -48,9 +48,6 @@ You cannot use project access tokens to create other group, project, or personal Project access tokens inherit the [default prefix setting](../../admin_area/settings/account_and_limit_settings.md#personal-access-token-prefix) configured for personal access tokens. -NOTE: -Project access tokens are not FIPS compliant and creation and use are disabled when [FIPS mode](../../../development/fips_compliance.md) is enabled. - ## Create a project access token > - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/89114) in GitLab 15.1, Owners can select Owner role for project access tokens. diff --git a/lib/api/ci/runner.rb b/lib/api/ci/runner.rb index 2d2dcc544f..a56601d3b2 100644 --- a/lib/api/ci/runner.rb +++ b/lib/api/ci/runner.rb @@ -94,7 +94,8 @@ module API success Entities::Ci::JobRequest::Response http_codes [[201, 'Job was scheduled'], [204, 'No job for Runner'], - [403, 'Forbidden']] + [403, 'Forbidden'], + [409, 'Conflict']] end params do requires :token, type: String, desc: %q(Runner's authentication token) diff --git a/lib/gitlab/api_authentication/token_resolver.rb b/lib/gitlab/api_authentication/token_resolver.rb index dd9039e37f..afada05592 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 c994f179b6..16bee187c8 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/git/repository.rb b/lib/gitlab/git/repository.rb index 9bbe17dcad..4294e3aaaa 100644 --- a/lib/gitlab/git/repository.rb +++ b/lib/gitlab/git/repository.rb @@ -855,7 +855,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, @@ -866,16 +870,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 9a3f5fb844..da2a81983e 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 f11437552e..6a4a4e2819 100644 --- a/lib/gitlab/gitaly_client/repository_service.rb +++ b/lib/gitlab/gitaly_client/repository_service.rb @@ -78,7 +78,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, @@ -89,7 +89,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 ) ) @@ -145,12 +146,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 ) GitalyClient.call( diff --git a/lib/gitlab/hook_data/project_builder.rb b/lib/gitlab/hook_data/project_builder.rb index ebd97d3ab1..aec842e061 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 2ee61705ec..be5bde356d 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/lib/gitlab/safe_device_detector.rb b/lib/gitlab/safe_device_detector.rb new file mode 100644 index 0000000000..ba1010ae5e --- /dev/null +++ b/lib/gitlab/safe_device_detector.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true +# rubocop:disable Gitlab/NamespacedClass +require 'device_detector' + +module Gitlab + class SafeDeviceDetector < ::DeviceDetector + USER_AGENT_MAX_SIZE = 1024 + + def initialize(user_agent) + super(user_agent) + @user_agent = user_agent && user_agent[0..USER_AGENT_MAX_SIZE] + end + end +end + +# rubocop:enable Gitlab/NamespacedClass diff --git a/spec/controllers/profiles/personal_access_tokens_controller_spec.rb b/spec/controllers/profiles/personal_access_tokens_controller_spec.rb index 8dee0490fd..160af8cf3f 100644 --- a/spec/controllers/profiles/personal_access_tokens_controller_spec.rb +++ b/spec/controllers/profiles/personal_access_tokens_controller_spec.rb @@ -36,14 +36,6 @@ RSpec.describe Profiles::PersonalAccessTokensController do expect(created_token.expires_at).to eq(expires_at) end - it 'does not allow creation when personal access tokens are disabled' do - allow(::Gitlab::CurrentSettings).to receive_messages(personal_access_tokens_disabled?: true) - - post :create, params: { personal_access_token: token_attributes } - - expect(response).to have_gitlab_http_status(:not_found) - end - it_behaves_like "#create access token" do let(:url) { :create } end @@ -78,14 +70,6 @@ RSpec.describe Profiles::PersonalAccessTokensController do ) end - it 'returns 404 when personal access tokens are disabled' do - allow(::Gitlab::CurrentSettings).to receive_messages(personal_access_tokens_disabled?: true) - - get :index - - expect(response).to have_gitlab_http_status(:not_found) - end - context "access_token_pagination feature flag is enabled" do before do stub_feature_flags(access_token_pagination: true) diff --git a/spec/controllers/projects/grafana_api_controller_spec.rb b/spec/controllers/projects/grafana_api_controller_spec.rb index 2e25b0271c..90ab49f946 100644 --- a/spec/controllers/projects/grafana_api_controller_spec.rb +++ b/spec/controllers/projects/grafana_api_controller_spec.rb @@ -2,13 +2,20 @@ require 'spec_helper' -RSpec.describe Projects::GrafanaApiController do - let_it_be(:project) { create(:project) } - let_it_be(:user) { create(:user) } +RSpec.describe Projects::GrafanaApiController, feature_category: :metrics do + let_it_be(:project) { create(:project, :public) } + let_it_be(:reporter) { create(:user) } + let_it_be(:guest) { create(:user) } + let(:anonymous) { nil } + let(:user) { reporter } + + before_all do + project.add_reporter(reporter) + project.add_guest(guest) + end before do - project.add_reporter(user) - sign_in(user) + sign_in(user) if user end describe 'GET #proxy' do @@ -41,6 +48,39 @@ RSpec.describe Projects::GrafanaApiController do end end + shared_examples_for 'accessible' do + let(:service_result) { nil } + + it 'returns non erroneous response' do + get :proxy, params: params + + # We don't care about the specific code as long it's not an error. + expect(response).to have_gitlab_http_status(:no_content) + end + end + + shared_examples_for 'not accessible' do + let(:service_result) { nil } + + it 'returns 404 Not found' do + get :proxy, params: params + + expect(response).to have_gitlab_http_status(:not_found) + expect(Grafana::ProxyService).not_to have_received(:new) + end + end + + shared_examples_for 'login required' do + let(:service_result) { nil } + + it 'redirects to login page' do + get :proxy, params: params + + expect(response).to redirect_to(new_user_session_path) + expect(Grafana::ProxyService).not_to have_received(:new) + end + end + context 'with a successful result' do let(:service_result) { { status: :success, body: '{}' } } @@ -96,6 +136,38 @@ RSpec.describe Projects::GrafanaApiController do it_behaves_like 'error response', :bad_request end end + + context 'as guest' do + let(:user) { guest } + + it_behaves_like 'not accessible' + end + + context 'as anonymous' do + let(:user) { anonymous } + + it_behaves_like 'not accessible' + end + + context 'on a private project' do + let_it_be(:project) { create(:project, :private) } + + before_all do + project.add_guest(guest) + end + + context 'as anonymous' do + let(:user) { anonymous } + + it_behaves_like 'login required' + end + + context 'as guest' do + let(:user) { guest } + + it_behaves_like 'accessible' + end + end end describe 'GET #metrics_dashboard' do diff --git a/spec/controllers/projects/jobs_controller_spec.rb b/spec/controllers/projects/jobs_controller_spec.rb index 556dd23c13..58627c4dc7 100644 --- a/spec/controllers/projects/jobs_controller_spec.rb +++ b/spec/controllers/projects/jobs_controller_spec.rb @@ -1380,7 +1380,7 @@ RSpec.describe Projects::JobsController, :clean_gitlab_redis_shared_state do { 'Channel' => { 'Subprotocols' => ["terminal.gitlab.com"], - 'Url' => 'wss://localhost/proxy/build/default_port/', + 'Url' => 'wss://gitlab.example.com/proxy/build/default_port/', 'Header' => { 'Authorization' => [nil] }, @@ -1536,7 +1536,8 @@ RSpec.describe Projects::JobsController, :clean_gitlab_redis_shared_state do allow(Gitlab::Workhorse).to receive(:verify_api_request!).and_return(nil) expect(job.runner_session_url).to start_with('https://') - expect(Gitlab::Workhorse).to receive(:channel_websocket).with(a_hash_including(url: "wss://localhost/proxy/build/default_port/")) + expect(Gitlab::Workhorse).to receive(:channel_websocket) + .with(a_hash_including(url: "wss://gitlab.example.com/proxy/build/default_port/")) make_request end diff --git a/spec/controllers/uploads_controller_spec.rb b/spec/controllers/uploads_controller_spec.rb index e128db8d1c..3e9c56d327 100644 --- a/spec/controllers/uploads_controller_spec.rb +++ b/spec/controllers/uploads_controller_spec.rb @@ -268,17 +268,35 @@ RSpec.describe UploadsController do end context "when not signed in" do - it "responds with status 200" do - get :show, params: { model: "user", mounted_as: "avatar", id: user.id, filename: "dk.png" } + context "when restricted visibility level is not set to public" do + before do + stub_application_setting(restricted_visibility_levels: []) + end - expect(response).to have_gitlab_http_status(:ok) + it "responds with status 200" do + get :show, params: { model: "user", mounted_as: "avatar", id: user.id, filename: "dk.png" } + + expect(response).to have_gitlab_http_status(:ok) + end + + it_behaves_like 'content publicly cached' do + subject do + get :show, params: { model: 'user', mounted_as: 'avatar', id: user.id, filename: 'dk.png' } + + response + end + end end - it_behaves_like 'content publicly cached' do - subject do - get :show, params: { model: 'user', mounted_as: 'avatar', id: user.id, filename: 'dk.png' } + context "when restricted visibility level is set to public" do + before do + stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC]) + end - response + it "responds with status 401" do + get :show, params: { model: "user", mounted_as: "avatar", id: user.id, filename: "dk.png" } + + expect(response).to have_gitlab_http_status(:unauthorized) end end end diff --git a/spec/factories/ci/builds.rb b/spec/factories/ci/builds.rb index 9a3b2837ab..8396ef480c 100644 --- a/spec/factories/ci/builds.rb +++ b/spec/factories/ci/builds.rb @@ -716,7 +716,7 @@ FactoryBot.define do trait :with_runner_session do after(:build) do |build| - build.build_runner_session(url: 'https://localhost') + build.build_runner_session(url: 'https://gitlab.example.com') end end diff --git a/spec/factories/project_hooks.rb b/spec/factories/project_hooks.rb index dbb5c357ac..946b3925ee 100644 --- a/spec/factories/project_hooks.rb +++ b/spec/factories/project_hooks.rb @@ -6,6 +6,10 @@ FactoryBot.define do enable_ssl_verification { false } project + trait :url_variables do + url_variables { { 'abc' => 'supers3cret' } } + end + trait :token do token { generate(:token) } end diff --git a/spec/features/projects_spec.rb b/spec/features/projects_spec.rb index cbd9340b73..cd9c173a4f 100644 --- a/spec/features/projects_spec.rb +++ b/spec/features/projects_spec.rb @@ -329,7 +329,7 @@ RSpec.describe 'Project' do it 'has working links to submodules' do click_link('645f6c4c') - expect(page).to have_selector('[data-testid="branches-select"]', text: '645f6c4c82fd3f5e06f67134450a570b795e55a6') + expect(page).to have_selector('.ref-selector', text: '645f6c4c82fd3f5e06f67134450a570b795e55a6') end context 'for signed commit on default branch', :js do diff --git a/spec/features/tags/developer_views_tags_spec.rb b/spec/features/tags/developer_views_tags_spec.rb index 57e1f7da04..e2399dd997 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/clusters/chain_certificates.pem b/spec/fixtures/clusters/chain_certificates.pem index fe6affec17..63f0b812b6 100644 --- a/spec/fixtures/clusters/chain_certificates.pem +++ b/spec/fixtures/clusters/chain_certificates.pem @@ -1,67 +1,66 @@ -----BEGIN CERTIFICATE----- -MIIGYzCCBUugAwIBAgIQAaQHyOeT/PBR4ioLKYneZDANBgkqhkiG9w0BAQsFADBY +MIIGYjCCBUqgAwIBAgIQATfkha/xTr3pbLHVWlPq4jANBgkqhkiG9w0BAQsFADBY MQswCQYDVQQGEwJCRTEZMBcGA1UEChMQR2xvYmFsU2lnbiBudi1zYTEuMCwGA1UE -AxMlR2xvYmFsU2lnbiBBdGxhcyBSMyBEViBUTFMgQ0EgSDIgMjAyMTAeFw0yMTEw -MTgxODUwMDRaFw0yMjExMTkxODUwMDNaMBsxGTAXBgNVBAMMEGFib3V0LmdpdGxh -Yi5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDWSo0eziN/0lq5 -dIcS7ZceJw2odzZeT0tRkcKEW8iagNul6JetrFlk6h5lxoLEu35+MK6/fWHNmt7u -eQk7HS0uRipskAzeGrL1Hvk8EjIcHXXTxpRu7JqWOu7ZSXwNxW5cqn7L9/N2gYwt -Jg/sfkv9AFQiNOdKrarKfbcBstxmra6rQbh5ggLG5UBT23N4ZrA3XnzvEx3+GjtO -u/a5izbk7FQP3gyXKyfm/SQRpNsytYa9jJqu5Hmyzfap5KaueOJbtJEOk8dR/HWR -i/gmAUevq62MNxorYbz8YU/P1468tS7iORkD31Tc2QWCMQSPya5qGaCGnz7dVgWy -E1xTPbBXAgMBAAGjggNkMIIDYDAbBgNVHREEFDASghBhYm91dC5naXRsYWIuY29t +AxMlR2xvYmFsU2lnbiBBdGxhcyBSMyBEViBUTFMgQ0EgMjAyMiBRMzAeFw0yMjA3 +MjIxOTQyMTFaFw0yMzA4MjMxOTQyMTBaMBsxGTAXBgNVBAMMEGFib3V0LmdpdGxh +Yi5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDFFFQs8EITaWo5 +0U18/mPTDLencU/7siJT/4P8oeDkemyx98wzK6vuNj/2JEZ3v1psKun5n8Pb/fHa +somKd/4icHgC4rnxrO6zayfb+cKzVghQe12Nj75lx6RtppqTgAmSOa3Tai5niICT +I8s3d2wsHtfEgqAavcD0/zdPIk25Ji7yfquldSthnlhQqI4Pm3OxTiyFj/V5ZhFl +IWZLvQaENjBSDVZQDcaPdWwodfXNA8fJmqk7cTLQ9P9NgjWvva7acl+Yd6hOFzV0 +EllBl/WF1KB+YzGuHI0CQHT7sv3GW1lXeE2EqrWoSdLTOSAqm6y02DyE79d1xvG6 +XXfX5ILlAgMBAAGjggNjMIIDXzAbBgNVHREEFDASghBhYm91dC5naXRsYWIuY29t MA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIw -HQYDVR0OBBYEFJFVruwpjWeUfGJXl3m5grAjhAwPMFcGA1UdIARQME4wCAYGZ4EM +HQYDVR0OBBYEFHK7MnjGDptQWjfmJ2fr3IrjxEopMFcGA1UdIARQME4wCAYGZ4EM AQIBMEIGCisGAQQBoDIKAQMwNDAyBggrBgEFBQcCARYmaHR0cHM6Ly93d3cuZ2xv YmFsc2lnbi5jb20vcmVwb3NpdG9yeS8wDAYDVR0TAQH/BAIwADCBngYIKwYBBQUH AQEEgZEwgY4wQAYIKwYBBQUHMAGGNGh0dHA6Ly9vY3NwLmdsb2JhbHNpZ24uY29t -L2NhL2dzYXRsYXNyM2R2dGxzY2FoMjIwMjEwSgYIKwYBBQUHMAKGPmh0dHA6Ly9z -ZWN1cmUuZ2xvYmFsc2lnbi5jb20vY2FjZXJ0L2dzYXRsYXNyM2R2dGxzY2FoMjIw -MjEuY3J0MB8GA1UdIwQYMBaAFCo0uar6vzyI8Ufy0hJ4vsXlqrBpMEgGA1UdHwRB +L2NhL2dzYXRsYXNyM2R2dGxzY2EyMDIycTMwSgYIKwYBBQUHMAKGPmh0dHA6Ly9z +ZWN1cmUuZ2xvYmFsc2lnbi5jb20vY2FjZXJ0L2dzYXRsYXNyM2R2dGxzY2EyMDIy +cTMuY3J0MB8GA1UdIwQYMBaAFPqROWOa+60QJOW+tbnaq9nERmmrMEgGA1UdHwRB MD8wPaA7oDmGN2h0dHA6Ly9jcmwuZ2xvYmFsc2lnbi5jb20vY2EvZ3NhdGxhc3Iz -ZHZ0bHNjYWgyMjAyMS5jcmwwggF+BgorBgEEAdZ5AgQCBIIBbgSCAWoBaAB3AG9T -dqwx8DEZ2JkApFEV/3cVHBHZAsEAKQaNsgiaN9kTAAABfJS9R5YAAAQDAEgwRgIh -AOOZmc41vB2ICwkwEB5Bmpm/X8UHfjbxwrCXEdeRmO+qAiEAg/JugZIrG2PeV4bA -Gm6rry7HUfB954bQJ4p0PeQVmwsAdABGpVXrdfqRIDC1oolp9PN9ESxBdL79SbiF -q/L8cP5tRwAAAXyUvUeOAAAEAwBFMEMCHyRAiTz2fZ8DuQF6hrVP+IMTCPBtjB3D -m4naI8tC/foCIDXFCRIYjRb00CFI6piLYGihRy+GYF5nMQhQ9uE6hltzAHcAUaOw -9f0BeZxWbbg3eI8MpHrMGyfL956IQpoN/tSLBeUAAAF8lL1ICgAABAMASDBGAiEA -5d/bXb9TPZWhwSH8GGji/LDFL6OJnZtOV94sBaDiFgMCIQCtl00oCRMFFnqsvBo6 -SRtnDqJkEHYBS12I4LyC+D1onjANBgkqhkiG9w0BAQsFAAOCAQEAE5xcno79J+Ec -DIPJKnJCugKiM7yKjCjCp/63osCbRC+jUwRyXBIe/oTdY3geKwDOQAvyEeJPSWP1 -LbNp0l3yHbYXfsYl/NMTrJpjrJrrRO5BxG/d3IPwXIlcZrrdDSoGfGYIF9N23iqB -in15L7B+PodTl8/mSQZTjbLoecPvl+AOcLyStcWCKYQUlQb3x4UV3R4Z1ukwGbBC -cDbTR2XOSJzA9ECJcxKnWjQRQUc54pdG3pt13Wu2dVapX5sWZpV05rga3bBDjCqw -DcfKuYbOChm2i6CQ578lAntPTIS02EkGFHrmYxrIAvlhGksHpJNJtRoff1KkQKni -r8emWp7D2Q== +ZHZ0bHNjYTIwMjJxMy5jcmwwggF9BgorBgEEAdZ5AgQCBIIBbQSCAWkBZwB1AG9T +dqwx8DEZ2JkApFEV/3cVHBHZAsEAKQaNsgiaN9kTAAABgiduiHEAAAQDAEYwRAIg +SYQrru/KAKfe+hUqpJmk7Fc8drkgtY3IcAurTOwbM68CIBYO9sbDspd5p7v17RQi +QQkjdRwSjHiIgvlX0Y1JqmXjAHYArfe++nz/EMiLnT2cHj4YarRnKV3PsQwkyoWG +NOvcgooAAAGCJ26IcQAABAMARzBFAiBc5a10annqMEH69bdEFy/Vo1gb3S3GQ993 +BCRV7ZXG4gIhAMqnsoKkU6ITwRXwE9KGjHnijJ8QrBrnK0i+JFaGe1ffAHYAs3N3 +B+GEUPhjhtYFqdwRCUp5LbFnDAuH3PADDnk2pZoAAAGCJ26I6QAABAMARzBFAiAf +lW8Agd0DB68YA8XAbnlq7QNHw3uRMzNdS8gtRUe75gIhANTe+mt2p1ryW83P31OW +jH3cEGJxdUNT/oDM3Fzesx94MA0GCSqGSIb3DQEBCwUAA4IBAQAfivKEmjqqOFFh +VsX2XYkoDtreghpqMwHMCLwNk852Alr/Seyv9Ilng8cunU4NmhvEtsYVXkfE4XvB +0QIVxkg1w7A+p7ejMjh6doLJ0aWNWIVW/DwOeP0qstF9lqvLdLDABoVn0BtYCDTH +gjG80e2xpvPiKHGvBL+hlOIJwUuIAT3jN23sS1GoiYQGKsz0lovB09/6MGG0Qj8C +3i9a59T9XBpwSKdpKd4u/CB6koBXD3atbBNBACuAMcFckTEtmkCFtSpqBuocJGKf +LB4MFVaEwrd7Lc1ACC1et5FDtEI4I3/CerkRZTV+mRz5n6tB91AK3dRvjElfhiuh +XXYRULvB -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- -MIIExTCCA62gAwIBAgIQeimFGrf0XWZ5UGZBtv/XHTANBgkqhkiG9w0BAQsFADBM +MIIEjzCCA3egAwIBAgIQfCoMIT/GVVNFyR8ZH7hO+jANBgkqhkiG9w0BAQsFADBM MSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMzETMBEGA1UEChMKR2xv -YmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjAeFw0yMTA2MTYxMjAwMDBaFw0y -NDA2MTYwMDAwMDBaMFgxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWdu -IG52LXNhMS4wLAYDVQQDEyVHbG9iYWxTaWduIEF0bGFzIFIzIERWIFRMUyBDQSBI -MiAyMDIxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1JTAQMj+QUYF -3d9X5eOWFOphbB6GpHE3J0uvUXcQwxnd8Jz26aQCE1ZYxJFEc2WmsxuVeVXU+rZj -7+MYD7Mg72bhuiwUdwRGRN4a2N122LfIQlTFlHu/fwcNqYX/fe3phvZt9upnH4oJ -aLBbay+t+HPPC4em74x2WKaIl31ZXzgzllLomnlLISLOKiQe1rEHp4yy3/yE2a4G -1l/lprA49dcyM/oylm9Bbkum2F4C+EOjHgTAoDVJrJpdWvPj0CU+HkmftujfFp4S -55LECSr2TfJt7xjgR3eLUx12nlpoauWEzZ0/i6OIDPfbmqcksw4ani/YO07LbRM6 -cY9VZzkAvwIDAQABo4IBlTCCAZEwDgYDVR0PAQH/BAQDAgGGMB0GA1UdJQQWMBQG +YmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjAeFw0yMjA0MjAxMjAwMDBaFw0y +NTA0MjAwMDAwMDBaMFgxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWdu +IG52LXNhMS4wLAYDVQQDEyVHbG9iYWxTaWduIEF0bGFzIFIzIERWIFRMUyBDQSAy +MDIyIFEzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuKh6ZjxOZpzO +N6VUNU02x5nTqCc28i/G1Rg+6QndBdbXLDQyfAhjSdEQN+V4XRFizm37Lz83lNuP +ezDpXizZVT+y27mgtWA3i6QGMjVQpAmvCkX/qB+bZY7dSuBAoeNjN1iQ3XU7/A4c +gkCYvXCxwUgUFDwES2nd1JwBpukh44IK/uSqvzSgjMvJeW4+XGpSnsTtK8Vp/lA8 +k521/y0oqGwGbJ3Fr7JZ+1l3DXR6iISk1B3UuiAGzLUeSE50IRWGdcDMWtEFz1cW +ehMX7MJKrtUecqoiWoycgjLEEOZCbiGGaHyAIzA1072wXgopK/AUsRg32Vklw+c4 +2enULTY1ZQIDAQABo4IBXzCCAVswDgYDVR0PAQH/BAQDAgGGMB0GA1UdJQQWMBQG CCsGAQUFBwMBBggrBgEFBQcDAjASBgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQW -BBQqNLmq+r88iPFH8tISeL7F5aqwaTAfBgNVHSMEGDAWgBSP8Et/qC5FJK5NUPpj +BBT6kTljmvutECTlvrW52qvZxEZpqzAfBgNVHSMEGDAWgBSP8Et/qC5FJK5NUPpj move4t0bvDB7BggrBgEFBQcBAQRvMG0wLgYIKwYBBQUHMAGGImh0dHA6Ly9vY3Nw Mi5nbG9iYWxzaWduLmNvbS9yb290cjMwOwYIKwYBBQUHMAKGL2h0dHA6Ly9zZWN1 cmUuZ2xvYmFsc2lnbi5jb20vY2FjZXJ0L3Jvb3QtcjMuY3J0MDYGA1UdHwQvMC0w -K6ApoCeGJWh0dHA6Ly9jcmwuZ2xvYmFsc2lnbi5jb20vcm9vdC1yMy5jcmwwVwYD -VR0gBFAwTjAIBgZngQwBAgEwQgYKKwYBBAGgMgoBAzA0MDIGCCsGAQUFBwIBFiZo -dHRwczovL3d3dy5nbG9iYWxzaWduLmNvbS9yZXBvc2l0b3J5LzANBgkqhkiG9w0B -AQsFAAOCAQEAEsIwXEhdAfoUGaKAnYfVI7zsOY7Sx8bpC/obGxXa4Kyu8CVx+TtT -g8WmKNF7+I7C51NZEmhvb8UDI1G9ny7iYIRDajQD5AeZowbfC69aHQSI9LiOeAZb -YaRDJfWps9redPwoaC0iT5R4xLOnWwCtmIho1bv/YG3pMAvaQ+qn04kuUvWO7LEp -u7FdHmx1DdgkefcqYgN/rAZ8E39S9VxWV+64PNUDey8vkAIH8FCTxbWiITty6dsH -SulKQ9pSa93k9PHTf+di08mMQBq5WBWTiFeMYZEWyE/z7NHdU3eLMZjq6y/nKlF9 -nywrToh4AgdZK6JnbU+lqbNiexJbaBoA3w== +K6ApoCeGJWh0dHA6Ly9jcmwuZ2xvYmFsc2lnbi5jb20vcm9vdC1yMy5jcmwwIQYD +VR0gBBowGDAIBgZngQwBAgEwDAYKKwYBBAGgMgoBAzANBgkqhkiG9w0BAQsFAAOC +AQEAFDMseeU/gsZwP9pZOKe7onasYRgFaFfZDfuKRrzxqOgMcAIdxi+X7TY+nlKG +L1xi2NVHQ5pz0Sslh59EtBTrJrwhR3QgvZ+kv7OAHU01fc25tdpV8pBQyLIXTg60 +YYgpX0RdA39XkYHQ6zCu1SrsgiDOTtKwi5UCYXPYaTT0rWMOXOQgH6l97Y7lHAS7 +Ip/HqSLKmT0Cp2foBi36BGu7SdJsmVdjbC3CYXjhILH79r/hgjk5PHvvfRqVSrJy +2lWQru3d4nCQfBrutTJaXc/W+kXyngEMMS+JhP4xYA/97qZbhNXHGOak+UAwKRge +/vxBtbkpBXWLYhpbIi6/5FlssA== -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4G diff --git a/spec/fixtures/clusters/intermediate_certificate.pem b/spec/fixtures/clusters/intermediate_certificate.pem index 21bada7356..b287201af1 100644 --- a/spec/fixtures/clusters/intermediate_certificate.pem +++ b/spec/fixtures/clusters/intermediate_certificate.pem @@ -1,28 +1,27 @@ -----BEGIN CERTIFICATE----- -MIIExTCCA62gAwIBAgIQeimFGrf0XWZ5UGZBtv/XHTANBgkqhkiG9w0BAQsFADBM +MIIEjzCCA3egAwIBAgIQfCoMIT/GVVNFyR8ZH7hO+jANBgkqhkiG9w0BAQsFADBM MSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMzETMBEGA1UEChMKR2xv -YmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjAeFw0yMTA2MTYxMjAwMDBaFw0y -NDA2MTYwMDAwMDBaMFgxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWdu -IG52LXNhMS4wLAYDVQQDEyVHbG9iYWxTaWduIEF0bGFzIFIzIERWIFRMUyBDQSBI -MiAyMDIxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1JTAQMj+QUYF -3d9X5eOWFOphbB6GpHE3J0uvUXcQwxnd8Jz26aQCE1ZYxJFEc2WmsxuVeVXU+rZj -7+MYD7Mg72bhuiwUdwRGRN4a2N122LfIQlTFlHu/fwcNqYX/fe3phvZt9upnH4oJ -aLBbay+t+HPPC4em74x2WKaIl31ZXzgzllLomnlLISLOKiQe1rEHp4yy3/yE2a4G -1l/lprA49dcyM/oylm9Bbkum2F4C+EOjHgTAoDVJrJpdWvPj0CU+HkmftujfFp4S -55LECSr2TfJt7xjgR3eLUx12nlpoauWEzZ0/i6OIDPfbmqcksw4ani/YO07LbRM6 -cY9VZzkAvwIDAQABo4IBlTCCAZEwDgYDVR0PAQH/BAQDAgGGMB0GA1UdJQQWMBQG +YmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjAeFw0yMjA0MjAxMjAwMDBaFw0y +NTA0MjAwMDAwMDBaMFgxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWdu +IG52LXNhMS4wLAYDVQQDEyVHbG9iYWxTaWduIEF0bGFzIFIzIERWIFRMUyBDQSAy +MDIyIFEzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuKh6ZjxOZpzO +N6VUNU02x5nTqCc28i/G1Rg+6QndBdbXLDQyfAhjSdEQN+V4XRFizm37Lz83lNuP +ezDpXizZVT+y27mgtWA3i6QGMjVQpAmvCkX/qB+bZY7dSuBAoeNjN1iQ3XU7/A4c +gkCYvXCxwUgUFDwES2nd1JwBpukh44IK/uSqvzSgjMvJeW4+XGpSnsTtK8Vp/lA8 +k521/y0oqGwGbJ3Fr7JZ+1l3DXR6iISk1B3UuiAGzLUeSE50IRWGdcDMWtEFz1cW +ehMX7MJKrtUecqoiWoycgjLEEOZCbiGGaHyAIzA1072wXgopK/AUsRg32Vklw+c4 +2enULTY1ZQIDAQABo4IBXzCCAVswDgYDVR0PAQH/BAQDAgGGMB0GA1UdJQQWMBQG CCsGAQUFBwMBBggrBgEFBQcDAjASBgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQW -BBQqNLmq+r88iPFH8tISeL7F5aqwaTAfBgNVHSMEGDAWgBSP8Et/qC5FJK5NUPpj +BBT6kTljmvutECTlvrW52qvZxEZpqzAfBgNVHSMEGDAWgBSP8Et/qC5FJK5NUPpj move4t0bvDB7BggrBgEFBQcBAQRvMG0wLgYIKwYBBQUHMAGGImh0dHA6Ly9vY3Nw Mi5nbG9iYWxzaWduLmNvbS9yb290cjMwOwYIKwYBBQUHMAKGL2h0dHA6Ly9zZWN1 cmUuZ2xvYmFsc2lnbi5jb20vY2FjZXJ0L3Jvb3QtcjMuY3J0MDYGA1UdHwQvMC0w -K6ApoCeGJWh0dHA6Ly9jcmwuZ2xvYmFsc2lnbi5jb20vcm9vdC1yMy5jcmwwVwYD -VR0gBFAwTjAIBgZngQwBAgEwQgYKKwYBBAGgMgoBAzA0MDIGCCsGAQUFBwIBFiZo -dHRwczovL3d3dy5nbG9iYWxzaWduLmNvbS9yZXBvc2l0b3J5LzANBgkqhkiG9w0B -AQsFAAOCAQEAEsIwXEhdAfoUGaKAnYfVI7zsOY7Sx8bpC/obGxXa4Kyu8CVx+TtT -g8WmKNF7+I7C51NZEmhvb8UDI1G9ny7iYIRDajQD5AeZowbfC69aHQSI9LiOeAZb -YaRDJfWps9redPwoaC0iT5R4xLOnWwCtmIho1bv/YG3pMAvaQ+qn04kuUvWO7LEp -u7FdHmx1DdgkefcqYgN/rAZ8E39S9VxWV+64PNUDey8vkAIH8FCTxbWiITty6dsH -SulKQ9pSa93k9PHTf+di08mMQBq5WBWTiFeMYZEWyE/z7NHdU3eLMZjq6y/nKlF9 -nywrToh4AgdZK6JnbU+lqbNiexJbaBoA3w== +K6ApoCeGJWh0dHA6Ly9jcmwuZ2xvYmFsc2lnbi5jb20vcm9vdC1yMy5jcmwwIQYD +VR0gBBowGDAIBgZngQwBAgEwDAYKKwYBBAGgMgoBAzANBgkqhkiG9w0BAQsFAAOC +AQEAFDMseeU/gsZwP9pZOKe7onasYRgFaFfZDfuKRrzxqOgMcAIdxi+X7TY+nlKG +L1xi2NVHQ5pz0Sslh59EtBTrJrwhR3QgvZ+kv7OAHU01fc25tdpV8pBQyLIXTg60 +YYgpX0RdA39XkYHQ6zCu1SrsgiDOTtKwi5UCYXPYaTT0rWMOXOQgH6l97Y7lHAS7 +Ip/HqSLKmT0Cp2foBi36BGu7SdJsmVdjbC3CYXjhILH79r/hgjk5PHvvfRqVSrJy +2lWQru3d4nCQfBrutTJaXc/W+kXyngEMMS+JhP4xYA/97qZbhNXHGOak+UAwKRge +/vxBtbkpBXWLYhpbIi6/5FlssA== -----END CERTIFICATE----- diff --git a/spec/fixtures/clusters/leaf_certificate.pem b/spec/fixtures/clusters/leaf_certificate.pem index aecb3fc8d4..b7b2f78a97 100644 --- a/spec/fixtures/clusters/leaf_certificate.pem +++ b/spec/fixtures/clusters/leaf_certificate.pem @@ -1,37 +1,37 @@ -----BEGIN CERTIFICATE----- -MIIGYzCCBUugAwIBAgIQAaQHyOeT/PBR4ioLKYneZDANBgkqhkiG9w0BAQsFADBY +MIIGYjCCBUqgAwIBAgIQATfkha/xTr3pbLHVWlPq4jANBgkqhkiG9w0BAQsFADBY MQswCQYDVQQGEwJCRTEZMBcGA1UEChMQR2xvYmFsU2lnbiBudi1zYTEuMCwGA1UE -AxMlR2xvYmFsU2lnbiBBdGxhcyBSMyBEViBUTFMgQ0EgSDIgMjAyMTAeFw0yMTEw -MTgxODUwMDRaFw0yMjExMTkxODUwMDNaMBsxGTAXBgNVBAMMEGFib3V0LmdpdGxh -Yi5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDWSo0eziN/0lq5 -dIcS7ZceJw2odzZeT0tRkcKEW8iagNul6JetrFlk6h5lxoLEu35+MK6/fWHNmt7u -eQk7HS0uRipskAzeGrL1Hvk8EjIcHXXTxpRu7JqWOu7ZSXwNxW5cqn7L9/N2gYwt -Jg/sfkv9AFQiNOdKrarKfbcBstxmra6rQbh5ggLG5UBT23N4ZrA3XnzvEx3+GjtO -u/a5izbk7FQP3gyXKyfm/SQRpNsytYa9jJqu5Hmyzfap5KaueOJbtJEOk8dR/HWR -i/gmAUevq62MNxorYbz8YU/P1468tS7iORkD31Tc2QWCMQSPya5qGaCGnz7dVgWy -E1xTPbBXAgMBAAGjggNkMIIDYDAbBgNVHREEFDASghBhYm91dC5naXRsYWIuY29t +AxMlR2xvYmFsU2lnbiBBdGxhcyBSMyBEViBUTFMgQ0EgMjAyMiBRMzAeFw0yMjA3 +MjIxOTQyMTFaFw0yMzA4MjMxOTQyMTBaMBsxGTAXBgNVBAMMEGFib3V0LmdpdGxh +Yi5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDFFFQs8EITaWo5 +0U18/mPTDLencU/7siJT/4P8oeDkemyx98wzK6vuNj/2JEZ3v1psKun5n8Pb/fHa +somKd/4icHgC4rnxrO6zayfb+cKzVghQe12Nj75lx6RtppqTgAmSOa3Tai5niICT +I8s3d2wsHtfEgqAavcD0/zdPIk25Ji7yfquldSthnlhQqI4Pm3OxTiyFj/V5ZhFl +IWZLvQaENjBSDVZQDcaPdWwodfXNA8fJmqk7cTLQ9P9NgjWvva7acl+Yd6hOFzV0 +EllBl/WF1KB+YzGuHI0CQHT7sv3GW1lXeE2EqrWoSdLTOSAqm6y02DyE79d1xvG6 +XXfX5ILlAgMBAAGjggNjMIIDXzAbBgNVHREEFDASghBhYm91dC5naXRsYWIuY29t MA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIw -HQYDVR0OBBYEFJFVruwpjWeUfGJXl3m5grAjhAwPMFcGA1UdIARQME4wCAYGZ4EM +HQYDVR0OBBYEFHK7MnjGDptQWjfmJ2fr3IrjxEopMFcGA1UdIARQME4wCAYGZ4EM AQIBMEIGCisGAQQBoDIKAQMwNDAyBggrBgEFBQcCARYmaHR0cHM6Ly93d3cuZ2xv YmFsc2lnbi5jb20vcmVwb3NpdG9yeS8wDAYDVR0TAQH/BAIwADCBngYIKwYBBQUH AQEEgZEwgY4wQAYIKwYBBQUHMAGGNGh0dHA6Ly9vY3NwLmdsb2JhbHNpZ24uY29t -L2NhL2dzYXRsYXNyM2R2dGxzY2FoMjIwMjEwSgYIKwYBBQUHMAKGPmh0dHA6Ly9z -ZWN1cmUuZ2xvYmFsc2lnbi5jb20vY2FjZXJ0L2dzYXRsYXNyM2R2dGxzY2FoMjIw -MjEuY3J0MB8GA1UdIwQYMBaAFCo0uar6vzyI8Ufy0hJ4vsXlqrBpMEgGA1UdHwRB +L2NhL2dzYXRsYXNyM2R2dGxzY2EyMDIycTMwSgYIKwYBBQUHMAKGPmh0dHA6Ly9z +ZWN1cmUuZ2xvYmFsc2lnbi5jb20vY2FjZXJ0L2dzYXRsYXNyM2R2dGxzY2EyMDIy +cTMuY3J0MB8GA1UdIwQYMBaAFPqROWOa+60QJOW+tbnaq9nERmmrMEgGA1UdHwRB MD8wPaA7oDmGN2h0dHA6Ly9jcmwuZ2xvYmFsc2lnbi5jb20vY2EvZ3NhdGxhc3Iz -ZHZ0bHNjYWgyMjAyMS5jcmwwggF+BgorBgEEAdZ5AgQCBIIBbgSCAWoBaAB3AG9T -dqwx8DEZ2JkApFEV/3cVHBHZAsEAKQaNsgiaN9kTAAABfJS9R5YAAAQDAEgwRgIh -AOOZmc41vB2ICwkwEB5Bmpm/X8UHfjbxwrCXEdeRmO+qAiEAg/JugZIrG2PeV4bA -Gm6rry7HUfB954bQJ4p0PeQVmwsAdABGpVXrdfqRIDC1oolp9PN9ESxBdL79SbiF -q/L8cP5tRwAAAXyUvUeOAAAEAwBFMEMCHyRAiTz2fZ8DuQF6hrVP+IMTCPBtjB3D -m4naI8tC/foCIDXFCRIYjRb00CFI6piLYGihRy+GYF5nMQhQ9uE6hltzAHcAUaOw -9f0BeZxWbbg3eI8MpHrMGyfL956IQpoN/tSLBeUAAAF8lL1ICgAABAMASDBGAiEA -5d/bXb9TPZWhwSH8GGji/LDFL6OJnZtOV94sBaDiFgMCIQCtl00oCRMFFnqsvBo6 -SRtnDqJkEHYBS12I4LyC+D1onjANBgkqhkiG9w0BAQsFAAOCAQEAE5xcno79J+Ec -DIPJKnJCugKiM7yKjCjCp/63osCbRC+jUwRyXBIe/oTdY3geKwDOQAvyEeJPSWP1 -LbNp0l3yHbYXfsYl/NMTrJpjrJrrRO5BxG/d3IPwXIlcZrrdDSoGfGYIF9N23iqB -in15L7B+PodTl8/mSQZTjbLoecPvl+AOcLyStcWCKYQUlQb3x4UV3R4Z1ukwGbBC -cDbTR2XOSJzA9ECJcxKnWjQRQUc54pdG3pt13Wu2dVapX5sWZpV05rga3bBDjCqw -DcfKuYbOChm2i6CQ578lAntPTIS02EkGFHrmYxrIAvlhGksHpJNJtRoff1KkQKni -r8emWp7D2Q== +ZHZ0bHNjYTIwMjJxMy5jcmwwggF9BgorBgEEAdZ5AgQCBIIBbQSCAWkBZwB1AG9T +dqwx8DEZ2JkApFEV/3cVHBHZAsEAKQaNsgiaN9kTAAABgiduiHEAAAQDAEYwRAIg +SYQrru/KAKfe+hUqpJmk7Fc8drkgtY3IcAurTOwbM68CIBYO9sbDspd5p7v17RQi +QQkjdRwSjHiIgvlX0Y1JqmXjAHYArfe++nz/EMiLnT2cHj4YarRnKV3PsQwkyoWG +NOvcgooAAAGCJ26IcQAABAMARzBFAiBc5a10annqMEH69bdEFy/Vo1gb3S3GQ993 +BCRV7ZXG4gIhAMqnsoKkU6ITwRXwE9KGjHnijJ8QrBrnK0i+JFaGe1ffAHYAs3N3 +B+GEUPhjhtYFqdwRCUp5LbFnDAuH3PADDnk2pZoAAAGCJ26I6QAABAMARzBFAiAf +lW8Agd0DB68YA8XAbnlq7QNHw3uRMzNdS8gtRUe75gIhANTe+mt2p1ryW83P31OW +jH3cEGJxdUNT/oDM3Fzesx94MA0GCSqGSIb3DQEBCwUAA4IBAQAfivKEmjqqOFFh +VsX2XYkoDtreghpqMwHMCLwNk852Alr/Seyv9Ilng8cunU4NmhvEtsYVXkfE4XvB +0QIVxkg1w7A+p7ejMjh6doLJ0aWNWIVW/DwOeP0qstF9lqvLdLDABoVn0BtYCDTH +gjG80e2xpvPiKHGvBL+hlOIJwUuIAT3jN23sS1GoiYQGKsz0lovB09/6MGG0Qj8C +3i9a59T9XBpwSKdpKd4u/CB6koBXD3atbBNBACuAMcFckTEtmkCFtSpqBuocJGKf +LB4MFVaEwrd7Lc1ACC1et5FDtEI4I3/CerkRZTV+mRz5n6tB91AK3dRvjElfhiuh +XXYRULvB -----END CERTIFICATE----- diff --git a/spec/fixtures/emails/valid_reply_signed_smime.eml b/spec/fixtures/emails/valid_reply_signed_smime.eml index 0c5e2c439a..965d922c95 100644 --- a/spec/fixtures/emails/valid_reply_signed_smime.eml +++ b/spec/fixtures/emails/valid_reply_signed_smime.eml @@ -1,294 +1,294 @@ -User-Agent: Microsoft-MacOutlook/10.22.0.200209 -Date: Mon, 17 Feb 2020 22:56:47 +0100 -Subject: Re: htmltest | test issue (#1) -From: "Louzan Martinez, Diego (ext) (SI BP R&D ZG)" - -To: Administrator / htmltest - -Message-ID: <012E37D9-2A3F-4AC8-B79A-871F42914D86@siemens.com> -Thread-Topic: htmltest | test issue (#1) -References: - - -In-Reply-To: -Content-type: multipart/signed; - protocol="application/pkcs7-signature"; - micalg=sha256; - boundary="B_3664825007_1904734766" -MIME-Version: 1.0 - ---B_3664825007_1904734766 -Content-type: multipart/mixed; - boundary="B_3664825007_384940722" - - ---B_3664825007_384940722 -Content-type: multipart/alternative; - boundary="B_3664825007_1519466360" - - ---B_3664825007_1519466360 -Content-type: text/plain; - charset="UTF-8" -Content-transfer-encoding: quoted-printable - -Me too, with an attachment - -=20 - -From: Administrator -Reply to: Administrator / htmltest -Date: Monday, 17 February 2020 at 22:55 -To: "Louzan Martinez, Diego (ext) (SOP IT STG XS)" -Subject: Re: htmltest | test issue (#1) - -=20 - -Administrator commented:=20 - -I pity the foo !!! - -=E2=80=94=20 -Reply to this email directly or view it on GitLab.=20 -You're receiving this email because of your account on 169.254.169.254. If = -you'd like to receive fewer emails, you can unsubscribe from this thread or = -adjust your notification settings.=20 - - ---B_3664825007_1519466360 -Content-type: text/html; - charset="UTF-8" -Content-transfer-encoding: quoted-printable - -GitLab

Me too, with an attachment

 

From: Administrator <dlouzan.dummy@gma= -il.com>
Reply to: Administrator / htmltest <dlouzan.dummy+c0= -34670b1623e617e15a3df64223d363@gmail.com>
Date: Monday, 17 Febr= -uary 2020 at 22:55
To: "Louzan Martinez, Diego (ext) (SOP IT = -STG XS)" <diego.louzan.ext@siemens.com>
Subject: Re: ht= -mltest | test issue (#1)

= - 

Administrator commented:

<= -div>

I pity the foo !!!

=E2=80=94
Reply to this = -email directly or view it on GitLab.
You're receiving this email because of you= -r account on 169.254.169.254. If you'd like to receive fewer emails, you can= - unsubscribe from this thread or adjust your notific= -ation settings.

- ---B_3664825007_1519466360-- - - ---B_3664825007_384940722 -Content-type: image/png; name="gitlab_logo.png"; - x-mac-creator="4F50494D"; - x-mac-type="504E4766" -Content-disposition: attachment; - filename="gitlab_logo.png" -Content-transfer-encoding: base64 - - -iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAIAAABMXPacAAAABnRSTlMA/wD/AP83WBt9AAAN -1UlEQVR4AexcZXPjSBTcXxOTvMy7xxfGZWaGaJmZmZmZmZmZmdnMzB7JNwv1qs6VOJY0tuWU -p/rz5PW0q0f99JQakcxK6eItQGZlBMgIkFkZATICZFZGgIwAmZURICMAshitiybrexXblk5D -NnOk2i3G6bCvmYcJWuaMCevVohPAsWGx6h/Zd/wrd2xbWf0EcB3YqsqmfnK0LZseYZCIBEBW -E/5p4Mp+wtCvJWO3Vqufv8dtHNoZCOo6ZYd1ahEJ4LtzRZ1fC+pTF9T1P7hZnQQIvHqiKW0I -BFU5lPfiCREJYFs5C4r7Cfu6BdVJAOeutVEErfPGRRhGFAIgu1Xft0VUfYaBbRmXI1ItFuvz -Gkd0jyKo65oXNupEIYD//g11QZ2o+tRF9QJP7lUPAYJvX2haNIkmmKv0Xj0rCgHsa+dDWRgA -x+al1eT5Z9+mCglaF02KsGyKBWCcdsOA1hXWZ6A7MB5X2vtPwG8a07tCgvoehchsSLEA/sd3 -sNtUWJ+mpEHgxaN0FyD08Y2mVbMKCarzavluXkyxAI5NS3AplcG5fVXa+8+h7TEI4kSWSgEY -t9NQ3j5GfcZhXRivJ439JxgwT+gfg6C+dymymlMmQOD5Q01xgxj1acoaBV8/S2P/+fJe2+b3 -GATV+bV9d6+lTADc88FFxIZz9/r0FcB9fE+VBO2r56RGAMYL7ZFYMI3qwfp9aek/oZB5Snks -dtD4cthSIEDw1VNNaaMq69O0bBp8/yot/Uf1Wdv+zyoJqgvr+h/eSoEAzl3roIjYcB3Yko4C -eE4fxK31eAja1y9MogDQHhnZPU4BTGP74jiTZv6DwpYZw+MkaBgEja9kCRB89xLaI1VC27p5 -6NPb9BIgrP2m6/hP1eyg8fX0XlIFcO3fHE9lAPeRnWnmP+ePqbIV8RN0bF6WHAGgPdKHkwDm -iQPZUDB9XoAhy5zRnAga6Y78Gl81SLVHYkPb9o/Q149p4z96ja5LDieCmpKG0PhKuACuwzvi -rwze1LtP7EsXAbyXT6lylFw5OnesTrQA0B4ZwLU4DPPUIWw4lA4PQIx1wQQeBI3Du7JeT8IF -CH35AO0RTtC2/yus/hIR/UImva5bPg+CmrLGwTfPEi6A+/heiCfckK3wnD0sfgF818+rc2ty -ogZw7tmQWAHYMG6P0FzLAlhmjoggJG7/YW1LpvImaBrVk2vjqwb39shfvOvTdfo3rFOJ2n8s -Jn3PYn7soPGVQAE8Zw6B//BBNp5nOi5q/7l9GSbM+AFPMCZKAGiPCIF13liYZxLhsq2YJZCg -aVxfNhggLgC0R/7lXxzMMxm0IvUfu0Xfp0wAO2h8vUuIAJ4L0B7hD3UOnmc6I04BYMJMINxH -d5EVANojY/jWRH6eifyCCTPBME8aBI0vYgKEDbg9kkukPphnEtWCCTPhgMYXSQG8V05De0Qg -1Hk1YZ5JFAsmzArrCWUHja+T+4kKwLLWhRPJFAfzTCJbjo2LCRI0T8ONrzAJAaA90r2AYH36 -3iUwz5TiBRNmg9sTJKjt8HdY/ZWYAL4bvNsjMeaZropHgMDzB5ri+gQJQuOLiACsbSm0R4jB -vmqOiPxn6wriBC2zRkYQIiAAfIBHFnr4kE9kH+CRAIcP+Wpw/QCPBGCe6aYYP8AjBfiQj78A -0B75W5YIiORDPufOtQkiaJkLH/LxFYB1W22j2xjL5MaWSsIoU9iGt/LfuYQbAKnEvau2cZ0S -RNBKFzE2vTABtNfDKxqEh8jC5VLyoBWmdnVVubXUeamBKremsXXdULkiIezwoS2uy349I0gA -5uFctD0LzaFQuQSVZxEGneXoitM1vGBIAeydlYgGakQxk0Lbspg7EyIsy1eAgJ051RLtyEJb -ZWiyAg0mX6W/P6XJU6Tq9NW5Cl9fCtGkeeGDmqBAW+Tfj+5YXsRr4CkAq7+N9tT+vsvOLLRB -gcbIiWsQLpdhu1T9nRoBDKXK0GAZ+d/+KBlap8CH9v3odilY1QWeAjBPFuEtMH5psJJCw6Sk -XUji6FozVS5k61STvP8MlaLlFNopgaNj7k3lJUDQyZxp82MLgAQtpAhXTKfMhdQ5Ci95/5Gg -eRTaIf3fuZ0oivhMnAVgjffR3rq/tgBsl6EZFHEXMpSlwIX0JeT8B6x/Kr54ZdGHtlvJaq5w -FoB5tvx/u4ARbZaj8UQvZFpi71wzBf7TkZD/wOmPlaONv6w/CsyDWRwFCLmZcx2iNwIN1lJo -pIygC/n6UfiBJNn+04eo/wyXodUUnH4UmFOlEb+VgwCs6THaVz96IwC+YZZSaCixCzmUdBfS -F2P/kRM7/SEStBgu3oqwpxaru8lBAObFmkr2AkghnaWjC1k7EPQfyffMtV0a+8SYR/PjFiDs -ZS50jb3dr3Q2RfBlAC7Ul8K2kCT/yVZ4euMATMj6J/7KXLHBnG6Fg21cArCW52h/w9jbEU9n -+IFEX6pMjgC6YmVwkJxQ5pKj9XDxxsSe2qzhbnwCvNpY9XagwSoK3z9EXMjWMSku9LfM2h78 -h3Dmig3myZI4BAj7mYs9q9yLfDqjs7x9kuFC6my5pxcJ/6GjM1eVYM62iwRdVQjA2t6gA405 -CEAuneHHEhyOEu4/RRQR/4HMxQF767LGh1UJ8GY7t00hnU0QfCHTEmuiXQi/pWoH/iMsc20C -6+cA5vmqmAIgP3OlP8dNIZ0phKYzOsvTR6nmMP/La2ZNuP+MgMzFGcz5zpGQq1IBWOsrdLA5 -530hnS0TkM7AhYqVCfSfQuw/ClKZiw/2N2QN9ysVgHm5Hu2EW4UHpGiusHRGS3BEgkhM3H/M -bbH/SAVlrlmQuXiCebygcgHOdeSxI5l0Bi7UG7uQPEH+4+oJ/kMoc/HAiaJKBYh+/uF3GWwU -lM7wIwp+UEmEANoCKjBQQThz8cBuZeUCHPqdx46E0xktsbQj6kLgP214+Q9krhX8rT/qYbRy -C7oxXOjukM4W8U1ndBZ+UFFly8n7Tw++/oOJzIfMJRTMpd6VCsBanqFjuWQ0wDfVTIq/CxVS -IvKfaZC5BOPwn6z+Tswgpr+DTpaS+WNb+KYzWkrWhfBWptY18bAUn4t3HM5cckHWDzieD+8m -Y7ajXd+Ym6PQLorAZbCOYzoDF+qpxKZB0H+c3fEFwCtzraEInP4uOXOtnHV8iPuVZNiLexI8 -QhmpdBYcqNCScyFNPhUYoOCeuaRoCYmLd39j9uW6SMjNdS6IZY0PfiQDgRVI0Tzu6YyWmtsI -diHwn1ZK7v4jQbMFZS54D/P9ZSTL8B1P9xmZBzN+zcfxxjbZ997hYG4u5OpByoXkzm5KRHO0 -/kmCM9du5ffBUI9W8CdKTJD9fBQd/VdoOhvLLZ0FsAsVUAT8J4/y9+foP6MFZ67Df7Dv90aQ -n8AHGvCegLncD+2U8ddgNdd0JjW3FuxCf+PZU+w/XP7uMGGZa6eUudCNNT9NwL+rCTq+T2vt -ayAonQ2RcHCh7sJdSI5nTxGd8MwFKff79IPfkrB/WcYiVn0ZnSxJTjrDjy7afEqY/yjw7Cmi -k5K5juex/7V3Dz5yhVEUwP+cce2GjWu7cW3btm03qm27QRXVtt2ZbO8op/r2vp7qS+a+uHHP -5r7z252ze2N7UUrZZxMB0FBw6GxQUJ1JdXlEXSHcn3oB7g/MFSPN5a75fyEAQGG5QIHUWe9I -wCskBYa4Qrg/rfADSNZces1Poeb/swAoKEBnM4Lq7H372B32Ct2RAUxb3B/KXHzN/wcBcFCA -zor92sQVIic01eTzprg/pLn0mn/Hgz/mKVC4moECobMgV4gd8snnTfWM5fTL/G1ZlK75HgTA -QUGu7eJAOhNG6RMaboDXKWOuhTAXUfM9CICGAnTGD/m4AR7MNQunn6j5HgTAQgEv5CnQGTHk -IwZ4MNfE+C80iE2o+Z4GgBTSUOgFKKg6G41vl5JDPmKANyKAuVDzO6HmexAAAQVSZxjy1cMV -ogd4OP0yc1uimgs1Hx9n8zIAHgp4GSwQnUWZCQ0xwBNzzYO5yJrvfwCAwmmBQklGZ8SQDwM8 -t7mm4cVL1HzvA+ChEE5OcOoMc2JqgAdzjcU3O4ma70EAPBQup/a3cUEBOhse168QMcCDuSLB -aj7xu329CICHAnTWHzrThnz6AA//+30VcxE1388AeChAZz0jxJAPAzynuYia738AxPPqRgYK -sWJ1Fv7xCgmvlAHMtwM8mGsSzKXW/AIIQIUCdKYP+fQBnkzYVkQcNb8ian5hBQAoNMPX5nc6 -Gwyd6UM+DPB0cyk1vwACUKAAnfWJ6kO+YgZ4vcRcePHqNb9gAlCggJfBTPyaLveQzzHA6wZz -OWu+BaBAATpThnx3McBzmctR8y0ABQrQmXvIhwGe21zrSqfOjUfNtwB0KEBnUegsN+SLOQd4 -MJde8y0ARwqAQj6DudBZZsiXcA5gekSSs2EureZbAAoUquKFPDWns++HfBjgwVyo+RfmoeZb -ADQUcjobk9HZN0M+DPBgLtT8I0TNtwDcUFiW0dm3Qz7cn4E5c2Vq/gCm5lsAChSgs+wVwgAP -5krX/LV8zbcAFCisjiRnxpI9wrkhX3qAlxCsibnYD+1YAAQUJkQ/dozL8ZEBzIf28eTYaHJt -Ga7mWwAEFPalNtdNDo89bphIfwBdzLWhBlnzLQD+JwoH+7/qVvFlpwqpPT34mm8B8M/n15+P -Lf90cGHRpxf4RwvAHt8DsMcCsADssQAsAHssAAvAni8AV5380akCdgAAAABJRU5ErkJggg== ---B_3664825007_384940722-- - ---B_3664825007_1904734766 -Content-type: application/pkcs7-signature; name="smime.p7s" -Content-transfer-encoding: base64 -Content-disposition: attachment; - filename="smime.p7s" - -MIIRpwYJKoZIhvcNAQcCoIIRmDCCEZQCAQExDzANBglghkgBZQMEAgEFADALBgkqhkiG9w0B -BwGggg8VMIIHojCCBYqgAwIBAgIEZ5a6PTANBgkqhkiG9w0BAQsFADCBtjELMAkGA1UEBhMC -REUxDzANBgNVBAgMBkJheWVybjERMA8GA1UEBwwITXVlbmNoZW4xEDAOBgNVBAoMB1NpZW1l -bnMxETAPBgNVBAUTCFpaWlpaWkE2MR0wGwYDVQQLDBRTaWVtZW5zIFRydXN0IENlbnRlcjE/ -MD0GA1UEAww2U2llbWVucyBJc3N1aW5nIENBIE1lZGl1bSBTdHJlbmd0aCBBdXRoZW50aWNh -dGlvbiAyMDE2MB4XDTE5MTEyMTE0NDQ0N1oXDTIwMTEyMTE0NDQ0N1owdzERMA8GA1UEBRMI -WjAwM0gwOFQxDjAMBgNVBCoMBURpZWdvMRgwFgYDVQQEDA9Mb3V6YW4gTWFydGluZXoxGDAW -BgNVBAoMD1NpZW1lbnMtUGFydG5lcjEeMBwGA1UEAwwVTG91emFuIE1hcnRpbmV6IERpZWdv -MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuInpNaC7NRYD+0pOpHDz2pk9xmPt -JGj860SF6Nmn6Eu9EMYKEDfneC6z5QcH+mPS2d0VWgqVVGbRXSPsxJtbi9TCWjQUZdHglEZK -z9zxoFDh2dvW5/+TOT5Jf78FXyqak0YtY6+oMjQ/i9RUqPL7sIlyXLrBYrILzQ9Afo+7bXZg -v3ypp6xtqAV2ctHzQWFi0onJzxLVYguiVb7fFF9rBEMvSZonuw5tvOwJIhbe5FDFOrDcfbyU -ofZ/wikIZ+A+CE5GryXuuQmGxJaC2QqOkRAWQDzLDx9nG+rKiEs5OvlfEZC7EV1PyjZ93coM -faCVdlAgcFZ5fvd37CjyjKl+1QIDAQABo4IC9DCCAvAwggEEBggrBgEFBQcBAQSB9zCB9DAy -BggrBgEFBQcwAoYmaHR0cDovL2FoLnNpZW1lbnMuY29tL3BraT9aWlpaWlpBNi5jcnQwQQYI -KwYBBQUHMAKGNWxkYXA6Ly9hbC5zaWVtZW5zLm5ldC9DTj1aWlpaWlpBNixMPVBLST9jQUNl -cnRpZmljYXRlMEkGCCsGAQUFBzAChj1sZGFwOi8vYWwuc2llbWVucy5jb20vQ049WlpaWlpa -QTYsbz1UcnVzdGNlbnRlcj9jQUNlcnRpZmljYXRlMDAGCCsGAQUFBzABhiRodHRwOi8vb2Nz -cC5wa2ktc2VydmljZXMuc2llbWVucy5jb20wHwYDVR0jBBgwFoAU+BVdRwxsd3tyxAIXkWii -tvdqCUQwDAYDVR0TAQH/BAIwADBFBgNVHSAEPjA8MDoGDSsGAQQBoWkHAgIEAQMwKTAnBggr -BgEFBQcCARYbaHR0cDovL3d3dy5zaWVtZW5zLmNvbS9wa2kvMIHKBgNVHR8EgcIwgb8wgbyg -gbmggbaGJmh0dHA6Ly9jaC5zaWVtZW5zLmNvbS9wa2k/WlpaWlpaQTYuY3JshkFsZGFwOi8v -Y2wuc2llbWVucy5uZXQvQ049WlpaWlpaQTYsTD1QS0k/Y2VydGlmaWNhdGVSZXZvY2F0aW9u -TGlzdIZJbGRhcDovL2NsLnNpZW1lbnMuY29tL0NOPVpaWlpaWkE2LG89VHJ1c3RjZW50ZXI/ -Y2VydGlmaWNhdGVSZXZvY2F0aW9uTGlzdDAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUH -AwQwDgYDVR0PAQH/BAQDAgeAMFUGA1UdEQROMEygLAYKKwYBBAGCNxQCA6AeDBxkaWVnby5s -b3V6YW4uZXh0QHNpZW1lbnMuY29tgRxkaWVnby5sb3V6YW4uZXh0QHNpZW1lbnMuY29tMB0G -A1UdDgQWBBQj8k8aqZey68w8ALYKGJSGMt5hZDANBgkqhkiG9w0BAQsFAAOCAgEAFDHqxpb1 -R9cB4noC9vx09bkNbmXCpVfl3XCQUmAWTznC0nwEssTTjo0PWuIV4C3jnsp0MRUeHZ6lsyhZ -OzS1ETwYgvj6wzjb8RF3wgn7N/JOvFGaErMz5HZpKOfzGiNpW6/Rmd4hsRDjAwOVQOXUTqc/ -0Bj3FMoLRCSWSnTp5HdyvrY2xOKHfTrTjzmcLdFaKE2F5n7+dBkwCKVfzut8CqfVq/I7ks4m -D1IHk93/P6l9U34R2FHPt6zRTNZcWmDirRSlMH4L18CnfiNPuDN/PtRYlt3Vng5EdYN0VCg2 -NM/uees0U4ingCb0NFjg66uQ/tjfPQk55MN4Wpls4N6TkMoTCWLiqZzYTGdmVQexzroL6940 -tmMr8LoN3TpPf0OdvdKEpyH7fzsx5QlmQyywIWec6X+Fx6+l0g91VJnPEtqACpfZIBZtviHl -gfX298w+SsvBK8C48Pqs8Ijh7tLrCxx7VMLVHZqwWWPK53ga+CDWmjoSQPxi+CPZF7kao6N5 -4GrJWwSHlHh6WzTbLyLvTJZZ775Utp4W8s8xMUsQJ413iYzEaC8FcSeNjSk5UiDDiHrKmzpM -tbApD3pUXStblUMKYGTG1Mj9BcEBFkCdoGlw/ulszIrKFfOyRNDG3Ay+Dj/oMjoKsJphu3px -wyft82rTer7UW/I7o0h0DAG4lkMwggdrMIIFU6ADAgECAgR5nlqfMA0GCSqGSIb3DQEBCwUA -MIGeMQswCQYDVQQGEwJERTEPMA0GA1UECAwGQmF5ZXJuMREwDwYDVQQHDAhNdWVuY2hlbjEQ -MA4GA1UECgwHU2llbWVuczERMA8GA1UEBRMIWlpaWlpaQTMxHTAbBgNVBAsMFFNpZW1lbnMg -VHJ1c3QgQ2VudGVyMScwJQYDVQQDDB5TaWVtZW5zIElzc3VpbmcgQ0EgRUUgRW5jIDIwMTYw -HhcNMTkwOTI3MDgwMTM5WhcNMjAwOTI3MDgwMTM3WjB3MREwDwYDVQQFEwhaMDAzSDA4VDEO -MAwGA1UEKgwFRGllZ28xGDAWBgNVBAQMD0xvdXphbiBNYXJ0aW5lejEYMBYGA1UECgwPU2ll -bWVucy1QYXJ0bmVyMR4wHAYDVQQDDBVMb3V6YW4gTWFydGluZXogRGllZ28wggEiMA0GCSqG -SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCyby5qKzZIrGYWRqxnaAyMt/a/uc0uMk0F3MjwxvPM -vh5DllUpqx0l8ZDakDjPhlEXTeoL4DHNgmh+CDCs76CppM3cNG/1W1Ajo/L2iwMoXaxYuQ/F -q7ED+02KEkWX2DDVVG3fhrUGP20QAq77xPDptmVWZnUnuobZBNYkC49Xfl9HJvkJL8P0+Jqb -Eae7p4roiEr7wNkGriwrVXgA3oPNF/W+OuI76JTNTajS/6PAK/GeqIvLjfuBXpdBZTY031nE -Cztca8vI1jUjQzVhS+0dWpvpfhkVumbvOnid8DI9lapYsX8dpZFsa3ya+T3tjUdGSOOKi0kg -lWf/XYyyfhmDAgMBAAGjggLVMIIC0TAdBgNVHQ4EFgQUprhTCDwNLfPImpSfWdq+QvPTo9Mw -JwYDVR0RBCAwHoEcZGllZ28ubG91emFuLmV4dEBzaWVtZW5zLmNvbTAOBgNVHQ8BAf8EBAMC -BDAwLAYDVR0lBCUwIwYIKwYBBQUHAwQGCisGAQQBgjcKAwQGCysGAQQBgjcKAwQBMIHKBgNV -HR8EgcIwgb8wgbyggbmggbaGJmh0dHA6Ly9jaC5zaWVtZW5zLmNvbS9wa2k/WlpaWlpaQTMu -Y3JshkFsZGFwOi8vY2wuc2llbWVucy5uZXQvQ049WlpaWlpaQTMsTD1QS0k/Y2VydGlmaWNh -dGVSZXZvY2F0aW9uTGlzdIZJbGRhcDovL2NsLnNpZW1lbnMuY29tL0NOPVpaWlpaWkEzLG89 -VHJ1c3RjZW50ZXI/Y2VydGlmaWNhdGVSZXZvY2F0aW9uTGlzdDBFBgNVHSAEPjA8MDoGDSsG -AQQBoWkHAgIEAQMwKTAnBggrBgEFBQcCARYbaHR0cDovL3d3dy5zaWVtZW5zLmNvbS9wa2kv -MAwGA1UdEwEB/wQCMAAwHwYDVR0jBBgwFoAUoassbqB68NPCTeof8R4hivwMre8wggEEBggr -BgEFBQcBAQSB9zCB9DAyBggrBgEFBQcwAoYmaHR0cDovL2FoLnNpZW1lbnMuY29tL3BraT9a -WlpaWlpBMy5jcnQwQQYIKwYBBQUHMAKGNWxkYXA6Ly9hbC5zaWVtZW5zLm5ldC9DTj1aWlpa -WlpBMyxMPVBLST9jQUNlcnRpZmljYXRlMEkGCCsGAQUFBzAChj1sZGFwOi8vYWwuc2llbWVu -cy5jb20vQ049WlpaWlpaQTMsbz1UcnVzdGNlbnRlcj9jQUNlcnRpZmljYXRlMDAGCCsGAQUF -BzABhiRodHRwOi8vb2NzcC5wa2ktc2VydmljZXMuc2llbWVucy5jb20wDQYJKoZIhvcNAQEL -BQADggIBAF98ZMNg28LgkwdjOdvOGbC1QitsWjZTyotmQESF0nClDLUhb0O5675vVixntbrf -eB8xy1+KRiadk40GnAIJ0YzmNl4Tav6hPYv9VBWe5olsWG7C4qB3Q/SwhvW/e+owxv1cBra8 -R3oRudiN81eTZQHyNghRephVqQG/dpPYqydoANfIhEpHa79QlpaCAeYl4896AZOS8HYbkDFs -hLdv7sEHtl79YuSWI1wBjbJl70c0Sb4wLRgCPuHyQj2Uw/vQ5xJlEvBDZAIXXe1TP/nqiuY6 -7nweJbbeqfFE6ZP3kCe+mEIWGSaO0iThZyLGer8fHs1XiEmhhPgvC7P7KodzpXU6+hX+ZzbD -DxEjFfetV5sh0aNSXG9xx4hZmS9bpImBGR8MvZ7cgxqItvLtY2xvfUbYW244d4RcWesaCDq3 -ZEIo6uCIzOzJAwjUdLIac+lLV0rxiHmb7O3cQ19kjpWDB31hmfrus/TKJ55pBKVWBX5m/mFv -K8Ep5USpGrNS0EzOP7I1kQZv2VsvAhSxk/m5FMLpDy8T0O8YgbLypTXoeJFWCF6RduSjVsaZ -lkAtTQYud683pjyOMxJXaQUYGU1PmEYSOonMkVsT9aBcxYkXLp+Ln/+8G0OCYu7dRdwnj+Ut -7yR/ltxtgDcaFApCb0qBTKbgbqZk1fASmkOp+kbdYmoUMYICVjCCAlICAQEwgb8wgbYxCzAJ -BgNVBAYTAkRFMQ8wDQYDVQQIDAZCYXllcm4xETAPBgNVBAcMCE11ZW5jaGVuMRAwDgYDVQQK -DAdTaWVtZW5zMREwDwYDVQQFEwhaWlpaWlpBNjEdMBsGA1UECwwUU2llbWVucyBUcnVzdCBD -ZW50ZXIxPzA9BgNVBAMMNlNpZW1lbnMgSXNzdWluZyBDQSBNZWRpdW0gU3RyZW5ndGggQXV0 -aGVudGljYXRpb24gMjAxNgIEZ5a6PTANBglghkgBZQMEAgEFAKBpMC8GCSqGSIb3DQEJBDEi -BCAOR58AbNfSrI+vtMs+dgAQtn3IVZ3RjYC5hz3j9k+6TTAYBgkqhkiG9w0BCQMxCwYJKoZI -hvcNAQcBMBwGCSqGSIb3DQEJBTEPFw0yMDAyMTcyMTU2NDdaMA0GCSqGSIb3DQEBAQUABIIB -AHLSBcFHhNHPevbwqvA2ecuVb/aKnj45CFF6l8esP1H5DRm1ee5qMKuIS84NFuFC9RUENNhW -DBzsB+BVGz64o1f8QgIklYVrIJ4JZ0q1abNG7NbkVKWIpS3CQo//YWShUTYg+JpKx4YbahGR -sP5zbufbU4eagrrqBChjPTLy+njdjwCNu0XPykBTKOOf6BMjnS33AYjHJyh83JOY7rw3IDLx -8POQH4g5EMRpl9354s0rEkIezMt7pfUAsqY3QnQ8hvlE4KTikPQ+tvLMK1l/ffcLAP8BdBNI -YA3ikb3qCoGNSLKieYzNnBPhNOIJELUtEEaljAFZYMQzMKCbI4JdiDs= - ---B_3664825007_1904734766-- +User-Agent: Microsoft-MacOutlook/10.22.0.200209 +Date: Mon, 17 Feb 2020 22:56:47 +0100 +Subject: Re: htmltest | test issue (#1) +From: "Louzan Martinez, Diego (ext) (SI BP R&D ZG)" + +To: Administrator / htmltest + +Message-ID: <012E37D9-2A3F-4AC8-B79A-871F42914D86@siemens.com> +Thread-Topic: htmltest | test issue (#1) +References: + + +In-Reply-To: +Content-type: multipart/signed; + protocol="application/pkcs7-signature"; + micalg=sha256; + boundary="B_3664825007_1904734766" +MIME-Version: 1.0 + +--B_3664825007_1904734766 +Content-type: multipart/mixed; + boundary="B_3664825007_384940722" + + +--B_3664825007_384940722 +Content-type: multipart/alternative; + boundary="B_3664825007_1519466360" + + +--B_3664825007_1519466360 +Content-type: text/plain; + charset="UTF-8" +Content-transfer-encoding: quoted-printable + +Me too, with an attachment + +=20 + +From: Administrator +Reply to: Administrator / htmltest +Date: Monday, 17 February 2020 at 22:55 +To: "Louzan Martinez, Diego (ext) (SOP IT STG XS)" +Subject: Re: htmltest | test issue (#1) + +=20 + +Administrator commented:=20 + +I pity the foo !!! + +=E2=80=94=20 +Reply to this email directly or view it on GitLab.=20 +You're receiving this email because of your account on 169.254.169.254. If = +you'd like to receive fewer emails, you can unsubscribe from this thread or = +adjust your notification settings.=20 + + +--B_3664825007_1519466360 +Content-type: text/html; + charset="UTF-8" +Content-transfer-encoding: quoted-printable + +GitLab

Me too, with an attachment

 

From: Administrator <dlouzan.dummy@gma= +il.com>
Reply to: Administrator / htmltest <dlouzan.dummy+c0= +34670b1623e617e15a3df64223d363@gmail.com>
Date: Monday, 17 Febr= +uary 2020 at 22:55
To: "Louzan Martinez, Diego (ext) (SOP IT = +STG XS)" <diego.louzan.ext@siemens.com>
Subject: Re: ht= +mltest | test issue (#1)

= + 

Administrator commented:

<= +div>

I pity the foo !!!

=E2=80=94
Reply to this = +email directly or view it on GitLab.
You're receiving this email because of you= +r account on 169.254.169.254. If you'd like to receive fewer emails, you can= + unsubscribe from this thread or adjust your notific= +ation settings.

+ +--B_3664825007_1519466360-- + + +--B_3664825007_384940722 +Content-type: image/png; name="gitlab_logo.png"; + x-mac-creator="4F50494D"; + x-mac-type="504E4766" +Content-disposition: attachment; + filename="gitlab_logo.png" +Content-transfer-encoding: base64 + + +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 +--B_3664825007_384940722-- + +--B_3664825007_1904734766 +Content-type: application/pkcs7-signature; name="smime.p7s" +Content-transfer-encoding: base64 +Content-disposition: attachment; + filename="smime.p7s" + +MIIRpwYJKoZIhvcNAQcCoIIRmDCCEZQCAQExDzANBglghkgBZQMEAgEFADALBgkqhkiG9w0B +BwGggg8VMIIHojCCBYqgAwIBAgIEZ5a6PTANBgkqhkiG9w0BAQsFADCBtjELMAkGA1UEBhMC +REUxDzANBgNVBAgMBkJheWVybjERMA8GA1UEBwwITXVlbmNoZW4xEDAOBgNVBAoMB1NpZW1l +bnMxETAPBgNVBAUTCFpaWlpaWkE2MR0wGwYDVQQLDBRTaWVtZW5zIFRydXN0IENlbnRlcjE/ +MD0GA1UEAww2U2llbWVucyBJc3N1aW5nIENBIE1lZGl1bSBTdHJlbmd0aCBBdXRoZW50aWNh +dGlvbiAyMDE2MB4XDTE5MTEyMTE0NDQ0N1oXDTIwMTEyMTE0NDQ0N1owdzERMA8GA1UEBRMI +WjAwM0gwOFQxDjAMBgNVBCoMBURpZWdvMRgwFgYDVQQEDA9Mb3V6YW4gTWFydGluZXoxGDAW +BgNVBAoMD1NpZW1lbnMtUGFydG5lcjEeMBwGA1UEAwwVTG91emFuIE1hcnRpbmV6IERpZWdv +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuInpNaC7NRYD+0pOpHDz2pk9xmPt +JGj860SF6Nmn6Eu9EMYKEDfneC6z5QcH+mPS2d0VWgqVVGbRXSPsxJtbi9TCWjQUZdHglEZK +z9zxoFDh2dvW5/+TOT5Jf78FXyqak0YtY6+oMjQ/i9RUqPL7sIlyXLrBYrILzQ9Afo+7bXZg +v3ypp6xtqAV2ctHzQWFi0onJzxLVYguiVb7fFF9rBEMvSZonuw5tvOwJIhbe5FDFOrDcfbyU +ofZ/wikIZ+A+CE5GryXuuQmGxJaC2QqOkRAWQDzLDx9nG+rKiEs5OvlfEZC7EV1PyjZ93coM +faCVdlAgcFZ5fvd37CjyjKl+1QIDAQABo4IC9DCCAvAwggEEBggrBgEFBQcBAQSB9zCB9DAy +BggrBgEFBQcwAoYmaHR0cDovL2FoLnNpZW1lbnMuY29tL3BraT9aWlpaWlpBNi5jcnQwQQYI +KwYBBQUHMAKGNWxkYXA6Ly9hbC5zaWVtZW5zLm5ldC9DTj1aWlpaWlpBNixMPVBLST9jQUNl +cnRpZmljYXRlMEkGCCsGAQUFBzAChj1sZGFwOi8vYWwuc2llbWVucy5jb20vQ049WlpaWlpa +QTYsbz1UcnVzdGNlbnRlcj9jQUNlcnRpZmljYXRlMDAGCCsGAQUFBzABhiRodHRwOi8vb2Nz +cC5wa2ktc2VydmljZXMuc2llbWVucy5jb20wHwYDVR0jBBgwFoAU+BVdRwxsd3tyxAIXkWii +tvdqCUQwDAYDVR0TAQH/BAIwADBFBgNVHSAEPjA8MDoGDSsGAQQBoWkHAgIEAQMwKTAnBggr +BgEFBQcCARYbaHR0cDovL3d3dy5zaWVtZW5zLmNvbS9wa2kvMIHKBgNVHR8EgcIwgb8wgbyg +gbmggbaGJmh0dHA6Ly9jaC5zaWVtZW5zLmNvbS9wa2k/WlpaWlpaQTYuY3JshkFsZGFwOi8v +Y2wuc2llbWVucy5uZXQvQ049WlpaWlpaQTYsTD1QS0k/Y2VydGlmaWNhdGVSZXZvY2F0aW9u +TGlzdIZJbGRhcDovL2NsLnNpZW1lbnMuY29tL0NOPVpaWlpaWkE2LG89VHJ1c3RjZW50ZXI/ +Y2VydGlmaWNhdGVSZXZvY2F0aW9uTGlzdDAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUH +AwQwDgYDVR0PAQH/BAQDAgeAMFUGA1UdEQROMEygLAYKKwYBBAGCNxQCA6AeDBxkaWVnby5s +b3V6YW4uZXh0QHNpZW1lbnMuY29tgRxkaWVnby5sb3V6YW4uZXh0QHNpZW1lbnMuY29tMB0G +A1UdDgQWBBQj8k8aqZey68w8ALYKGJSGMt5hZDANBgkqhkiG9w0BAQsFAAOCAgEAFDHqxpb1 +R9cB4noC9vx09bkNbmXCpVfl3XCQUmAWTznC0nwEssTTjo0PWuIV4C3jnsp0MRUeHZ6lsyhZ +OzS1ETwYgvj6wzjb8RF3wgn7N/JOvFGaErMz5HZpKOfzGiNpW6/Rmd4hsRDjAwOVQOXUTqc/ +0Bj3FMoLRCSWSnTp5HdyvrY2xOKHfTrTjzmcLdFaKE2F5n7+dBkwCKVfzut8CqfVq/I7ks4m +D1IHk93/P6l9U34R2FHPt6zRTNZcWmDirRSlMH4L18CnfiNPuDN/PtRYlt3Vng5EdYN0VCg2 +NM/uees0U4ingCb0NFjg66uQ/tjfPQk55MN4Wpls4N6TkMoTCWLiqZzYTGdmVQexzroL6940 +tmMr8LoN3TpPf0OdvdKEpyH7fzsx5QlmQyywIWec6X+Fx6+l0g91VJnPEtqACpfZIBZtviHl +gfX298w+SsvBK8C48Pqs8Ijh7tLrCxx7VMLVHZqwWWPK53ga+CDWmjoSQPxi+CPZF7kao6N5 +4GrJWwSHlHh6WzTbLyLvTJZZ775Utp4W8s8xMUsQJ413iYzEaC8FcSeNjSk5UiDDiHrKmzpM +tbApD3pUXStblUMKYGTG1Mj9BcEBFkCdoGlw/ulszIrKFfOyRNDG3Ay+Dj/oMjoKsJphu3px +wyft82rTer7UW/I7o0h0DAG4lkMwggdrMIIFU6ADAgECAgR5nlqfMA0GCSqGSIb3DQEBCwUA +MIGeMQswCQYDVQQGEwJERTEPMA0GA1UECAwGQmF5ZXJuMREwDwYDVQQHDAhNdWVuY2hlbjEQ +MA4GA1UECgwHU2llbWVuczERMA8GA1UEBRMIWlpaWlpaQTMxHTAbBgNVBAsMFFNpZW1lbnMg +VHJ1c3QgQ2VudGVyMScwJQYDVQQDDB5TaWVtZW5zIElzc3VpbmcgQ0EgRUUgRW5jIDIwMTYw +HhcNMTkwOTI3MDgwMTM5WhcNMjAwOTI3MDgwMTM3WjB3MREwDwYDVQQFEwhaMDAzSDA4VDEO +MAwGA1UEKgwFRGllZ28xGDAWBgNVBAQMD0xvdXphbiBNYXJ0aW5lejEYMBYGA1UECgwPU2ll +bWVucy1QYXJ0bmVyMR4wHAYDVQQDDBVMb3V6YW4gTWFydGluZXogRGllZ28wggEiMA0GCSqG +SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCyby5qKzZIrGYWRqxnaAyMt/a/uc0uMk0F3MjwxvPM +vh5DllUpqx0l8ZDakDjPhlEXTeoL4DHNgmh+CDCs76CppM3cNG/1W1Ajo/L2iwMoXaxYuQ/F +q7ED+02KEkWX2DDVVG3fhrUGP20QAq77xPDptmVWZnUnuobZBNYkC49Xfl9HJvkJL8P0+Jqb +Eae7p4roiEr7wNkGriwrVXgA3oPNF/W+OuI76JTNTajS/6PAK/GeqIvLjfuBXpdBZTY031nE +Cztca8vI1jUjQzVhS+0dWpvpfhkVumbvOnid8DI9lapYsX8dpZFsa3ya+T3tjUdGSOOKi0kg +lWf/XYyyfhmDAgMBAAGjggLVMIIC0TAdBgNVHQ4EFgQUprhTCDwNLfPImpSfWdq+QvPTo9Mw +JwYDVR0RBCAwHoEcZGllZ28ubG91emFuLmV4dEBzaWVtZW5zLmNvbTAOBgNVHQ8BAf8EBAMC +BDAwLAYDVR0lBCUwIwYIKwYBBQUHAwQGCisGAQQBgjcKAwQGCysGAQQBgjcKAwQBMIHKBgNV +HR8EgcIwgb8wgbyggbmggbaGJmh0dHA6Ly9jaC5zaWVtZW5zLmNvbS9wa2k/WlpaWlpaQTMu +Y3JshkFsZGFwOi8vY2wuc2llbWVucy5uZXQvQ049WlpaWlpaQTMsTD1QS0k/Y2VydGlmaWNh +dGVSZXZvY2F0aW9uTGlzdIZJbGRhcDovL2NsLnNpZW1lbnMuY29tL0NOPVpaWlpaWkEzLG89 +VHJ1c3RjZW50ZXI/Y2VydGlmaWNhdGVSZXZvY2F0aW9uTGlzdDBFBgNVHSAEPjA8MDoGDSsG +AQQBoWkHAgIEAQMwKTAnBggrBgEFBQcCARYbaHR0cDovL3d3dy5zaWVtZW5zLmNvbS9wa2kv +MAwGA1UdEwEB/wQCMAAwHwYDVR0jBBgwFoAUoassbqB68NPCTeof8R4hivwMre8wggEEBggr +BgEFBQcBAQSB9zCB9DAyBggrBgEFBQcwAoYmaHR0cDovL2FoLnNpZW1lbnMuY29tL3BraT9a +WlpaWlpBMy5jcnQwQQYIKwYBBQUHMAKGNWxkYXA6Ly9hbC5zaWVtZW5zLm5ldC9DTj1aWlpa +WlpBMyxMPVBLST9jQUNlcnRpZmljYXRlMEkGCCsGAQUFBzAChj1sZGFwOi8vYWwuc2llbWVu +cy5jb20vQ049WlpaWlpaQTMsbz1UcnVzdGNlbnRlcj9jQUNlcnRpZmljYXRlMDAGCCsGAQUF +BzABhiRodHRwOi8vb2NzcC5wa2ktc2VydmljZXMuc2llbWVucy5jb20wDQYJKoZIhvcNAQEL +BQADggIBAF98ZMNg28LgkwdjOdvOGbC1QitsWjZTyotmQESF0nClDLUhb0O5675vVixntbrf +eB8xy1+KRiadk40GnAIJ0YzmNl4Tav6hPYv9VBWe5olsWG7C4qB3Q/SwhvW/e+owxv1cBra8 +R3oRudiN81eTZQHyNghRephVqQG/dpPYqydoANfIhEpHa79QlpaCAeYl4896AZOS8HYbkDFs +hLdv7sEHtl79YuSWI1wBjbJl70c0Sb4wLRgCPuHyQj2Uw/vQ5xJlEvBDZAIXXe1TP/nqiuY6 +7nweJbbeqfFE6ZP3kCe+mEIWGSaO0iThZyLGer8fHs1XiEmhhPgvC7P7KodzpXU6+hX+ZzbD +DxEjFfetV5sh0aNSXG9xx4hZmS9bpImBGR8MvZ7cgxqItvLtY2xvfUbYW244d4RcWesaCDq3 +ZEIo6uCIzOzJAwjUdLIac+lLV0rxiHmb7O3cQ19kjpWDB31hmfrus/TKJ55pBKVWBX5m/mFv +K8Ep5USpGrNS0EzOP7I1kQZv2VsvAhSxk/m5FMLpDy8T0O8YgbLypTXoeJFWCF6RduSjVsaZ +lkAtTQYud683pjyOMxJXaQUYGU1PmEYSOonMkVsT9aBcxYkXLp+Ln/+8G0OCYu7dRdwnj+Ut +7yR/ltxtgDcaFApCb0qBTKbgbqZk1fASmkOp+kbdYmoUMYICVjCCAlICAQEwgb8wgbYxCzAJ +BgNVBAYTAkRFMQ8wDQYDVQQIDAZCYXllcm4xETAPBgNVBAcMCE11ZW5jaGVuMRAwDgYDVQQK +DAdTaWVtZW5zMREwDwYDVQQFEwhaWlpaWlpBNjEdMBsGA1UECwwUU2llbWVucyBUcnVzdCBD +ZW50ZXIxPzA9BgNVBAMMNlNpZW1lbnMgSXNzdWluZyBDQSBNZWRpdW0gU3RyZW5ndGggQXV0 +aGVudGljYXRpb24gMjAxNgIEZ5a6PTANBglghkgBZQMEAgEFAKBpMC8GCSqGSIb3DQEJBDEi +BCAOR58AbNfSrI+vtMs+dgAQtn3IVZ3RjYC5hz3j9k+6TTAYBgkqhkiG9w0BCQMxCwYJKoZI +hvcNAQcBMBwGCSqGSIb3DQEJBTEPFw0yMDAyMTcyMTU2NDdaMA0GCSqGSIb3DQEBAQUABIIB +AHLSBcFHhNHPevbwqvA2ecuVb/aKnj45CFF6l8esP1H5DRm1ee5qMKuIS84NFuFC9RUENNhW +DBzsB+BVGz64o1f8QgIklYVrIJ4JZ0q1abNG7NbkVKWIpS3CQo//YWShUTYg+JpKx4YbahGR +sP5zbufbU4eagrrqBChjPTLy+njdjwCNu0XPykBTKOOf6BMjnS33AYjHJyh83JOY7rw3IDLx +8POQH4g5EMRpl9354s0rEkIezMt7pfUAsqY3QnQ8hvlE4KTikPQ+tvLMK1l/ffcLAP8BdBNI +YA3ikb3qCoGNSLKieYzNnBPhNOIJELUtEEaljAFZYMQzMKCbI4JdiDs= + +--B_3664825007_1904734766-- diff --git a/spec/fixtures/packages/nuget/corrupted_package.nupkg b/spec/fixtures/packages/nuget/corrupted_package.nupkg new file mode 100644 index 0000000000000000000000000000000000000000..54f05c62aeaaf98c858174969b4c80b63077567d GIT binary patch literal 3513 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 literal 0 HcmV?d00001 diff --git a/spec/helpers/diff_helper_spec.rb b/spec/helpers/diff_helper_spec.rb index 93efce6b58..6ac4bdf67c 100644 --- a/spec/helpers/diff_helper_spec.rb +++ b/spec/helpers/diff_helper_spec.rb @@ -445,6 +445,19 @@ RSpec.describe DiffHelper do end end + describe '#params_with_whitespace' do + before do + controller.params[:protocol] = 'HACKED!' + controller.params[:host] = 'HACKED!' + end + + subject { helper.params_with_whitespace } + + it "filters with safe_params" do + expect(subject).to eq({ 'w' => 1 }) + end + end + describe "#render_fork_suggestion" do subject { helper.render_fork_suggestion } diff --git a/spec/helpers/submodule_helper_spec.rb b/spec/helpers/submodule_helper_spec.rb index a419b6b9c8..2e8304e8b4 100644 --- a/spec/helpers/submodule_helper_spec.rb +++ b/spec/helpers/submodule_helper_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe SubmoduleHelper do +RSpec.describe SubmoduleHelper, feature_category: :source_code_management do include RepoHelpers let(:submodule_item) { double(id: 'hash', path: 'rack') } diff --git a/spec/lib/gitlab/api_authentication/token_resolver_spec.rb b/spec/lib/gitlab/api_authentication/token_resolver_spec.rb index bbc6bf0d48..9f86b95651 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 e2226952d1..b0193f6353 100644 --- a/spec/lib/gitlab/auth/auth_finders_spec.rb +++ b/spec/lib/gitlab/auth/auth_finders_spec.rb @@ -188,7 +188,7 @@ RSpec.describe Gitlab::Auth::AuthFinders do end it 'returns nil if valid feed_token and disabled' do - allow(Gitlab::CurrentSettings).to receive_messages(disable_feed_token: true) + stub_application_setting(disable_feed_token: true) set_param(:feed_token, user.feed_token) expect(find_user_from_feed_token(:rss)).to be_nil @@ -388,6 +388,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/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb index f3d3fd2034..e9dcfb4376 100644 --- a/spec/lib/gitlab/git/repository_spec.rb +++ b/spec/lib/gitlab/git/repository_spec.rb @@ -528,12 +528,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 @@ -2454,7 +2455,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 7e3a1bf61b..10a099af4f 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 58ace05b0d..5aef250afa 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 729712510e..f80faac563 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 76446adf7b..ea71c5442f 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/lib/gitlab/safe_device_detector_spec.rb b/spec/lib/gitlab/safe_device_detector_spec.rb new file mode 100644 index 0000000000..c37dc1e1c7 --- /dev/null +++ b/spec/lib/gitlab/safe_device_detector_spec.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +require 'fast_spec_helper' +require 'device_detector' +require_relative '../../../lib/gitlab/safe_device_detector' + +RSpec.describe Gitlab::SafeDeviceDetector, feature_category: :authentication_and_authorization do + it 'retains the behavior for normal user agents' do + chrome_user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 \ + (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36" + + expect(described_class.new(chrome_user_agent).user_agent).to be_eql(chrome_user_agent) + expect(described_class.new(chrome_user_agent).name).to be_eql('Chrome') + end + + it 'truncates big user agents' do + big_user_agent = "chrome #{'abc' * 1024}" + expect(described_class.new(big_user_agent).user_agent).not_to be_eql(big_user_agent) + end +end diff --git a/spec/lib/gitlab/usage_data_counters/hll_redis_counter_spec.rb b/spec/lib/gitlab/usage_data_counters/hll_redis_counter_spec.rb index d0b935d59d..9acfa3401b 100644 --- a/spec/lib/gitlab/usage_data_counters/hll_redis_counter_spec.rb +++ b/spec/lib/gitlab/usage_data_counters/hll_redis_counter_spec.rb @@ -89,31 +89,32 @@ RSpec.describe Gitlab::UsageDataCounters::HLLRedisCounter, :clean_gitlab_redis_s describe '.categories' do it 'gets CE unique category names' do expect(described_class.categories).to include( - 'deploy_token_packages', - 'user_packages', - 'ecosystem', 'analytics', - 'ide_edit', - 'search', - 'source_code', - 'incident_management', - 'incident_management_alerts', - 'testing', - 'issues_edit', - 'snippets', - 'code_review', - 'terraform', 'ci_templates', - 'quickactions', - 'pipeline_authoring', - 'secure', - 'importer', - 'geo', - 'work_items', 'ci_users', + 'code_review', + 'deploy_token_packages', + 'ecosystem', + 'environments', 'error_tracking', + 'geo', + 'ide_edit', + 'importer', + 'incident_management_alerts', + 'incident_management', + 'issues_edit', + 'kubernetes_agent', 'manage', - 'kubernetes_agent' + 'pipeline_authoring', + 'quickactions', + 'search', + 'secure', + 'snippets', + 'source_code', + 'terraform', + 'testing', + 'user_packages', + 'work_items' ) end end diff --git a/spec/mailers/devise_mailer_spec.rb b/spec/mailers/devise_mailer_spec.rb index 360eb82792..b8e768b7a5 100644 --- a/spec/mailers/devise_mailer_spec.rb +++ b/spec/mailers/devise_mailer_spec.rb @@ -11,7 +11,7 @@ RSpec.describe DeviseMailer do subject { described_class.confirmation_instructions(user, 'faketoken', {}) } context "when confirming a new account" do - let(:user) { build(:user, created_at: 1.minute.ago, unconfirmed_email: nil) } + let(:user) { create(:user, created_at: 1.minute.ago) } it "shows the expected text" do expect(subject.body.encoded).to have_text "Welcome" @@ -20,7 +20,13 @@ RSpec.describe DeviseMailer do end context "when confirming the unconfirmed_email" do - let(:user) { build(:user, unconfirmed_email: 'jdoe@example.com') } + subject { described_class.confirmation_instructions(user, user.confirmation_token, { to: user.unconfirmed_email }) } + + let(:user) { create(:user) } + + before do + user.update!(email: 'unconfirmed-email@example.com') + end it "shows the expected text" do expect(subject.body.encoded).not_to have_text "Welcome" @@ -30,7 +36,7 @@ RSpec.describe DeviseMailer do end context "when re-confirming the primary email after a security issue" do - let(:user) { build(:user, created_at: 10.days.ago, unconfirmed_email: nil) } + let(:user) { create(:user, created_at: Devise.confirm_within.ago) } it "shows the expected text" do expect(subject.body.encoded).not_to have_text "Welcome" diff --git a/spec/models/application_setting_spec.rb b/spec/models/application_setting_spec.rb index 77bb6b502b..b38555dbac 100644 --- a/spec/models/application_setting_spec.rb +++ b/spec/models/application_setting_spec.rb @@ -1466,10 +1466,4 @@ RSpec.describe ApplicationSetting do expect(setting.personal_access_token_prefix).to eql('glpat-') end end - - describe '.personal_access_tokens_disabled?' do - it 'is false' do - expect(setting.personal_access_tokens_disabled?).to eq(false) - end - end end diff --git a/spec/models/ci/build_runner_session_spec.rb b/spec/models/ci/build_runner_session_spec.rb index ed5ed456d7..8dfe854511 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 = '' @@ -72,7 +117,7 @@ RSpec.describe Ci::BuildRunnerSession, model: true do let(:specification) { subject.service_specification(service: service, port: port, path: path, subprotocols: subprotocols) } it 'returns service proxy url' do - expect(specification[:url]).to eq "https://localhost/proxy/#{service}/#{port}/#{path}" + expect(specification[:url]).to eq "https://gitlab.example.com/proxy/#{service}/#{port}/#{path}" end it 'returns default service proxy websocket subprotocol' do @@ -85,11 +130,17 @@ 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 } it 'uses the default port name' do - expect(specification[:url]).to eq "https://localhost/proxy/#{service}/default_port/#{path}" + expect(specification[:url]).to eq "https://gitlab.example.com/proxy/#{service}/default_port/#{path}" end end @@ -97,7 +148,7 @@ RSpec.describe Ci::BuildRunnerSession, model: true do let(:service) { '' } it 'uses the service name "build" as default' do - expect(specification[:url]).to eq "https://localhost/proxy/build/#{port}/#{path}" + expect(specification[:url]).to eq "https://gitlab.example.com/proxy/build/#{port}/#{path}" end end diff --git a/spec/models/hooks/web_hook_log_spec.rb b/spec/models/hooks/web_hook_log_spec.rb index 3441dfda7d..e65330d253 100644 --- a/spec/models/hooks/web_hook_log_spec.rb +++ b/spec/models/hooks/web_hook_log_spec.rb @@ -185,4 +185,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 da8c10b67a..2e8e63e20e 100644 --- a/spec/models/hooks/web_hook_spec.rb +++ b/spec/models/hooks/web_hook_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe WebHook do +RSpec.describe WebHook, feature_category: :integrations do include AfterNextHelpers let_it_be(:project) { create(:project) } @@ -131,6 +131,62 @@ RSpec.describe WebHook do expect(hook.push_events_branch_filter).to eq('') 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 + + describe 'before_validation :reset_url_variables' do + subject(:hook) { build_stubbed(:project_hook, :url_variables, project: project, url: 'http://example.com/{abc}') } + + it 'resets url variables if url changed' do + hook.url = 'http://example.com/new-hook' + + expect(hook).to be_valid + expect(hook.url_variables).to eq({}) + end + + it 'resets url variables if url is changed but url variables stayed the same' do + hook.url = 'http://test.example.com/{abc}' + + expect(hook).not_to be_valid + expect(hook.url_variables).to eq({}) + end + + it 'does not reset url variables if both url and url variables are changed' do + hook.url = 'http://example.com/{one}/{two}' + hook.url_variables = { 'one' => 'foo', 'two' => 'bar' } + + expect(hook).to be_valid + expect(hook.url_variables).to eq({ 'one' => 'foo', 'two' => 'bar' }) + end + end end describe 'encrypted attributes' do @@ -242,7 +298,7 @@ RSpec.describe WebHook do end describe '#executable?' do - let(:web_hook) { create(:project_hook, project: project) } + let_it_be(:web_hook) { create(:project_hook, project: project) } where(:recent_failures, :not_until, :executable) do [ diff --git a/spec/models/integrations/jira_spec.rb b/spec/models/integrations/jira_spec.rb index 9f928442b2..a1c8767b82 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 db79185d75..ba1a29a8b2 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 75887e49dc..fd0da8583b 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -5521,8 +5521,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 6fbf69ec23..b38ff76c6c 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -1224,11 +1224,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/models/user_spec.rb b/spec/models/user_spec.rb index 8ebf3d7016..a180e108b5 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -292,6 +292,34 @@ RSpec.describe User do end end end + + describe 'confirmation instructions for unconfirmed email' do + let(:unconfirmed_email) { 'first-unconfirmed-email@example.com' } + let(:another_unconfirmed_email) { 'another-unconfirmed-email@example.com' } + + context 'when email is changed to another before performing the job that sends confirmation instructions for previous email change request' do + it "mentions the recipient's email in the message body", :aggregate_failures do + same_user = User.find(user.id) + same_user.update!(email: unconfirmed_email) + + user.update!(email: another_unconfirmed_email) + + perform_enqueued_jobs + + confirmation_instructions_for_unconfirmed_email = ActionMailer::Base.deliveries.find do |message| + message.subject == 'Confirmation instructions' && message.to.include?(unconfirmed_email) + end + expect(confirmation_instructions_for_unconfirmed_email.html_part.body.encoded).to match same_user.unconfirmed_email + expect(confirmation_instructions_for_unconfirmed_email.text_part.body.encoded).to match same_user.unconfirmed_email + + confirmation_instructions_for_another_unconfirmed_email = ActionMailer::Base.deliveries.find do |message| + message.subject == 'Confirmation instructions' && message.to.include?(another_unconfirmed_email) + end + expect(confirmation_instructions_for_another_unconfirmed_email.html_part.body.encoded).to match user.unconfirmed_email + expect(confirmation_instructions_for_another_unconfirmed_email.text_part.body.encoded).to match user.unconfirmed_email + end + end + end end describe 'validations' do diff --git a/spec/policies/project_policy_spec.rb b/spec/policies/project_policy_spec.rb index 40ee2e662b..800b036748 100644 --- a/spec/policies/project_policy_spec.rb +++ b/spec/policies/project_policy_spec.rb @@ -643,6 +643,35 @@ RSpec.describe ProjectPolicy do end end + describe 'read_grafana', feature_category: :metrics do + using RSpec::Parameterized::TableSyntax + + let(:policy) { :read_grafana } + + where(:project_visibility, :role, :allowed) do + :public | :anonymous | false + :public | :guest | false + :public | :reporter | true + :internal | :anonymous | false + :internal | :guest | true + :internal | :reporter | true + :private | :anonymous | false + :private | :guest | true + :private | :reporter | true + end + + with_them do + let(:current_user) { public_send(role) } + let(:project) { public_send("#{project_visibility}_project") } + + if params[:allowed] + it { is_expected.to be_allowed(policy) } + else + it { is_expected.not_to be_allowed(policy) } + end + end + end + describe 'update_max_artifacts_size' do context 'when no user' do let(:current_user) { anonymous } diff --git a/spec/requests/admin/impersonation_tokens_controller_spec.rb b/spec/requests/admin/impersonation_tokens_controller_spec.rb index ee0e12ad0c..2017a512bc 100644 --- a/spec/requests/admin/impersonation_tokens_controller_spec.rb +++ b/spec/requests/admin/impersonation_tokens_controller_spec.rb @@ -10,18 +10,6 @@ RSpec.describe Admin::ImpersonationTokensController, :enable_admin_mode do sign_in(admin) end - context 'when impersonation is enabled' do - before do - stub_config_setting(impersonation_enabled: true) - end - - it 'responds ok' do - get admin_user_impersonation_tokens_path(user_id: user.username) - - expect(response).to have_gitlab_http_status(:ok) - end - end - context "when impersonation is disabled" do before do stub_config_setting(impersonation_enabled: false) 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 d4f734e7bd..baa16b052a 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 c648d28c1b..6e927aaba9 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/error_tracking/list_projects_service_spec.rb b/spec/services/error_tracking/list_projects_service_spec.rb index ce391bd1ca..8408adcc21 100644 --- a/spec/services/error_tracking/list_projects_service_spec.rb +++ b/spec/services/error_tracking/list_projects_service_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe ErrorTracking::ListProjectsService do +RSpec.describe ErrorTracking::ListProjectsService, feature_category: :integrations do let_it_be(:user) { create(:user) } let_it_be(:project, reload: true) { create(:project) } @@ -51,15 +51,33 @@ RSpec.describe ErrorTracking::ListProjectsService do end context 'masked param token' do - let(:params) { ActionController::Parameters.new(token: "*********", api_host: new_api_host) } + let(:params) { ActionController::Parameters.new(token: "*********", api_host: api_host) } - before do - expect(error_tracking_setting).to receive(:list_sentry_projects) + context 'with the current api host' do + let(:api_host) { 'https://sentrytest.gitlab.com' } + + before do + expect(error_tracking_setting).to receive(:list_sentry_projects) .and_return({ projects: [] }) + end + + it 'uses database token' do + expect { subject.execute }.not_to change { error_tracking_setting.token } + end end - it 'uses database token' do - expect { subject.execute }.not_to change { error_tracking_setting.token } + context 'with a new api host' do + let(:api_host) { new_api_host } + + 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 end diff --git a/spec/services/packages/nuget/metadata_extraction_service_spec.rb b/spec/services/packages/nuget/metadata_extraction_service_spec.rb index fc21cfd502..12bab30b4a 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 6dc7294854..b3f8980a7b 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/users/update_service_spec.rb b/spec/services/users/update_service_spec.rb index 411cd7316d..f4ea757f81 100644 --- a/spec/services/users/update_service_spec.rb +++ b/spec/services/users/update_service_spec.rb @@ -77,6 +77,34 @@ RSpec.describe Users::UpdateService do subject end + context 'when race condition' do + # See https://gitlab.com/gitlab-org/gitlab/-/issues/382957 + it 'updates email for stale user', :aggregate_failures do + unconfirmed_email = 'unconfirmed-email-user-has-access-to@example.com' + forgery_email = 'forgery@example.com' + + user.update!(email: unconfirmed_email) + + stale_user = User.find(user.id) + + service1 = described_class.new(stale_user, { email: unconfirmed_email }.merge(user: stale_user)) + + service2 = described_class.new(user, { email: forgery_email }.merge(user: user)) + + service2.execute + reloaded_user = User.find(user.id) + expect(reloaded_user.unconfirmed_email).to eq(forgery_email) + expect(stale_user.confirmation_token).not_to eq(user.confirmation_token) + expect(reloaded_user.confirmation_token).to eq(user.confirmation_token) + + service1.execute + reloaded_user = User.find(user.id) + expect(reloaded_user.unconfirmed_email).to eq(unconfirmed_email) + expect(stale_user.confirmation_token).not_to eq(user.confirmation_token) + expect(reloaded_user.confirmation_token).to eq(stale_user.confirmation_token) + end + end + context 'when check_password is true' do def update_user(user, opts) described_class.new(user, opts.merge(user: user)).execute(check_password: true) @@ -139,9 +167,24 @@ RSpec.describe Users::UpdateService do update_user(user, job_title: 'supreme leader of the universe') end.not_to change { user.user_canonical_email } end + + it 'does not reset unconfirmed email' do + unconfirmed_email = 'unconfirmed-email@example.com' + user.update!(email: unconfirmed_email) + + expect do + update_user(user, job_title: 'supreme leader of the universe') + end.not_to change { user.unconfirmed_email } + end end end + it 'does not try to reset unconfirmed email for a new user' do + expect do + update_user(build(:user), job_title: 'supreme leader of the universe') + end.not_to raise_error + end + def update_user(user, opts) described_class.new(user, opts.merge(user: user)).execute end diff --git a/spec/services/web_hook_service_spec.rb b/spec/services/web_hook_service_spec.rb index 551c3dbcc8..3fb4f938e6 100644 --- a/spec/services/web_hook_service_spec.rb +++ b/spec/services/web_hook_service_spec.rb @@ -129,7 +129,10 @@ RSpec.describe WebHookService, :request_store, :clean_gitlab_redis_shared_state context 'there is userinfo' do before do - project_hook.update!(url: 'http://{one}:{two}@example.com') + project_hook.update!( + url: 'http://{one}:{two}@example.com', + url_variables: { 'one' => 'a', 'two' => 'b' } + ) stub_full_request('http://example.com', method: :post) end diff --git a/spec/services/web_hooks/log_execution_service_spec.rb b/spec/services/web_hooks/log_execution_service_spec.rb index 1b8ff9f2a0..fd97d01fa9 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/helpers/gpg_helpers.rb b/spec/support/helpers/gpg_helpers.rb index 7e78fd86de..cc8ee6c98e 100644 --- a/spec/support/helpers/gpg_helpers.rb +++ b/spec/support/helpers/gpg_helpers.rb @@ -145,91 +145,92 @@ module GpgHelpers '5F7EA3981A5845B141ABD522CCFBE19F00AC8B1D' end + # passphrase for secret key 2 is: + # qHGKKXBZ72VamRMUH6d7LsLWsHnJJx3p def secret_key2 <<~KEY.strip -----BEGIN PGP PRIVATE KEY BLOCK----- - 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 - 4f6xMfFu2A== - =3YL6 + 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 + =VtBY -----END PGP PRIVATE KEY BLOCK----- KEY end @@ -238,44 +239,44 @@ module GpgHelpers <<~KEY.strip -----BEGIN PGP PUBLIC KEY BLOCK----- - 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 - =RAwd + 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 + 7ysn+Hu60VIDw8enikBOwZE1oTbrKiTywMbufg== + =cp3L -----END PGP PUBLIC KEY BLOCK----- KEY end @@ -285,7 +286,7 @@ module GpgHelpers end def fingerprint2 - 'C447A6F6BFD9CEF8FB371785571625A930241179' + 'EC164842311CCF9F005DA1A34FD830CEB3E86C97' end def names 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 989de1dbfb..702964a261 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 - expect(page).to have_content tag_name - expect(page).to have_link("ReleaseName", href: project_release_path(project, release)) + 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 + + 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 diff --git a/spec/support/shared_examples/policies/resource_access_token_shared_examples.rb b/spec/support/shared_examples/policies/resource_access_token_shared_examples.rb index 337ad024fc..cc91b73449 100644 --- a/spec/support/shared_examples/policies/resource_access_token_shared_examples.rb +++ b/spec/support/shared_examples/policies/resource_access_token_shared_examples.rb @@ -71,26 +71,3 @@ RSpec.shared_examples 'Self-managed Core resource access tokens' do end end end - -RSpec.shared_examples 'GitLab.com Core resource access tokens' do - before do - allow(::Gitlab).to receive(:com?).and_return(true) - stub_ee_application_setting(should_check_namespace_plan: true) - end - - context 'with owner access' do - let(:current_user) { owner } - - context 'create resource access tokens' do - it { is_expected.not_to be_allowed(:create_resource_access_tokens) } - end - - context 'read resource access tokens' do - it { is_expected.not_to be_allowed(:read_resource_access_tokens) } - end - - context 'destroy resource access tokens' do - it { is_expected.not_to be_allowed(:destroy_resource_access_tokens) } - end - end -end diff --git a/spec/views/layouts/nav/sidebar/_profile.html.haml_spec.rb b/spec/views/layouts/nav/sidebar/_profile.html.haml_spec.rb index f5a0a7a935..3d28be68b2 100644 --- a/spec/views/layouts/nav/sidebar/_profile.html.haml_spec.rb +++ b/spec/views/layouts/nav/sidebar/_profile.html.haml_spec.rb @@ -11,20 +11,4 @@ RSpec.describe 'layouts/nav/sidebar/_profile' do it_behaves_like 'has nav sidebar' it_behaves_like 'sidebar includes snowplow attributes', 'render', 'user_side_navigation', 'user_side_navigation' - - it 'has a link to access tokens' do - render - - expect(rendered).to have_link(_('Access Tokens'), href: profile_personal_access_tokens_path) - end - - context 'when personal access tokens are disabled' do - it 'does not have a link to access tokens' do - allow(::Gitlab::CurrentSettings).to receive_messages(personal_access_tokens_disabled?: true) - - render - - expect(rendered).not_to have_link(_('Access Tokens'), href: profile_personal_access_tokens_path) - end - end end