Add latest changes from gitlab-org/security/gitlab@16-1-stable-ee
This commit is contained in:
parent
d7fe9575a0
commit
62ed09f455
|
|
@ -5107,7 +5107,6 @@ RSpec/MissingFeatureCategory:
|
|||
- 'spec/policies/ci/bridge_policy_spec.rb'
|
||||
- 'spec/policies/ci/build_policy_spec.rb'
|
||||
- 'spec/policies/ci/pipeline_policy_spec.rb'
|
||||
- 'spec/policies/ci/pipeline_schedule_policy_spec.rb'
|
||||
- 'spec/policies/ci/trigger_policy_spec.rb'
|
||||
- 'spec/policies/clusters/agent_policy_spec.rb'
|
||||
- 'spec/policies/clusters/agent_token_policy_spec.rb'
|
||||
|
|
|
|||
|
|
@ -175,7 +175,7 @@ export default {
|
|||
originalStartLineCode,
|
||||
...(discussion.line_codes || []),
|
||||
];
|
||||
const fileHash = discussion.diff_file.file_hash;
|
||||
const fileHash = discussion.diff_file?.file_hash;
|
||||
const lineCheck = (line) =>
|
||||
discussionLineCodes.some(
|
||||
(discussionLineCode) =>
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ export function prepareRawDiffFile({ file, allFiles, meta = false, index = -1 })
|
|||
}
|
||||
|
||||
export function collapsedType(file) {
|
||||
const isManual = typeof file.viewer?.manuallyCollapsed === 'boolean';
|
||||
const isManual = typeof file?.viewer?.manuallyCollapsed === 'boolean';
|
||||
|
||||
return isManual ? DIFF_FILE_MANUAL_COLLAPSE : DIFF_FILE_AUTOMATIC_COLLAPSE;
|
||||
}
|
||||
|
|
@ -85,8 +85,8 @@ export function collapsedType(file) {
|
|||
export function isCollapsed(file) {
|
||||
const type = collapsedType(file);
|
||||
const collapsedStates = {
|
||||
[DIFF_FILE_AUTOMATIC_COLLAPSE]: file.viewer?.automaticallyCollapsed || false,
|
||||
[DIFF_FILE_MANUAL_COLLAPSE]: file.viewer?.manuallyCollapsed,
|
||||
[DIFF_FILE_AUTOMATIC_COLLAPSE]: file?.viewer?.automaticallyCollapsed || false,
|
||||
[DIFF_FILE_MANUAL_COLLAPSE]: file?.viewer?.manuallyCollapsed,
|
||||
};
|
||||
|
||||
return collapsedStates[type];
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ export default {
|
|||
return getDiffMode(this.discussion.diff_file);
|
||||
},
|
||||
diffViewerMode() {
|
||||
return this.discussion.diff_file.viewer.name;
|
||||
return this.discussion.diff_file?.viewer.name;
|
||||
},
|
||||
fileDiffRefs() {
|
||||
return this.discussion.diff_file.diff_refs;
|
||||
|
|
@ -96,6 +96,7 @@ export default {
|
|||
<template>
|
||||
<div :class="{ 'text-file': isTextFile }" class="diff-file file-holder">
|
||||
<diff-file-header
|
||||
v-if="discussion.diff_file"
|
||||
:discussion-path="discussion.discussion_path"
|
||||
:diff-file="discussion.diff_file"
|
||||
:can-current-user-fork="false"
|
||||
|
|
|
|||
|
|
@ -157,7 +157,7 @@ export default {
|
|||
return !this.discussionResolved ? this.discussion.resolve_with_issue_path : '';
|
||||
},
|
||||
canShowReplyActions() {
|
||||
if (this.shouldRenderDiffs && !this.discussion.diff_file.diff_refs) {
|
||||
if (this.shouldRenderDiffs && !this.discussion.diff_file?.diff_refs) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@ class Projects::PipelineSchedulesController < Projects::ApplicationController
|
|||
end
|
||||
|
||||
def new
|
||||
@schedule = project.pipeline_schedules.new
|
||||
end
|
||||
|
||||
def create
|
||||
|
|
@ -102,6 +101,15 @@ class Projects::PipelineSchedulesController < Projects::ApplicationController
|
|||
variables_attributes: [:id, :variable_type, :key, :secret_value, :_destroy])
|
||||
end
|
||||
|
||||
def new_schedule
|
||||
# We need the `ref` here for `authorize_create_pipeline_schedule!`
|
||||
@schedule ||= project.pipeline_schedules.new(ref: params.dig(:schedule, :ref))
|
||||
end
|
||||
|
||||
def authorize_create_pipeline_schedule!
|
||||
return access_denied! unless can?(current_user, :create_pipeline_schedule, new_schedule)
|
||||
end
|
||||
|
||||
def authorize_play_pipeline_schedule!
|
||||
return access_denied! unless can?(current_user, :play_pipeline_schedule, schedule)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -579,6 +579,8 @@ class Project < ApplicationRecord
|
|||
validates :max_artifacts_size, numericality: { only_integer: true, greater_than: 0, allow_nil: true }
|
||||
validates :suggestion_commit_message, length: { maximum: MAX_SUGGESTIONS_TEMPLATE_LENGTH }
|
||||
|
||||
validate :path_availability, if: :path_changed?
|
||||
|
||||
# Scopes
|
||||
scope :pending_delete, -> { where(pending_delete: true) }
|
||||
scope :without_deleted, -> { where(pending_delete: false) }
|
||||
|
|
@ -3241,6 +3243,15 @@ class Project < ApplicationRecord
|
|||
end
|
||||
strong_memoize_attr :frozen_outbound_job_token_scopes?
|
||||
|
||||
def path_availability
|
||||
base, _, host = path.partition('.')
|
||||
|
||||
return unless host == Gitlab.config.pages&.dig('host')
|
||||
return unless ProjectSetting.where(pages_unique_domain: base).exists?
|
||||
|
||||
errors.add(:path, s_('Project|already in use'))
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def pages_unique_domain_enabled?
|
||||
|
|
|
|||
|
|
@ -59,6 +59,8 @@ class ProjectSetting < ApplicationRecord
|
|||
|
||||
validate :validates_mr_default_target_self
|
||||
|
||||
validate :pages_unique_domain_availability, if: :pages_unique_domain_changed?
|
||||
|
||||
attribute :legacy_open_source_license_available, default: -> do
|
||||
Feature.enabled?(:legacy_open_source_license_available, type: :ops)
|
||||
end
|
||||
|
|
@ -109,6 +111,15 @@ class ProjectSetting < ApplicationRecord
|
|||
pages_unique_domain_enabled ||
|
||||
pages_unique_domain_in_database.present?
|
||||
end
|
||||
|
||||
def pages_unique_domain_availability
|
||||
host = Gitlab.config.pages&.dig('host')
|
||||
|
||||
return if host.blank?
|
||||
return unless Project.where(path: "#{pages_unique_domain}.#{host}").exists?
|
||||
|
||||
errors.add(:pages_unique_domain, s_('ProjectSetting|already in use'))
|
||||
end
|
||||
end
|
||||
|
||||
ProjectSetting.prepend_mod
|
||||
|
|
|
|||
|
|
@ -5,7 +5,18 @@ module Ci
|
|||
alias_method :pipeline_schedule, :subject
|
||||
|
||||
condition(:protected_ref) do
|
||||
ref_protected?(@user, @subject.project, @subject.project.repository.tag_exists?(@subject.ref), @subject.ref)
|
||||
if full_ref?(@subject.ref)
|
||||
is_tag = Gitlab::Git.tag_ref?(@subject.ref)
|
||||
ref_name = Gitlab::Git.ref_name(@subject.ref)
|
||||
else
|
||||
# NOTE: this block should not be removed
|
||||
# until the full ref validation is in place
|
||||
# and all old refs are updated and validated
|
||||
is_tag = @subject.project.repository.tag_exists?(@subject.ref)
|
||||
ref_name = @subject.ref
|
||||
end
|
||||
|
||||
ref_protected?(@user, @subject.project, is_tag, ref_name)
|
||||
end
|
||||
|
||||
condition(:owner_of_schedule) do
|
||||
|
|
@ -31,6 +42,15 @@ module Ci
|
|||
enable :take_ownership_pipeline_schedule
|
||||
end
|
||||
|
||||
rule { protected_ref }.prevent :play_pipeline_schedule
|
||||
rule { protected_ref }.policy do
|
||||
prevent :play_pipeline_schedule
|
||||
prevent :create_pipeline_schedule
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def full_ref?(ref)
|
||||
Gitlab::Git.tag_ref?(ref) || Gitlab::Git.branch_ref?(ref)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ module Discussions
|
|||
active_diff_discussions = merge_request.notes.new_diff_notes.discussions.select do |discussion|
|
||||
discussion.active?(merge_request.diff_refs)
|
||||
end
|
||||
paths = active_diff_discussions.flat_map { |n| n.diff_file.paths }
|
||||
paths = active_diff_discussions.flat_map { |n| n.diff_file&.paths }
|
||||
|
||||
[active_diff_discussions, paths]
|
||||
end
|
||||
|
|
|
|||
|
|
@ -34,8 +34,13 @@ module Banzai
|
|||
# https://github.com/vmg/rinku/blob/v2.0.1/ext/rinku/autolink.c#L65
|
||||
#
|
||||
# Rubular: http://rubular.com/r/nrL3r9yUiq
|
||||
# Note that it's not possible to use Gitlab::UntrustedRegexp for LINK_PATTERN,
|
||||
# as `(?<!` is unsupported in `re2`, see https://github.com/google/re2/wiki/Syntax
|
||||
LINK_PATTERN = %r{([a-z][a-z0-9\+\.-]+://[^\s>]+)(?<!\?|!|\.|,|:)}.freeze
|
||||
|
||||
ENTITY_UNTRUSTED = '((?:&[\w#]+;)+)\z'
|
||||
ENTITY_UNTRUSTED_REGEX = Gitlab::UntrustedRegexp.new(ENTITY_UNTRUSTED, multiline: false)
|
||||
|
||||
# Text matching LINK_PATTERN inside these elements will not be linked
|
||||
IGNORE_PARENTS = %w(a code kbd pre script style).to_set
|
||||
|
||||
|
|
@ -85,10 +90,14 @@ module Banzai
|
|||
# Remove any trailing HTML entities and store them for appending
|
||||
# outside the link element. The entity must be marked HTML safe in
|
||||
# order to be output literally rather than escaped.
|
||||
match.gsub!(/((?:&[\w#]+;)+)\z/, '')
|
||||
dropped = (Regexp.last_match(1) || '').html_safe
|
||||
dropped = ''
|
||||
match = ENTITY_UNTRUSTED_REGEX.replace_gsub(match) do |entities|
|
||||
dropped = entities[1].html_safe
|
||||
|
||||
# To match the behaviour of Rinku, if the matched link ends with a
|
||||
''
|
||||
end
|
||||
|
||||
# To match the behavior of Rinku, if the matched link ends with a
|
||||
# closing part of a matched pair of punctuation, we remove that trailing
|
||||
# character unless there are an equal number of closing and opening
|
||||
# characters in the link.
|
||||
|
|
|
|||
|
|
@ -10,13 +10,12 @@ module Gitlab
|
|||
def execute
|
||||
return if host.blank?
|
||||
|
||||
gitlab_host = ::Settings.pages.host.downcase.prepend(".")
|
||||
gitlab_host = ::Gitlab.config.pages.host.downcase.prepend(".")
|
||||
|
||||
if host.ends_with?(gitlab_host)
|
||||
name = host.delete_suffix(gitlab_host)
|
||||
|
||||
by_namespace_domain(name) ||
|
||||
by_unique_domain(name)
|
||||
by_unique_domain(name) || by_namespace_domain(name)
|
||||
else
|
||||
by_custom_domain(host)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -131,7 +131,7 @@ module Gitlab
|
|||
# `NAMESPACE_FORMAT_REGEX`, with the negative lookbehind assertion removed. This means that the client-side validation
|
||||
# will pass for usernames ending in `.atom` and `.git`, but will be caught by the server-side validation.
|
||||
PATH_START_CHAR = '[a-zA-Z0-9_\.]'
|
||||
PATH_REGEX_STR = PATH_START_CHAR + '[a-zA-Z0-9_\-\.]*'
|
||||
PATH_REGEX_STR = PATH_START_CHAR + '[a-zA-Z0-9_\-\.]' + "{0,#{Namespace::URL_MAX_LENGTH - 1}}"
|
||||
NAMESPACE_FORMAT_REGEX_JS = PATH_REGEX_STR + '[a-zA-Z0-9_\-]|[a-zA-Z0-9_]'
|
||||
|
||||
NO_SUFFIX_REGEX = /(?<!\.git|\.atom)/.freeze
|
||||
|
|
|
|||
|
|
@ -36421,6 +36421,9 @@ msgstr ""
|
|||
msgid "ProjectSettings|With GitLab Pages you can host your static websites on GitLab. GitLab Pages uses a caching mechanism for efficiency. Your changes may not take effect until that cache is invalidated, which usually takes less than a minute."
|
||||
msgstr ""
|
||||
|
||||
msgid "ProjectSetting|already in use"
|
||||
msgstr ""
|
||||
|
||||
msgid "ProjectTemplates|.NET Core"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -36718,6 +36721,9 @@ msgstr ""
|
|||
msgid "ProjectsNew|Your project will be created at:"
|
||||
msgstr ""
|
||||
|
||||
msgid "Project|already in use"
|
||||
msgstr ""
|
||||
|
||||
msgid "PrometheusAlerts|exceeded"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@
|
|||
"@gitlab/svgs": "3.53.0",
|
||||
"@gitlab/ui": "64.10.1",
|
||||
"@gitlab/visual-review-tools": "1.7.3",
|
||||
"@gitlab/web-ide": "0.0.1-dev-20230614124516",
|
||||
"@gitlab/web-ide": "0.0.1-dev-20230713160749-patch-1",
|
||||
"@mattiasbuelens/web-streams-adapter": "^0.1.0",
|
||||
"@popperjs/core": "^2.11.2",
|
||||
"@rails/actioncable": "6.1.4-7",
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ require 'spec_helper'
|
|||
|
||||
RSpec.describe Projects::PipelineSchedulesController, feature_category: :continuous_integration do
|
||||
include AccessMatchersForController
|
||||
using RSpec::Parameterized::TableSyntax
|
||||
|
||||
let_it_be(:user) { create(:user) }
|
||||
let_it_be(:project) { create(:project, :public, :repository) }
|
||||
|
|
@ -45,6 +46,43 @@ RSpec.describe Projects::PipelineSchedulesController, feature_category: :continu
|
|||
end
|
||||
end
|
||||
|
||||
shared_examples 'protecting ref' do
|
||||
where(:branch_access_levels, :tag_access_level, :maintainer_accessible, :developer_accessible) do
|
||||
[:no_one_can_push, :no_one_can_merge] | :no_one_can_create | \
|
||||
:be_denied_for | :be_denied_for
|
||||
[:maintainers_can_push, :maintainers_can_merge] | :maintainers_can_create | \
|
||||
:be_allowed_for | :be_denied_for
|
||||
[:developers_can_push, :developers_can_merge] | :developers_can_create | \
|
||||
:be_allowed_for | :be_allowed_for
|
||||
end
|
||||
|
||||
with_them do
|
||||
context 'when branch is protected' do
|
||||
let(:ref_prefix) { 'heads' }
|
||||
let(:ref_name) { 'master' }
|
||||
|
||||
before do
|
||||
create(:protected_branch, *branch_access_levels, name: ref_name, project: project)
|
||||
end
|
||||
|
||||
it { expect { go }.to try(maintainer_accessible, :maintainer).of(project) }
|
||||
it { expect { go }.to try(developer_accessible, :developer).of(project) }
|
||||
end
|
||||
|
||||
context 'when tag is protected' do
|
||||
let(:ref_prefix) { 'tags' }
|
||||
let(:ref_name) { 'v1.0.0' }
|
||||
|
||||
before do
|
||||
create(:protected_tag, tag_access_level, name: ref_name, project: project)
|
||||
end
|
||||
|
||||
it { expect { go }.to try(maintainer_accessible, :maintainer).of(project) }
|
||||
it { expect { go }.to try(developer_accessible, :developer).of(project) }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET #index' do
|
||||
render_views
|
||||
|
||||
|
|
@ -158,7 +196,9 @@ RSpec.describe Projects::PipelineSchedulesController, feature_category: :continu
|
|||
end
|
||||
|
||||
describe 'security' do
|
||||
let(:schedule) { attributes_for(:ci_pipeline_schedule) }
|
||||
let(:schedule) { attributes_for(:ci_pipeline_schedule, ref: "refs/#{ref_prefix}/#{ref_name}") }
|
||||
let(:ref_prefix) { 'heads' }
|
||||
let(:ref_name) { "master" }
|
||||
|
||||
it 'is allowed for admin when admin mode enabled', :enable_admin_mode do
|
||||
expect { go }.to be_allowed_for(:admin)
|
||||
|
|
@ -177,6 +217,8 @@ RSpec.describe Projects::PipelineSchedulesController, feature_category: :continu
|
|||
it { expect { go }.to be_denied_for(:user) }
|
||||
it { expect { go }.to be_denied_for(:external) }
|
||||
it { expect { go }.to be_denied_for(:visitor) }
|
||||
|
||||
it_behaves_like 'protecting ref'
|
||||
end
|
||||
|
||||
def go
|
||||
|
|
@ -427,7 +469,7 @@ RSpec.describe Projects::PipelineSchedulesController, feature_category: :continu
|
|||
end
|
||||
|
||||
describe 'POST #play', :clean_gitlab_redis_rate_limiting do
|
||||
let(:ref) { 'master' }
|
||||
let(:ref_name) { 'master' }
|
||||
|
||||
before do
|
||||
project.add_developer(user)
|
||||
|
|
@ -443,7 +485,7 @@ RSpec.describe Projects::PipelineSchedulesController, feature_category: :continu
|
|||
it 'does not allow pipeline to be executed' do
|
||||
expect(RunPipelineScheduleWorker).not_to receive(:perform_async)
|
||||
|
||||
post :play, params: { namespace_id: project.namespace.to_param, project_id: project, id: pipeline_schedule.id }
|
||||
go
|
||||
|
||||
expect(response).to have_gitlab_http_status(:not_found)
|
||||
end
|
||||
|
|
@ -453,16 +495,14 @@ RSpec.describe Projects::PipelineSchedulesController, feature_category: :continu
|
|||
it 'executes a new pipeline' do
|
||||
expect(RunPipelineScheduleWorker).to receive(:perform_async).with(pipeline_schedule.id, user.id).and_return('job-123')
|
||||
|
||||
post :play, params: { namespace_id: project.namespace.to_param, project_id: project, id: pipeline_schedule.id }
|
||||
go
|
||||
|
||||
expect(flash[:notice]).to start_with 'Successfully scheduled a pipeline to run'
|
||||
expect(response).to have_gitlab_http_status(:found)
|
||||
end
|
||||
|
||||
it 'prevents users from scheduling the same pipeline repeatedly' do
|
||||
2.times do
|
||||
post :play, params: { namespace_id: project.namespace.to_param, project_id: project, id: pipeline_schedule.id }
|
||||
end
|
||||
2.times { go }
|
||||
|
||||
expect(flash.to_a.size).to eq(2)
|
||||
expect(flash[:alert]).to eq _('You cannot play this scheduled pipeline at the moment. Please wait a minute.')
|
||||
|
|
@ -470,17 +510,14 @@ RSpec.describe Projects::PipelineSchedulesController, feature_category: :continu
|
|||
end
|
||||
end
|
||||
|
||||
context 'when a developer attempts to schedule a protected ref' do
|
||||
it 'does not allow pipeline to be executed' do
|
||||
create(:protected_branch, project: project, name: ref)
|
||||
protected_schedule = create(:ci_pipeline_schedule, project: project, ref: ref)
|
||||
describe 'security' do
|
||||
let!(:pipeline_schedule) { create(:ci_pipeline_schedule, project: project, ref: "refs/#{ref_prefix}/#{ref_name}") }
|
||||
|
||||
expect(RunPipelineScheduleWorker).not_to receive(:perform_async)
|
||||
it_behaves_like 'protecting ref'
|
||||
end
|
||||
|
||||
post :play, params: { namespace_id: project.namespace.to_param, project_id: project, id: protected_schedule.id }
|
||||
|
||||
expect(response).to have_gitlab_http_status(:not_found)
|
||||
end
|
||||
def go
|
||||
post :play, params: { namespace_id: project.namespace.to_param, project_id: project, id: pipeline_schedule.id }
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -54,6 +54,10 @@ FactoryBot.define do
|
|||
end
|
||||
end
|
||||
|
||||
factory :file_diff_position do
|
||||
position_type { 'file' }
|
||||
end
|
||||
|
||||
factory :image_diff_position do
|
||||
position_type { 'image' }
|
||||
x { 1 }
|
||||
|
|
|
|||
|
|
@ -0,0 +1,49 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe 'User views comment on a diff file', :js, feature_category: :code_review_workflow do
|
||||
include MergeRequestDiffHelpers
|
||||
include RepoHelpers
|
||||
|
||||
let_it_be(:project) { create(:project, :repository) }
|
||||
let_it_be(:merge_request) do
|
||||
create(:merge_request_with_diffs, source_project: project, target_project: project, source_branch: 'merge-test')
|
||||
end
|
||||
|
||||
let_it_be(:user) { create(:user) }
|
||||
|
||||
before_all do
|
||||
project.add_maintainer(user)
|
||||
end
|
||||
|
||||
before do
|
||||
sign_in(user)
|
||||
|
||||
visit(diffs_project_merge_request_path(project, merge_request))
|
||||
end
|
||||
|
||||
context 'with invalid start_sha position' do
|
||||
before do
|
||||
diff_refs = Gitlab::Diff::DiffRefs.new(
|
||||
base_sha: merge_request.diff_refs.base_sha,
|
||||
start_sha: 'fakesha',
|
||||
head_sha: merge_request.diff_refs.head_sha
|
||||
)
|
||||
position = build(:file_diff_position, file: 'files/ruby/popen.rb', diff_refs: diff_refs)
|
||||
create(:diff_note_on_merge_request, noteable: merge_request, project: project, position: position)
|
||||
end
|
||||
|
||||
it 'renders diffs' do
|
||||
visit diffs_project_merge_request_path(project, merge_request)
|
||||
|
||||
expect(page).to have_selector('.diff-file')
|
||||
end
|
||||
|
||||
it 'renders discussion on overview tab' do
|
||||
visit project_merge_request_path(project, merge_request)
|
||||
|
||||
expect(page).to have_selector('.note-discussion')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -226,6 +226,22 @@ RSpec.describe Banzai::Filter::AutolinkFilter, feature_category: :team_planning
|
|||
end
|
||||
end
|
||||
|
||||
it 'protects against malicious backtracking' do
|
||||
doc = "http://#{'&' * 1_000_000}x"
|
||||
|
||||
expect do
|
||||
Timeout.timeout(30.seconds) { filter(doc) }
|
||||
end.not_to raise_error
|
||||
end
|
||||
|
||||
it 'does not timeout with excessively long scheme' do
|
||||
doc = "#{'h' * 1_000_000}://example.com"
|
||||
|
||||
expect do
|
||||
Timeout.timeout(30.seconds) { filter(doc) }
|
||||
end.not_to raise_error
|
||||
end
|
||||
|
||||
# Rinku does not escape these characters in HTML attributes, but content_tag
|
||||
# does. We don't care about that difference for these specs, though.
|
||||
def unescape(html)
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ RSpec.describe Banzai::Filter::References::ProjectReferenceFilter, feature_categ
|
|||
|
||||
it_behaves_like 'fails fast', 'A' * 50000
|
||||
it_behaves_like 'fails fast', '/a' * 50000
|
||||
it_behaves_like 'fails fast', "mailto:#{'a-' * 499_000}@aaaaaaaa..aaaaaaaa.example.com"
|
||||
end
|
||||
|
||||
it 'allows references with text after the > character' do
|
||||
|
|
|
|||
|
|
@ -9,6 +9,10 @@ RSpec.describe Gitlab::Pages::VirtualHostFinder, feature_category: :pages do
|
|||
project.update_pages_deployment!(create(:pages_deployment, project: project))
|
||||
end
|
||||
|
||||
before do
|
||||
stub_pages_setting(host: 'example.com')
|
||||
end
|
||||
|
||||
it 'returns nil when host is empty' do
|
||||
expect(described_class.new(nil).execute).to be_nil
|
||||
expect(described_class.new('').execute).to be_nil
|
||||
|
|
@ -69,7 +73,7 @@ RSpec.describe Gitlab::Pages::VirtualHostFinder, feature_category: :pages do
|
|||
end
|
||||
|
||||
it 'returns the virual domain with no lookup_paths' do
|
||||
virtual_domain = described_class.new("#{project.namespace.path}.#{Settings.pages.host}").execute
|
||||
virtual_domain = described_class.new("#{project.namespace.path}.example.com").execute
|
||||
|
||||
expect(virtual_domain).to be_an_instance_of(Pages::VirtualDomain)
|
||||
expect(virtual_domain.cache_key).to match(/pages_domain_for_namespace_#{project.namespace.id}_/)
|
||||
|
|
@ -82,7 +86,7 @@ RSpec.describe Gitlab::Pages::VirtualHostFinder, feature_category: :pages do
|
|||
end
|
||||
|
||||
it 'returns the virual domain with no lookup_paths' do
|
||||
virtual_domain = described_class.new("#{project.namespace.path}.#{Settings.pages.host}".downcase).execute
|
||||
virtual_domain = described_class.new("#{project.namespace.path}.example.com".downcase).execute
|
||||
|
||||
expect(virtual_domain).to be_an_instance_of(Pages::VirtualDomain)
|
||||
expect(virtual_domain.cache_key).to be_nil
|
||||
|
|
@ -104,7 +108,7 @@ RSpec.describe Gitlab::Pages::VirtualHostFinder, feature_category: :pages do
|
|||
end
|
||||
|
||||
it 'returns the virual domain when there are pages deployed for the project' do
|
||||
virtual_domain = described_class.new("#{project.namespace.path}.#{Settings.pages.host}").execute
|
||||
virtual_domain = described_class.new("#{project.namespace.path}.example.com").execute
|
||||
|
||||
expect(virtual_domain).to be_an_instance_of(Pages::VirtualDomain)
|
||||
expect(virtual_domain.cache_key).to match(/pages_domain_for_namespace_#{project.namespace.id}_/)
|
||||
|
|
@ -113,7 +117,7 @@ RSpec.describe Gitlab::Pages::VirtualHostFinder, feature_category: :pages do
|
|||
end
|
||||
|
||||
it 'finds domain with case-insensitive' do
|
||||
virtual_domain = described_class.new("#{project.namespace.path}.#{Settings.pages.host.upcase}").execute
|
||||
virtual_domain = described_class.new("#{project.namespace.path}.Example.com").execute
|
||||
|
||||
expect(virtual_domain).to be_an_instance_of(Pages::VirtualDomain)
|
||||
expect(virtual_domain.cache_key).to match(/pages_domain_for_namespace_#{project.namespace.id}_/)
|
||||
|
|
@ -127,7 +131,7 @@ RSpec.describe Gitlab::Pages::VirtualHostFinder, feature_category: :pages do
|
|||
end
|
||||
|
||||
it 'returns the virual domain when there are pages deployed for the project' do
|
||||
virtual_domain = described_class.new("#{project.namespace.path}.#{Settings.pages.host}").execute
|
||||
virtual_domain = described_class.new("#{project.namespace.path}.example.com").execute
|
||||
|
||||
expect(virtual_domain).to be_an_instance_of(Pages::VirtualDomain)
|
||||
expect(virtual_domain.cache_key).to be_nil
|
||||
|
|
@ -143,7 +147,7 @@ RSpec.describe Gitlab::Pages::VirtualHostFinder, feature_category: :pages do
|
|||
project.project_setting.update!(pages_unique_domain: 'unique-domain')
|
||||
end
|
||||
|
||||
subject(:virtual_domain) { described_class.new("unique-domain.#{Settings.pages.host.upcase}").execute }
|
||||
subject(:virtual_domain) { described_class.new('unique-domain.example.com').execute }
|
||||
|
||||
context 'when pages unique domain is enabled' do
|
||||
before_all do
|
||||
|
|
@ -171,6 +175,19 @@ RSpec.describe Gitlab::Pages::VirtualHostFinder, feature_category: :pages do
|
|||
expect(virtual_domain.lookup_paths.first.project_id).to eq(project.id)
|
||||
end
|
||||
|
||||
context 'when a project path conflicts with a unique domain' do
|
||||
it 'prioritizes the unique domain project' do
|
||||
group = create(:group, path: 'unique-domain')
|
||||
other_project = build(:project, path: 'unique-domain.example.com', group: group)
|
||||
other_project.save!(validate: false)
|
||||
other_project.update_pages_deployment!(create(:pages_deployment, project: other_project))
|
||||
other_project.mark_pages_as_deployed
|
||||
|
||||
expect(virtual_domain).to be_an_instance_of(Pages::VirtualDomain)
|
||||
expect(virtual_domain.lookup_paths.first.project_id).to eq(project.id)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when :cache_pages_domain_api is disabled' do
|
||||
before do
|
||||
stub_feature_flags(cache_pages_domain_api: false)
|
||||
|
|
|
|||
|
|
@ -77,12 +77,36 @@ RSpec.describe ProjectSetting, type: :model, feature_category: :groups_and_proje
|
|||
expect(project_setting).not_to be_valid
|
||||
expect(project_setting.errors.full_messages).to include("Pages unique domain has already been taken")
|
||||
end
|
||||
|
||||
it "validates if the pages_unique_domain already exist as a project path" do
|
||||
stub_pages_setting(host: 'example.com')
|
||||
|
||||
create(:project, path: "random-unique-domain.example.com")
|
||||
project_setting = build(:project_setting, pages_unique_domain: "random-unique-domain")
|
||||
|
||||
expect(project_setting).not_to be_valid
|
||||
expect(project_setting.errors.full_messages_for(:pages_unique_domain))
|
||||
.to match(["Pages unique domain already in use"])
|
||||
end
|
||||
|
||||
context "when updating" do
|
||||
it "validates if the pages_unique_domain already exist as a project path" do
|
||||
stub_pages_setting(host: 'example.com')
|
||||
project_setting = create(:project_setting)
|
||||
|
||||
create(:project, path: "random-unique-domain.example.com")
|
||||
|
||||
expect(project_setting.update(pages_unique_domain: "random-unique-domain")).to eq(false)
|
||||
expect(project_setting.errors.full_messages_for(:pages_unique_domain))
|
||||
.to match(["Pages unique domain already in use"])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'target_platforms=' do
|
||||
it 'stringifies and sorts' do
|
||||
project_setting = build(:project_setting, target_platforms: [:watchos, :ios])
|
||||
expect(project_setting.target_platforms).to eq %w(ios watchos)
|
||||
expect(project_setting.target_platforms).to eq %w[ios watchos]
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -827,6 +827,37 @@ RSpec.describe Project, factory_default: :keep, feature_category: :groups_and_pr
|
|||
expect(project).to be_valid
|
||||
end
|
||||
|
||||
context 'when validating if path already exist as pages unique domain' do
|
||||
before do
|
||||
stub_pages_setting(host: 'example.com')
|
||||
end
|
||||
|
||||
it 'rejects paths that match pages unique domain' do
|
||||
create(:project_setting, pages_unique_domain: 'some-unique-domain')
|
||||
|
||||
project = build(:project, path: 'some-unique-domain.example.com')
|
||||
|
||||
expect(project).not_to be_valid
|
||||
expect(project.errors.full_messages_for(:path)).to match(['Path already in use'])
|
||||
end
|
||||
|
||||
it 'accepts path when the host does not match' do
|
||||
create(:project_setting, pages_unique_domain: 'some-unique-domain')
|
||||
|
||||
project = build(:project, path: 'some-unique-domain.another-example.com')
|
||||
|
||||
expect(project).to be_valid
|
||||
end
|
||||
|
||||
it 'accepts path when the domain does not match' do
|
||||
create(:project_setting, pages_unique_domain: 'another-unique-domain')
|
||||
|
||||
project = build(:project, path: 'some-unique-domain.example.com')
|
||||
|
||||
expect(project).to be_valid
|
||||
end
|
||||
end
|
||||
|
||||
context 'path is unchanged' do
|
||||
let_it_be(:invalid_path_project) do
|
||||
project = create(:project, :repository, :public)
|
||||
|
|
@ -4921,6 +4952,33 @@ RSpec.describe Project, factory_default: :keep, feature_category: :groups_and_pr
|
|||
project.update!(visibility_level: Gitlab::VisibilityLevel::INTERNAL)
|
||||
end
|
||||
|
||||
context 'when validating if path already exist as pages unique domain' do
|
||||
before do
|
||||
stub_pages_setting(host: 'example.com')
|
||||
end
|
||||
|
||||
it 'rejects paths that match pages unique domain' do
|
||||
stub_pages_setting(host: 'example.com')
|
||||
create(:project_setting, pages_unique_domain: 'some-unique-domain')
|
||||
|
||||
expect(project.update(path: 'some-unique-domain.example.com')).to eq(false)
|
||||
expect(project.errors.full_messages_for(:path)).to match(['Path already in use'])
|
||||
end
|
||||
|
||||
it 'accepts path when the host does not match' do
|
||||
create(:project_setting, pages_unique_domain: 'some-unique-domain')
|
||||
|
||||
expect(project.update(path: 'some-unique-domain.another-example.com')).to eq(true)
|
||||
end
|
||||
|
||||
it 'accepts path when the domain does not match' do
|
||||
stub_pages_setting(host: 'example.com')
|
||||
create(:project_setting, pages_unique_domain: 'another-unique-domain')
|
||||
|
||||
expect(project.update(path: 'some-unique-domain.example.com')).to eq(true)
|
||||
end
|
||||
end
|
||||
|
||||
it 'does not validate the visibility' do
|
||||
expect(project).not_to receive(:visibility_level_allowed_as_fork).and_call_original
|
||||
expect(project).not_to receive(:visibility_level_allowed_by_group).and_call_original
|
||||
|
|
|
|||
|
|
@ -2,10 +2,13 @@
|
|||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Ci::PipelineSchedulePolicy, :models, :clean_gitlab_redis_cache do
|
||||
RSpec.describe Ci::PipelineSchedulePolicy, :models, :clean_gitlab_redis_cache, feature_category: :continuous_integration do
|
||||
using RSpec::Parameterized::TableSyntax
|
||||
|
||||
let_it_be(:user) { create(:user) }
|
||||
let_it_be(:project) { create(:project, :repository) }
|
||||
let_it_be(:pipeline_schedule, reload: true) { create(:ci_pipeline_schedule, :nightly, project: project) }
|
||||
let_it_be_with_reload(:project) { create(:project, :repository, create_tag: tag_ref_name) }
|
||||
let_it_be_with_reload(:pipeline_schedule) { create(:ci_pipeline_schedule, :nightly, project: project) }
|
||||
let_it_be(:tag_ref_name) { "v1.0.0" }
|
||||
|
||||
let(:policy) do
|
||||
described_class.new(user, pipeline_schedule)
|
||||
|
|
@ -13,51 +16,143 @@ RSpec.describe Ci::PipelineSchedulePolicy, :models, :clean_gitlab_redis_cache do
|
|||
|
||||
describe 'rules' do
|
||||
describe 'rules for protected ref' do
|
||||
before do
|
||||
project.add_developer(user)
|
||||
end
|
||||
context 'for branch' do
|
||||
%w[refs/heads/master master].each do |branch_ref|
|
||||
context "with #{branch_ref}" do
|
||||
let_it_be(:branch_ref_name) { "master" }
|
||||
let_it_be(:branch_pipeline_schedule) do
|
||||
create(:ci_pipeline_schedule, :nightly, project: project, ref: branch_ref)
|
||||
end
|
||||
|
||||
context 'when no one can push or merge to the branch' do
|
||||
before do
|
||||
create(:protected_branch, :no_one_can_push, name: pipeline_schedule.ref, project: project)
|
||||
end
|
||||
where(:push_access_level, :merge_access_level, :project_role, :accessible) do
|
||||
:no_one_can_push | :no_one_can_merge | :owner | :be_disallowed
|
||||
:no_one_can_push | :no_one_can_merge | :maintainer | :be_disallowed
|
||||
:no_one_can_push | :no_one_can_merge | :developer | :be_disallowed
|
||||
:no_one_can_push | :no_one_can_merge | :reporter | :be_disallowed
|
||||
:no_one_can_push | :no_one_can_merge | :guest | :be_disallowed
|
||||
|
||||
it 'does not include ability to play pipeline schedule' do
|
||||
expect(policy).to be_disallowed :play_pipeline_schedule
|
||||
:maintainers_can_push | :no_one_can_merge | :owner | :be_allowed
|
||||
:maintainers_can_push | :no_one_can_merge | :maintainer | :be_allowed
|
||||
:maintainers_can_push | :no_one_can_merge | :developer | :be_disallowed
|
||||
:maintainers_can_push | :no_one_can_merge | :reporter | :be_disallowed
|
||||
:maintainers_can_push | :no_one_can_merge | :guest | :be_disallowed
|
||||
|
||||
:developers_can_push | :no_one_can_merge | :owner | :be_allowed
|
||||
:developers_can_push | :no_one_can_merge | :maintainer | :be_allowed
|
||||
:developers_can_push | :no_one_can_merge | :developer | :be_allowed
|
||||
:developers_can_push | :no_one_can_merge | :reporter | :be_disallowed
|
||||
:developers_can_push | :no_one_can_merge | :guest | :be_disallowed
|
||||
|
||||
:no_one_can_push | :maintainers_can_merge | :owner | :be_allowed
|
||||
:no_one_can_push | :maintainers_can_merge | :maintainer | :be_allowed
|
||||
:no_one_can_push | :maintainers_can_merge | :developer | :be_disallowed
|
||||
:no_one_can_push | :maintainers_can_merge | :reporter | :be_disallowed
|
||||
:no_one_can_push | :maintainers_can_merge | :guest | :be_disallowed
|
||||
|
||||
:maintainers_can_push | :maintainers_can_merge | :owner | :be_allowed
|
||||
:maintainers_can_push | :maintainers_can_merge | :maintainer | :be_allowed
|
||||
:maintainers_can_push | :maintainers_can_merge | :developer | :be_disallowed
|
||||
:maintainers_can_push | :maintainers_can_merge | :reporter | :be_disallowed
|
||||
:maintainers_can_push | :maintainers_can_merge | :guest | :be_disallowed
|
||||
|
||||
:developers_can_push | :maintainers_can_merge | :owner | :be_allowed
|
||||
:developers_can_push | :maintainers_can_merge | :maintainer | :be_allowed
|
||||
:developers_can_push | :maintainers_can_merge | :developer | :be_allowed
|
||||
:developers_can_push | :maintainers_can_merge | :reporter | :be_disallowed
|
||||
:developers_can_push | :maintainers_can_merge | :guest | :be_disallowed
|
||||
|
||||
:no_one_can_push | :developers_can_merge | :owner | :be_allowed
|
||||
:no_one_can_push | :developers_can_merge | :maintainer | :be_allowed
|
||||
:no_one_can_push | :developers_can_merge | :developer | :be_allowed
|
||||
:no_one_can_push | :developers_can_merge | :reporter | :be_disallowed
|
||||
:no_one_can_push | :developers_can_merge | :guest | :be_disallowed
|
||||
|
||||
:maintainers_can_push | :developers_can_merge | :owner | :be_allowed
|
||||
:maintainers_can_push | :developers_can_merge | :maintainer | :be_allowed
|
||||
:maintainers_can_push | :developers_can_merge | :developer | :be_allowed
|
||||
:maintainers_can_push | :developers_can_merge | :reporter | :be_disallowed
|
||||
:maintainers_can_push | :developers_can_merge | :guest | :be_disallowed
|
||||
|
||||
:developers_can_push | :developers_can_merge | :owner | :be_allowed
|
||||
:developers_can_push | :developers_can_merge | :maintainer | :be_allowed
|
||||
:developers_can_push | :developers_can_merge | :developer | :be_allowed
|
||||
:developers_can_push | :developers_can_merge | :reporter | :be_disallowed
|
||||
:developers_can_push | :developers_can_merge | :guest | :be_disallowed
|
||||
end
|
||||
|
||||
with_them do
|
||||
before do
|
||||
create(:protected_branch, push_access_level, merge_access_level, name: branch_ref_name,
|
||||
project: project)
|
||||
project.add_role(user, project_role)
|
||||
end
|
||||
|
||||
context 'for create_pipeline_schedule' do
|
||||
subject(:policy) { described_class.new(user, new_branch_pipeline_schedule) }
|
||||
|
||||
let(:new_branch_pipeline_schedule) { project.pipeline_schedules.new(ref: branch_ref) }
|
||||
|
||||
it { expect(policy).to try(accessible, :create_pipeline_schedule) }
|
||||
end
|
||||
|
||||
context 'for play_pipeline_schedule' do
|
||||
subject(:policy) { described_class.new(user, branch_pipeline_schedule) }
|
||||
|
||||
it { expect(policy).to try(accessible, :play_pipeline_schedule) }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when developers can push to the branch' do
|
||||
before do
|
||||
create(:protected_branch, :developers_can_merge, name: pipeline_schedule.ref, project: project)
|
||||
end
|
||||
context 'for tag' do
|
||||
%w[refs/tags/v1.0.0 v1.0.0].each do |tag_ref|
|
||||
context "with #{tag_ref}" do
|
||||
let_it_be(:tag_pipeline_schedule) do
|
||||
create(:ci_pipeline_schedule, :nightly, project: project, ref: tag_ref)
|
||||
end
|
||||
|
||||
it 'includes ability to update pipeline' do
|
||||
expect(policy).to be_allowed :play_pipeline_schedule
|
||||
end
|
||||
end
|
||||
where(:access_level, :project_role, :accessible) do
|
||||
:no_one_can_create | :owner | :be_disallowed
|
||||
:no_one_can_create | :maintainer | :be_disallowed
|
||||
:no_one_can_create | :developer | :be_disallowed
|
||||
:no_one_can_create | :reporter | :be_disallowed
|
||||
:no_one_can_create | :guest | :be_disallowed
|
||||
|
||||
context 'when no one can create the tag' do
|
||||
let(:tag) { 'v1.0.0' }
|
||||
:maintainers_can_create | :owner | :be_allowed
|
||||
:maintainers_can_create | :maintainer | :be_allowed
|
||||
:maintainers_can_create | :developer | :be_disallowed
|
||||
:maintainers_can_create | :reporter | :be_disallowed
|
||||
:maintainers_can_create | :guest | :be_disallowed
|
||||
|
||||
before do
|
||||
pipeline_schedule.update!(ref: tag)
|
||||
:developers_can_create | :owner | :be_allowed
|
||||
:developers_can_create | :maintainer | :be_allowed
|
||||
:developers_can_create | :developer | :be_allowed
|
||||
:developers_can_create | :reporter | :be_disallowed
|
||||
:developers_can_create | :guest | :be_disallowed
|
||||
end
|
||||
|
||||
create(:protected_tag, :no_one_can_create, name: pipeline_schedule.ref, project: project)
|
||||
end
|
||||
with_them do
|
||||
before do
|
||||
create(:protected_tag, access_level, name: tag_ref_name, project: project)
|
||||
project.add_role(user, project_role)
|
||||
end
|
||||
|
||||
it 'does not include ability to play pipeline schedule' do
|
||||
expect(policy).to be_disallowed :play_pipeline_schedule
|
||||
end
|
||||
end
|
||||
context 'for create_pipeline_schedule' do
|
||||
subject(:policy) { described_class.new(user, new_tag_pipeline_schedule) }
|
||||
|
||||
context 'when no one can create the tag but it is not a tag' do
|
||||
before do
|
||||
create(:protected_tag, :no_one_can_create, name: pipeline_schedule.ref, project: project)
|
||||
end
|
||||
let(:new_tag_pipeline_schedule) { project.pipeline_schedules.new(ref: tag_ref) }
|
||||
|
||||
it 'includes ability to play pipeline schedule' do
|
||||
expect(policy).to be_allowed :play_pipeline_schedule
|
||||
it { expect(policy).to try(accessible, :create_pipeline_schedule) }
|
||||
end
|
||||
|
||||
context 'for play_pipeline_schedule' do
|
||||
subject(:policy) { described_class.new(user, tag_pipeline_schedule) }
|
||||
|
||||
it { expect(policy).to try(accessible, :play_pipeline_schedule) }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -6,7 +6,9 @@ RSpec.describe Ci::PipelineSchedules::UpdateService, feature_category: :continuo
|
|||
let_it_be(:user) { create(:user) }
|
||||
let_it_be(:reporter) { create(:user) }
|
||||
let_it_be(:project) { create(:project, :public, :repository) }
|
||||
let_it_be(:pipeline_schedule) { create(:ci_pipeline_schedule, project: project, owner: user) }
|
||||
let_it_be(:pipeline_schedule) do
|
||||
create(:ci_pipeline_schedule, project: project, owner: user, ref: 'master')
|
||||
end
|
||||
|
||||
before_all do
|
||||
project.add_maintainer(user)
|
||||
|
|
|
|||
|
|
@ -1146,10 +1146,10 @@
|
|||
resolved "https://registry.yarnpkg.com/@gitlab/visual-review-tools/-/visual-review-tools-1.7.3.tgz#9ea641146436da388ffbad25d7f2abe0df52c235"
|
||||
integrity sha512-NMV++7Ew1FSBDN1xiZaauU9tfeSfgDHcOLpn+8bGpP+O5orUPm2Eu66R5eC5gkjBPaXosNAxNWtriee+aFk4+g==
|
||||
|
||||
"@gitlab/web-ide@0.0.1-dev-20230614124516":
|
||||
version "0.0.1-dev-20230614124516"
|
||||
resolved "https://registry.yarnpkg.com/@gitlab/web-ide/-/web-ide-0.0.1-dev-20230614124516.tgz#87ef511112e954dc3614e28fdf635b140bab22b7"
|
||||
integrity sha512-VRKiDzhRNIHsyKbln8YtQjVvsOkF+4N1OUqaldu2gYX4Nfw9g57sTwnnX44aYcHFgPVgo5a/ScRpnCiBl6Phxg==
|
||||
"@gitlab/web-ide@0.0.1-dev-20230713160749-patch-1":
|
||||
version "0.0.1-dev-20230713160749-patch-1"
|
||||
resolved "https://registry.yarnpkg.com/@gitlab/web-ide/-/web-ide-0.0.1-dev-20230713160749-patch-1.tgz#6420b55aae444533f9a4bd6269503d98a72aaa2e"
|
||||
integrity sha512-Dh8XQyPwDY6fkd/A+hTHCqrD23u5qnlaxKu5myyxDEgBNGgu4SGblFU9B6NHNm8eGUZk6Cs5MuMk+NUvWRKbmA==
|
||||
|
||||
"@graphql-eslint/eslint-plugin@3.19.1":
|
||||
version "3.19.1"
|
||||
|
|
|
|||
Loading…
Reference in New Issue