summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/behaviors/markdown/render_observability.js45
-rw-r--r--app/assets/javascripts/pages/projects/project.js3
-rw-r--r--app/assets/javascripts/repository/index.js7
-rw-r--r--app/assets/javascripts/repository/utils/ref_switcher_utils.js27
-rw-r--r--app/controllers/concerns/confirm_email_warning.rb14
-rw-r--r--app/controllers/projects/blob_controller.rb10
-rw-r--r--app/controllers/projects/refs_controller.rb2
-rw-r--r--app/controllers/projects/tree_controller.rb9
-rw-r--r--app/controllers/projects_controller.rb10
-rw-r--r--app/finders/environments/environment_names_finder.rb11
-rw-r--r--app/finders/notes_finder.rb8
-rw-r--r--app/models/concerns/taskable.rb36
-rw-r--r--app/models/hooks/web_hook.rb18
-rw-r--r--app/models/project_feature.rb3
-rw-r--r--app/policies/project_policy.rb1
-rw-r--r--app/services/merge_requests/push_options_handler_service.rb10
-rw-r--r--app/views/explore/projects/page_out_of_bounds.html.haml2
-rw-r--r--app/views/projects/tree/_tree_header.html.haml2
18 files changed, 153 insertions, 65 deletions
diff --git a/app/assets/javascripts/behaviors/markdown/render_observability.js b/app/assets/javascripts/behaviors/markdown/render_observability.js
index 704d85cf22e..d5d46c10efd 100644
--- a/app/assets/javascripts/behaviors/markdown/render_observability.js
+++ b/app/assets/javascripts/behaviors/markdown/render_observability.js
@@ -7,23 +7,36 @@ export function getFrameSrc(url) {
}
const mountVueComponent = (element) => {
- const url = [element.dataset.frameUrl];
+ const { frameUrl, observabilityUrl } = element.dataset;
- return new Vue({
- el: element,
- render(h) {
- return h('iframe', {
- style: {
- height: '366px',
- width: '768px',
- },
- attrs: {
- src: getFrameSrc(url),
- frameBorder: '0',
- },
- });
- },
- });
+ try {
+ if (
+ !observabilityUrl ||
+ !frameUrl ||
+ new URL(frameUrl)?.host !== new URL(observabilityUrl).host
+ )
+ return;
+
+ // eslint-disable-next-line no-new
+ new Vue({
+ el: element,
+ render(h) {
+ return h('iframe', {
+ style: {
+ height: '366px',
+ width: '768px',
+ },
+ attrs: {
+ src: getFrameSrc(frameUrl),
+ frameBorder: '0',
+ },
+ });
+ },
+ });
+ } catch (e) {
+ // eslint-disable-next-line no-console
+ console.error(e);
+ }
};
export default function renderObservability(elements) {
diff --git a/app/assets/javascripts/pages/projects/project.js b/app/assets/javascripts/pages/projects/project.js
index 5773737c41b..a4b3b83a855 100644
--- a/app/assets/javascripts/pages/projects/project.js
+++ b/app/assets/javascripts/pages/projects/project.js
@@ -110,9 +110,10 @@ export default class Project {
const urlParams = { [fieldName]: ref };
if (params.group === BRANCH_GROUP_NAME) {
urlParams.ref_type = BRANCH_REF_TYPE;
- } else {
+ } else if (params.group === TAG_GROUP_NAME) {
urlParams.ref_type = TAG_REF_TYPE;
}
+
link.href = mergeUrlParams(urlParams, linkTarget);
}
diff --git a/app/assets/javascripts/repository/index.js b/app/assets/javascripts/repository/index.js
index 494e270a66c..95e0c94527b 100644
--- a/app/assets/javascripts/repository/index.js
+++ b/app/assets/javascripts/repository/index.js
@@ -2,7 +2,7 @@ import { GlButton } from '@gitlab/ui';
import Vue from 'vue';
import Vuex from 'vuex';
import { parseBoolean } from '~/lib/utils/common_utils';
-import { escapeFileUrl, visitUrl } from '~/lib/utils/url_utility';
+import { joinPaths, escapeFileUrl, visitUrl } from '~/lib/utils/url_utility';
import { __ } from '~/locale';
import initWebIdeLink from '~/pages/projects/shared/web_ide_link';
import PerformancePlugin from '~/performance/vue_performance_plugin';
@@ -121,7 +121,7 @@ export default function setupVueRepositoryList() {
if (!refSwitcherEl) return false;
- const { projectId, projectRootPath } = refSwitcherEl.dataset;
+ const { projectId, projectRootPath, refType } = refSwitcherEl.dataset;
return new Vue({
el: refSwitcherEl,
@@ -129,7 +129,8 @@ export default function setupVueRepositoryList() {
return createElement(RefSelector, {
props: {
projectId,
- value: ref,
+ value: refType ? joinPaths('refs', refType, ref) : ref,
+ useSymbolicRefNames: true,
},
on: {
input(selectedRef) {
diff --git a/app/assets/javascripts/repository/utils/ref_switcher_utils.js b/app/assets/javascripts/repository/utils/ref_switcher_utils.js
index c62f7f709c4..bcad4a2c822 100644
--- a/app/assets/javascripts/repository/utils/ref_switcher_utils.js
+++ b/app/assets/javascripts/repository/utils/ref_switcher_utils.js
@@ -16,22 +16,29 @@ const getNamespaceTargetRegex = (ref) => new RegExp(`(/-/(blob|tree))/${ref}/(.*
* @param {string} selectedRef - The selected ref from the ref dropdown.
*/
export function generateRefDestinationPath(projectRootPath, ref, selectedRef) {
- const currentPath = window.location.pathname;
- const encodedHash = '%23';
+ const url = new URL(window.location.href);
+ const currentPath = url.pathname;
+ let refType = null;
let namespace = '/-/tree';
let target;
+ let actualRef = selectedRef;
+
+ const matches = selectedRef.match(/^refs\/(heads|tags)\/(.+)/);
+ if (matches) {
+ [, refType, actualRef] = matches;
+ }
+ if (refType) {
+ url.searchParams.set('ref_type', refType);
+ } else {
+ url.searchParams.delete('ref_type');
+ }
+
const NAMESPACE_TARGET_REGEX = getNamespaceTargetRegex(ref);
const match = NAMESPACE_TARGET_REGEX.exec(currentPath);
if (match) {
[, namespace, , target] = match;
}
+ url.pathname = joinPaths(projectRootPath, namespace, actualRef, target);
- const destinationPath = joinPaths(
- projectRootPath,
- namespace,
- encodeURI(selectedRef).replace(/#/g, encodedHash),
- target,
- );
-
- return `${destinationPath}${window.location.hash}`;
+ return url.toString();
}
diff --git a/app/controllers/concerns/confirm_email_warning.rb b/app/controllers/concerns/confirm_email_warning.rb
index ec5140bf223..2711c823275 100644
--- a/app/controllers/concerns/confirm_email_warning.rb
+++ b/app/controllers/concerns/confirm_email_warning.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true
module ConfirmEmailWarning
+ include Gitlab::Utils::StrongMemoize
extend ActiveSupport::Concern
included do
@@ -17,11 +18,9 @@ module ConfirmEmailWarning
return unless current_user
return if current_user.confirmed?
- email = current_user.unconfirmed_email || current_user.email
-
flash.now[:warning] = format(
confirm_warning_message,
- email: email,
+ email: email_to_display,
resend_link: view_context.link_to(_('Resend it'), user_confirmation_path(user: { email: email }), method: :post),
update_link: view_context.link_to(_('Update it'), profile_path)
).html_safe
@@ -29,7 +28,16 @@ module ConfirmEmailWarning
private
+ def email
+ current_user.unconfirmed_email || current_user.email
+ end
+ strong_memoize_attr :email
+
def confirm_warning_message
_("Please check your email (%{email}) to verify that you own this address and unlock the power of CI/CD. Didn't receive it? %{resend_link}. Wrong email address? %{update_link}.")
end
+
+ def email_to_display
+ html_escape(email)
+ end
end
diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb
index 59cea00e26b..b71af82535a 100644
--- a/app/controllers/projects/blob_controller.rb
+++ b/app/controllers/projects/blob_controller.rb
@@ -31,6 +31,7 @@ class Projects::BlobController < Projects::ApplicationController
before_action :authorize_edit_tree!, only: [:new, :create, :update, :destroy]
before_action :commit, except: [:new, :create]
+ before_action :check_for_ambiguous_ref, only: [:show]
before_action :blob, except: [:new, :create]
before_action :require_branch_head, only: [:edit, :update]
before_action :editor_variables, except: [:show, :preview, :diff]
@@ -145,6 +146,15 @@ class Projects::BlobController < Projects::ApplicationController
end
end
+ def check_for_ambiguous_ref
+ @ref_type = ref_type
+
+ if @ref_type == ExtractsRef::BRANCH_REF_TYPE && ambiguous_ref?(@project, @ref)
+ branch = @project.repository.find_branch(@ref)
+ redirect_to project_blob_path(@project, File.join(branch.target, @path))
+ end
+ end
+
def commit
@commit ||= @repository.commit(@ref)
diff --git a/app/controllers/projects/refs_controller.rb b/app/controllers/projects/refs_controller.rb
index 4c2bd2a9d42..f55fc2242a4 100644
--- a/app/controllers/projects/refs_controller.rb
+++ b/app/controllers/projects/refs_controller.rb
@@ -22,7 +22,7 @@ class Projects::RefsController < Projects::ApplicationController
when "tree"
project_tree_path(@project, @id)
when "blob"
- project_blob_path(@project, @id)
+ project_blob_path(@project, @id, ref_type: ref_type)
when "graph"
project_network_path(@project, @id, ref_type: ref_type)
when "graphs"
diff --git a/app/controllers/projects/tree_controller.rb b/app/controllers/projects/tree_controller.rb
index 737a6290431..1f7912e15df 100644
--- a/app/controllers/projects/tree_controller.rb
+++ b/app/controllers/projects/tree_controller.rb
@@ -28,6 +28,15 @@ class Projects::TreeController < Projects::ApplicationController
def show
return render_404 unless @commit
+ @ref_type = ref_type
+ if @ref_type == BRANCH_REF_TYPE && ambiguous_ref?(@project, @ref)
+ branch = @project.repository.find_branch(@ref)
+ if branch
+ redirect_to project_tree_path(@project, branch.target)
+ return
+ end
+ end
+
if tree.entries.empty?
if @repository.blob_at(@commit.id, @path)
redirect_to project_blob_path(@project, File.join(@ref, @path))
diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
index 71ad747b6b1..d71b782c62b 100644
--- a/app/controllers/projects_controller.rb
+++ b/app/controllers/projects_controller.rb
@@ -171,11 +171,19 @@ class ProjectsController < Projects::ApplicationController
flash.now[:alert] = _("Project '%{project_name}' queued for deletion.") % { project_name: @project.name }
end
+ if ambiguous_ref?(@project, @ref)
+ branch = @project.repository.find_branch(@ref)
+
+ # The files view would render a ref other than the default branch
+ # This redirect can be removed once the view is fixed
+ redirect_to(project_tree_path(@project, branch.target), alert: _("The default branch of this project clashes with another ref"))
+ return
+ end
+
respond_to do |format|
format.html do
@notification_setting = current_user.notification_settings_for(@project) if current_user
@project = @project.present(current_user: current_user)
-
render_landing_page
end
diff --git a/app/finders/environments/environment_names_finder.rb b/app/finders/environments/environment_names_finder.rb
index d4928f0fc84..ffb689f45e2 100644
--- a/app/finders/environments/environment_names_finder.rb
+++ b/app/finders/environments/environment_names_finder.rb
@@ -32,18 +32,9 @@ module Environments
end
def namespace_environments
- # We assume reporter access is needed for the :read_environment permission
- # here. This expection is also present in
- # IssuableFinder::Params#min_access_level, which is used for filtering out
- # merge requests that don't have the right permissions.
- #
- # We use this approach so we don't need to load every project into memory
- # just to verify if we can see their environments. Doing so would not be
- # efficient, and possibly mess up pagination if certain projects are not
- # meant to be visible.
projects = project_or_group
.all_projects
- .public_or_visible_to_user(current_user, Gitlab::Access::REPORTER)
+ .filter_by_feature_visibility(:environments, current_user)
Environment.for_project(projects)
end
diff --git a/app/finders/notes_finder.rb b/app/finders/notes_finder.rb
index c542ffbce7e..81017290f12 100644
--- a/app/finders/notes_finder.rb
+++ b/app/finders/notes_finder.rb
@@ -30,6 +30,7 @@ class NotesFinder
notes = init_collection
notes = since_fetch_at(notes)
notes = notes.with_notes_filter(@params[:notes_filter]) if notes_filter?
+ notes = redact_internal(notes)
sort(notes)
end
@@ -181,6 +182,13 @@ class NotesFinder
notes.order_by(sort)
end
+
+ def redact_internal(notes)
+ subject = @project || target
+ return notes if Ability.allowed?(@current_user, :read_internal_note, subject)
+
+ notes.not_internal
+ end
end
NotesFinder.prepend_mod_with('NotesFinder')
diff --git a/app/models/concerns/taskable.rb b/app/models/concerns/taskable.rb
index f9eba4cc2fe..dee1c820f23 100644
--- a/app/models/concerns/taskable.rb
+++ b/app/models/concerns/taskable.rb
@@ -24,25 +24,37 @@ module Taskable
(\s.+) # followed by whitespace and some text.
}x.freeze
+ ITEM_PATTERN_UNTRUSTED =
+ '^' \
+ '(?:(?:>\s{0,4})*)' \
+ '(?P<prefix>(?:\s*(?:[-+*]|(?:\d+\.)))+)' \
+ '\s+' \
+ '(?P<checkbox>' \
+ "#{COMPLETE_PATTERN.source}|#{INCOMPLETE_PATTERN.source}" \
+ ')' \
+ '(?P<label>\s.+)'.freeze
+
# ignore tasks in code or html comment blocks. HTML blocks
# are ok as we allow tasks inside <detail> blocks
- REGEX = %r{
- #{::Gitlab::Regex.markdown_code_or_html_comments}
- |
- (?<task_item>
- #{ITEM_PATTERN}
- )
- }mx.freeze
+ REGEX =
+ "#{::Gitlab::Regex.markdown_code_or_html_comments_untrusted}" \
+ "|" \
+ "(?P<task_item>" \
+ "#{ITEM_PATTERN_UNTRUSTED}" \
+ ")".freeze
def self.get_tasks(content)
items = []
- content.to_s.scan(REGEX) do
- next unless $~[:task_item]
+ regex = Gitlab::UntrustedRegexp.new(REGEX, multiline: true)
+ regex.scan(content.to_s).each do |match|
+ next unless regex.extract_named_group(:task_item, match)
+
+ prefix = regex.extract_named_group(:prefix, match)
+ checkbox = regex.extract_named_group(:checkbox, match)
+ label = regex.extract_named_group(:label, match)
- $~[:task_item].scan(ITEM_PATTERN) do |prefix, checkbox, label|
- items << TaskList::Item.new("#{prefix.strip} #{checkbox}", label.strip)
- end
+ items << TaskList::Item.new("#{prefix.strip} #{checkbox}", label.strip)
end
items
diff --git a/app/models/hooks/web_hook.rb b/app/models/hooks/web_hook.rb
index 819152a38c8..7202a530feb 100644
--- a/app/models/hooks/web_hook.rb
+++ b/app/models/hooks/web_hook.rb
@@ -41,7 +41,7 @@ class WebHook < ApplicationRecord
after_initialize :initialize_url_variables
before_validation :reset_token
- before_validation :reset_url_variables, unless: ->(hook) { hook.is_a?(ServiceHook) }
+ before_validation :reset_url_variables, unless: ->(hook) { hook.is_a?(ServiceHook) }, on: :update
before_validation :set_branch_filter_nil, if: :branch_filter_strategy_all_branches?
validates :push_events_branch_filter, untrusted_regexp: true, if: :branch_filter_strategy_regex?
validates :push_events_branch_filter, "web_hooks/wildcard_branch_filter": true, if: :branch_filter_strategy_wildcard?
@@ -150,7 +150,7 @@ class WebHook < ApplicationRecord
# See app/validators/json_schemas/web_hooks_url_variables.json
VARIABLE_REFERENCE_RE = /\{([A-Za-z]+[0-9]*(?:[._-][A-Za-z0-9]+)*)\}/.freeze
- def interpolated_url
+ def interpolated_url(url = self.url, url_variables = self.url_variables)
return url unless url.include?('{')
vars = url_variables
@@ -176,7 +176,19 @@ class WebHook < ApplicationRecord
end
def reset_url_variables
- self.url_variables = {} if url_changed? && !encrypted_url_variables_changed?
+ interpolated_url_was = interpolated_url(decrypt_url_was, url_variables_were)
+
+ return if url_variables_were.empty? || interpolated_url_was == interpolated_url
+
+ self.url_variables = {} if url_changed? && url_variables_were.to_a.intersection(url_variables.to_a).any?
+ end
+
+ def decrypt_url_was
+ self.class.decrypt_url(encrypted_url_was, iv: Base64.decode64(encrypted_url_iv_was))
+ end
+
+ def url_variables_were
+ self.class.decrypt_url_variables(encrypted_url_variables_was, iv: encrypted_url_variables_iv_was)
end
def next_failure_count
diff --git a/app/models/project_feature.rb b/app/models/project_feature.rb
index 168646bbe41..23b0665cb74 100644
--- a/app/models/project_feature.rb
+++ b/app/models/project_feature.rb
@@ -36,7 +36,8 @@ class ProjectFeature < ApplicationRecord
merge_requests: Gitlab::Access::REPORTER,
metrics_dashboard: Gitlab::Access::REPORTER,
container_registry: Gitlab::Access::REPORTER,
- package_registry: Gitlab::Access::REPORTER
+ package_registry: Gitlab::Access::REPORTER,
+ environments: Gitlab::Access::REPORTER
}.freeze
PRIVATE_FEATURES_MIN_ACCESS_LEVEL_FOR_PRIVATE_PROJECT = { repository: Gitlab::Access::REPORTER }.freeze
diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb
index 875520d24be..3d22002e828 100644
--- a/app/policies/project_policy.rb
+++ b/app/policies/project_policy.rb
@@ -412,7 +412,6 @@ class ProjectPolicy < BasePolicy
end
rule { can?(:metrics_dashboard) }.policy do
- enable :read_prometheus
enable :read_deployment
end
diff --git a/app/services/merge_requests/push_options_handler_service.rb b/app/services/merge_requests/push_options_handler_service.rb
index 235dc6678df..e9abafceb13 100644
--- a/app/services/merge_requests/push_options_handler_service.rb
+++ b/app/services/merge_requests/push_options_handler_service.rb
@@ -54,7 +54,15 @@ module MergeRequests
end
def validate_service
- errors << 'User is required' if current_user.nil?
+ if current_user.nil?
+ errors << 'User is required'
+ return
+ end
+
+ unless current_user&.can?(:read_code, target_project)
+ errors << 'User access was denied'
+ return
+ end
unless target_project.merge_requests_enabled?
errors << "Merge requests are not enabled for project #{target_project.full_path}"
diff --git a/app/views/explore/projects/page_out_of_bounds.html.haml b/app/views/explore/projects/page_out_of_bounds.html.haml
index ef5ee2c679e..e13768a3ccb 100644
--- a/app/views/explore/projects/page_out_of_bounds.html.haml
+++ b/app/views/explore/projects/page_out_of_bounds.html.haml
@@ -18,5 +18,5 @@
%h5= _("Maximum page reached")
%p= _("Sorry, you have exceeded the maximum browsable page number. Please use the API to explore further.")
- = render Pajamas::ButtonComponent.new(href: request.params.merge(page: @max_page_number)) do
+ = render Pajamas::ButtonComponent.new(href: safe_params.merge(page: @max_page_number)) do
= _("Back to page %{number}") % { number: @max_page_number }
diff --git a/app/views/projects/tree/_tree_header.html.haml b/app/views/projects/tree/_tree_header.html.haml
index 6cd3c584f2a..d494d9cc36d 100644
--- a/app/views/projects/tree/_tree_header.html.haml
+++ b/app/views/projects/tree/_tree_header.html.haml
@@ -2,7 +2,7 @@
.tree-ref-container.gl-display-flex.gl-flex-wrap.gl-gap-2.mb-2.mb-md-0
.tree-ref-holder.gl-max-w-26
- #js-tree-ref-switcher{ data: { project_id: @project.id, project_root_path: project_path(@project) } }
+ #js-tree-ref-switcher{ data: { project_id: @project.id, ref_type: @ref_type.to_s, project_root_path: project_path(@project) } }
#js-repo-breadcrumb{ data: breadcrumb_data_attributes }