diff --git a/CHANGELOG.md b/CHANGELOG.md
index aeb9d376f8..e8cfa50add 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,15 @@
documentation](doc/development/changelog.md) for instructions on adding your own
entry.
+## 13.11.4 (2021-05-14)
+
+### Fixed (3 changes)
+
+- Fix N+1 SQL queries in PipelinesController#show. !60794
+- Omit trailing slash when proxying pre-authorized routes with no suffix. !61638
+- Omit trailing slash when checking allowed requests in the read-only middleware. !61641
+
+
## 13.11.3 (2021-04-30)
### Fixed (1 change)
diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION
index fd9f285315..33498dc40c 100644
--- a/GITALY_SERVER_VERSION
+++ b/GITALY_SERVER_VERSION
@@ -1 +1 @@
-13.11.3
\ No newline at end of file
+13.11.5
\ No newline at end of file
diff --git a/Gemfile.lock b/Gemfile.lock
index 007ca87b46..059dcbed1d 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -144,7 +144,7 @@ GEM
coderay (>= 1.0.0)
erubi (>= 1.0.0)
rack (>= 0.9.0)
- bindata (2.4.8)
+ bindata (2.4.10)
binding_ninja (0.2.3)
bootsnap (1.4.6)
msgpack (~> 1.0)
diff --git a/VERSION b/VERSION
index fd9f285315..33498dc40c 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-13.11.3
\ No newline at end of file
+13.11.5
\ No newline at end of file
diff --git a/app/assets/javascripts/notebook/cells/markdown.vue b/app/assets/javascripts/notebook/cells/markdown.vue
index c09db6851e..97b0557cbe 100644
--- a/app/assets/javascripts/notebook/cells/markdown.vue
+++ b/app/assets/javascripts/notebook/cells/markdown.vue
@@ -160,6 +160,7 @@ export default {
'var',
],
ALLOWED_ATTR: ['class', 'style', 'href', 'src'],
+ ALLOW_DATA_ATTR: false,
});
},
},
diff --git a/app/controllers/jira_connect/app_descriptor_controller.rb b/app/controllers/jira_connect/app_descriptor_controller.rb
index 137f830e40..fee8b43aa6 100644
--- a/app/controllers/jira_connect/app_descriptor_controller.rb
+++ b/app/controllers/jira_connect/app_descriptor_controller.rb
@@ -31,6 +31,7 @@ class JiraConnect::AppDescriptorController < JiraConnect::ApplicationController
scopes: %w(READ WRITE DELETE),
apiVersion: 1,
apiMigrations: {
+ 'context-qsh': true,
gdpr: true
}
}
diff --git a/app/controllers/oauth/authorizations_controller.rb b/app/controllers/oauth/authorizations_controller.rb
index 857f36e383..ddf70c1892 100644
--- a/app/controllers/oauth/authorizations_controller.rb
+++ b/app/controllers/oauth/authorizations_controller.rb
@@ -14,8 +14,9 @@ class Oauth::AuthorizationsController < Doorkeeper::AuthorizationsController
if pre_auth.authorizable?
if skip_authorization? || matching_token?
auth = authorization.authorize
+ parsed_redirect_uri = URI.parse(auth.redirect_uri)
session.delete(:user_return_to)
- redirect_to auth.redirect_uri
+ render "doorkeeper/authorizations/redirect", locals: { redirect_uri: parsed_redirect_uri }, layout: false
else
render "doorkeeper/authorizations/new"
end
diff --git a/app/controllers/projects/pipelines_controller.rb b/app/controllers/projects/pipelines_controller.rb
index ee1e10221e..9f326ef59f 100644
--- a/app/controllers/projects/pipelines_controller.rb
+++ b/app/controllers/projects/pipelines_controller.rb
@@ -216,7 +216,7 @@ class Projects::PipelinesController < Projects::ApplicationController
end
def render_show
- @stages = @pipeline.stages.with_latest_and_retried_statuses
+ @stages = @pipeline.stages
respond_to do |format|
format.html do
diff --git a/app/helpers/markup_helper.rb b/app/helpers/markup_helper.rb
index ad206d0e5b..6c19fcc912 100644
--- a/app/helpers/markup_helper.rb
+++ b/app/helpers/markup_helper.rb
@@ -118,6 +118,7 @@ module MarkupHelper
def markup(file_name, text, context = {})
context[:project] ||= @project
+ context[:text_source] ||= :blob
html = context.delete(:rendered) || markup_unsafe(file_name, text, context)
prepare_for_rendering(html, context)
end
diff --git a/app/models/ci/stage.rb b/app/models/ci/stage.rb
index 9dd75150ac..5ae97dcd49 100644
--- a/app/models/ci/stage.rb
+++ b/app/models/ci/stage.rb
@@ -6,6 +6,7 @@ module Ci
include Importable
include Ci::HasStatus
include Gitlab::OptimisticLocking
+ include Presentable
enum status: Ci::HasStatus::STATUSES_ENUM
@@ -22,12 +23,6 @@ module Ci
scope :ordered, -> { order(position: :asc) }
scope :in_pipelines, ->(pipelines) { where(pipeline: pipelines) }
scope :by_name, ->(names) { where(name: names) }
- scope :with_latest_and_retried_statuses, -> do
- includes(
- latest_statuses: [:pipeline, project: :namespace],
- retried_statuses: [:pipeline, project: :namespace]
- )
- end
with_options unless: :importing? do
validates :project, presence: true
diff --git a/app/policies/concerns/policy_actor.rb b/app/policies/concerns/policy_actor.rb
index 75849fb10c..d254b87ae1 100644
--- a/app/policies/concerns/policy_actor.rb
+++ b/app/policies/concerns/policy_actor.rb
@@ -80,6 +80,10 @@ module PolicyActor
def can_read_all_resources?
false
end
+
+ def password_expired?
+ false
+ end
end
PolicyActor.prepend_if_ee('EE::PolicyActor')
diff --git a/app/policies/global_policy.rb b/app/policies/global_policy.rb
index d16c4734b2..2bdccf8304 100644
--- a/app/policies/global_policy.rb
+++ b/app/policies/global_policy.rb
@@ -15,6 +15,10 @@ class GlobalPolicy < BasePolicy
@user&.required_terms_not_accepted?
end
+ condition(:password_expired, scope: :user) do
+ @user&.password_expired?
+ end
+
condition(:project_bot, scope: :user) { @user&.project_bot? }
condition(:migration_bot, scope: :user) { @user&.migration_bot? }
@@ -73,6 +77,12 @@ class GlobalPolicy < BasePolicy
prevent :access_git
end
+ rule { password_expired }.policy do
+ prevent :access_api
+ prevent :access_git
+ prevent :use_slash_commands
+ end
+
rule { can_create_group }.policy do
enable :create_group
end
diff --git a/app/presenters/ci/stage_presenter.rb b/app/presenters/ci/stage_presenter.rb
new file mode 100644
index 0000000000..9ec3f8d153
--- /dev/null
+++ b/app/presenters/ci/stage_presenter.rb
@@ -0,0 +1,32 @@
+# frozen_string_literal: true
+
+module Ci
+ class StagePresenter < Gitlab::View::Presenter::Delegated
+ presents :stage
+
+ def latest_ordered_statuses
+ preload_statuses(stage.statuses.latest_ordered)
+ end
+
+ def retried_ordered_statuses
+ preload_statuses(stage.statuses.retried_ordered)
+ end
+
+ private
+
+ def preload_statuses(statuses)
+ loaded_statuses = statuses.load
+ statuses.tap do |statuses|
+ # rubocop: disable CodeReuse/ActiveRecord
+ ActiveRecord::Associations::Preloader.new.preload(preloadable_statuses(loaded_statuses), %w[pipeline tags job_artifacts_archive metadata])
+ # rubocop: enable CodeReuse/ActiveRecord
+ end
+ end
+
+ def preloadable_statuses(statuses)
+ statuses.reject do |status|
+ status.instance_of?(::GenericCommitStatus) || status.instance_of?(::Ci::Bridge)
+ end
+ end
+ end
+end
diff --git a/app/serializers/member_entity.rb b/app/serializers/member_entity.rb
index 6cbdaeea5e..3b75e876b9 100644
--- a/app/serializers/member_entity.rb
+++ b/app/serializers/member_entity.rb
@@ -40,7 +40,9 @@ class MemberEntity < Grape::Entity
expose :valid_level_roles, as: :valid_roles
- expose :user, if: -> (member) { member.user.present? }, using: MemberUserEntity
+ expose :user, if: -> (member) { member.user.present? } do |member, options|
+ MemberUserEntity.represent(member.user, source: options[:source])
+ end
expose :invite, if: -> (member) { member.invite? } do
expose :email do |member|
diff --git a/app/views/doorkeeper/authorizations/redirect.html.haml b/app/views/doorkeeper/authorizations/redirect.html.haml
new file mode 100644
index 0000000000..9580f33c88
--- /dev/null
+++ b/app/views/doorkeeper/authorizations/redirect.html.haml
@@ -0,0 +1,8 @@
+%h3.page-title= _("Redirecting")
+
+%div
+ %a{ :href => redirect_uri } Click here to redirect to #{redirect_uri}
+
+= javascript_tag do
+ :plain
+ window.location= "#{redirect_uri}";
diff --git a/app/views/projects/stage/_stage.html.haml b/app/views/projects/stage/_stage.html.haml
index 92bfd5a48a..387c8fb323 100644
--- a/app/views/projects/stage/_stage.html.haml
+++ b/app/views/projects/stage/_stage.html.haml
@@ -1,3 +1,5 @@
+- stage = stage.present(current_user: current_user)
+
%tr
%th{ colspan: 10 }
%strong
@@ -6,8 +8,8 @@
= ci_icon_for_status(stage.status)
= stage.name.titleize
-= render stage.latest_statuses, stage: false, ref: false, pipeline_link: false, allow_retry: true
-= render stage.retried_statuses, stage: false, ref: false, pipeline_link: false, retried: true
+= render stage.latest_ordered_statuses, stage: false, ref: false, pipeline_link: false, allow_retry: true
+= render stage.retried_ordered_statuses, stage: false, ref: false, pipeline_link: false, retried: true
%tr
%td{ colspan: 10 }
diff --git a/config/initializers/lograge.rb b/config/initializers/lograge.rb
index e8479bc6aa..61e357808d 100644
--- a/config/initializers/lograge.rb
+++ b/config/initializers/lograge.rb
@@ -18,6 +18,7 @@ unless Gitlab::Runtime.sidekiq?
data[:db_duration_s] = Gitlab::Utils.ms_to_round_sec(data.delete(:db)) if data[:db]
data[:view_duration_s] = Gitlab::Utils.ms_to_round_sec(data.delete(:view)) if data[:view]
data[:duration_s] = Gitlab::Utils.ms_to_round_sec(data.delete(:duration)) if data[:duration]
+ data[:location] = Gitlab::Utils.removes_sensitive_data_from_url(data[:location]) if data[:location]
# Remove empty hashes to prevent type mismatches
# These are set to empty hashes in Lograge's ActionCable subscriber
diff --git a/db/migrate/20210519154058_schedule_update_users_where_two_factor_auth_required_from_group.rb b/db/migrate/20210519154058_schedule_update_users_where_two_factor_auth_required_from_group.rb
new file mode 100644
index 0000000000..2da84301a7
--- /dev/null
+++ b/db/migrate/20210519154058_schedule_update_users_where_two_factor_auth_required_from_group.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+
+class ScheduleUpdateUsersWhereTwoFactorAuthRequiredFromGroup < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ MIGRATION = 'UpdateUsersWhereTwoFactorAuthRequiredFromGroup'
+ DELAY_INTERVAL = 2.minutes
+ BATCH_SIZE = 10_000
+ INDEX_NAME = 'index_users_require_two_factor_authentication_from_group_false'
+
+ disable_ddl_transaction!
+
+ class User < ActiveRecord::Base
+ include EachBatch
+
+ self.table_name = 'users'
+ end
+
+ def up
+ add_concurrent_index :users,
+ :require_two_factor_authentication_from_group,
+ where: 'require_two_factor_authentication_from_group = FALSE',
+ name: INDEX_NAME
+
+ relation = User.where(require_two_factor_authentication_from_group: false)
+
+ queue_background_migration_jobs_by_range_at_intervals(
+ relation, MIGRATION, DELAY_INTERVAL, batch_size: BATCH_SIZE)
+ end
+
+ def down
+ remove_concurrent_index_by_name :users, INDEX_NAME
+ end
+end
diff --git a/db/schema_migrations/20210519154058 b/db/schema_migrations/20210519154058
new file mode 100644
index 0000000000..9bd277e92d
--- /dev/null
+++ b/db/schema_migrations/20210519154058
@@ -0,0 +1 @@
+bdd82fc5cb2bbb322125c153c741002725853e23cd0ae0edbfd80563a4a87f2f
\ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index db46364581..e09851aa10 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -24170,6 +24170,8 @@ CREATE INDEX index_users_ops_dashboard_projects_on_project_id ON users_ops_dashb
CREATE UNIQUE INDEX index_users_ops_dashboard_projects_on_user_id_and_project_id ON users_ops_dashboard_projects USING btree (user_id, project_id);
+CREATE INDEX index_users_require_two_factor_authentication_from_group_false ON users USING btree (require_two_factor_authentication_from_group) WHERE (require_two_factor_authentication_from_group = false);
+
CREATE INDEX index_users_security_dashboard_projects_on_user_id ON users_security_dashboard_projects USING btree (user_id);
CREATE INDEX index_users_star_projects_on_project_id ON users_star_projects USING btree (project_id);
diff --git a/doc/user/profile/img/notification_global_settings.png b/doc/user/profile/img/notification_global_settings.png
deleted file mode 100644
index 699f726c44..0000000000
Binary files a/doc/user/profile/img/notification_global_settings.png and /dev/null differ
diff --git a/doc/user/profile/img/notification_global_settings_v13_12.png b/doc/user/profile/img/notification_global_settings_v13_12.png
new file mode 100644
index 0000000000..2989543c2d
Binary files /dev/null and b/doc/user/profile/img/notification_global_settings_v13_12.png differ
diff --git a/doc/user/profile/notifications.md b/doc/user/profile/notifications.md
index 4d890e249e..c0d232ba49 100644
--- a/doc/user/profile/notifications.md
+++ b/doc/user/profile/notifications.md
@@ -39,31 +39,32 @@ You can tune the notifications you receive by combining your notification settin
- [Notification scope](#notification-scope)
- [Notification levels](#notification-levels)
-### Editing notification settings
+## Editing notification settings
To edit your notification settings:
-1. Click on your profile picture and select **Settings**.
-1. Click **Notifications** in the left sidebar.
+1. In the top-right corner, select your avatar.
+1. Select **Preferences**.
+1. In the left sidebar, select **Notifications**.
1. Edit the desired notification settings. Edited settings are automatically saved and enabled.
These notification settings apply only to you. They do not affect the notifications received by anyone else in the same project or group.
-
-
## Global notification settings
Your **Global notification settings** are the default settings unless you select
different values for a project or a group.
-- **Notification email**: The email address your notifications are sent to.
-- **Global notification level**: The default [notification level](#notification-levels)
+- **Notification email**: the email address your notifications are sent to.
+- **Receive product marketing emails**: select this check box to receive
+ [periodic emails](#product-marketing-emails) about GitLab features.
+- **Global notification level**: the default [notification level](#notification-levels)
which applies to all your notifications.
-- **Receive product marketing emails**: Select this check box to receive periodic
- emails about GitLab features.
-- **Receive notifications about your own activity**: Select this check box to receive
+- **Receive notifications about your own activity**: select this check box to receive
notifications about your own activity. Not selected by default.
+
+
### Notification scope
You can tune the scope of your notifications by selecting different notification levels for each project and group.
@@ -85,15 +86,16 @@ You can select a notification level for each project to help you closely monitor
To select a notification level for a project, use either of these methods:
-1. Click on your profile picture and select **Settings**.
-1. Click **Notifications** in the left sidebar.
+1. In the top-right corner, select your avatar.
+1. Select **Preferences**.
+1. In the left sidebar, select **Notifications**.
1. Locate the project in the **Projects** section.
1. Select the desired [notification level](#notification-levels).
Or:
-1. Navigate to the project's page.
-1. Click the notification dropdown, marked with a bell icon.
+1. Go to your project.
+1. Select the notification dropdown, marked with a bell icon (**{notifications}**).
1. Select the desired [notification level](#notification-levels).
@@ -109,15 +111,16 @@ You can select a notification level and email address for each group.
To select a notification level for a group, use either of these methods:
-1. Click on your profile picture and select **Settings**.
-1. Click **Notifications** in the left sidebar.
+1. In the top-right corner, select your avatar.
+1. Select **Preferences**.
+1. In the left sidebar, select **Notifications**.
1. Locate the project in the **Groups** section.
1. Select the desired [notification level](#notification-levels).
---
-1. Navigate to the group's page.
-1. Click the notification dropdown, marked with a bell icon.
+1. Go to your group.
+1. Select the notification dropdown, marked with a bell icon (**{notifications}**).
1. Select the desired [notification level](#notification-levels).
##### Group notification email address
@@ -126,8 +129,9 @@ To select a notification level for a group, use either of these methods:
You can select an email address to receive notifications for each group you belong to. This could be useful, for example, if you work freelance, and want to keep email about clients' projects separate.
-1. Click on your profile picture and select **Settings**.
-1. Click **Notifications** in the left sidebar.
+1. In the top-right corner, select your avatar.
+1. Select **Preferences**.
+1. In the left sidebar, select **Notifications**.
1. Locate the project in the **Groups** section.
1. Select the desired email address.
@@ -144,6 +148,27 @@ For each project and group you can select one of the following levels:
| Disabled | Turns off notifications. |
| Custom | Receive notifications for custom selected events. |
+### Product marketing emails
+
+You can receive emails that teach you about various GitLab features.
+This is enabled by default.
+
+To opt out, [edit your notification settings](#editing-notification-settings) and clear the
+**Receive product marketing emails** checkbox.
+
+Disabling these emails does not disable all emails.
+Learn how to [opt out of all emails from GitLab](#opt-out-of-all-gitlab-emails).
+
+#### Self-managed product marketing emails **(FREE SELF)**
+
+The self-managed installation generates and automatically sends these emails based on user actions.
+Turning this on does not cause your GitLab instance or your company to send any personal information to
+GitLab Inc.
+
+An instance administrator can configure this setting for all users. If you choose to opt out, your
+setting overrides the instance-wide setting, even when an administrator later enables these emails
+for all users.
+
## Notification events
Users are notified of the following events:
@@ -176,7 +201,11 @@ To enable notifications on one specific issue, merge request or epic, you need t
- **Disable**: If you are receiving notifications for updates to that issue but no
longer want to receive them, unsubscribe from it.
-Configuring this notification on an epic doesn't make you automatically subscribed to the issue that are linked to the epic.
+Disabling this toggle only unsubscribes you from updates related to this issue, merge request, or epic.
+Learn how to [opt out of all emails from GitLab](#opt-out-of-all-gitlab-emails).
+
+Enabling this notification on an epic doesn't automatically subscribe you to the issues linked
+to the epic.
For most events, the notification is sent to:
@@ -229,7 +258,7 @@ If the title or description of an issue or merge request is
changed, notifications are sent to any **new** mentions by `@username` as
if they had been mentioned in the original text.
-You don't receive notifications for Issues, Merge Requests or Milestones created
+You don't receive notifications for issues, merge requests or milestones created
by yourself (except when an issue is due). You only receive automatic
notifications when somebody else comments or adds changes to the ones that
you've created or mentions you.
@@ -250,6 +279,21 @@ The participants are:
- Authors of comments on the design.
- Anyone that is `@mentioned` in a comment on the design.
+## Opt out of all GitLab emails
+
+If you no longer wish to receive any email notifications:
+
+1. [Go to the Notifications settings page.](#editing-notification-settings)
+1. Clear the **Receive product marketing emails** checkbox.
+1. Set your **Global notification level** to **Disabled**.
+1. Clear the **Receive notifications about your own activity** checkbox.
+1. If you belong to any groups or projects, set their notification setting to **Global** or
+ **Disabled**.
+
+On self-managed installations, even after doing this, your instance administrator
+[can still email you](../../tools/email.md).
+To unsubscribe, select the unsubscribe link in one of these emails.
+
## Filtering email
Notification email messages include GitLab-specific headers. You can filter the notification emails based on the content of these headers to better manage your notifications. For example, you could filter all emails for a specific project where you are being assigned either a merge request or issue.
diff --git a/doc/user/project/description_templates.md b/doc/user/project/description_templates.md
index 3acef242ce..4a2bd56b7b 100644
--- a/doc/user/project/description_templates.md
+++ b/doc/user/project/description_templates.md
@@ -106,9 +106,10 @@ instance or the project's parent groups.
### Set instance-level description templates **(PREMIUM SELF)**
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/52360) in GitLab 13.9.
-> - It's [deployed behind a feature flag](../feature_flags.md), disabled by default.
-> - [Became enabled by default](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/56737) in GitLab 13.11.
-> - It's enabled by default on GitLab.com.
+> - [Deployed behind a feature flag](../feature_flags.md), disabled by default.
+> - [Enabled by default](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/56737) in GitLab 13.11.
+> - Enabled by default on GitLab.com.
+> - Recommended for production use.
> - For GitLab self-managed instances, GitLab administrators can opt to [disable it](#enable-or-disable-issue-and-merge-request-description-templates-at-group-and-instance-level). **(PREMIUM SELF)**
You can set a description template at the **instance level** for issues
@@ -131,9 +132,10 @@ Learn more about [instance template repository](../admin_area/settings/instance_
### Set group-level description templates **(PREMIUM)**
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/52360) in GitLab 13.9.
-> - It's [deployed behind a feature flag](../feature_flags.md), disabled by default.
-> - [Became enabled by default](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/56737) in GitLab 13.11.
-> - It's enabled by default on GitLab.com.
+> - [Deployed behind a feature flag](../feature_flags.md), disabled by default.
+> - [Enabled by default](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/56737) in GitLab 13.11.
+> - Enabled by default on GitLab.com.
+> - Recommended for production use.
> - For GitLab self-managed instances, GitLab administrators can opt to [disable it](#enable-or-disable-issue-and-merge-request-description-templates-at-group-and-instance-level). **(PREMIUM SELF)**
With **group-level** description templates, you can store your templates in a single repository and
@@ -230,26 +232,26 @@ it's very hard to read otherwise.)
/assign @qa-tester
```
-## Enable or disable issue and merge request description templates at group and instance level
+## Enable or disable issue and merge request description templates at group and instance level **(PREMIUM SELF)**
Setting issue and merge request description templates at group and instance levels
-is under development and not ready for production use. It is deployed behind a
+is under development but ready for production use. It is deployed behind a
feature flag that is **enabled by default**.
[GitLab administrators with access to the GitLab Rails console](../../administration/feature_flags.md)
can disable it.
-To enable it:
-
-```ruby
-Feature.enable(:inherited_issuable_templates)
-```
-
To disable it:
```ruby
Feature.disable(:inherited_issuable_templates)
```
+To enable it:
+
+```ruby
+Feature.enable(:inherited_issuable_templates)
+```
+
The feature flag affects these features:
- Setting a templates project as issue and merge request description templates source at group level.
diff --git a/lib/api/lint.rb b/lib/api/lint.rb
index e0806674c6..945cdf3edb 100644
--- a/lib/api/lint.rb
+++ b/lib/api/lint.rb
@@ -11,7 +11,7 @@ module API
optional :include_merged_yaml, type: Boolean, desc: 'Whether or not to include merged CI config yaml in the response'
end
post '/lint' do
- unauthorized! if Gitlab::CurrentSettings.signup_disabled? && current_user.nil?
+ unauthorized! if (Gitlab::CurrentSettings.signup_disabled? || Gitlab::CurrentSettings.signup_limited?) && current_user.nil?
result = Gitlab::Ci::YamlProcessor.new(params[:content], user: current_user).execute
diff --git a/lib/banzai/filter/absolute_link_filter.rb b/lib/banzai/filter/absolute_link_filter.rb
index a9bdb004c4..cc7bf3ed55 100644
--- a/lib/banzai/filter/absolute_link_filter.rb
+++ b/lib/banzai/filter/absolute_link_filter.rb
@@ -6,10 +6,13 @@ module Banzai
module Filter
# HTML filter that converts relative urls into absolute ones.
class AbsoluteLinkFilter < HTML::Pipeline::Filter
+ CSS = 'a.gfm'
+ XPATH = Gitlab::Utils::Nokogiri.css_to_xpath(CSS).freeze
+
def call
return doc unless context[:only_path] == false
- doc.search('a.gfm').each do |el|
+ doc.xpath(XPATH).each do |el|
process_link_attr el.attribute('href')
end
diff --git a/lib/banzai/filter/ascii_doc_post_processing_filter.rb b/lib/banzai/filter/ascii_doc_post_processing_filter.rb
index 09f0fd7df4..83c729e13b 100644
--- a/lib/banzai/filter/ascii_doc_post_processing_filter.rb
+++ b/lib/banzai/filter/ascii_doc_post_processing_filter.rb
@@ -3,14 +3,20 @@
module Banzai
module Filter
class AsciiDocPostProcessingFilter < HTML::Pipeline::Filter
+ CSS_MATH = '[data-math-style]'
+ XPATH_MATH = Gitlab::Utils::Nokogiri.css_to_xpath(CSS_MATH).freeze
+ CSS_MERM = '[data-mermaid-style]'
+ XPATH_MERM = Gitlab::Utils::Nokogiri.css_to_xpath(CSS_MERM).freeze
+
def call
- doc.search('[data-math-style]').each do |node|
+ doc.xpath(XPATH_MATH).each do |node|
node.set_attribute('class', 'code math js-render-math')
end
- doc.search('[data-mermaid-style]').each do |node|
+ doc.xpath(XPATH_MERM).each do |node|
node.set_attribute('class', 'js-render-mermaid')
end
+
doc
end
end
diff --git a/lib/banzai/filter/base_relative_link_filter.rb b/lib/banzai/filter/base_relative_link_filter.rb
index fd526df4c4..60d09b69a1 100644
--- a/lib/banzai/filter/base_relative_link_filter.rb
+++ b/lib/banzai/filter/base_relative_link_filter.rb
@@ -7,23 +7,20 @@ module Banzai
class BaseRelativeLinkFilter < HTML::Pipeline::Filter
include Gitlab::Utils::StrongMemoize
+ CSS = 'a:not(.gfm), img:not(.gfm), video:not(.gfm), audio:not(.gfm)'
+ XPATH = Gitlab::Utils::Nokogiri.css_to_xpath(CSS).freeze
+
protected
def linkable_attributes
strong_memoize(:linkable_attributes) do
attrs = []
- attrs += doc.search('a:not(.gfm)').map do |el|
- el.attribute('href')
+ attrs += doc.xpath(XPATH).flat_map do |el|
+ [el.attribute('href'), el.attribute('src'), el.attribute('data-src')]
end
- attrs += doc.search('img:not(.gfm), video:not(.gfm), audio:not(.gfm)').flat_map do |el|
- [el.attribute('src'), el.attribute('data-src')]
- end
-
- attrs.reject do |attr|
- attr.blank? || attr.value.start_with?('//')
- end
+ attrs.reject { |attr| attr.blank? || attr.value.start_with?('//') }
end
end
diff --git a/lib/banzai/filter/color_filter.rb b/lib/banzai/filter/color_filter.rb
index 0aca744163..58e9b8cdba 100644
--- a/lib/banzai/filter/color_filter.rb
+++ b/lib/banzai/filter/color_filter.rb
@@ -7,8 +7,11 @@ module Banzai
class ColorFilter < HTML::Pipeline::Filter
COLOR_CHIP_CLASS = 'gfm-color_chip'
+ CSS = 'code'
+ XPATH = Gitlab::Utils::Nokogiri.css_to_xpath(CSS).freeze
+
def call
- doc.css('code').each do |node|
+ doc.xpath(XPATH).each do |node|
color = ColorParser.parse(node.content)
node << color_chip(color) if color
end
diff --git a/lib/banzai/filter/custom_emoji_filter.rb b/lib/banzai/filter/custom_emoji_filter.rb
index 1ee8f4e31e..e26c5d36f2 100644
--- a/lib/banzai/filter/custom_emoji_filter.rb
+++ b/lib/banzai/filter/custom_emoji_filter.rb
@@ -9,7 +9,7 @@ module Banzai
return doc unless context[:project]
return doc unless Feature.enabled?(:custom_emoji, context[:project])
- doc.search(".//text()").each do |node|
+ doc.xpath('descendant-or-self::text()').each do |node|
content = node.to_html
next if has_ancestor?(node, IGNORED_ANCESTOR_TAGS)
diff --git a/lib/banzai/filter/emoji_filter.rb b/lib/banzai/filter/emoji_filter.rb
index 8952a3ff6b..9d24bf028b 100644
--- a/lib/banzai/filter/emoji_filter.rb
+++ b/lib/banzai/filter/emoji_filter.rb
@@ -11,7 +11,7 @@ module Banzai
IGNORE_UNICODE_EMOJIS = %w(™ © ®).freeze
def call
- doc.search(".//text()").each do |node|
+ doc.xpath('descendant-or-self::text()').each do |node|
content = node.to_html
next if has_ancestor?(node, IGNORED_ANCESTOR_TAGS)
diff --git a/lib/banzai/filter/footnote_filter.rb b/lib/banzai/filter/footnote_filter.rb
index 5474242e03..0f856dc0eb 100644
--- a/lib/banzai/filter/footnote_filter.rb
+++ b/lib/banzai/filter/footnote_filter.rb
@@ -23,17 +23,23 @@ module Banzai
FOOTNOTE_LINK_REFERENCE_PATTERN = /\A#{FOOTNOTE_LINK_ID_PREFIX}\d+\z/.freeze
FOOTNOTE_START_NUMBER = 1
+ CSS_SECTION = "ol > li[id=#{FOOTNOTE_ID_PREFIX}#{FOOTNOTE_START_NUMBER}]"
+ XPATH_SECTION = Gitlab::Utils::Nokogiri.css_to_xpath(CSS_SECTION).freeze
+ CSS_FOOTNOTE = 'sup > a[id]'
+ XPATH_FOOTNOTE = Gitlab::Utils::Nokogiri.css_to_xpath(CSS_FOOTNOTE).freeze
+
def call
- return doc unless first_footnote = doc.at_css("ol > li[id=#{fn_id(FOOTNOTE_START_NUMBER)}]")
+ return doc unless first_footnote = doc.at_xpath(XPATH_SECTION)
# Sanitization stripped off the section wrapper - add it back in
first_footnote.parent.wrap('