New upstream version 15.8.5+ds1
This commit is contained in:
parent
b439698ad0
commit
25a2104fa7
61 changed files with 1271 additions and 453 deletions
|
@ -30,7 +30,7 @@ workflow:
|
||||||
# If `$FORCE_GITLAB_CI` is set, create a pipeline.
|
# If `$FORCE_GITLAB_CI` is set, create a pipeline.
|
||||||
- if: '$FORCE_GITLAB_CI'
|
- if: '$FORCE_GITLAB_CI'
|
||||||
variables:
|
variables:
|
||||||
RUBY_VERSION: "3.0"
|
RUBY_VERSION: "2.7"
|
||||||
# As part of the process of creating RCs automatically, we update stable
|
# As part of the process of creating RCs automatically, we update stable
|
||||||
# branches with the changes of the most recent production deployment. The
|
# branches with the changes of the most recent production deployment. The
|
||||||
# merge requests used for this merge a branch release-tools/X into a stable
|
# merge requests used for this merge a branch release-tools/X into a stable
|
||||||
|
@ -46,10 +46,10 @@ workflow:
|
||||||
# For (detached) merge request pipelines.
|
# For (detached) merge request pipelines.
|
||||||
- if: '$CI_MERGE_REQUEST_IID'
|
- if: '$CI_MERGE_REQUEST_IID'
|
||||||
variables:
|
variables:
|
||||||
RUBY_VERSION: "3.0"
|
RUBY_VERSION: "2.7"
|
||||||
OMNIBUS_GITLAB_RUBY3_BUILD: "true"
|
OMNIBUS_GITLAB_RUBY2_BUILD: "true"
|
||||||
OMNIBUS_GITLAB_CACHE_EDITION: "GITLAB_RUBY3"
|
OMNIBUS_GITLAB_CACHE_EDITION: "GITLAB_RUBY2"
|
||||||
PIPELINE_NAME: 'Ruby 3 $CI_MERGE_REQUEST_EVENT_TYPE MR pipeline'
|
PIPELINE_NAME: 'Ruby 2 $CI_MERGE_REQUEST_EVENT_TYPE MR pipeline'
|
||||||
# For the scheduled pipelines, we set specific variables.
|
# For the scheduled pipelines, we set specific variables.
|
||||||
- if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $CI_PIPELINE_SOURCE == "schedule"'
|
- if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $CI_PIPELINE_SOURCE == "schedule"'
|
||||||
variables:
|
variables:
|
||||||
|
|
21
CHANGELOG.md
21
CHANGELOG.md
|
@ -2,6 +2,27 @@
|
||||||
documentation](doc/development/changelog.md) for instructions on adding your own
|
documentation](doc/development/changelog.md) for instructions on adding your own
|
||||||
entry.
|
entry.
|
||||||
|
|
||||||
|
## 15.8.5 (2023-03-30)
|
||||||
|
|
||||||
|
### Security (16 changes)
|
||||||
|
|
||||||
|
- [Fix rubocop offenses in lib/gitlab/url_sanitizer.rb](gitlab-org/security/gitlab@ddc04cf7059e411e20033b95e1297381d64d4b22) ([merge request](gitlab-org/security/gitlab!3175))
|
||||||
|
- [Add checks to remove open redirects from Observability URL](gitlab-org/security/gitlab@a22ce3851128eb900dbabe9e38c07889967a2915) ([merge request](gitlab-org/security/gitlab!3032))
|
||||||
|
- [Redirect to tree from project root on ref collision](gitlab-org/security/gitlab@fad24ae9d8fa0e7bd9eff0c9e6914c8267451b4d) ([merge request](gitlab-org/security/gitlab!3134))
|
||||||
|
- [Fixes soft email confirmation alert vulnerability](gitlab-org/security/gitlab@85be0fbfc98cdb774d68070479e35be22f6ba40a) ([merge request](gitlab-org/security/gitlab!3125))
|
||||||
|
- [Restrict Prometheus API access on public projects](gitlab-org/security/gitlab@2df2fa2dc4b9015d044d0ddc5d26e17e9e5f85c0) ([merge request](gitlab-org/security/gitlab!3164))
|
||||||
|
- [Verify that users have access to the parent of the fork](gitlab-org/security/gitlab@53f7f06843eea4d666d361f5a1d349bd1e3f4312) ([merge request](gitlab-org/security/gitlab!3085))
|
||||||
|
- [Protect webhook secrets by resetting url_variables](gitlab-org/security/gitlab@9fa9dbff463f6015ffaf8d082db3d41ae623763e) ([merge request](gitlab-org/security/gitlab!3141))
|
||||||
|
- [Replace Unicode space chars with spaces](gitlab-org/security/gitlab@20d77d4d680d13f916fb69de0d79802753421c8f) ([merge request](gitlab-org/security/gitlab!3137))
|
||||||
|
- [Check access to parent when creating and updating epics](gitlab-org/security/gitlab@0fed113756b27a3a078f87f29711b225e1ed4cce) ([merge request](gitlab-org/security/gitlab!3150))
|
||||||
|
- [Improve Gitlab::UrlSanitizer regex to match more URIs](gitlab-org/security/gitlab@2285088f37aca877b1dcd59c728cdf33171b30cb) ([merge request](gitlab-org/security/gitlab!3109))
|
||||||
|
- [Check access to target project before looking for branch](gitlab-org/security/gitlab@37b8d855d87c88170322e6a6d4c285fee6c6cb64) ([merge request](gitlab-org/security/gitlab!3038))
|
||||||
|
- [Fix the potential leak of internal notes](gitlab-org/security/gitlab@66f8cc2eb13509397b980d53a4b67ca03d8903f7) ([merge request](gitlab-org/security/gitlab!3121))
|
||||||
|
- [Filter namespace environments by feature visibility](gitlab-org/security/gitlab@e1859de393b4794e1356d6318e56ede4b557c059) ([merge request](gitlab-org/security/gitlab!3112))
|
||||||
|
- [Check access to reorder issues in epic tree](gitlab-org/security/gitlab@13f9c6231cea956f73355c5b5b820163f523e7d8) ([merge request](gitlab-org/security/gitlab!3100))
|
||||||
|
- [Fix security report authorization](gitlab-org/security/gitlab@19baab85c7a5a64a09e3e4808e8550fc72e18323) ([merge request](gitlab-org/security/gitlab!3105))
|
||||||
|
- [Prevent XSS attack in "Maximum page reached" page](gitlab-org/security/gitlab@be5491c5db05161e4b14d53900dd19b66848de48) ([merge request](gitlab-org/security/gitlab!3131))
|
||||||
|
|
||||||
## 15.8.4 (2023-03-02)
|
## 15.8.4 (2023-03-02)
|
||||||
|
|
||||||
### Security (12 changes)
|
### Security (12 changes)
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
15.8.4
|
15.8.5
|
|
@ -1 +1 @@
|
||||||
15.8.4
|
15.8.5
|
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
||||||
15.8.4
|
15.8.5
|
|
@ -7,23 +7,36 @@ export function getFrameSrc(url) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const mountVueComponent = (element) => {
|
const mountVueComponent = (element) => {
|
||||||
const url = [element.dataset.frameUrl];
|
const { frameUrl, observabilityUrl } = element.dataset;
|
||||||
|
|
||||||
return new Vue({
|
try {
|
||||||
el: element,
|
if (
|
||||||
render(h) {
|
!observabilityUrl ||
|
||||||
return h('iframe', {
|
!frameUrl ||
|
||||||
style: {
|
new URL(frameUrl)?.host !== new URL(observabilityUrl).host
|
||||||
height: '366px',
|
)
|
||||||
width: '768px',
|
return;
|
||||||
},
|
|
||||||
attrs: {
|
// eslint-disable-next-line no-new
|
||||||
src: getFrameSrc(url),
|
new Vue({
|
||||||
frameBorder: '0',
|
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) {
|
export default function renderObservability(elements) {
|
||||||
|
|
|
@ -118,9 +118,10 @@ export default class Project {
|
||||||
const urlParams = { [fieldName]: ref };
|
const urlParams = { [fieldName]: ref };
|
||||||
if (params.group === BRANCH_GROUP_NAME) {
|
if (params.group === BRANCH_GROUP_NAME) {
|
||||||
urlParams.ref_type = BRANCH_REF_TYPE;
|
urlParams.ref_type = BRANCH_REF_TYPE;
|
||||||
} else {
|
} else if (params.group === TAG_GROUP_NAME) {
|
||||||
urlParams.ref_type = TAG_REF_TYPE;
|
urlParams.ref_type = TAG_REF_TYPE;
|
||||||
}
|
}
|
||||||
|
|
||||||
link.href = mergeUrlParams(urlParams, linkTarget);
|
link.href = mergeUrlParams(urlParams, linkTarget);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { GlButton } from '@gitlab/ui';
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import Vuex from 'vuex';
|
import Vuex from 'vuex';
|
||||||
import { parseBoolean } from '~/lib/utils/common_utils';
|
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 { __ } from '~/locale';
|
||||||
import initWebIdeLink from '~/pages/projects/shared/web_ide_link';
|
import initWebIdeLink from '~/pages/projects/shared/web_ide_link';
|
||||||
import PerformancePlugin from '~/performance/vue_performance_plugin';
|
import PerformancePlugin from '~/performance/vue_performance_plugin';
|
||||||
|
@ -119,7 +119,7 @@ export default function setupVueRepositoryList() {
|
||||||
|
|
||||||
if (!refSwitcherEl) return false;
|
if (!refSwitcherEl) return false;
|
||||||
|
|
||||||
const { projectId, projectRootPath } = refSwitcherEl.dataset;
|
const { projectId, projectRootPath, refType } = refSwitcherEl.dataset;
|
||||||
|
|
||||||
return new Vue({
|
return new Vue({
|
||||||
el: refSwitcherEl,
|
el: refSwitcherEl,
|
||||||
|
@ -127,11 +127,12 @@ export default function setupVueRepositoryList() {
|
||||||
return createElement(RefSelector, {
|
return createElement(RefSelector, {
|
||||||
props: {
|
props: {
|
||||||
projectId,
|
projectId,
|
||||||
value: ref,
|
value: refType ? joinPaths('refs', refType, ref) : ref,
|
||||||
|
useSymbolicRefNames: true,
|
||||||
},
|
},
|
||||||
on: {
|
on: {
|
||||||
input(selectedRef) {
|
input(selectedRef) {
|
||||||
visitUrl(generateRefDestinationPath(projectRootPath, selectedRef));
|
visitUrl(generateRefDestinationPath(projectRootPath, ref, selectedRef));
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -15,22 +15,30 @@ const NAMESPACE_TARGET_REGEX = /(\/-\/(blob|tree))\/.*?\/(.*)/;
|
||||||
* @param {string} projectRootPath - The root path for a project.
|
* @param {string} projectRootPath - The root path for a project.
|
||||||
* @param {string} selectedRef - The selected ref from the ref dropdown.
|
* @param {string} selectedRef - The selected ref from the ref dropdown.
|
||||||
*/
|
*/
|
||||||
export function generateRefDestinationPath(projectRootPath, selectedRef) {
|
|
||||||
const currentPath = window.location.pathname;
|
export function generateRefDestinationPath(projectRootPath, ref, selectedRef) {
|
||||||
const encodedHash = '%23';
|
const url = new URL(window.location.href);
|
||||||
|
const currentPath = url.pathname;
|
||||||
|
let refType = null;
|
||||||
let namespace = '/-/tree';
|
let namespace = '/-/tree';
|
||||||
let target;
|
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 match = NAMESPACE_TARGET_REGEX.exec(currentPath);
|
const match = NAMESPACE_TARGET_REGEX.exec(currentPath);
|
||||||
if (match) {
|
if (match) {
|
||||||
[, namespace, , target] = match;
|
[, namespace, , target] = match;
|
||||||
}
|
}
|
||||||
|
url.pathname = joinPaths(projectRootPath, namespace, actualRef, target);
|
||||||
|
|
||||||
const destinationPath = joinPaths(
|
return url.toString();
|
||||||
projectRootPath,
|
|
||||||
namespace,
|
|
||||||
encodeURI(selectedRef).replace(/#/g, encodedHash),
|
|
||||||
target,
|
|
||||||
);
|
|
||||||
|
|
||||||
return `${destinationPath}${window.location.hash}`;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
module ConfirmEmailWarning
|
module ConfirmEmailWarning
|
||||||
|
include Gitlab::Utils::StrongMemoize
|
||||||
extend ActiveSupport::Concern
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
included do
|
included do
|
||||||
|
@ -17,11 +18,9 @@ module ConfirmEmailWarning
|
||||||
return unless current_user
|
return unless current_user
|
||||||
return if current_user.confirmed?
|
return if current_user.confirmed?
|
||||||
|
|
||||||
email = current_user.unconfirmed_email || current_user.email
|
|
||||||
|
|
||||||
flash.now[:warning] = format(
|
flash.now[:warning] = format(
|
||||||
confirm_warning_message,
|
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),
|
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)
|
update_link: view_context.link_to(_('Update it'), profile_path)
|
||||||
).html_safe
|
).html_safe
|
||||||
|
@ -29,7 +28,16 @@ module ConfirmEmailWarning
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def email
|
||||||
|
current_user.unconfirmed_email || current_user.email
|
||||||
|
end
|
||||||
|
strong_memoize_attr :email
|
||||||
|
|
||||||
def confirm_warning_message
|
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}.")
|
_("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
|
end
|
||||||
|
|
||||||
|
def email_to_display
|
||||||
|
html_escape(email)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -31,6 +31,7 @@ class Projects::BlobController < Projects::ApplicationController
|
||||||
before_action :authorize_edit_tree!, only: [:new, :create, :update, :destroy]
|
before_action :authorize_edit_tree!, only: [:new, :create, :update, :destroy]
|
||||||
|
|
||||||
before_action :commit, except: [:new, :create]
|
before_action :commit, except: [:new, :create]
|
||||||
|
before_action :check_for_ambiguous_ref, only: [:show]
|
||||||
before_action :blob, except: [:new, :create]
|
before_action :blob, except: [:new, :create]
|
||||||
before_action :require_branch_head, only: [:edit, :update]
|
before_action :require_branch_head, only: [:edit, :update]
|
||||||
before_action :editor_variables, except: [:show, :preview, :diff]
|
before_action :editor_variables, except: [:show, :preview, :diff]
|
||||||
|
@ -145,6 +146,15 @@ class Projects::BlobController < Projects::ApplicationController
|
||||||
end
|
end
|
||||||
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
|
def commit
|
||||||
@commit ||= @repository.commit(@ref)
|
@commit ||= @repository.commit(@ref)
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,7 @@ class Projects::RefsController < Projects::ApplicationController
|
||||||
when "tree"
|
when "tree"
|
||||||
project_tree_path(@project, @id)
|
project_tree_path(@project, @id)
|
||||||
when "blob"
|
when "blob"
|
||||||
project_blob_path(@project, @id)
|
project_blob_path(@project, @id, ref_type: ref_type)
|
||||||
when "graph"
|
when "graph"
|
||||||
if Feature.enabled?(:use_ref_type_parameter, @project)
|
if Feature.enabled?(:use_ref_type_parameter, @project)
|
||||||
project_network_path(@project, @id, ref_type: ref_type)
|
project_network_path(@project, @id, ref_type: ref_type)
|
||||||
|
|
|
@ -28,6 +28,15 @@ class Projects::TreeController < Projects::ApplicationController
|
||||||
def show
|
def show
|
||||||
return render_404 unless @commit
|
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 tree.entries.empty?
|
||||||
if @repository.blob_at(@commit.id, @path)
|
if @repository.blob_at(@commit.id, @path)
|
||||||
redirect_to project_blob_path(@project, File.join(@ref, @path))
|
redirect_to project_blob_path(@project, File.join(@ref, @path))
|
||||||
|
|
|
@ -172,11 +172,19 @@ class ProjectsController < Projects::ApplicationController
|
||||||
flash.now[:alert] = _("Project '%{project_name}' queued for deletion.") % { project_name: @project.name }
|
flash.now[:alert] = _("Project '%{project_name}' queued for deletion.") % { project_name: @project.name }
|
||||||
end
|
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|
|
respond_to do |format|
|
||||||
format.html do
|
format.html do
|
||||||
@notification_setting = current_user.notification_settings_for(@project) if current_user
|
@notification_setting = current_user.notification_settings_for(@project) if current_user
|
||||||
@project = @project.present(current_user: current_user)
|
@project = @project.present(current_user: current_user)
|
||||||
|
|
||||||
render_landing_page
|
render_landing_page
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -32,18 +32,9 @@ module Environments
|
||||||
end
|
end
|
||||||
|
|
||||||
def namespace_environments
|
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
|
projects = project_or_group
|
||||||
.all_projects
|
.all_projects
|
||||||
.public_or_visible_to_user(current_user, Gitlab::Access::REPORTER)
|
.filter_by_feature_visibility(:environments, current_user)
|
||||||
|
|
||||||
Environment.for_project(projects)
|
Environment.for_project(projects)
|
||||||
end
|
end
|
||||||
|
|
|
@ -30,6 +30,7 @@ class NotesFinder
|
||||||
notes = init_collection
|
notes = init_collection
|
||||||
notes = since_fetch_at(notes)
|
notes = since_fetch_at(notes)
|
||||||
notes = notes.with_notes_filter(@params[:notes_filter]) if notes_filter?
|
notes = notes.with_notes_filter(@params[:notes_filter]) if notes_filter?
|
||||||
|
notes = redact_internal(notes)
|
||||||
sort(notes)
|
sort(notes)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -181,6 +182,13 @@ class NotesFinder
|
||||||
|
|
||||||
notes.order_by(sort)
|
notes.order_by(sort)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def redact_internal(notes)
|
||||||
|
subject = @project || target
|
||||||
|
return notes if Ability.allowed?(@current_user, :reporter_access, subject)
|
||||||
|
|
||||||
|
notes.not_internal
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
NotesFinder.prepend_mod_with('NotesFinder')
|
NotesFinder.prepend_mod_with('NotesFinder')
|
||||||
|
|
|
@ -41,7 +41,7 @@ class WebHook < ApplicationRecord
|
||||||
after_initialize :initialize_url_variables
|
after_initialize :initialize_url_variables
|
||||||
|
|
||||||
before_validation :reset_token
|
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?
|
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, untrusted_regexp: true, if: :branch_filter_strategy_regex?
|
||||||
validates :push_events_branch_filter, "web_hooks/wildcard_branch_filter": true, if: :branch_filter_strategy_wildcard?
|
validates :push_events_branch_filter, "web_hooks/wildcard_branch_filter": true, if: :branch_filter_strategy_wildcard?
|
||||||
|
@ -189,7 +189,7 @@ class WebHook < ApplicationRecord
|
||||||
# See app/validators/json_schemas/web_hooks_url_variables.json
|
# See app/validators/json_schemas/web_hooks_url_variables.json
|
||||||
VARIABLE_REFERENCE_RE = /\{([A-Za-z]+[0-9]*(?:[._-][A-Za-z0-9]+)*)\}/.freeze
|
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?('{')
|
return url unless url.include?('{')
|
||||||
|
|
||||||
vars = url_variables
|
vars = url_variables
|
||||||
|
@ -215,7 +215,19 @@ class WebHook < ApplicationRecord
|
||||||
end
|
end
|
||||||
|
|
||||||
def reset_url_variables
|
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
|
end
|
||||||
|
|
||||||
def next_failure_count
|
def next_failure_count
|
||||||
|
|
|
@ -36,7 +36,8 @@ class ProjectFeature < ApplicationRecord
|
||||||
merge_requests: Gitlab::Access::REPORTER,
|
merge_requests: Gitlab::Access::REPORTER,
|
||||||
metrics_dashboard: Gitlab::Access::REPORTER,
|
metrics_dashboard: Gitlab::Access::REPORTER,
|
||||||
container_registry: Gitlab::Access::REPORTER,
|
container_registry: Gitlab::Access::REPORTER,
|
||||||
package_registry: Gitlab::Access::REPORTER
|
package_registry: Gitlab::Access::REPORTER,
|
||||||
|
environments: Gitlab::Access::REPORTER
|
||||||
}.freeze
|
}.freeze
|
||||||
PRIVATE_FEATURES_MIN_ACCESS_LEVEL_FOR_PRIVATE_PROJECT = { repository: Gitlab::Access::REPORTER }.freeze
|
PRIVATE_FEATURES_MIN_ACCESS_LEVEL_FOR_PRIVATE_PROJECT = { repository: Gitlab::Access::REPORTER }.freeze
|
||||||
|
|
||||||
|
|
|
@ -404,7 +404,6 @@ class ProjectPolicy < BasePolicy
|
||||||
end
|
end
|
||||||
|
|
||||||
rule { can?(:metrics_dashboard) }.policy do
|
rule { can?(:metrics_dashboard) }.policy do
|
||||||
enable :read_prometheus
|
|
||||||
enable :read_deployment
|
enable :read_deployment
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -54,7 +54,15 @@ module MergeRequests
|
||||||
end
|
end
|
||||||
|
|
||||||
def validate_service
|
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?
|
unless target_project.merge_requests_enabled?
|
||||||
errors << "Merge requests are not enabled for project #{target_project.full_path}"
|
errors << "Merge requests are not enabled for project #{target_project.full_path}"
|
||||||
|
|
|
@ -18,5 +18,5 @@
|
||||||
%h5= _("Maximum page reached")
|
%h5= _("Maximum page reached")
|
||||||
%p= _("Sorry, you have exceeded the maximum browsable page number. Please use the API to explore further.")
|
%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 }
|
= _("Back to page %{number}") % { number: @max_page_number }
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
.tree-ref-container.gl-display-flex.mb-2.mb-md-0
|
.tree-ref-container.gl-display-flex.mb-2.mb-md-0
|
||||||
.tree-ref-holder
|
.tree-ref-holder
|
||||||
#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 }
|
#js-repo-breadcrumb{ data: breadcrumb_data_attributes }
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class NullifyLastErrorFromProjectMirrorData < Gitlab::Database::Migration[2.1]
|
||||||
|
MIGRATION = 'NullifyLastErrorFromProjectMirrorData'
|
||||||
|
INTERVAL = 2.minutes
|
||||||
|
BATCH_SIZE = 10_000
|
||||||
|
SUB_BATCH_SIZE = 1_000
|
||||||
|
|
||||||
|
disable_ddl_transaction!
|
||||||
|
restrict_gitlab_migration gitlab_schema: :gitlab_main
|
||||||
|
|
||||||
|
def up
|
||||||
|
queue_batched_background_migration(
|
||||||
|
MIGRATION,
|
||||||
|
:project_mirror_data,
|
||||||
|
:id,
|
||||||
|
job_interval: INTERVAL,
|
||||||
|
batch_size: BATCH_SIZE,
|
||||||
|
sub_batch_size: SUB_BATCH_SIZE
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def down
|
||||||
|
delete_batched_background_migration(MIGRATION, :project_mirror_data, :id, [])
|
||||||
|
end
|
||||||
|
end
|
1
db/schema_migrations/20230208131808
Normal file
1
db/schema_migrations/20230208131808
Normal file
|
@ -0,0 +1 @@
|
||||||
|
784f8f189eee7b5cf3136f0a859874a1d170d2b148f4c260f968b144816f1322
|
|
@ -7028,7 +7028,7 @@ references and their corresponding code points.</p>
|
||||||
<copy-code></copy-code>
|
<copy-code></copy-code>
|
||||||
</div>
|
</div>
|
||||||
<div class="gl-relative markdown-code-block js-markdown-code">
|
<div class="gl-relative markdown-code-block js-markdown-code">
|
||||||
<pre data-sourcepos="7591:1-7595:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"><p> &amp; © Æ Ď</span>
|
<pre data-sourcepos="7591:1-7595:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"><p> &amp; © Æ Ď</span>
|
||||||
<span id="LC2" class="line" lang="plaintext">¾ ℋ ⅆ</span>
|
<span id="LC2" class="line" lang="plaintext">¾ ℋ ⅆ</span>
|
||||||
<span id="LC3" class="line" lang="plaintext">∲ ≧̸</p></span></code></pre>
|
<span id="LC3" class="line" lang="plaintext">∲ ≧̸</p></span></code></pre>
|
||||||
<copy-code></copy-code>
|
<copy-code></copy-code>
|
||||||
|
@ -7344,11 +7344,11 @@ stripped in this way:</p>
|
||||||
<div>
|
<div>
|
||||||
<div><a href="#example-343">Example 343</a></div>
|
<div><a href="#example-343">Example 343</a></div>
|
||||||
<div class="gl-relative markdown-code-block js-markdown-code">
|
<div class="gl-relative markdown-code-block js-markdown-code">
|
||||||
<pre data-sourcepos="7960:1-7962:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">` b `</span></code></pre>
|
<pre data-sourcepos="7960:1-7962:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">` b `</span></code></pre>
|
||||||
<copy-code></copy-code>
|
<copy-code></copy-code>
|
||||||
</div>
|
</div>
|
||||||
<div class="gl-relative markdown-code-block js-markdown-code">
|
<div class="gl-relative markdown-code-block js-markdown-code">
|
||||||
<pre data-sourcepos="7964:1-7966:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"><p><code> b </code></p></span></code></pre>
|
<pre data-sourcepos="7964:1-7966:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"><p><code> b </code></p></span></code></pre>
|
||||||
<copy-code></copy-code>
|
<copy-code></copy-code>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -7356,12 +7356,12 @@ stripped in this way:</p>
|
||||||
<div>
|
<div>
|
||||||
<div><a href="#example-344">Example 344</a></div>
|
<div><a href="#example-344">Example 344</a></div>
|
||||||
<div class="gl-relative markdown-code-block js-markdown-code">
|
<div class="gl-relative markdown-code-block js-markdown-code">
|
||||||
<pre data-sourcepos="7974:1-7977:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">` `</span>
|
<pre data-sourcepos="7974:1-7977:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">` `</span>
|
||||||
<span id="LC2" class="line" lang="plaintext">` `</span></code></pre>
|
<span id="LC2" class="line" lang="plaintext">` `</span></code></pre>
|
||||||
<copy-code></copy-code>
|
<copy-code></copy-code>
|
||||||
</div>
|
</div>
|
||||||
<div class="gl-relative markdown-code-block js-markdown-code">
|
<div class="gl-relative markdown-code-block js-markdown-code">
|
||||||
<pre data-sourcepos="7979:1-7982:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"><p><code> </code></span>
|
<pre data-sourcepos="7979:1-7982:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"><p><code> </code></span>
|
||||||
<span id="LC2" class="line" lang="plaintext"><code> </code></p></span></code></pre>
|
<span id="LC2" class="line" lang="plaintext"><code> </code></p></span></code></pre>
|
||||||
<copy-code></copy-code>
|
<copy-code></copy-code>
|
||||||
</div>
|
</div>
|
||||||
|
@ -7832,11 +7832,11 @@ not part of a [left-flanking delimiter run]:</p>
|
||||||
<div>
|
<div>
|
||||||
<div><a href="#example-363">Example 363</a></div>
|
<div><a href="#example-363">Example 363</a></div>
|
||||||
<div class="gl-relative markdown-code-block js-markdown-code">
|
<div class="gl-relative markdown-code-block js-markdown-code">
|
||||||
<pre data-sourcepos="8485:1-8487:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">* a *</span></code></pre>
|
<pre data-sourcepos="8485:1-8487:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">* a *</span></code></pre>
|
||||||
<copy-code></copy-code>
|
<copy-code></copy-code>
|
||||||
</div>
|
</div>
|
||||||
<div class="gl-relative markdown-code-block js-markdown-code">
|
<div class="gl-relative markdown-code-block js-markdown-code">
|
||||||
<pre data-sourcepos="8489:1-8491:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"><p>* a *</p></span></code></pre>
|
<pre data-sourcepos="8489:1-8491:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"><p>* a *</p></span></code></pre>
|
||||||
<copy-code></copy-code>
|
<copy-code></copy-code>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -9790,7 +9790,7 @@ Other [Unicode whitespace] like non-breaking space doesn't work.</p>
|
||||||
<div>
|
<div>
|
||||||
<div><a href="#example-515">Example 515</a></div>
|
<div><a href="#example-515">Example 515</a></div>
|
||||||
<div class="gl-relative markdown-code-block js-markdown-code">
|
<div class="gl-relative markdown-code-block js-markdown-code">
|
||||||
<pre data-sourcepos="10823:1-10825:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[link](/url "title")</span></code></pre>
|
<pre data-sourcepos="10823:1-10825:32" lang="plaintext" class="code highlight js-syntax-highlight language-plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[link](/url "title")</span></code></pre>
|
||||||
<copy-code></copy-code>
|
<copy-code></copy-code>
|
||||||
</div>
|
</div>
|
||||||
<div class="gl-relative markdown-code-block js-markdown-code">
|
<div class="gl-relative markdown-code-block js-markdown-code">
|
||||||
|
|
|
@ -203,6 +203,10 @@ module API
|
||||||
render_api_error!("Target project id:#{params[:from_project_id]} is not a fork of project id:#{params[:id]}", 400)
|
render_api_error!("Target project id:#{params[:from_project_id]} is not a fork of project id:#{params[:id]}", 400)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
unless can?(current_user, :read_code, target_project)
|
||||||
|
forbidden!("You don't have access to this fork's parent project")
|
||||||
|
end
|
||||||
|
|
||||||
cache_key = compare_cache_key(current_user, user_project, target_project, declared_params)
|
cache_key = compare_cache_key(current_user, user_project, target_project, declared_params)
|
||||||
|
|
||||||
cache_action(cache_key, expires_in: 1.minute) do
|
cache_action(cache_key, expires_in: 1.minute) do
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
require 'uri'
|
||||||
|
|
||||||
module Banzai
|
module Banzai
|
||||||
module Filter
|
module Filter
|
||||||
|
@ -15,7 +16,8 @@ module Banzai
|
||||||
doc.document.create_element(
|
doc.document.create_element(
|
||||||
'div',
|
'div',
|
||||||
class: 'js-render-observability',
|
class: 'js-render-observability',
|
||||||
'data-frame-url': url
|
'data-frame-url': url,
|
||||||
|
'data-observability-url': Gitlab::Observability.observability_url
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -28,8 +30,15 @@ module Banzai
|
||||||
# obtained from the target link
|
# obtained from the target link
|
||||||
def element_to_embed(node)
|
def element_to_embed(node)
|
||||||
url = node['href']
|
url = node['href']
|
||||||
|
uri = URI.parse(url)
|
||||||
|
observability_uri = URI.parse(Gitlab::Observability.observability_url)
|
||||||
|
|
||||||
create_element(url)
|
if uri.scheme == observability_uri.scheme &&
|
||||||
|
uri.port == observability_uri.port &&
|
||||||
|
uri.host.casecmp?(observability_uri.host) &&
|
||||||
|
uri.path.downcase.exclude?("auth/start")
|
||||||
|
create_element(url)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
|
@ -5,7 +5,8 @@
|
||||||
# Can be extended for different types of repository object, e.g. Project or Snippet
|
# Can be extended for different types of repository object, e.g. Project or Snippet
|
||||||
module ExtractsRef
|
module ExtractsRef
|
||||||
InvalidPathError = Class.new(StandardError)
|
InvalidPathError = Class.new(StandardError)
|
||||||
|
BRANCH_REF_TYPE = 'heads'
|
||||||
|
TAG_REF_TYPE = 'tags'
|
||||||
# Given a string containing both a Git tree-ish, such as a branch or tag, and
|
# Given a string containing both a Git tree-ish, such as a branch or tag, and
|
||||||
# a filesystem path joined by forward slashes, attempts to separate the two.
|
# a filesystem path joined by forward slashes, attempts to separate the two.
|
||||||
#
|
#
|
||||||
|
@ -91,7 +92,7 @@ module ExtractsRef
|
||||||
def ref_type
|
def ref_type
|
||||||
return unless params[:ref_type].present?
|
return unless params[:ref_type].present?
|
||||||
|
|
||||||
params[:ref_type] == 'tags' ? 'tags' : 'heads'
|
params[:ref_type] == TAG_REF_TYPE ? TAG_REF_TYPE : BRANCH_REF_TYPE
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
@ -154,4 +155,13 @@ module ExtractsRef
|
||||||
def repository_container
|
def repository_container
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def ambiguous_ref?(project, ref)
|
||||||
|
return true if project.repository.ambiguous_ref?(ref)
|
||||||
|
|
||||||
|
return false unless ref&.starts_with?('refs/')
|
||||||
|
|
||||||
|
unprefixed_ref = ref.sub(%r{^refs/(heads|tags)/}, '')
|
||||||
|
project.repository.commit(unprefixed_ref).present?
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module Gitlab
|
||||||
|
module BackgroundMigration
|
||||||
|
# Nullifies last_error value from project_mirror_data table as they
|
||||||
|
# potentially included sensitive data.
|
||||||
|
# https://gitlab.com/gitlab-org/security/gitlab/-/merge_requests/3041
|
||||||
|
class NullifyLastErrorFromProjectMirrorData < BatchedMigrationJob
|
||||||
|
feature_category :source_code_management
|
||||||
|
operation_name :update_all
|
||||||
|
|
||||||
|
def perform
|
||||||
|
each_sub_batch { |rel| rel.update_all(last_error: nil) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -9,6 +9,12 @@ module Gitlab
|
||||||
# https://idiosyncratic-ruby.com/41-proper-unicoding.html
|
# https://idiosyncratic-ruby.com/41-proper-unicoding.html
|
||||||
BIDI_REGEXP = /\p{Bidi Control}/.freeze
|
BIDI_REGEXP = /\p{Bidi Control}/.freeze
|
||||||
|
|
||||||
|
# Regular expression for identifying space characters
|
||||||
|
#
|
||||||
|
# In web browsers space characters can be confused with simple
|
||||||
|
# spaces which may be misleading
|
||||||
|
SPACE_REGEXP = /\p{Space_Separator}/.freeze
|
||||||
|
|
||||||
class << self
|
class << self
|
||||||
# Warning message used to highlight bidi characters in the GUI
|
# Warning message used to highlight bidi characters in the GUI
|
||||||
def bidi_warning
|
def bidi_warning
|
||||||
|
|
|
@ -2,15 +2,37 @@
|
||||||
|
|
||||||
module Gitlab
|
module Gitlab
|
||||||
class UrlSanitizer
|
class UrlSanitizer
|
||||||
|
include Gitlab::Utils::StrongMemoize
|
||||||
|
|
||||||
ALLOWED_SCHEMES = %w[http https ssh git].freeze
|
ALLOWED_SCHEMES = %w[http https ssh git].freeze
|
||||||
ALLOWED_WEB_SCHEMES = %w[http https].freeze
|
ALLOWED_WEB_SCHEMES = %w[http https].freeze
|
||||||
|
SCHEMIFIED_SCHEME = 'glschemelessuri'
|
||||||
|
SCHEMIFY_PLACEHOLDER = "#{SCHEMIFIED_SCHEME}://"
|
||||||
|
# URI::DEFAULT_PARSER.make_regexp will only match URLs with schemes or
|
||||||
|
# relative URLs. This section will match schemeless URIs with userinfo
|
||||||
|
# e.g. user:pass@gitlab.com but will not match scp-style URIs e.g.
|
||||||
|
# user@server:path/to/file)
|
||||||
|
#
|
||||||
|
# The userinfo part is very loose compared to URI's implementation so we
|
||||||
|
# also match non-escaped userinfo e.g foo:b?r@gitlab.com which should be
|
||||||
|
# encoded as foo:b%3Fr@gitlab.com
|
||||||
|
URI_REGEXP = %r{
|
||||||
|
(?:
|
||||||
|
#{URI::DEFAULT_PARSER.make_regexp(ALLOWED_SCHEMES)}
|
||||||
|
|
|
||||||
|
(?:(?:(?!@)[%#{URI::REGEXP::PATTERN::UNRESERVED}#{URI::REGEXP::PATTERN::RESERVED}])+(?:@))
|
||||||
|
(?# negative lookahead ensures this isn't an SCP-style URL: [host]:[rel_path|abs_path] server:path/to/file)
|
||||||
|
(?!#{URI::REGEXP::PATTERN::HOST}:(?:#{URI::REGEXP::PATTERN::REL_PATH}|#{URI::REGEXP::PATTERN::ABS_PATH}))
|
||||||
|
#{URI::REGEXP::PATTERN::HOSTPORT}
|
||||||
|
)
|
||||||
|
}x.freeze
|
||||||
|
|
||||||
def self.sanitize(content)
|
def self.sanitize(content)
|
||||||
regexp = URI::DEFAULT_PARSER.make_regexp(ALLOWED_SCHEMES)
|
content.gsub(URI_REGEXP) do |url|
|
||||||
|
new(url).masked_url
|
||||||
content.gsub(regexp) { |url| new(url).masked_url }
|
rescue Addressable::URI::InvalidURIError
|
||||||
rescue Addressable::URI::InvalidURIError
|
''
|
||||||
content.gsub(regexp, '')
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.valid?(url, allowed_schemes: ALLOWED_SCHEMES)
|
def self.valid?(url, allowed_schemes: ALLOWED_SCHEMES)
|
||||||
|
@ -37,17 +59,6 @@ module Gitlab
|
||||||
@url = parse_url(url)
|
@url = parse_url(url)
|
||||||
end
|
end
|
||||||
|
|
||||||
def sanitized_url
|
|
||||||
@sanitized_url ||= safe_url.to_s
|
|
||||||
end
|
|
||||||
|
|
||||||
def masked_url
|
|
||||||
url = @url.dup
|
|
||||||
url.password = "*****" if url.password.present?
|
|
||||||
url.user = "*****" if url.user.present?
|
|
||||||
url.to_s
|
|
||||||
end
|
|
||||||
|
|
||||||
def credentials
|
def credentials
|
||||||
@credentials ||= { user: @url.user.presence, password: @url.password.presence }
|
@credentials ||= { user: @url.user.presence, password: @url.password.presence }
|
||||||
end
|
end
|
||||||
|
@ -56,15 +67,37 @@ module Gitlab
|
||||||
credentials[:user]
|
credentials[:user]
|
||||||
end
|
end
|
||||||
|
|
||||||
def full_url
|
def sanitized_url
|
||||||
@full_url ||= generate_full_url.to_s
|
safe_url = @url.dup
|
||||||
|
safe_url.password = nil
|
||||||
|
safe_url.user = nil
|
||||||
|
reverse_schemify(safe_url.to_s)
|
||||||
end
|
end
|
||||||
|
strong_memoize_attr :sanitized_url
|
||||||
|
|
||||||
|
def masked_url
|
||||||
|
url = @url.dup
|
||||||
|
url.password = "*****" if url.password.present?
|
||||||
|
url.user = "*****" if url.user.present?
|
||||||
|
reverse_schemify(url.to_s)
|
||||||
|
end
|
||||||
|
strong_memoize_attr :masked_url
|
||||||
|
|
||||||
|
def full_url
|
||||||
|
return reverse_schemify(@url.to_s) unless valid_credentials?
|
||||||
|
|
||||||
|
url = @url.dup
|
||||||
|
url.password = encode_percent(credentials[:password]) if credentials[:password].present?
|
||||||
|
url.user = encode_percent(credentials[:user]) if credentials[:user].present?
|
||||||
|
reverse_schemify(url.to_s)
|
||||||
|
end
|
||||||
|
strong_memoize_attr :full_url
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def parse_url(url)
|
def parse_url(url)
|
||||||
url = url.to_s.strip
|
url = schemify(url.to_s.strip)
|
||||||
match = url.match(%r{\A(?:git|ssh|http(?:s?))\://(?:(.+)(?:@))?(.+)})
|
match = url.match(%r{\A(?:(?:#{SCHEMIFIED_SCHEME}|git|ssh|http(?:s?)):)?//(?:(.+)(?:@))?(.+)}o)
|
||||||
raw_credentials = match[1] if match
|
raw_credentials = match[1] if match
|
||||||
|
|
||||||
if raw_credentials.present?
|
if raw_credentials.present?
|
||||||
|
@ -83,24 +116,19 @@ module Gitlab
|
||||||
url
|
url
|
||||||
end
|
end
|
||||||
|
|
||||||
def generate_full_url
|
def schemify(url)
|
||||||
return @url unless valid_credentials?
|
# Prepend the placeholder scheme unless the URL has a scheme or is relative
|
||||||
|
url.prepend(SCHEMIFY_PLACEHOLDER) unless url.starts_with?(%r{(?:#{URI::REGEXP::PATTERN::SCHEME}:)?//}o)
|
||||||
@url.dup.tap do |generated|
|
url
|
||||||
generated.password = encode_percent(credentials[:password]) if credentials[:password].present?
|
|
||||||
generated.user = encode_percent(credentials[:user]) if credentials[:user].present?
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def safe_url
|
def reverse_schemify(url)
|
||||||
safe_url = @url.dup
|
url.slice!(SCHEMIFY_PLACEHOLDER) if url.starts_with?(SCHEMIFY_PLACEHOLDER)
|
||||||
safe_url.password = nil
|
url
|
||||||
safe_url.user = nil
|
|
||||||
safe_url
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def valid_credentials?
|
def valid_credentials?
|
||||||
credentials && credentials.is_a?(Hash) && credentials.any?
|
credentials.is_a?(Hash) && credentials.values.any?
|
||||||
end
|
end
|
||||||
|
|
||||||
def encode_percent(string)
|
def encode_percent(string)
|
||||||
|
|
|
@ -25,7 +25,10 @@ module Rouge
|
||||||
yield %(<span id="LC#{@line_number}" class="line" lang="#{@tag}">)
|
yield %(<span id="LC#{@line_number}" class="line" lang="#{@tag}">)
|
||||||
|
|
||||||
line.each do |token, value|
|
line.each do |token, value|
|
||||||
yield highlight_unicode_control_characters(span(token, value.chomp! || value))
|
value = value.chomp! || value
|
||||||
|
value = replace_space_characters(value)
|
||||||
|
|
||||||
|
yield highlight_unicode_control_characters(span(token, value))
|
||||||
end
|
end
|
||||||
|
|
||||||
yield ellipsis if @ellipsis_indexes.include?(@line_number - 1) && @ellipsis_svg.present?
|
yield ellipsis if @ellipsis_indexes.include?(@line_number - 1) && @ellipsis_svg.present?
|
||||||
|
@ -42,6 +45,10 @@ module Rouge
|
||||||
%(<span class="gl-px-2 gl-rounded-base gl-mx-2 gl-bg-gray-100 gl-cursor-help has-tooltip" title="Content has been trimmed">#{@ellipsis_svg}</span>)
|
%(<span class="gl-px-2 gl-rounded-base gl-mx-2 gl-bg-gray-100 gl-cursor-help has-tooltip" title="Content has been trimmed">#{@ellipsis_svg}</span>)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def replace_space_characters(text)
|
||||||
|
text.gsub(Gitlab::Unicode::SPACE_REGEXP, ' ')
|
||||||
|
end
|
||||||
|
|
||||||
def highlight_unicode_control_characters(text)
|
def highlight_unicode_control_characters(text)
|
||||||
text.gsub(Gitlab::Unicode::BIDI_REGEXP) do |char|
|
text.gsub(Gitlab::Unicode::BIDI_REGEXP) do |char|
|
||||||
%(<span class="unicode-bidi has-tooltip" data-toggle="tooltip" title="#{Gitlab::Unicode.bidi_warning}">#{char}</span>)
|
%(<span class="unicode-bidi has-tooltip" data-toggle="tooltip" title="#{Gitlab::Unicode.bidi_warning}">#{char}</span>)
|
||||||
|
|
|
@ -42005,6 +42005,9 @@ msgstr ""
|
||||||
msgid "The default branch for this project has been changed. Please update your bookmarks."
|
msgid "The default branch for this project has been changed. Please update your bookmarks."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "The default branch of this project clashes with another ref"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "The dependency list details information about the components used within your project."
|
msgid "The dependency list details information about the components used within your project."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|
|
@ -59,6 +59,7 @@ RSpec.describe Admin::HooksController do
|
||||||
enable_ssl_verification: false,
|
enable_ssl_verification: false,
|
||||||
url_variables: [
|
url_variables: [
|
||||||
{ key: 'token', value: 'some secret value' },
|
{ key: 'token', value: 'some secret value' },
|
||||||
|
{ key: 'baz', value: 'qux' },
|
||||||
{ key: 'foo', value: nil }
|
{ key: 'foo', value: nil }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -71,7 +72,7 @@ RSpec.describe Admin::HooksController do
|
||||||
expect(flash[:notice]).to include('was updated')
|
expect(flash[:notice]).to include('was updated')
|
||||||
expect(hook).to have_attributes(hook_params.except(:url_variables))
|
expect(hook).to have_attributes(hook_params.except(:url_variables))
|
||||||
expect(hook).to have_attributes(
|
expect(hook).to have_attributes(
|
||||||
url_variables: { 'token' => 'some secret value', 'baz' => 'woo' }
|
url_variables: { 'token' => 'some secret value', 'baz' => 'qux' }
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
require 'spec_helper'
|
require 'spec_helper'
|
||||||
|
|
||||||
RSpec.describe ConfirmEmailWarning do
|
RSpec.describe ConfirmEmailWarning, feature_category: :system_access do
|
||||||
before do
|
before do
|
||||||
stub_feature_flags(soft_email_confirmation: true)
|
stub_feature_flags(soft_email_confirmation: true)
|
||||||
end
|
end
|
||||||
|
@ -82,6 +82,38 @@ RSpec.describe ConfirmEmailWarning do
|
||||||
it { is_expected.to set_confirm_warning_for(user.email) }
|
it { is_expected.to set_confirm_warning_for(user.email) }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'when user is being impersonated' do
|
||||||
|
let(:impersonator) { create(:admin) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
allow(controller).to receive(:session).and_return({ impersonator_id: impersonator.id })
|
||||||
|
|
||||||
|
get :index
|
||||||
|
end
|
||||||
|
|
||||||
|
it { is_expected.to set_confirm_warning_for(user.email) }
|
||||||
|
|
||||||
|
context 'when impersonated user email has html in their email' do
|
||||||
|
let(:user) { create(:user, confirmed_at: nil, unconfirmed_email: "malicious@test.com<form><input/title='<script>alert(document.domain)</script>'>") }
|
||||||
|
|
||||||
|
it { is_expected.to set_confirm_warning_for("malicious@test.com<form><input/title='<script>alert(document.domain)</script>'>") }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when user is not being impersonated' do
|
||||||
|
before do
|
||||||
|
get :index
|
||||||
|
end
|
||||||
|
|
||||||
|
it { is_expected.to set_confirm_warning_for(user.email) }
|
||||||
|
|
||||||
|
context 'when user email has html in their email' do
|
||||||
|
let(:user) { create(:user, confirmed_at: nil, unconfirmed_email: "malicious@test.com<form><input/title='<script>alert(document.domain)</script>'>") }
|
||||||
|
|
||||||
|
it { is_expected.to set_confirm_warning_for("malicious@test.com<form><input/title='<script>alert(document.domain)</script>'>") }
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,15 +2,16 @@
|
||||||
|
|
||||||
require 'spec_helper'
|
require 'spec_helper'
|
||||||
|
|
||||||
RSpec.describe Projects::BlobController do
|
RSpec.describe Projects::BlobController, feature_category: :source_code_management do
|
||||||
include ProjectForksHelper
|
include ProjectForksHelper
|
||||||
|
|
||||||
let(:project) { create(:project, :public, :repository, previous_default_branch: previous_default_branch) }
|
let(:project) { create(:project, :public, :repository, previous_default_branch: previous_default_branch) }
|
||||||
let(:previous_default_branch) { nil }
|
let(:previous_default_branch) { nil }
|
||||||
|
|
||||||
describe "GET show" do
|
describe "GET show" do
|
||||||
def request
|
let(:params) { { namespace_id: project.namespace, project_id: project, id: id } }
|
||||||
get(:show, params: { namespace_id: project.namespace, project_id: project, id: id })
|
let(:request) do
|
||||||
|
get(:show, params: params)
|
||||||
end
|
end
|
||||||
|
|
||||||
render_views
|
render_views
|
||||||
|
@ -18,10 +19,34 @@ RSpec.describe Projects::BlobController do
|
||||||
context 'with file path' do
|
context 'with file path' do
|
||||||
before do
|
before do
|
||||||
expect(::Gitlab::GitalyClient).to receive(:allow_ref_name_caching).and_call_original
|
expect(::Gitlab::GitalyClient).to receive(:allow_ref_name_caching).and_call_original
|
||||||
|
project.repository.add_tag(project.creator, 'ambiguous_ref', RepoHelpers.sample_commit.id)
|
||||||
|
project.repository.add_branch(project.creator, 'ambiguous_ref', RepoHelpers.another_sample_commit.id)
|
||||||
request
|
request
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'when the ref is ambiguous' do
|
||||||
|
let(:ref) { 'ambiguous_ref' }
|
||||||
|
let(:path) { 'README.md' }
|
||||||
|
let(:id) { "#{ref}/#{path}" }
|
||||||
|
let(:params) { { namespace_id: project.namespace, project_id: project, id: id, ref_type: ref_type } }
|
||||||
|
|
||||||
|
context 'and explicitly requesting a branch' do
|
||||||
|
let(:ref_type) { 'heads' }
|
||||||
|
|
||||||
|
it 'redirects to blob#show with sha for the branch' do
|
||||||
|
expect(response).to redirect_to(project_blob_path(project, "#{RepoHelpers.another_sample_commit.id}/#{path}"))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'and explicitly requesting a tag' do
|
||||||
|
let(:ref_type) { 'tags' }
|
||||||
|
|
||||||
|
it 'responds with success' do
|
||||||
|
expect(response).to be_ok
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
context "valid branch, valid file" do
|
context "valid branch, valid file" do
|
||||||
let(:id) { 'master/README.md' }
|
let(:id) { 'master/README.md' }
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ RSpec.describe Projects::ClustersController do
|
||||||
include GoogleApi::CloudPlatformHelpers
|
include GoogleApi::CloudPlatformHelpers
|
||||||
include KubernetesHelpers
|
include KubernetesHelpers
|
||||||
|
|
||||||
let_it_be(:project) { create(:project) }
|
let_it_be_with_reload(:project) { create(:project) }
|
||||||
|
|
||||||
let(:user) { create(:user) }
|
let(:user) { create(:user) }
|
||||||
|
|
||||||
|
@ -140,6 +140,27 @@ RSpec.describe Projects::ClustersController do
|
||||||
expect(response).to redirect_to(new_user_session_path)
|
expect(response).to redirect_to(new_user_session_path)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'with a public project' do
|
||||||
|
before do
|
||||||
|
project.update!(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
|
||||||
|
project.project_feature.update!(metrics_dashboard_access_level: ProjectFeature::ENABLED)
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with guest user' do
|
||||||
|
let(:prometheus_body) { nil }
|
||||||
|
|
||||||
|
before do
|
||||||
|
project.add_guest(user)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns 404' do
|
||||||
|
get :prometheus_proxy, params: prometheus_proxy_params
|
||||||
|
|
||||||
|
expect(response).to have_gitlab_http_status(:not_found)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ require 'spec_helper'
|
||||||
|
|
||||||
RSpec.describe Projects::Environments::PrometheusApiController do
|
RSpec.describe Projects::Environments::PrometheusApiController do
|
||||||
let_it_be(:user) { create(:user) }
|
let_it_be(:user) { create(:user) }
|
||||||
let_it_be(:project) { create(:project) }
|
let_it_be_with_reload(:project) { create(:project) }
|
||||||
let_it_be(:proxyable) { create(:environment, project: project) }
|
let_it_be(:proxyable) { create(:environment, project: project) }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
|
@ -70,6 +70,27 @@ RSpec.describe Projects::Environments::PrometheusApiController do
|
||||||
expect(response).to redirect_to(new_user_session_path)
|
expect(response).to redirect_to(new_user_session_path)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'with a public project' do
|
||||||
|
before do
|
||||||
|
project.update!(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
|
||||||
|
project.project_feature.update!(metrics_dashboard_access_level: ProjectFeature::ENABLED)
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with guest user' do
|
||||||
|
let(:prometheus_body) { nil }
|
||||||
|
|
||||||
|
before do
|
||||||
|
project.add_guest(user)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns 404' do
|
||||||
|
get :prometheus_proxy, params: prometheus_proxy_params
|
||||||
|
|
||||||
|
expect(response).to have_gitlab_http_status(:not_found)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -31,7 +31,7 @@ RSpec.describe Projects::RefsController, feature_category: :source_code_manageme
|
||||||
'tree' | nil | lazy { project_tree_path(project, id) }
|
'tree' | nil | lazy { project_tree_path(project, id) }
|
||||||
'tree' | 'heads' | lazy { project_tree_path(project, id) }
|
'tree' | 'heads' | lazy { project_tree_path(project, id) }
|
||||||
'blob' | nil | lazy { project_blob_path(project, id) }
|
'blob' | nil | lazy { project_blob_path(project, id) }
|
||||||
'blob' | 'heads' | lazy { project_blob_path(project, id) }
|
'blob' | 'heads' | lazy { project_blob_path(project, id, ref_type: 'heads') }
|
||||||
'graph' | nil | lazy { project_network_path(project, id) }
|
'graph' | nil | lazy { project_network_path(project, id) }
|
||||||
'graph' | 'heads' | lazy { project_network_path(project, id) }
|
'graph' | 'heads' | lazy { project_network_path(project, id) }
|
||||||
'graphs' | nil | lazy { project_graph_path(project, id) }
|
'graphs' | nil | lazy { project_graph_path(project, id) }
|
||||||
|
@ -60,7 +60,7 @@ RSpec.describe Projects::RefsController, feature_category: :source_code_manageme
|
||||||
'tree' | nil | lazy { project_tree_path(project, id) }
|
'tree' | nil | lazy { project_tree_path(project, id) }
|
||||||
'tree' | 'heads' | lazy { project_tree_path(project, id) }
|
'tree' | 'heads' | lazy { project_tree_path(project, id) }
|
||||||
'blob' | nil | lazy { project_blob_path(project, id) }
|
'blob' | nil | lazy { project_blob_path(project, id) }
|
||||||
'blob' | 'heads' | lazy { project_blob_path(project, id) }
|
'blob' | 'heads' | lazy { project_blob_path(project, id, ref_type: 'heads') }
|
||||||
'graph' | nil | lazy { project_network_path(project, id) }
|
'graph' | nil | lazy { project_network_path(project, id) }
|
||||||
'graph' | 'heads' | lazy { project_network_path(project, id, ref_type: 'heads') }
|
'graph' | 'heads' | lazy { project_network_path(project, id, ref_type: 'heads') }
|
||||||
'graphs' | nil | lazy { project_graph_path(project, id) }
|
'graphs' | nil | lazy { project_graph_path(project, id) }
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
require 'spec_helper'
|
require 'spec_helper'
|
||||||
|
|
||||||
RSpec.describe Projects::TreeController do
|
RSpec.describe Projects::TreeController, feature_category: :source_code_management do
|
||||||
let(:project) { create(:project, :repository, previous_default_branch: previous_default_branch) }
|
let(:project) { create(:project, :repository, previous_default_branch: previous_default_branch) }
|
||||||
let(:previous_default_branch) { nil }
|
let(:previous_default_branch) { nil }
|
||||||
let(:user) { create(:user) }
|
let(:user) { create(:user) }
|
||||||
|
@ -15,18 +15,41 @@ RSpec.describe Projects::TreeController do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "GET show" do
|
describe "GET show" do
|
||||||
|
let(:params) do
|
||||||
|
{
|
||||||
|
namespace_id: project.namespace.to_param, project_id: project, id: id
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
# Make sure any errors accessing the tree in our views bubble up to this spec
|
# Make sure any errors accessing the tree in our views bubble up to this spec
|
||||||
render_views
|
render_views
|
||||||
|
|
||||||
before do
|
before do
|
||||||
expect(::Gitlab::GitalyClient).to receive(:allow_ref_name_caching).and_call_original
|
expect(::Gitlab::GitalyClient).to receive(:allow_ref_name_caching).and_call_original
|
||||||
|
project.repository.add_tag(project.creator, 'ambiguous_ref', RepoHelpers.sample_commit.id)
|
||||||
|
project.repository.add_branch(project.creator, 'ambiguous_ref', RepoHelpers.another_sample_commit.id)
|
||||||
|
get :show, params: params
|
||||||
|
end
|
||||||
|
|
||||||
get(:show,
|
context 'when the ref is ambiguous' do
|
||||||
params: {
|
let(:id) { 'ambiguous_ref' }
|
||||||
namespace_id: project.namespace.to_param,
|
let(:params) { { namespace_id: project.namespace, project_id: project, id: id, ref_type: ref_type } }
|
||||||
project_id: project,
|
|
||||||
id: id
|
context 'and explicitly requesting a branch' do
|
||||||
})
|
let(:ref_type) { 'heads' }
|
||||||
|
|
||||||
|
it 'redirects to blob#show with sha for the branch' do
|
||||||
|
expect(response).to redirect_to(project_tree_path(project, RepoHelpers.another_sample_commit.id))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'and explicitly requesting a tag' do
|
||||||
|
let(:ref_type) { 'tags' }
|
||||||
|
|
||||||
|
it 'responds with success' do
|
||||||
|
expect(response).to be_ok
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context "valid branch, no path" do
|
context "valid branch, no path" do
|
||||||
|
|
|
@ -163,6 +163,69 @@ RSpec.describe ProjectsController do
|
||||||
expect(assigns(:notification_setting).level).to eq("watch")
|
expect(assigns(:notification_setting).level).to eq("watch")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'when there is a tag with the same name as the default branch' do
|
||||||
|
let_it_be(:tagged_project) { create(:project, :public, :custom_repo, files: ['somefile']) }
|
||||||
|
let(:tree_with_default_branch) do
|
||||||
|
branch = tagged_project.repository.find_branch(tagged_project.default_branch)
|
||||||
|
project_tree_path(tagged_project, branch.target)
|
||||||
|
end
|
||||||
|
|
||||||
|
before do
|
||||||
|
tagged_project.repository.create_file(
|
||||||
|
tagged_project.creator,
|
||||||
|
'file_for_tag',
|
||||||
|
'content for file',
|
||||||
|
message: "Automatically created file",
|
||||||
|
branch_name: 'branch-to-tag'
|
||||||
|
)
|
||||||
|
|
||||||
|
tagged_project.repository.add_tag(
|
||||||
|
tagged_project.creator,
|
||||||
|
tagged_project.default_branch, # tag name
|
||||||
|
'branch-to-tag' # target
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'redirects to tree view for the default branch' do
|
||||||
|
get :show, params: { namespace_id: tagged_project.namespace, id: tagged_project }
|
||||||
|
expect(response).to redirect_to(tree_with_default_branch)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when the default branch name can resolve to another ref' do
|
||||||
|
let!(:project_with_default_branch) do
|
||||||
|
create(:project, :public, :custom_repo, files: ['somefile']).tap do |p|
|
||||||
|
p.repository.create_branch("refs/heads/refs/heads/#{other_ref}", 'master')
|
||||||
|
p.change_head("refs/heads/#{other_ref}")
|
||||||
|
end.reload
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:other_ref) { 'branch-name' }
|
||||||
|
|
||||||
|
context 'but there is no other ref' do
|
||||||
|
it 'responds with ok' do
|
||||||
|
get :show, params: { namespace_id: project_with_default_branch.namespace, id: project_with_default_branch }
|
||||||
|
expect(response).to be_ok
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'and that other ref exists' do
|
||||||
|
let(:tree_with_default_branch) do
|
||||||
|
branch = project_with_default_branch.repository.find_branch(project_with_default_branch.default_branch)
|
||||||
|
project_tree_path(project_with_default_branch, branch.target)
|
||||||
|
end
|
||||||
|
|
||||||
|
before do
|
||||||
|
project_with_default_branch.repository.create_branch(other_ref, 'master')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'redirects to tree view for the default branch' do
|
||||||
|
get :show, params: { namespace_id: project_with_default_branch.namespace, id: project_with_default_branch }
|
||||||
|
expect(response).to redirect_to(tree_with_default_branch)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "when project repository is disabled" do
|
describe "when project repository is disabled" do
|
||||||
|
|
|
@ -7,7 +7,7 @@ FactoryBot.define do
|
||||||
project
|
project
|
||||||
|
|
||||||
trait :url_variables do
|
trait :url_variables do
|
||||||
url_variables { { 'abc' => 'supers3cret' } }
|
url_variables { { 'abc' => 'supers3cret', 'def' => 'foobar' } }
|
||||||
end
|
end
|
||||||
|
|
||||||
trait :token do
|
trait :token do
|
||||||
|
|
|
@ -271,6 +271,36 @@ RSpec.describe 'Admin::Users::User', feature_category: :user_management do
|
||||||
icon = first('[data-testid="incognito-icon"]')
|
icon = first('[data-testid="incognito-icon"]')
|
||||||
expect(icon).not_to be nil
|
expect(icon).not_to be nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'when viewing the confirm email warning', :js do
|
||||||
|
let_it_be(:another_user) { create(:user, :unconfirmed) }
|
||||||
|
|
||||||
|
let(:warning_alert) { page.find(:css, '[data-testid="alert-warning"]') }
|
||||||
|
let(:expected_styling) { { 'pointer-events' => 'none', 'cursor' => 'default' } }
|
||||||
|
|
||||||
|
context 'with an email that does not contain HTML' do
|
||||||
|
before do
|
||||||
|
subject
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'displays the warning alert including the email' do
|
||||||
|
expect(warning_alert.text).to include("Please check your email (#{another_user.email}) to verify")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with an email that contains HTML' do
|
||||||
|
let(:malicious_email) { "malicious@test.com<form><input/title='<script>alert(document.domain)</script>'>" }
|
||||||
|
let(:another_user) { create(:user, confirmed_at: nil, unconfirmed_email: malicious_email) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
subject
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'displays the impersonation alert, excludes email, and disables links' do
|
||||||
|
expect(warning_alert.text).to include("check your email (#{another_user.unconfirmed_email}) to verify")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'ending impersonation' do
|
context 'ending impersonation' do
|
||||||
|
|
|
@ -6,6 +6,7 @@ RSpec.describe Environments::EnvironmentNamesFinder do
|
||||||
describe '#execute' do
|
describe '#execute' do
|
||||||
let!(:group) { create(:group) }
|
let!(:group) { create(:group) }
|
||||||
let!(:public_project) { create(:project, :public, namespace: group) }
|
let!(:public_project) { create(:project, :public, namespace: group) }
|
||||||
|
let_it_be_with_reload(:public_project_with_private_environments) { create(:project, :public) }
|
||||||
let!(:private_project) { create(:project, :private, namespace: group) }
|
let!(:private_project) { create(:project, :private, namespace: group) }
|
||||||
let!(:user) { create(:user) }
|
let!(:user) { create(:user) }
|
||||||
|
|
||||||
|
@ -14,6 +15,11 @@ RSpec.describe Environments::EnvironmentNamesFinder do
|
||||||
create(:environment, name: 'gprd', project: public_project)
|
create(:environment, name: 'gprd', project: public_project)
|
||||||
create(:environment, name: 'gprd', project: private_project)
|
create(:environment, name: 'gprd', project: private_project)
|
||||||
create(:environment, name: 'gcny', project: private_project)
|
create(:environment, name: 'gcny', project: private_project)
|
||||||
|
create(:environment, name: 'gprivprd', project: public_project_with_private_environments)
|
||||||
|
create(:environment, name: 'gprivstg', project: public_project_with_private_environments)
|
||||||
|
|
||||||
|
public_project_with_private_environments.update!(namespace: group)
|
||||||
|
public_project_with_private_environments.project_feature.update!(environments_access_level: Featurable::PRIVATE)
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'using a group' do
|
context 'using a group' do
|
||||||
|
@ -23,7 +29,7 @@ RSpec.describe Environments::EnvironmentNamesFinder do
|
||||||
|
|
||||||
names = described_class.new(group, user).execute
|
names = described_class.new(group, user).execute
|
||||||
|
|
||||||
expect(names).to eq(%w[gcny gprd gstg])
|
expect(names).to eq(%w[gcny gprd gprivprd gprivstg gstg])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -33,7 +39,7 @@ RSpec.describe Environments::EnvironmentNamesFinder do
|
||||||
|
|
||||||
names = described_class.new(group, user).execute
|
names = described_class.new(group, user).execute
|
||||||
|
|
||||||
expect(names).to eq(%w[gcny gprd gstg])
|
expect(names).to eq(%w[gcny gprd gprivprd gprivstg gstg])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -57,8 +63,18 @@ RSpec.describe Environments::EnvironmentNamesFinder do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'with a public project reporter which has private environments' do
|
||||||
|
it 'returns environment names for public projects' do
|
||||||
|
public_project_with_private_environments.add_reporter(user)
|
||||||
|
|
||||||
|
names = described_class.new(group, user).execute
|
||||||
|
|
||||||
|
expect(names).to eq(%w[gprd gprivprd gprivstg gstg])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
context 'with a group guest' do
|
context 'with a group guest' do
|
||||||
it 'returns environment names for all public projects' do
|
it 'returns environment names for public projects' do
|
||||||
group.add_guest(user)
|
group.add_guest(user)
|
||||||
|
|
||||||
names = described_class.new(group, user).execute
|
names = described_class.new(group, user).execute
|
||||||
|
@ -68,7 +84,7 @@ RSpec.describe Environments::EnvironmentNamesFinder do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with a non-member' do
|
context 'with a non-member' do
|
||||||
it 'returns environment names for all public projects' do
|
it 'returns environment names for only public projects with public environments' do
|
||||||
names = described_class.new(group, user).execute
|
names = described_class.new(group, user).execute
|
||||||
|
|
||||||
expect(names).to eq(%w[gprd gstg])
|
expect(names).to eq(%w[gprd gstg])
|
||||||
|
@ -76,7 +92,7 @@ RSpec.describe Environments::EnvironmentNamesFinder do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'without a user' do
|
context 'without a user' do
|
||||||
it 'returns environment names for all public projects' do
|
it 'returns environment names for only public projects with public environments' do
|
||||||
names = described_class.new(group).execute
|
names = described_class.new(group).execute
|
||||||
|
|
||||||
expect(names).to eq(%w[gprd gstg])
|
expect(names).to eq(%w[gprd gstg])
|
||||||
|
|
|
@ -106,6 +106,26 @@ RSpec.describe NotesFinder do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'for notes on public issue in public project' do
|
||||||
|
let_it_be(:public_project) { create(:project, :public) }
|
||||||
|
let_it_be(:guest_member) { create(:user) }
|
||||||
|
let_it_be(:reporter_member) { create(:user) }
|
||||||
|
let_it_be(:guest_project_member) { create(:project_member, :guest, user: guest_member, project: public_project) }
|
||||||
|
let_it_be(:reporter_project_member) { create(:project_member, :reporter, user: reporter_member, project: public_project) }
|
||||||
|
let_it_be(:internal_note) { create(:note_on_issue, project: public_project, internal: true) }
|
||||||
|
let_it_be(:public_note) { create(:note_on_issue, project: public_project) }
|
||||||
|
|
||||||
|
it 'shows all notes when the current_user has reporter access' do
|
||||||
|
notes = described_class.new(reporter_member, project: public_project).execute
|
||||||
|
expect(notes).to contain_exactly internal_note, public_note
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'shows only public notes when the current_user has guest access' do
|
||||||
|
notes = described_class.new(guest_member, project: public_project).execute
|
||||||
|
expect(notes).to contain_exactly public_note
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
context 'for target type' do
|
context 'for target type' do
|
||||||
let(:project) { create(:project, :repository) }
|
let(:project) { create(:project, :repository) }
|
||||||
let!(:note1) { create :note_on_issue, project: project }
|
let!(:note1) { create :note_on_issue, project: project }
|
||||||
|
|
588
spec/fixtures/emails/valid_reply_signed_smime.eml
vendored
588
spec/fixtures/emails/valid_reply_signed_smime.eml
vendored
|
@ -1,294 +1,294 @@
|
||||||
User-Agent: Microsoft-MacOutlook/10.22.0.200209
|
User-Agent: Microsoft-MacOutlook/10.22.0.200209
|
||||||
Date: Mon, 17 Feb 2020 22:56:47 +0100
|
Date: Mon, 17 Feb 2020 22:56:47 +0100
|
||||||
Subject: Re: htmltest | test issue (#1)
|
Subject: Re: htmltest | test issue (#1)
|
||||||
From: "Louzan Martinez, Diego (ext) (SI BP R&D ZG)"
|
From: "Louzan Martinez, Diego (ext) (SI BP R&D ZG)"
|
||||||
<diego.louzan.ext@siemens.com>
|
<diego.louzan.ext@siemens.com>
|
||||||
To: Administrator / htmltest
|
To: Administrator / htmltest
|
||||||
<dlouzan.dummy+c034670b1623e617e15a3df64223d363@gmail.com>
|
<dlouzan.dummy+c034670b1623e617e15a3df64223d363@gmail.com>
|
||||||
Message-ID: <012E37D9-2A3F-4AC8-B79A-871F42914D86@siemens.com>
|
Message-ID: <012E37D9-2A3F-4AC8-B79A-871F42914D86@siemens.com>
|
||||||
Thread-Topic: htmltest | test issue (#1)
|
Thread-Topic: htmltest | test issue (#1)
|
||||||
References: <reply-c034670b1623e617e15a3df64223d363@169.254.169.254>
|
References: <reply-c034670b1623e617e15a3df64223d363@169.254.169.254>
|
||||||
<issue_451@169.254.169.254>
|
<issue_451@169.254.169.254>
|
||||||
<note_1797@169.254.169.254>
|
<note_1797@169.254.169.254>
|
||||||
In-Reply-To: <note_1797@169.254.169.254>
|
In-Reply-To: <note_1797@169.254.169.254>
|
||||||
Content-type: multipart/signed;
|
Content-type: multipart/signed;
|
||||||
protocol="application/pkcs7-signature";
|
protocol="application/pkcs7-signature";
|
||||||
micalg=sha256;
|
micalg=sha256;
|
||||||
boundary="B_3664825007_1904734766"
|
boundary="B_3664825007_1904734766"
|
||||||
MIME-Version: 1.0
|
MIME-Version: 1.0
|
||||||
|
|
||||||
--B_3664825007_1904734766
|
--B_3664825007_1904734766
|
||||||
Content-type: multipart/mixed;
|
Content-type: multipart/mixed;
|
||||||
boundary="B_3664825007_384940722"
|
boundary="B_3664825007_384940722"
|
||||||
|
|
||||||
|
|
||||||
--B_3664825007_384940722
|
--B_3664825007_384940722
|
||||||
Content-type: multipart/alternative;
|
Content-type: multipart/alternative;
|
||||||
boundary="B_3664825007_1519466360"
|
boundary="B_3664825007_1519466360"
|
||||||
|
|
||||||
|
|
||||||
--B_3664825007_1519466360
|
--B_3664825007_1519466360
|
||||||
Content-type: text/plain;
|
Content-type: text/plain;
|
||||||
charset="UTF-8"
|
charset="UTF-8"
|
||||||
Content-transfer-encoding: quoted-printable
|
Content-transfer-encoding: quoted-printable
|
||||||
|
|
||||||
Me too, with an attachment
|
Me too, with an attachment
|
||||||
|
|
||||||
=20
|
=20
|
||||||
|
|
||||||
From: Administrator <dlouzan.dummy@gmail.com>
|
From: Administrator <dlouzan.dummy@gmail.com>
|
||||||
Reply to: Administrator / htmltest <dlouzan.dummy+c034670b1623e617e15a3df64=
|
Reply to: Administrator / htmltest <dlouzan.dummy+c034670b1623e617e15a3df64=
|
||||||
223d363@gmail.com>
|
223d363@gmail.com>
|
||||||
Date: Monday, 17 February 2020 at 22:55
|
Date: Monday, 17 February 2020 at 22:55
|
||||||
To: "Louzan Martinez, Diego (ext) (SOP IT STG XS)" <diego.louzan.ext@siemen=
|
To: "Louzan Martinez, Diego (ext) (SOP IT STG XS)" <diego.louzan.ext@siemen=
|
||||||
s.com>
|
s.com>
|
||||||
Subject: Re: htmltest | test issue (#1)
|
Subject: Re: htmltest | test issue (#1)
|
||||||
|
|
||||||
=20
|
=20
|
||||||
|
|
||||||
Administrator commented:=20
|
Administrator commented:=20
|
||||||
|
|
||||||
I pity the foo !!!
|
I pity the foo !!!
|
||||||
|
|
||||||
=E2=80=94=20
|
=E2=80=94=20
|
||||||
Reply to this email directly or view it on GitLab.=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'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 =
|
you'd like to receive fewer emails, you can unsubscribe from this thread or =
|
||||||
adjust your notification settings.=20
|
adjust your notification settings.=20
|
||||||
|
|
||||||
|
|
||||||
--B_3664825007_1519466360
|
--B_3664825007_1519466360
|
||||||
Content-type: text/html;
|
Content-type: text/html;
|
||||||
charset="UTF-8"
|
charset="UTF-8"
|
||||||
Content-transfer-encoding: quoted-printable
|
Content-transfer-encoding: quoted-printable
|
||||||
|
|
||||||
<html xmlns:o=3D"urn:schemas-microsoft-com:office:office" xmlns:w=3D"urn:schema=
|
<html xmlns:o=3D"urn:schemas-microsoft-com:office:office" xmlns:w=3D"urn:schema=
|
||||||
s-microsoft-com:office:word" xmlns:m=3D"http://schemas.microsoft.com/office/20=
|
s-microsoft-com:office:word" xmlns:m=3D"http://schemas.microsoft.com/office/20=
|
||||||
04/12/omml" xmlns=3D"http://www.w3.org/TR/REC-html40"><head><meta http-equiv=3DC=
|
04/12/omml" xmlns=3D"http://www.w3.org/TR/REC-html40"><head><meta http-equiv=3DC=
|
||||||
ontent-Type content=3D"text/html; charset=3Dutf-8"><meta name=3DGenerator content=3D=
|
ontent-Type content=3D"text/html; charset=3Dutf-8"><meta name=3DGenerator content=3D=
|
||||||
"Microsoft Word 15 (filtered medium)"><title>GitLab</title><style><!--
|
"Microsoft Word 15 (filtered medium)"><title>GitLab</title><style><!--
|
||||||
/* Font Definitions */
|
/* Font Definitions */
|
||||||
@font-face
|
@font-face
|
||||||
{font-family:"Cambria Math";
|
{font-family:"Cambria Math";
|
||||||
panose-1:2 4 5 3 5 4 6 3 2 4;}
|
panose-1:2 4 5 3 5 4 6 3 2 4;}
|
||||||
@font-face
|
@font-face
|
||||||
{font-family:Calibri;
|
{font-family:Calibri;
|
||||||
panose-1:2 15 5 2 2 2 4 3 2 4;}
|
panose-1:2 15 5 2 2 2 4 3 2 4;}
|
||||||
/* Style Definitions */
|
/* Style Definitions */
|
||||||
p.MsoNormal, li.MsoNormal, div.MsoNormal
|
p.MsoNormal, li.MsoNormal, div.MsoNormal
|
||||||
{margin:0cm;
|
{margin:0cm;
|
||||||
margin-bottom:.0001pt;
|
margin-bottom:.0001pt;
|
||||||
font-size:11.0pt;
|
font-size:11.0pt;
|
||||||
font-family:"Calibri",sans-serif;}
|
font-family:"Calibri",sans-serif;}
|
||||||
a:link, span.MsoHyperlink
|
a:link, span.MsoHyperlink
|
||||||
{mso-style-priority:99;
|
{mso-style-priority:99;
|
||||||
color:blue;
|
color:blue;
|
||||||
text-decoration:underline;}
|
text-decoration:underline;}
|
||||||
span.EmailStyle19
|
span.EmailStyle19
|
||||||
{mso-style-type:personal-reply;
|
{mso-style-type:personal-reply;
|
||||||
font-family:"Calibri",sans-serif;
|
font-family:"Calibri",sans-serif;
|
||||||
color:windowtext;}
|
color:windowtext;}
|
||||||
.MsoChpDefault
|
.MsoChpDefault
|
||||||
{mso-style-type:export-only;
|
{mso-style-type:export-only;
|
||||||
font-size:10.0pt;}
|
font-size:10.0pt;}
|
||||||
@page WordSection1
|
@page WordSection1
|
||||||
{size:612.0pt 792.0pt;
|
{size:612.0pt 792.0pt;
|
||||||
margin:72.0pt 72.0pt 72.0pt 72.0pt;}
|
margin:72.0pt 72.0pt 72.0pt 72.0pt;}
|
||||||
div.WordSection1
|
div.WordSection1
|
||||||
{page:WordSection1;}
|
{page:WordSection1;}
|
||||||
--></style></head><body lang=3Den-ES link=3Dblue vlink=3Dpurple><div class=3DWordSe=
|
--></style></head><body lang=3Den-ES link=3Dblue vlink=3Dpurple><div class=3DWordSe=
|
||||||
ction1><p class=3DMsoNormal><span lang=3DEN-US style=3D'mso-fareast-language:EN-US=
|
ction1><p class=3DMsoNormal><span lang=3DEN-US style=3D'mso-fareast-language:EN-US=
|
||||||
'>Me too, with an attachment<o:p></o:p></span></p><p class=3DMsoNormal><span s=
|
'>Me too, with an attachment<o:p></o:p></span></p><p class=3DMsoNormal><span s=
|
||||||
tyle=3D'mso-fareast-language:EN-US'><o:p> </o:p></span></p><div style=3D'bo=
|
tyle=3D'mso-fareast-language:EN-US'><o:p> </o:p></span></p><div style=3D'bo=
|
||||||
rder:none;border-top:solid #B5C4DF 1.0pt;padding:3.0pt 0cm 0cm 0cm'><p class=
|
rder:none;border-top:solid #B5C4DF 1.0pt;padding:3.0pt 0cm 0cm 0cm'><p class=
|
||||||
=3DMsoNormal><b><span style=3D'font-size:12.0pt;color:black'>From: </span></b><s=
|
=3DMsoNormal><b><span style=3D'font-size:12.0pt;color:black'>From: </span></b><s=
|
||||||
pan style=3D'font-size:12.0pt;color:black'>Administrator <dlouzan.dummy@gma=
|
pan style=3D'font-size:12.0pt;color:black'>Administrator <dlouzan.dummy@gma=
|
||||||
il.com><br><b>Reply to: </b>Administrator / htmltest <dlouzan.dummy+c0=
|
il.com><br><b>Reply to: </b>Administrator / htmltest <dlouzan.dummy+c0=
|
||||||
34670b1623e617e15a3df64223d363@gmail.com><br><b>Date: </b>Monday, 17 Febr=
|
34670b1623e617e15a3df64223d363@gmail.com><br><b>Date: </b>Monday, 17 Febr=
|
||||||
uary 2020 at 22:55<br><b>To: </b>"Louzan Martinez, Diego (ext) (SOP IT =
|
uary 2020 at 22:55<br><b>To: </b>"Louzan Martinez, Diego (ext) (SOP IT =
|
||||||
STG XS)" <diego.louzan.ext@siemens.com><br><b>Subject: </b>Re: ht=
|
STG XS)" <diego.louzan.ext@siemens.com><br><b>Subject: </b>Re: ht=
|
||||||
mltest | test issue (#1)<o:p></o:p></span></p></div><div><p class=3DMsoNormal>=
|
mltest | test issue (#1)<o:p></o:p></span></p></div><div><p class=3DMsoNormal>=
|
||||||
<o:p> </o:p></p></div><div><p><span style=3D'color:#777777'><a href=3D"http=
|
<o:p> </o:p></p></div><div><p><span style=3D'color:#777777'><a href=3D"http=
|
||||||
://localhost:3000/root">Administrator</a> commented: <o:p></o:p></span></p><=
|
://localhost:3000/root">Administrator</a> commented: <o:p></o:p></span></p><=
|
||||||
div><p>I pity the foo !!!<o:p></o:p></p></div></div><div style=3D'margin-top:7=
|
div><p>I pity the foo !!!<o:p></o:p></p></div></div><div style=3D'margin-top:7=
|
||||||
.5pt'><p><span style=3D'font-size:12.0pt;color:#777777'>=E2=80=94 <br>Reply to this =
|
.5pt'><p><span style=3D'font-size:12.0pt;color:#777777'>=E2=80=94 <br>Reply to this =
|
||||||
email directly or <a href=3D"http://localhost:3000/root/htmltest/issues/1#note=
|
email directly or <a href=3D"http://localhost:3000/root/htmltest/issues/1#note=
|
||||||
_1797">view it on GitLab</a>. <br>You're receiving this email because of you=
|
_1797">view it on GitLab</a>. <br>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=
|
r account on 169.254.169.254. If you'd like to receive fewer emails, you can=
|
||||||
<a href=3D"http://localhost:3000/sent_notifications/c034670b1623e617e15a3df64=
|
<a href=3D"http://localhost:3000/sent_notifications/c034670b1623e617e15a3df64=
|
||||||
223d363/unsubscribe">unsubscribe</a> from this thread or adjust your notific=
|
223d363/unsubscribe">unsubscribe</a> from this thread or adjust your notific=
|
||||||
ation settings. <o:p></o:p></span></p></div></div></body></html>
|
ation settings. <o:p></o:p></span></p></div></div></body></html>
|
||||||
|
|
||||||
--B_3664825007_1519466360--
|
--B_3664825007_1519466360--
|
||||||
|
|
||||||
|
|
||||||
--B_3664825007_384940722
|
--B_3664825007_384940722
|
||||||
Content-type: image/png; name="gitlab_logo.png";
|
Content-type: image/png; name="gitlab_logo.png";
|
||||||
x-mac-creator="4F50494D";
|
x-mac-creator="4F50494D";
|
||||||
x-mac-type="504E4766"
|
x-mac-type="504E4766"
|
||||||
Content-disposition: attachment;
|
Content-disposition: attachment;
|
||||||
filename="gitlab_logo.png"
|
filename="gitlab_logo.png"
|
||||||
Content-transfer-encoding: base64
|
Content-transfer-encoding: base64
|
||||||
|
|
||||||
|
|
||||||
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
|
||||||
--B_3664825007_384940722--
|
--B_3664825007_384940722--
|
||||||
|
|
||||||
--B_3664825007_1904734766
|
--B_3664825007_1904734766
|
||||||
Content-type: application/pkcs7-signature; name="smime.p7s"
|
Content-type: application/pkcs7-signature; name="smime.p7s"
|
||||||
Content-transfer-encoding: base64
|
Content-transfer-encoding: base64
|
||||||
Content-disposition: attachment;
|
Content-disposition: attachment;
|
||||||
filename="smime.p7s"
|
filename="smime.p7s"
|
||||||
|
|
||||||
MIIRpwYJKoZIhvcNAQcCoIIRmDCCEZQCAQExDzANBglghkgBZQMEAgEFADALBgkqhkiG9w0B
|
MIIRpwYJKoZIhvcNAQcCoIIRmDCCEZQCAQExDzANBglghkgBZQMEAgEFADALBgkqhkiG9w0B
|
||||||
BwGggg8VMIIHojCCBYqgAwIBAgIEZ5a6PTANBgkqhkiG9w0BAQsFADCBtjELMAkGA1UEBhMC
|
BwGggg8VMIIHojCCBYqgAwIBAgIEZ5a6PTANBgkqhkiG9w0BAQsFADCBtjELMAkGA1UEBhMC
|
||||||
REUxDzANBgNVBAgMBkJheWVybjERMA8GA1UEBwwITXVlbmNoZW4xEDAOBgNVBAoMB1NpZW1l
|
REUxDzANBgNVBAgMBkJheWVybjERMA8GA1UEBwwITXVlbmNoZW4xEDAOBgNVBAoMB1NpZW1l
|
||||||
bnMxETAPBgNVBAUTCFpaWlpaWkE2MR0wGwYDVQQLDBRTaWVtZW5zIFRydXN0IENlbnRlcjE/
|
bnMxETAPBgNVBAUTCFpaWlpaWkE2MR0wGwYDVQQLDBRTaWVtZW5zIFRydXN0IENlbnRlcjE/
|
||||||
MD0GA1UEAww2U2llbWVucyBJc3N1aW5nIENBIE1lZGl1bSBTdHJlbmd0aCBBdXRoZW50aWNh
|
MD0GA1UEAww2U2llbWVucyBJc3N1aW5nIENBIE1lZGl1bSBTdHJlbmd0aCBBdXRoZW50aWNh
|
||||||
dGlvbiAyMDE2MB4XDTE5MTEyMTE0NDQ0N1oXDTIwMTEyMTE0NDQ0N1owdzERMA8GA1UEBRMI
|
dGlvbiAyMDE2MB4XDTE5MTEyMTE0NDQ0N1oXDTIwMTEyMTE0NDQ0N1owdzERMA8GA1UEBRMI
|
||||||
WjAwM0gwOFQxDjAMBgNVBCoMBURpZWdvMRgwFgYDVQQEDA9Mb3V6YW4gTWFydGluZXoxGDAW
|
WjAwM0gwOFQxDjAMBgNVBCoMBURpZWdvMRgwFgYDVQQEDA9Mb3V6YW4gTWFydGluZXoxGDAW
|
||||||
BgNVBAoMD1NpZW1lbnMtUGFydG5lcjEeMBwGA1UEAwwVTG91emFuIE1hcnRpbmV6IERpZWdv
|
BgNVBAoMD1NpZW1lbnMtUGFydG5lcjEeMBwGA1UEAwwVTG91emFuIE1hcnRpbmV6IERpZWdv
|
||||||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuInpNaC7NRYD+0pOpHDz2pk9xmPt
|
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuInpNaC7NRYD+0pOpHDz2pk9xmPt
|
||||||
JGj860SF6Nmn6Eu9EMYKEDfneC6z5QcH+mPS2d0VWgqVVGbRXSPsxJtbi9TCWjQUZdHglEZK
|
JGj860SF6Nmn6Eu9EMYKEDfneC6z5QcH+mPS2d0VWgqVVGbRXSPsxJtbi9TCWjQUZdHglEZK
|
||||||
z9zxoFDh2dvW5/+TOT5Jf78FXyqak0YtY6+oMjQ/i9RUqPL7sIlyXLrBYrILzQ9Afo+7bXZg
|
z9zxoFDh2dvW5/+TOT5Jf78FXyqak0YtY6+oMjQ/i9RUqPL7sIlyXLrBYrILzQ9Afo+7bXZg
|
||||||
v3ypp6xtqAV2ctHzQWFi0onJzxLVYguiVb7fFF9rBEMvSZonuw5tvOwJIhbe5FDFOrDcfbyU
|
v3ypp6xtqAV2ctHzQWFi0onJzxLVYguiVb7fFF9rBEMvSZonuw5tvOwJIhbe5FDFOrDcfbyU
|
||||||
ofZ/wikIZ+A+CE5GryXuuQmGxJaC2QqOkRAWQDzLDx9nG+rKiEs5OvlfEZC7EV1PyjZ93coM
|
ofZ/wikIZ+A+CE5GryXuuQmGxJaC2QqOkRAWQDzLDx9nG+rKiEs5OvlfEZC7EV1PyjZ93coM
|
||||||
faCVdlAgcFZ5fvd37CjyjKl+1QIDAQABo4IC9DCCAvAwggEEBggrBgEFBQcBAQSB9zCB9DAy
|
faCVdlAgcFZ5fvd37CjyjKl+1QIDAQABo4IC9DCCAvAwggEEBggrBgEFBQcBAQSB9zCB9DAy
|
||||||
BggrBgEFBQcwAoYmaHR0cDovL2FoLnNpZW1lbnMuY29tL3BraT9aWlpaWlpBNi5jcnQwQQYI
|
BggrBgEFBQcwAoYmaHR0cDovL2FoLnNpZW1lbnMuY29tL3BraT9aWlpaWlpBNi5jcnQwQQYI
|
||||||
KwYBBQUHMAKGNWxkYXA6Ly9hbC5zaWVtZW5zLm5ldC9DTj1aWlpaWlpBNixMPVBLST9jQUNl
|
KwYBBQUHMAKGNWxkYXA6Ly9hbC5zaWVtZW5zLm5ldC9DTj1aWlpaWlpBNixMPVBLST9jQUNl
|
||||||
cnRpZmljYXRlMEkGCCsGAQUFBzAChj1sZGFwOi8vYWwuc2llbWVucy5jb20vQ049WlpaWlpa
|
cnRpZmljYXRlMEkGCCsGAQUFBzAChj1sZGFwOi8vYWwuc2llbWVucy5jb20vQ049WlpaWlpa
|
||||||
QTYsbz1UcnVzdGNlbnRlcj9jQUNlcnRpZmljYXRlMDAGCCsGAQUFBzABhiRodHRwOi8vb2Nz
|
QTYsbz1UcnVzdGNlbnRlcj9jQUNlcnRpZmljYXRlMDAGCCsGAQUFBzABhiRodHRwOi8vb2Nz
|
||||||
cC5wa2ktc2VydmljZXMuc2llbWVucy5jb20wHwYDVR0jBBgwFoAU+BVdRwxsd3tyxAIXkWii
|
cC5wa2ktc2VydmljZXMuc2llbWVucy5jb20wHwYDVR0jBBgwFoAU+BVdRwxsd3tyxAIXkWii
|
||||||
tvdqCUQwDAYDVR0TAQH/BAIwADBFBgNVHSAEPjA8MDoGDSsGAQQBoWkHAgIEAQMwKTAnBggr
|
tvdqCUQwDAYDVR0TAQH/BAIwADBFBgNVHSAEPjA8MDoGDSsGAQQBoWkHAgIEAQMwKTAnBggr
|
||||||
BgEFBQcCARYbaHR0cDovL3d3dy5zaWVtZW5zLmNvbS9wa2kvMIHKBgNVHR8EgcIwgb8wgbyg
|
BgEFBQcCARYbaHR0cDovL3d3dy5zaWVtZW5zLmNvbS9wa2kvMIHKBgNVHR8EgcIwgb8wgbyg
|
||||||
gbmggbaGJmh0dHA6Ly9jaC5zaWVtZW5zLmNvbS9wa2k/WlpaWlpaQTYuY3JshkFsZGFwOi8v
|
gbmggbaGJmh0dHA6Ly9jaC5zaWVtZW5zLmNvbS9wa2k/WlpaWlpaQTYuY3JshkFsZGFwOi8v
|
||||||
Y2wuc2llbWVucy5uZXQvQ049WlpaWlpaQTYsTD1QS0k/Y2VydGlmaWNhdGVSZXZvY2F0aW9u
|
Y2wuc2llbWVucy5uZXQvQ049WlpaWlpaQTYsTD1QS0k/Y2VydGlmaWNhdGVSZXZvY2F0aW9u
|
||||||
TGlzdIZJbGRhcDovL2NsLnNpZW1lbnMuY29tL0NOPVpaWlpaWkE2LG89VHJ1c3RjZW50ZXI/
|
TGlzdIZJbGRhcDovL2NsLnNpZW1lbnMuY29tL0NOPVpaWlpaWkE2LG89VHJ1c3RjZW50ZXI/
|
||||||
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
|
||||||
Cztca8vI1jUjQzVhS+0dWpvpfhkVumbvOnid8DI9lapYsX8dpZFsa3ya+T3tjUdGSOOKi0kg
|
Cztca8vI1jUjQzVhS+0dWpvpfhkVumbvOnid8DI9lapYsX8dpZFsa3ya+T3tjUdGSOOKi0kg
|
||||||
lWf/XYyyfhmDAgMBAAGjggLVMIIC0TAdBgNVHQ4EFgQUprhTCDwNLfPImpSfWdq+QvPTo9Mw
|
lWf/XYyyfhmDAgMBAAGjggLVMIIC0TAdBgNVHQ4EFgQUprhTCDwNLfPImpSfWdq+QvPTo9Mw
|
||||||
JwYDVR0RBCAwHoEcZGllZ28ubG91emFuLmV4dEBzaWVtZW5zLmNvbTAOBgNVHQ8BAf8EBAMC
|
JwYDVR0RBCAwHoEcZGllZ28ubG91emFuLmV4dEBzaWVtZW5zLmNvbTAOBgNVHQ8BAf8EBAMC
|
||||||
BDAwLAYDVR0lBCUwIwYIKwYBBQUHAwQGCisGAQQBgjcKAwQGCysGAQQBgjcKAwQBMIHKBgNV
|
BDAwLAYDVR0lBCUwIwYIKwYBBQUHAwQGCisGAQQBgjcKAwQGCysGAQQBgjcKAwQBMIHKBgNV
|
||||||
HR8EgcIwgb8wgbyggbmggbaGJmh0dHA6Ly9jaC5zaWVtZW5zLmNvbS9wa2k/WlpaWlpaQTMu
|
HR8EgcIwgb8wgbyggbmggbaGJmh0dHA6Ly9jaC5zaWVtZW5zLmNvbS9wa2k/WlpaWlpaQTMu
|
||||||
Y3JshkFsZGFwOi8vY2wuc2llbWVucy5uZXQvQ049WlpaWlpaQTMsTD1QS0k/Y2VydGlmaWNh
|
Y3JshkFsZGFwOi8vY2wuc2llbWVucy5uZXQvQ049WlpaWlpaQTMsTD1QS0k/Y2VydGlmaWNh
|
||||||
dGVSZXZvY2F0aW9uTGlzdIZJbGRhcDovL2NsLnNpZW1lbnMuY29tL0NOPVpaWlpaWkEzLG89
|
dGVSZXZvY2F0aW9uTGlzdIZJbGRhcDovL2NsLnNpZW1lbnMuY29tL0NOPVpaWlpaWkEzLG89
|
||||||
VHJ1c3RjZW50ZXI/Y2VydGlmaWNhdGVSZXZvY2F0aW9uTGlzdDBFBgNVHSAEPjA8MDoGDSsG
|
VHJ1c3RjZW50ZXI/Y2VydGlmaWNhdGVSZXZvY2F0aW9uTGlzdDBFBgNVHSAEPjA8MDoGDSsG
|
||||||
AQQBoWkHAgIEAQMwKTAnBggrBgEFBQcCARYbaHR0cDovL3d3dy5zaWVtZW5zLmNvbS9wa2kv
|
AQQBoWkHAgIEAQMwKTAnBggrBgEFBQcCARYbaHR0cDovL3d3dy5zaWVtZW5zLmNvbS9wa2kv
|
||||||
MAwGA1UdEwEB/wQCMAAwHwYDVR0jBBgwFoAUoassbqB68NPCTeof8R4hivwMre8wggEEBggr
|
MAwGA1UdEwEB/wQCMAAwHwYDVR0jBBgwFoAUoassbqB68NPCTeof8R4hivwMre8wggEEBggr
|
||||||
BgEFBQcBAQSB9zCB9DAyBggrBgEFBQcwAoYmaHR0cDovL2FoLnNpZW1lbnMuY29tL3BraT9a
|
BgEFBQcBAQSB9zCB9DAyBggrBgEFBQcwAoYmaHR0cDovL2FoLnNpZW1lbnMuY29tL3BraT9a
|
||||||
WlpaWlpBMy5jcnQwQQYIKwYBBQUHMAKGNWxkYXA6Ly9hbC5zaWVtZW5zLm5ldC9DTj1aWlpa
|
WlpaWlpBMy5jcnQwQQYIKwYBBQUHMAKGNWxkYXA6Ly9hbC5zaWVtZW5zLm5ldC9DTj1aWlpa
|
||||||
WlpBMyxMPVBLST9jQUNlcnRpZmljYXRlMEkGCCsGAQUFBzAChj1sZGFwOi8vYWwuc2llbWVu
|
WlpBMyxMPVBLST9jQUNlcnRpZmljYXRlMEkGCCsGAQUFBzAChj1sZGFwOi8vYWwuc2llbWVu
|
||||||
cy5jb20vQ049WlpaWlpaQTMsbz1UcnVzdGNlbnRlcj9jQUNlcnRpZmljYXRlMDAGCCsGAQUF
|
cy5jb20vQ049WlpaWlpaQTMsbz1UcnVzdGNlbnRlcj9jQUNlcnRpZmljYXRlMDAGCCsGAQUF
|
||||||
BzABhiRodHRwOi8vb2NzcC5wa2ktc2VydmljZXMuc2llbWVucy5jb20wDQYJKoZIhvcNAQEL
|
BzABhiRodHRwOi8vb2NzcC5wa2ktc2VydmljZXMuc2llbWVucy5jb20wDQYJKoZIhvcNAQEL
|
||||||
BQADggIBAF98ZMNg28LgkwdjOdvOGbC1QitsWjZTyotmQESF0nClDLUhb0O5675vVixntbrf
|
BQADggIBAF98ZMNg28LgkwdjOdvOGbC1QitsWjZTyotmQESF0nClDLUhb0O5675vVixntbrf
|
||||||
eB8xy1+KRiadk40GnAIJ0YzmNl4Tav6hPYv9VBWe5olsWG7C4qB3Q/SwhvW/e+owxv1cBra8
|
eB8xy1+KRiadk40GnAIJ0YzmNl4Tav6hPYv9VBWe5olsWG7C4qB3Q/SwhvW/e+owxv1cBra8
|
||||||
R3oRudiN81eTZQHyNghRephVqQG/dpPYqydoANfIhEpHa79QlpaCAeYl4896AZOS8HYbkDFs
|
R3oRudiN81eTZQHyNghRephVqQG/dpPYqydoANfIhEpHa79QlpaCAeYl4896AZOS8HYbkDFs
|
||||||
hLdv7sEHtl79YuSWI1wBjbJl70c0Sb4wLRgCPuHyQj2Uw/vQ5xJlEvBDZAIXXe1TP/nqiuY6
|
hLdv7sEHtl79YuSWI1wBjbJl70c0Sb4wLRgCPuHyQj2Uw/vQ5xJlEvBDZAIXXe1TP/nqiuY6
|
||||||
7nweJbbeqfFE6ZP3kCe+mEIWGSaO0iThZyLGer8fHs1XiEmhhPgvC7P7KodzpXU6+hX+ZzbD
|
7nweJbbeqfFE6ZP3kCe+mEIWGSaO0iThZyLGer8fHs1XiEmhhPgvC7P7KodzpXU6+hX+ZzbD
|
||||||
DxEjFfetV5sh0aNSXG9xx4hZmS9bpImBGR8MvZ7cgxqItvLtY2xvfUbYW244d4RcWesaCDq3
|
DxEjFfetV5sh0aNSXG9xx4hZmS9bpImBGR8MvZ7cgxqItvLtY2xvfUbYW244d4RcWesaCDq3
|
||||||
ZEIo6uCIzOzJAwjUdLIac+lLV0rxiHmb7O3cQ19kjpWDB31hmfrus/TKJ55pBKVWBX5m/mFv
|
ZEIo6uCIzOzJAwjUdLIac+lLV0rxiHmb7O3cQ19kjpWDB31hmfrus/TKJ55pBKVWBX5m/mFv
|
||||||
K8Ep5USpGrNS0EzOP7I1kQZv2VsvAhSxk/m5FMLpDy8T0O8YgbLypTXoeJFWCF6RduSjVsaZ
|
K8Ep5USpGrNS0EzOP7I1kQZv2VsvAhSxk/m5FMLpDy8T0O8YgbLypTXoeJFWCF6RduSjVsaZ
|
||||||
lkAtTQYud683pjyOMxJXaQUYGU1PmEYSOonMkVsT9aBcxYkXLp+Ln/+8G0OCYu7dRdwnj+Ut
|
lkAtTQYud683pjyOMxJXaQUYGU1PmEYSOonMkVsT9aBcxYkXLp+Ln/+8G0OCYu7dRdwnj+Ut
|
||||||
7yR/ltxtgDcaFApCb0qBTKbgbqZk1fASmkOp+kbdYmoUMYICVjCCAlICAQEwgb8wgbYxCzAJ
|
7yR/ltxtgDcaFApCb0qBTKbgbqZk1fASmkOp+kbdYmoUMYICVjCCAlICAQEwgb8wgbYxCzAJ
|
||||||
BgNVBAYTAkRFMQ8wDQYDVQQIDAZCYXllcm4xETAPBgNVBAcMCE11ZW5jaGVuMRAwDgYDVQQK
|
BgNVBAYTAkRFMQ8wDQYDVQQIDAZCYXllcm4xETAPBgNVBAcMCE11ZW5jaGVuMRAwDgYDVQQK
|
||||||
DAdTaWVtZW5zMREwDwYDVQQFEwhaWlpaWlpBNjEdMBsGA1UECwwUU2llbWVucyBUcnVzdCBD
|
DAdTaWVtZW5zMREwDwYDVQQFEwhaWlpaWlpBNjEdMBsGA1UECwwUU2llbWVucyBUcnVzdCBD
|
||||||
ZW50ZXIxPzA9BgNVBAMMNlNpZW1lbnMgSXNzdWluZyBDQSBNZWRpdW0gU3RyZW5ndGggQXV0
|
ZW50ZXIxPzA9BgNVBAMMNlNpZW1lbnMgSXNzdWluZyBDQSBNZWRpdW0gU3RyZW5ndGggQXV0
|
||||||
aGVudGljYXRpb24gMjAxNgIEZ5a6PTANBglghkgBZQMEAgEFAKBpMC8GCSqGSIb3DQEJBDEi
|
aGVudGljYXRpb24gMjAxNgIEZ5a6PTANBglghkgBZQMEAgEFAKBpMC8GCSqGSIb3DQEJBDEi
|
||||||
BCAOR58AbNfSrI+vtMs+dgAQtn3IVZ3RjYC5hz3j9k+6TTAYBgkqhkiG9w0BCQMxCwYJKoZI
|
BCAOR58AbNfSrI+vtMs+dgAQtn3IVZ3RjYC5hz3j9k+6TTAYBgkqhkiG9w0BCQMxCwYJKoZI
|
||||||
hvcNAQcBMBwGCSqGSIb3DQEJBTEPFw0yMDAyMTcyMTU2NDdaMA0GCSqGSIb3DQEBAQUABIIB
|
hvcNAQcBMBwGCSqGSIb3DQEJBTEPFw0yMDAyMTcyMTU2NDdaMA0GCSqGSIb3DQEBAQUABIIB
|
||||||
AHLSBcFHhNHPevbwqvA2ecuVb/aKnj45CFF6l8esP1H5DRm1ee5qMKuIS84NFuFC9RUENNhW
|
AHLSBcFHhNHPevbwqvA2ecuVb/aKnj45CFF6l8esP1H5DRm1ee5qMKuIS84NFuFC9RUENNhW
|
||||||
DBzsB+BVGz64o1f8QgIklYVrIJ4JZ0q1abNG7NbkVKWIpS3CQo//YWShUTYg+JpKx4YbahGR
|
DBzsB+BVGz64o1f8QgIklYVrIJ4JZ0q1abNG7NbkVKWIpS3CQo//YWShUTYg+JpKx4YbahGR
|
||||||
sP5zbufbU4eagrrqBChjPTLy+njdjwCNu0XPykBTKOOf6BMjnS33AYjHJyh83JOY7rw3IDLx
|
sP5zbufbU4eagrrqBChjPTLy+njdjwCNu0XPykBTKOOf6BMjnS33AYjHJyh83JOY7rw3IDLx
|
||||||
8POQH4g5EMRpl9354s0rEkIezMt7pfUAsqY3QnQ8hvlE4KTikPQ+tvLMK1l/ffcLAP8BdBNI
|
8POQH4g5EMRpl9354s0rEkIezMt7pfUAsqY3QnQ8hvlE4KTikPQ+tvLMK1l/ffcLAP8BdBNI
|
||||||
YA3ikb3qCoGNSLKieYzNnBPhNOIJELUtEEaljAFZYMQzMKCbI4JdiDs=
|
YA3ikb3qCoGNSLKieYzNnBPhNOIJELUtEEaljAFZYMQzMKCbI4JdiDs=
|
||||||
|
|
||||||
--B_3664825007_1904734766--
|
--B_3664825007_1904734766--
|
||||||
|
|
|
@ -16,7 +16,7 @@ describe('Observability iframe renderer', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('renders an observability iframe', () => {
|
it('renders an observability iframe', () => {
|
||||||
document.body.innerHTML = `<div class="js-render-observability" data-frame-url="https://observe.gitlab.com/"></div>`;
|
document.body.innerHTML = `<div class="js-render-observability" data-frame-url="https://observe.gitlab.com/" data-observability-url="https://observe.gitlab.com/" ></div>`;
|
||||||
|
|
||||||
expect(findObservabilityIframes()).toHaveLength(0);
|
expect(findObservabilityIframes()).toHaveLength(0);
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ describe('Observability iframe renderer', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('renders iframe with dark param when GL has dark theme', () => {
|
it('renders iframe with dark param when GL has dark theme', () => {
|
||||||
document.body.innerHTML = `<div class="js-render-observability" data-frame-url="https://observe.gitlab.com/"></div>`;
|
document.body.innerHTML = `<div class="js-render-observability" data-frame-url="https://observe.gitlab.com/" data-observability-url="https://observe.gitlab.com/"></div>`;
|
||||||
jest.spyOn(ColorUtils, 'darkModeEnabled').mockImplementation(() => true);
|
jest.spyOn(ColorUtils, 'darkModeEnabled').mockImplementation(() => true);
|
||||||
|
|
||||||
expect(findObservabilityIframes('dark')).toHaveLength(0);
|
expect(findObservabilityIframes('dark')).toHaveLength(0);
|
||||||
|
@ -35,4 +35,12 @@ describe('Observability iframe renderer', () => {
|
||||||
|
|
||||||
expect(findObservabilityIframes('dark')).toHaveLength(1);
|
expect(findObservabilityIframes('dark')).toHaveLength(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('does not render if url is different from observability url', () => {
|
||||||
|
document.body.innerHTML = `<div class="js-render-observability" data-frame-url="https://example.com/" data-observability-url="https://observe.gitlab.com/"></div>`;
|
||||||
|
|
||||||
|
renderEmbeddedObservability();
|
||||||
|
|
||||||
|
expect(findObservabilityIframes()).toHaveLength(0);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { generateRefDestinationPath } from '~/repository/utils/ref_switcher_utils';
|
import { generateRefDestinationPath } from '~/repository/utils/ref_switcher_utils';
|
||||||
import setWindowLocation from 'helpers/set_window_location_helper';
|
import setWindowLocation from 'helpers/set_window_location_helper';
|
||||||
|
import { TEST_HOST } from 'spec/test_constants';
|
||||||
import { refWithSpecialCharMock, encodedRefWithSpecialCharMock } from '../mock_data';
|
import { refWithSpecialCharMock, encodedRefWithSpecialCharMock } from '../mock_data';
|
||||||
|
|
||||||
const projectRootPath = 'root/Project1';
|
const projectRootPath = 'root/Project1';
|
||||||
|
@ -16,14 +17,38 @@ describe('generateRefDestinationPath', () => {
|
||||||
${`${projectRootPath}/-/blob/${currentRef}/dir1/test.js`} | ${`${projectRootPath}/-/blob/${selectedRef}/dir1/test.js`}
|
${`${projectRootPath}/-/blob/${currentRef}/dir1/test.js`} | ${`${projectRootPath}/-/blob/${selectedRef}/dir1/test.js`}
|
||||||
${`${projectRootPath}/-/blob/${currentRef}/dir1/dir2/test.js`} | ${`${projectRootPath}/-/blob/${selectedRef}/dir1/dir2/test.js`}
|
${`${projectRootPath}/-/blob/${currentRef}/dir1/dir2/test.js`} | ${`${projectRootPath}/-/blob/${selectedRef}/dir1/dir2/test.js`}
|
||||||
${`${projectRootPath}/-/blob/${currentRef}/dir1/dir2/test.js#L123`} | ${`${projectRootPath}/-/blob/${selectedRef}/dir1/dir2/test.js#L123`}
|
${`${projectRootPath}/-/blob/${currentRef}/dir1/dir2/test.js#L123`} | ${`${projectRootPath}/-/blob/${selectedRef}/dir1/dir2/test.js#L123`}
|
||||||
`('generates the correct destination path for $currentPath', ({ currentPath, result }) => {
|
`('generates the correct destination path for $currentPath', ({ currentPath, result }) => {
|
||||||
setWindowLocation(currentPath);
|
setWindowLocation(currentPath);
|
||||||
expect(generateRefDestinationPath(projectRootPath, selectedRef)).toBe(result);
|
expect(generateRefDestinationPath(projectRootPath, currentRef, selectedRef)).toBe(
|
||||||
|
`${TEST_HOST}/${result}`,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when using symbolic ref names', () => {
|
||||||
|
it.each`
|
||||||
|
currentPath | nextRef | result
|
||||||
|
${`${projectRootPath}/-/blob/${currentRef}/dir1/dir2/test.js#L123`} | ${'someHash'} | ${`${projectRootPath}/-/blob/someHash/dir1/dir2/test.js#L123`}
|
||||||
|
${`${projectRootPath}/-/blob/${currentRef}/dir1/dir2/test.js#L123`} | ${'refs/heads/prefixedByUseSymbolicRefNames'} | ${`${projectRootPath}/-/blob/prefixedByUseSymbolicRefNames/dir1/dir2/test.js?ref_type=heads#L123`}
|
||||||
|
${`${projectRootPath}/-/blob/${currentRef}/dir1/dir2/test.js#L123`} | ${'refs/tags/prefixedByUseSymbolicRefNames'} | ${`${projectRootPath}/-/blob/prefixedByUseSymbolicRefNames/dir1/dir2/test.js?ref_type=tags#L123`}
|
||||||
|
${`${projectRootPath}/-/tree/${currentRef}/dir1/dir2/test.js#L123`} | ${'refs/heads/prefixedByUseSymbolicRefNames'} | ${`${projectRootPath}/-/tree/prefixedByUseSymbolicRefNames/dir1/dir2/test.js?ref_type=heads#L123`}
|
||||||
|
${`${projectRootPath}/-/tree/${currentRef}/dir1/dir2/test.js#L123`} | ${'refs/tags/prefixedByUseSymbolicRefNames'} | ${`${projectRootPath}/-/tree/prefixedByUseSymbolicRefNames/dir1/dir2/test.js?ref_type=tags#L123`}
|
||||||
|
${`${projectRootPath}/-/tree/${currentRef}/dir1/dir2/test.js#L123`} | ${'refs/heads/refs/heads/branchNameContainsPrefix'} | ${`${projectRootPath}/-/tree/refs/heads/branchNameContainsPrefix/dir1/dir2/test.js?ref_type=heads#L123`}
|
||||||
|
`(
|
||||||
|
'generates the correct destination path for $currentPath with ref type when it can be extracted',
|
||||||
|
({ currentPath, result, nextRef }) => {
|
||||||
|
setWindowLocation(currentPath);
|
||||||
|
expect(generateRefDestinationPath(projectRootPath, currentRef, nextRef)).toBe(
|
||||||
|
`${TEST_HOST}/${result}`,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('encodes the selected ref', () => {
|
it('encodes the selected ref', () => {
|
||||||
const result = `${projectRootPath}/-/tree/${encodedRefWithSpecialCharMock}`;
|
const result = `${projectRootPath}/-/tree/${encodedRefWithSpecialCharMock}`;
|
||||||
|
|
||||||
expect(generateRefDestinationPath(projectRootPath, refWithSpecialCharMock)).toBe(result);
|
expect(generateRefDestinationPath(projectRootPath, currentRef, refWithSpecialCharMock)).toBe(
|
||||||
|
`${TEST_HOST}/${result}`,
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -26,7 +26,7 @@ RSpec.describe HooksHelper do
|
||||||
it 'returns proper data' do
|
it 'returns proper data' do
|
||||||
expect(subject).to match(
|
expect(subject).to match(
|
||||||
url: project_hook.url,
|
url: project_hook.url,
|
||||||
url_variables: Gitlab::Json.dump([{ key: 'abc' }])
|
url_variables: Gitlab::Json.dump([{ key: 'abc' }, { key: 'def' }])
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -34,6 +34,58 @@ RSpec.describe Banzai::Filter::InlineObservabilityFilter do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'when the document contains an embeddable observability link with redirect' do
|
||||||
|
let(:url) { 'https://observe.gitlab.com@example.com/12345' }
|
||||||
|
|
||||||
|
it 'leaves the original link unchanged' do
|
||||||
|
expect(doc.at_css('a').to_s).to eq(input)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not append an observability charts placeholder' do
|
||||||
|
node = doc.at_css('.js-render-observability')
|
||||||
|
|
||||||
|
expect(node).not_to be_present
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when the document contains an embeddable observability link with different port' do
|
||||||
|
let(:url) { 'https://observe.gitlab.com:3000/12345' }
|
||||||
|
let(:observe_url) { 'https://observe.gitlab.com:3001' }
|
||||||
|
|
||||||
|
before do
|
||||||
|
stub_env('OVERRIDE_OBSERVABILITY_URL', observe_url)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'leaves the original link unchanged' do
|
||||||
|
expect(doc.at_css('a').to_s).to eq(input)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not append an observability charts placeholder' do
|
||||||
|
node = doc.at_css('.js-render-observability')
|
||||||
|
|
||||||
|
expect(node).not_to be_present
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when the document contains an embeddable observability link with auth/start' do
|
||||||
|
let(:url) { 'https://observe.gitlab.com/auth/start' }
|
||||||
|
let(:observe_url) { 'https://observe.gitlab.com' }
|
||||||
|
|
||||||
|
before do
|
||||||
|
stub_env('OVERRIDE_OBSERVABILITY_URL', observe_url)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'leaves the original link unchanged' do
|
||||||
|
expect(doc.at_css('a').to_s).to eq(input)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not append an observability charts placeholder' do
|
||||||
|
node = doc.at_css('.js-render-observability')
|
||||||
|
|
||||||
|
expect(node).not_to be_present
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
context 'when feature flag is disabled' do
|
context 'when feature flag is disabled' do
|
||||||
let(:url) { 'https://observe.gitlab.com/12345' }
|
let(:url) { 'https://observe.gitlab.com/12345' }
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,84 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
RSpec.describe Gitlab::BackgroundMigration::NullifyLastErrorFromProjectMirrorData, feature_category: :source_code_management do # rubocop:disable Layout/LineLength
|
||||||
|
it 'nullifies last_error column on all rows' do
|
||||||
|
namespaces = table(:namespaces)
|
||||||
|
projects = table(:projects)
|
||||||
|
project_import_states = table(:project_mirror_data)
|
||||||
|
|
||||||
|
group = namespaces.create!(name: 'gitlab', path: 'gitlab-org')
|
||||||
|
|
||||||
|
project_namespace_1 = namespaces.create!(name: 'gitlab', path: 'gitlab-org')
|
||||||
|
project_namespace_2 = namespaces.create!(name: 'gitlab', path: 'gitlab-org')
|
||||||
|
project_namespace_3 = namespaces.create!(name: 'gitlab', path: 'gitlab-org')
|
||||||
|
|
||||||
|
project_1 = projects.create!(
|
||||||
|
namespace_id: group.id,
|
||||||
|
project_namespace_id: project_namespace_1.id,
|
||||||
|
name: 'test1'
|
||||||
|
)
|
||||||
|
project_2 = projects.create!(
|
||||||
|
namespace_id: group.id,
|
||||||
|
project_namespace_id: project_namespace_2.id,
|
||||||
|
name: 'test2'
|
||||||
|
)
|
||||||
|
project_3 = projects.create!(
|
||||||
|
namespace_id: group.id,
|
||||||
|
project_namespace_id: project_namespace_3.id,
|
||||||
|
name: 'test3'
|
||||||
|
)
|
||||||
|
|
||||||
|
project_import_state_1 = project_import_states.create!(
|
||||||
|
project_id: project_1.id,
|
||||||
|
status: 0,
|
||||||
|
last_update_started_at: 1.hour.ago,
|
||||||
|
last_update_scheduled_at: 1.hour.ago,
|
||||||
|
last_update_at: 1.hour.ago,
|
||||||
|
last_successful_update_at: 2.days.ago,
|
||||||
|
last_error: '13:fetch remote: "fatal: unable to look up user:pass@gitlab.com (port 9418) (nodename nor servname provided, or not known)\n": exit status 128.', # rubocop:disable Layout/LineLength
|
||||||
|
correlation_id_value: SecureRandom.uuid,
|
||||||
|
jid: SecureRandom.uuid
|
||||||
|
)
|
||||||
|
|
||||||
|
project_import_states.create!(
|
||||||
|
project_id: project_2.id,
|
||||||
|
status: 1,
|
||||||
|
last_update_started_at: 1.hour.ago,
|
||||||
|
last_update_scheduled_at: 1.hour.ago,
|
||||||
|
last_update_at: 1.hour.ago,
|
||||||
|
last_successful_update_at: nil,
|
||||||
|
next_execution_timestamp: 1.day.from_now,
|
||||||
|
last_error: '',
|
||||||
|
correlation_id_value: SecureRandom.uuid,
|
||||||
|
jid: SecureRandom.uuid
|
||||||
|
)
|
||||||
|
|
||||||
|
project_import_state_3 = project_import_states.create!(
|
||||||
|
project_id: project_3.id,
|
||||||
|
status: 2,
|
||||||
|
last_update_started_at: 1.hour.ago,
|
||||||
|
last_update_scheduled_at: 1.hour.ago,
|
||||||
|
last_update_at: 1.hour.ago,
|
||||||
|
last_successful_update_at: 1.hour.ago,
|
||||||
|
next_execution_timestamp: 1.day.from_now,
|
||||||
|
last_error: nil,
|
||||||
|
correlation_id_value: SecureRandom.uuid,
|
||||||
|
jid: SecureRandom.uuid
|
||||||
|
)
|
||||||
|
|
||||||
|
migration = described_class.new(
|
||||||
|
start_id: project_import_state_1.id,
|
||||||
|
end_id: project_import_state_3.id,
|
||||||
|
batch_table: :project_mirror_data,
|
||||||
|
batch_column: :id,
|
||||||
|
sub_batch_size: 1,
|
||||||
|
pause_ms: 0,
|
||||||
|
connection: ApplicationRecord.connection
|
||||||
|
)
|
||||||
|
|
||||||
|
w_last_error_count = -> { project_import_states.where.not(last_error: nil).count } # rubocop:disable CodeReuse/ActiveRecord
|
||||||
|
expect { migration.perform }.to change(&w_last_error_count).from(2).to(0)
|
||||||
|
end
|
||||||
|
end
|
|
@ -10,29 +10,36 @@ RSpec.describe Gitlab::UrlSanitizer do
|
||||||
# We want to try with multi-line content because is how error messages are formatted
|
# We want to try with multi-line content because is how error messages are formatted
|
||||||
described_class.sanitize(%Q{
|
described_class.sanitize(%Q{
|
||||||
remote: Not Found
|
remote: Not Found
|
||||||
fatal: repository '#{url}' not found
|
fatal: repository `#{url}` not found
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
where(:input, :output) do
|
where(:input, :output) do
|
||||||
'http://user:pass@test.com/root/repoC.git/' | 'http://*****:*****@test.com/root/repoC.git/'
|
# http(s), ssh, git, relative, and schemeless URLs should all be masked correctly
|
||||||
'https://user:pass@test.com/root/repoA.git/' | 'https://*****:*****@test.com/root/repoA.git/'
|
urls = ['http://', 'https://', 'ssh://', 'git://', '//', ''].flat_map do |protocol|
|
||||||
'ssh://user@host.test/path/to/repo.git' | 'ssh://*****@host.test/path/to/repo.git'
|
[
|
||||||
|
["#{protocol}test.com", "#{protocol}test.com"],
|
||||||
# git protocol does not support authentication but clean any details anyway
|
["#{protocol}test.com/", "#{protocol}test.com/"],
|
||||||
'git://user:pass@host.test/path/to/repo.git' | 'git://*****:*****@host.test/path/to/repo.git'
|
["#{protocol}test.com/path/to/repo.git", "#{protocol}test.com/path/to/repo.git"],
|
||||||
'git://host.test/path/to/repo.git' | 'git://host.test/path/to/repo.git'
|
["#{protocol}user@test.com", "#{protocol}*****@test.com"],
|
||||||
|
["#{protocol}user:pass@test.com", "#{protocol}*****:*****@test.com"],
|
||||||
|
["#{protocol}user:@test.com", "#{protocol}*****@test.com"],
|
||||||
|
["#{protocol}:pass@test.com", "#{protocol}:*****@test.com"]
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
# SCP-style URLs are left unmodified
|
# SCP-style URLs are left unmodified
|
||||||
'user@server:project.git' | 'user@server:project.git'
|
urls << ['user@server:project.git', 'user@server:project.git']
|
||||||
'user:pass@server:project.git' | 'user:pass@server:project.git'
|
urls << ['user:@server:project.git', 'user:@server:project.git']
|
||||||
|
urls << [':pass@server:project.git', ':pass@server:project.git']
|
||||||
|
urls << ['user:pass@server:project.git', 'user:pass@server:project.git']
|
||||||
|
|
||||||
# return an empty string for invalid URLs
|
# return an empty string for invalid URLs
|
||||||
'ssh://' | ''
|
urls << ['ssh://', '']
|
||||||
end
|
end
|
||||||
|
|
||||||
with_them do
|
with_them do
|
||||||
it { expect(sanitize_url(input)).to include("repository '#{output}' not found") }
|
it { expect(sanitize_url(input)).to include("repository `#{output}` not found") }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
require 'spec_helper'
|
require 'spec_helper'
|
||||||
|
|
||||||
RSpec.describe Rouge::Formatters::HTMLGitlab do
|
RSpec.describe Rouge::Formatters::HTMLGitlab, feature_category: :source_code_management do
|
||||||
describe '#format' do
|
describe '#format' do
|
||||||
subject { described_class.format(tokens, **options) }
|
subject { described_class.format(tokens, **options) }
|
||||||
|
|
||||||
|
@ -67,5 +67,24 @@ RSpec.describe Rouge::Formatters::HTMLGitlab do
|
||||||
is_expected.to include(%{<span class="unicode-bidi has-tooltip" data-toggle="tooltip" title="#{message}">}).exactly(4).times
|
is_expected.to include(%{<span class="unicode-bidi has-tooltip" data-toggle="tooltip" title="#{message}">}).exactly(4).times
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'when space characters and zero-width spaces are used' do
|
||||||
|
let(:lang) { 'ruby' }
|
||||||
|
let(:tokens) { lexer.lex(code, continue: false) }
|
||||||
|
|
||||||
|
let(:code) do
|
||||||
|
<<~JS
|
||||||
|
def\u00a0\u1680\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000hello
|
||||||
|
JS
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'replaces the space characters with spaces' do
|
||||||
|
is_expected.to eq(
|
||||||
|
"<span id=\"LC1\" class=\"line\" lang=\"ruby\">" \
|
||||||
|
"<span class=\"k\">def</span><span class=\"err\"> </span><span class=\"n\">hello</span>" \
|
||||||
|
"</span>"
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
require_migration!
|
||||||
|
|
||||||
|
RSpec.describe NullifyLastErrorFromProjectMirrorData, feature_category: :source_code_management do
|
||||||
|
let(:migration) { described_class::MIGRATION }
|
||||||
|
|
||||||
|
before do
|
||||||
|
migrate!
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#up' do
|
||||||
|
it 'schedules background jobs for each batch of projects' do
|
||||||
|
expect(migration).to(
|
||||||
|
have_scheduled_batched_migration(
|
||||||
|
table_name: :project_mirror_data,
|
||||||
|
column_name: :id,
|
||||||
|
interval: described_class::INTERVAL,
|
||||||
|
batch_size: described_class::BATCH_SIZE,
|
||||||
|
sub_batch_size: described_class::SUB_BATCH_SIZE
|
||||||
|
)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#down' do
|
||||||
|
before do
|
||||||
|
schema_migrate_down!
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'deletes all batched migration records' do
|
||||||
|
expect(migration).not_to have_scheduled_batched_migration
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -302,7 +302,7 @@ RSpec.describe ErrorTracking::ProjectErrorTrackingSetting do
|
||||||
it { expect(result[:issue].gitlab_commit_path).to eq(nil) }
|
it { expect(result[:issue].gitlab_commit_path).to eq(nil) }
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when repo commit matches first relase version' do
|
context 'when repo commit matches first release version' do
|
||||||
let(:commit) { instance_double(Commit, id: commit_id) }
|
let(:commit) { instance_double(Commit, id: commit_id) }
|
||||||
let(:repository) { instance_double(Repository, commit: commit) }
|
let(:repository) { instance_double(Repository, commit: commit) }
|
||||||
|
|
||||||
|
|
|
@ -242,6 +242,22 @@ RSpec.describe WebHook, feature_category: :integrations do
|
||||||
expect(hook.url_variables).to eq({})
|
expect(hook.url_variables).to eq({})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'resets url variables if url is changed and url variables are appended' do
|
||||||
|
hook.url = 'http://suspicious.example.com/{abc}/{foo}'
|
||||||
|
hook.url_variables = hook.url_variables.merge('foo' => 'bar')
|
||||||
|
|
||||||
|
expect(hook).not_to be_valid
|
||||||
|
expect(hook.url_variables).to eq({})
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'resets url variables if url is changed and url variables are removed' do
|
||||||
|
hook.url = 'http://suspicious.example.com/{abc}'
|
||||||
|
hook.url_variables = hook.url_variables.except("def")
|
||||||
|
|
||||||
|
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
|
it 'does not reset url variables if both url and url variables are changed' do
|
||||||
hook.url = 'http://example.com/{one}/{two}'
|
hook.url = 'http://example.com/{one}/{two}'
|
||||||
hook.url_variables = { 'one' => 'foo', 'two' => 'bar' }
|
hook.url_variables = { 'one' => 'foo', 'two' => 'bar' }
|
||||||
|
@ -249,6 +265,18 @@ RSpec.describe WebHook, feature_category: :integrations do
|
||||||
expect(hook).to be_valid
|
expect(hook).to be_valid
|
||||||
expect(hook.url_variables).to eq({ 'one' => 'foo', 'two' => 'bar' })
|
expect(hook.url_variables).to eq({ 'one' => 'foo', 'two' => 'bar' })
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'without url variables' do
|
||||||
|
subject(:hook) { build_stubbed(:project_hook, project: project, url: 'http://example.com') }
|
||||||
|
|
||||||
|
it 'does not reset url variables' 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
|
end
|
||||||
|
|
||||||
it "only consider these branch filter strategies are valid" do
|
it "only consider these branch filter strategies are valid" do
|
||||||
|
|
|
@ -697,6 +697,39 @@ RSpec.describe ProjectPolicy, feature_category: :authentication_and_authorizatio
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe 'read_prometheus', feature_category: :metrics do
|
||||||
|
using RSpec::Parameterized::TableSyntax
|
||||||
|
|
||||||
|
before do
|
||||||
|
project.project_feature.update!(metrics_dashboard_access_level: ProjectFeature::ENABLED)
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:policy) { :read_prometheus }
|
||||||
|
|
||||||
|
where(:project_visibility, :role, :allowed) do
|
||||||
|
:public | :anonymous | false
|
||||||
|
:public | :guest | false
|
||||||
|
:public | :reporter | true
|
||||||
|
:internal | :anonymous | false
|
||||||
|
:internal | :guest | false
|
||||||
|
:internal | :reporter | true
|
||||||
|
:private | :anonymous | false
|
||||||
|
:private | :guest | false
|
||||||
|
: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
|
describe 'update_max_artifacts_size' do
|
||||||
context 'when no user' do
|
context 'when no user' do
|
||||||
let(:current_user) { anonymous }
|
let(:current_user) { anonymous }
|
||||||
|
@ -972,7 +1005,7 @@ RSpec.describe ProjectPolicy, feature_category: :authentication_and_authorizatio
|
||||||
let(:current_user) { guest }
|
let(:current_user) { guest }
|
||||||
|
|
||||||
it { is_expected.to be_allowed(:metrics_dashboard) }
|
it { is_expected.to be_allowed(:metrics_dashboard) }
|
||||||
it { is_expected.to be_allowed(:read_prometheus) }
|
it { is_expected.to be_disallowed(:read_prometheus) }
|
||||||
it { is_expected.to be_allowed(:read_deployment) }
|
it { is_expected.to be_allowed(:read_deployment) }
|
||||||
it { is_expected.to be_allowed(:read_metrics_user_starred_dashboard) }
|
it { is_expected.to be_allowed(:read_metrics_user_starred_dashboard) }
|
||||||
it { is_expected.to be_allowed(:create_metrics_user_starred_dashboard) }
|
it { is_expected.to be_allowed(:create_metrics_user_starred_dashboard) }
|
||||||
|
@ -982,7 +1015,7 @@ RSpec.describe ProjectPolicy, feature_category: :authentication_and_authorizatio
|
||||||
let(:current_user) { anonymous }
|
let(:current_user) { anonymous }
|
||||||
|
|
||||||
it { is_expected.to be_allowed(:metrics_dashboard) }
|
it { is_expected.to be_allowed(:metrics_dashboard) }
|
||||||
it { is_expected.to be_allowed(:read_prometheus) }
|
it { is_expected.to be_disallowed(:read_prometheus) }
|
||||||
it { is_expected.to be_allowed(:read_deployment) }
|
it { is_expected.to be_allowed(:read_deployment) }
|
||||||
it { is_expected.to be_disallowed(:read_metrics_user_starred_dashboard) }
|
it { is_expected.to be_disallowed(:read_metrics_user_starred_dashboard) }
|
||||||
it { is_expected.to be_disallowed(:create_metrics_user_starred_dashboard) }
|
it { is_expected.to be_disallowed(:create_metrics_user_starred_dashboard) }
|
||||||
|
@ -1008,12 +1041,14 @@ RSpec.describe ProjectPolicy, feature_category: :authentication_and_authorizatio
|
||||||
let(:current_user) { guest }
|
let(:current_user) { guest }
|
||||||
|
|
||||||
it { is_expected.to be_disallowed(:metrics_dashboard) }
|
it { is_expected.to be_disallowed(:metrics_dashboard) }
|
||||||
|
it { is_expected.to be_disallowed(:read_prometheus) }
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with anonymous' do
|
context 'with anonymous' do
|
||||||
let(:current_user) { anonymous }
|
let(:current_user) { anonymous }
|
||||||
|
|
||||||
it { is_expected.to be_disallowed(:metrics_dashboard) }
|
it { is_expected.to be_disallowed(:metrics_dashboard) }
|
||||||
|
it { is_expected.to be_disallowed(:read_prometheus) }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -1036,7 +1071,7 @@ RSpec.describe ProjectPolicy, feature_category: :authentication_and_authorizatio
|
||||||
let(:current_user) { guest }
|
let(:current_user) { guest }
|
||||||
|
|
||||||
it { is_expected.to be_allowed(:metrics_dashboard) }
|
it { is_expected.to be_allowed(:metrics_dashboard) }
|
||||||
it { is_expected.to be_allowed(:read_prometheus) }
|
it { is_expected.to be_disallowed(:read_prometheus) }
|
||||||
it { is_expected.to be_allowed(:read_deployment) }
|
it { is_expected.to be_allowed(:read_deployment) }
|
||||||
it { is_expected.to be_allowed(:read_metrics_user_starred_dashboard) }
|
it { is_expected.to be_allowed(:read_metrics_user_starred_dashboard) }
|
||||||
it { is_expected.to be_allowed(:create_metrics_user_starred_dashboard) }
|
it { is_expected.to be_allowed(:create_metrics_user_starred_dashboard) }
|
||||||
|
@ -1046,6 +1081,7 @@ RSpec.describe ProjectPolicy, feature_category: :authentication_and_authorizatio
|
||||||
let(:current_user) { anonymous }
|
let(:current_user) { anonymous }
|
||||||
|
|
||||||
it { is_expected.to be_disallowed(:metrics_dashboard) }
|
it { is_expected.to be_disallowed(:metrics_dashboard) }
|
||||||
|
it { is_expected.to be_disallowed(:read_prometheus) }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -1068,12 +1104,14 @@ RSpec.describe ProjectPolicy, feature_category: :authentication_and_authorizatio
|
||||||
let(:current_user) { guest }
|
let(:current_user) { guest }
|
||||||
|
|
||||||
it { is_expected.to be_disallowed(:metrics_dashboard) }
|
it { is_expected.to be_disallowed(:metrics_dashboard) }
|
||||||
|
it { is_expected.to be_disallowed(:read_prometheus) }
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with anonymous' do
|
context 'with anonymous' do
|
||||||
let(:current_user) { anonymous }
|
let(:current_user) { anonymous }
|
||||||
|
|
||||||
it { is_expected.to be_disallowed(:metrics_dashboard) }
|
it { is_expected.to be_disallowed(:metrics_dashboard) }
|
||||||
|
it { is_expected.to be_disallowed(:read_prometheus) }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -1092,12 +1130,14 @@ RSpec.describe ProjectPolicy, feature_category: :authentication_and_authorizatio
|
||||||
let(:current_user) { guest }
|
let(:current_user) { guest }
|
||||||
|
|
||||||
it { is_expected.to be_disallowed(:metrics_dashboard) }
|
it { is_expected.to be_disallowed(:metrics_dashboard) }
|
||||||
|
it { is_expected.to be_disallowed(:read_prometheus) }
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with anonymous' do
|
context 'with anonymous' do
|
||||||
let(:current_user) { anonymous }
|
let(:current_user) { anonymous }
|
||||||
|
|
||||||
it { is_expected.to be_disallowed(:metrics_dashboard) }
|
it { is_expected.to be_disallowed(:metrics_dashboard) }
|
||||||
|
it { is_expected.to be_disallowed(:read_prometheus) }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -2016,7 +2056,7 @@ RSpec.describe ProjectPolicy, feature_category: :authentication_and_authorizatio
|
||||||
:public | ProjectFeature::ENABLED | :anonymous | true
|
:public | ProjectFeature::ENABLED | :anonymous | true
|
||||||
:public | ProjectFeature::PRIVATE | :maintainer | true
|
:public | ProjectFeature::PRIVATE | :maintainer | true
|
||||||
:public | ProjectFeature::PRIVATE | :developer | true
|
:public | ProjectFeature::PRIVATE | :developer | true
|
||||||
:public | ProjectFeature::PRIVATE | :guest | true
|
:public | ProjectFeature::PRIVATE | :guest | false
|
||||||
:public | ProjectFeature::PRIVATE | :anonymous | false
|
:public | ProjectFeature::PRIVATE | :anonymous | false
|
||||||
:public | ProjectFeature::DISABLED | :maintainer | false
|
:public | ProjectFeature::DISABLED | :maintainer | false
|
||||||
:public | ProjectFeature::DISABLED | :developer | false
|
:public | ProjectFeature::DISABLED | :developer | false
|
||||||
|
@ -2028,7 +2068,7 @@ RSpec.describe ProjectPolicy, feature_category: :authentication_and_authorizatio
|
||||||
:internal | ProjectFeature::ENABLED | :anonymous | false
|
:internal | ProjectFeature::ENABLED | :anonymous | false
|
||||||
:internal | ProjectFeature::PRIVATE | :maintainer | true
|
:internal | ProjectFeature::PRIVATE | :maintainer | true
|
||||||
:internal | ProjectFeature::PRIVATE | :developer | true
|
:internal | ProjectFeature::PRIVATE | :developer | true
|
||||||
:internal | ProjectFeature::PRIVATE | :guest | true
|
:internal | ProjectFeature::PRIVATE | :guest | false
|
||||||
:internal | ProjectFeature::PRIVATE | :anonymous | false
|
:internal | ProjectFeature::PRIVATE | :anonymous | false
|
||||||
:internal | ProjectFeature::DISABLED | :maintainer | false
|
:internal | ProjectFeature::DISABLED | :maintainer | false
|
||||||
:internal | ProjectFeature::DISABLED | :developer | false
|
:internal | ProjectFeature::DISABLED | :developer | false
|
||||||
|
|
|
@ -573,6 +573,22 @@ RSpec.describe API::Repositories, feature_category: :source_code_management do
|
||||||
context 'when authenticated', 'as a developer' do
|
context 'when authenticated', 'as a developer' do
|
||||||
it_behaves_like 'repository compare' do
|
it_behaves_like 'repository compare' do
|
||||||
let(:current_user) { user }
|
let(:current_user) { user }
|
||||||
|
|
||||||
|
context 'when user does not have read access to the parent project' do
|
||||||
|
let_it_be(:group) { create(:group) }
|
||||||
|
let(:forked_project) { fork_project(project, current_user, repository: true, namespace: group) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
forked_project.add_guest(current_user)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns 403 error' do
|
||||||
|
get api(route, current_user), params: { from: 'improve/awesome', to: 'feature', from_project_id: forked_project.id }
|
||||||
|
|
||||||
|
expect(response).to have_gitlab_http_status(:forbidden)
|
||||||
|
expect(json_response['message']).to eq("403 Forbidden - You don't have access to this fork's parent project")
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -861,6 +861,21 @@ RSpec.describe MergeRequests::PushOptionsHandlerService do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe 'when user does not have access to target project' do
|
||||||
|
let(:push_options) { { create: true, target: 'my-branch' } }
|
||||||
|
let(:changes) { default_branch_changes }
|
||||||
|
|
||||||
|
before do
|
||||||
|
allow(user1).to receive(:can?).with(:read_code, project).and_return(false)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'records an error', :sidekiq_inline do
|
||||||
|
service.execute
|
||||||
|
|
||||||
|
expect(service.errors).to eq(["User access was denied"])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe 'when MRs are not enabled' do
|
describe 'when MRs are not enabled' do
|
||||||
let(:project) { create(:project, :public, :repository).tap { |pr| pr.add_developer(user1) } }
|
let(:project) { create(:project, :public, :repository).tap { |pr| pr.add_developer(user1) } }
|
||||||
let(:push_options) { { create: true } }
|
let(:push_options) { { create: true } }
|
||||||
|
|
|
@ -130,8 +130,8 @@ RSpec.describe WebHookService, :request_store, :clean_gitlab_redis_shared_state
|
||||||
context 'there is userinfo' do
|
context 'there is userinfo' do
|
||||||
before do
|
before do
|
||||||
project_hook.update!(
|
project_hook.update!(
|
||||||
url: 'http://{one}:{two}@example.com',
|
url: 'http://{foo}:{bar}@example.com',
|
||||||
url_variables: { 'one' => 'a', 'two' => 'b' }
|
url_variables: { 'foo' => 'a', 'bar' => 'b' }
|
||||||
)
|
)
|
||||||
stub_full_request('http://example.com', method: :post)
|
stub_full_request('http://example.com', method: :post)
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
RSpec.describe 'explore/projects/page_out_of_bounds.html.haml', feature_category: :projects do
|
||||||
|
let(:page_limit) { 10 }
|
||||||
|
let(:unsafe_param) { 'hacked_using_unsafe_param!' }
|
||||||
|
|
||||||
|
before do
|
||||||
|
assign(:max_page_number, page_limit)
|
||||||
|
|
||||||
|
controller.params[:action] = 'index'
|
||||||
|
controller.params[:host] = unsafe_param
|
||||||
|
controller.params[:protocol] = unsafe_param
|
||||||
|
controller.params[:sort] = 'name_asc'
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'removes unsafe params from the link' do
|
||||||
|
render
|
||||||
|
|
||||||
|
href = "/explore/projects?page=#{page_limit}&sort=name_asc"
|
||||||
|
button_text = format(_("Back to page %{number}"), number: page_limit)
|
||||||
|
expect(rendered).to have_link(button_text, href: href)
|
||||||
|
expect(rendered).not_to include(unsafe_param)
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in a new issue