Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
a3764262c0
commit
40e8ba2fc8
|
|
@ -127,7 +127,7 @@ variables:
|
|||
# Run with decomposed databases by default
|
||||
DECOMPOSED_DB: "true"
|
||||
|
||||
DOCS_REVIEW_APPS_DOMAIN: "docs.gitlab-review-app"
|
||||
DOCS_REVIEW_APPS_DOMAIN: "docs.gitlab-review.app"
|
||||
DOCS_GITLAB_REPO_SUFFIX: "ee"
|
||||
|
||||
REVIEW_APPS_IMAGE: "${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images/ruby-3.0:gcloud-383-kubectl-1.23-helm-3.5"
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ include:
|
|||
- local: .gitlab/ci/package-and-test/rules.gitlab-ci.yml
|
||||
- local: .gitlab/ci/package-and-test/variables.gitlab-ci.yml
|
||||
- project: gitlab-org/quality/pipeline-common
|
||||
ref: 1.4.0
|
||||
ref: 1.3.0
|
||||
file:
|
||||
- /ci/base.gitlab-ci.yml
|
||||
- /ci/allure-report.yml
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
include:
|
||||
- project: gitlab-org/quality/pipeline-common
|
||||
ref: 1.4.0
|
||||
ref: 1.3.0
|
||||
file:
|
||||
- /ci/base.gitlab-ci.yml
|
||||
- /ci/allure-report.yml
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
ef8362fdf1c0eca9c73fb0fa4dc5b45c5c7965d8
|
||||
3d5bb4cdfd5f31e5e5f0c7506905755e51d0b611
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ export default {
|
|||
},
|
||||
dropdownPopperOpts: {
|
||||
placement: 'bottom',
|
||||
positionFixed: true,
|
||||
},
|
||||
components: {
|
||||
CiIcon,
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
import { GlTableLite } from '@gitlab/ui';
|
||||
import { __, s__ } from '~/locale';
|
||||
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
|
||||
import { durationTimeFormatted } from '~/lib/utils/datetime_utility';
|
||||
import CiBadge from '~/vue_shared/components/ci_badge_link.vue';
|
||||
import RunnerTags from '~/runner/components/runner_tags.vue';
|
||||
import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue';
|
||||
|
|
@ -47,6 +48,14 @@ export default {
|
|||
commitPath(job) {
|
||||
return job.commitPath;
|
||||
},
|
||||
duration(job) {
|
||||
const { duration } = job;
|
||||
return duration ? durationTimeFormatted(duration) : '';
|
||||
},
|
||||
queued(job) {
|
||||
const { queuedDuration } = job;
|
||||
return queuedDuration ? durationTimeFormatted(queuedDuration) : '';
|
||||
},
|
||||
},
|
||||
fields: [
|
||||
tableField({ key: 'status', label: s__('Job|Status') }),
|
||||
|
|
@ -54,6 +63,8 @@ export default {
|
|||
tableField({ key: 'project', label: __('Project') }),
|
||||
tableField({ key: 'commit', label: __('Commit') }),
|
||||
tableField({ key: 'finished_at', label: s__('Job|Finished at') }),
|
||||
tableField({ key: 'duration', label: s__('Job|Duration') }),
|
||||
tableField({ key: 'queued', label: s__('Job|Queued') }),
|
||||
tableField({ key: 'tags', label: s__('Runners|Tags') }),
|
||||
],
|
||||
};
|
||||
|
|
@ -84,12 +95,20 @@ export default {
|
|||
<link-cell :href="commitPath(item)"> {{ commitShortSha(item) }}</link-cell>
|
||||
</template>
|
||||
|
||||
<template #cell(tags)="{ item = {} }">
|
||||
<runner-tags :tag-list="item.tags" />
|
||||
</template>
|
||||
|
||||
<template #cell(finished_at)="{ item = {} }">
|
||||
<time-ago v-if="item.finishedAt" :time="item.finishedAt" />
|
||||
</template>
|
||||
|
||||
<template #cell(duration)="{ item = {} }">
|
||||
{{ duration(item) }}
|
||||
</template>
|
||||
|
||||
<template #cell(queued)="{ item = {} }">
|
||||
{{ queued(item) }}
|
||||
</template>
|
||||
|
||||
<template #cell(tags)="{ item = {} }">
|
||||
<runner-tags :tag-list="item.tags" />
|
||||
</template>
|
||||
</gl-table-lite>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -25,8 +25,10 @@ query getRunnerJobs($id: CiRunnerID!, $first: Int, $last: Int, $before: String,
|
|||
}
|
||||
shortSha
|
||||
commitPath
|
||||
tags
|
||||
finishedAt
|
||||
duration
|
||||
queuedDuration
|
||||
tags
|
||||
}
|
||||
pageInfo {
|
||||
...PageInfo
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ module WebHooks
|
|||
|
||||
unless hook.valid?
|
||||
self.hooks = relation.select(&:persisted?)
|
||||
flash[:alert] = hook.errors.full_messages.join.html_safe
|
||||
flash[:alert] = hook.errors.full_messages.to_sentence.html_safe
|
||||
end
|
||||
|
||||
redirect_to action: :index
|
||||
|
|
@ -64,7 +64,9 @@ module WebHooks
|
|||
end
|
||||
|
||||
def hook_param_names
|
||||
%i[enable_ssl_verification token url push_events_branch_filter]
|
||||
param_names = %i[enable_ssl_verification token url push_events_branch_filter]
|
||||
param_names.push(:branch_filter_strategy) if Feature.enabled?(:enhanced_webhook_support_regex)
|
||||
param_names
|
||||
end
|
||||
|
||||
def destroy_hook(hook)
|
||||
|
|
|
|||
|
|
@ -3,14 +3,36 @@
|
|||
class ActiveHookFilter
|
||||
def initialize(hook)
|
||||
@hook = hook
|
||||
@push_events_filter_matcher = RefMatcher.new(@hook.push_events_branch_filter)
|
||||
end
|
||||
|
||||
def matches?(hooks_scope, data)
|
||||
return true if hooks_scope != :push_hooks
|
||||
return true unless hooks_scope == :push_hooks
|
||||
|
||||
matches_branch?(data)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def matches_branch?(data)
|
||||
return true if @hook.push_events_branch_filter.blank?
|
||||
|
||||
branch_name = Gitlab::Git.branch_name(data[:ref])
|
||||
@push_events_filter_matcher.matches?(branch_name)
|
||||
|
||||
if Feature.disabled?(:enhanced_webhook_support_regex)
|
||||
return RefMatcher.new(@hook.push_events_branch_filter).matches?(branch_name)
|
||||
end
|
||||
|
||||
case @hook.branch_filter_strategy
|
||||
when 'all_branches'
|
||||
true
|
||||
when 'wildcard'
|
||||
RefMatcher.new(@hook.push_events_branch_filter).matches?(branch_name)
|
||||
when 'regex'
|
||||
begin
|
||||
Gitlab::UntrustedRegexp.new(@hook.push_events_branch_filter) === branch_name
|
||||
rescue RegexpError
|
||||
false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -36,11 +36,22 @@ class WebHook < ApplicationRecord
|
|||
validates :url, public_url: true, unless: ->(hook) { hook.is_a?(SystemHook) }
|
||||
|
||||
validates :token, format: { without: /\n/ }
|
||||
validates :push_events_branch_filter, branch_filter: true
|
||||
after_initialize :initialize_url_variables
|
||||
before_validation :set_branch_filter_nil, \
|
||||
if: -> { branch_filter_strategy_all_branches? && enhanced_webhook_support_regex? }
|
||||
validates :push_events_branch_filter, \
|
||||
untrusted_regexp: true, if: -> { branch_filter_strategy_regex? && enhanced_webhook_support_regex? }
|
||||
validates :push_events_branch_filter, \
|
||||
"web_hooks/wildcard_branch_filter": true, if: -> { branch_filter_strategy_wildcard? }
|
||||
|
||||
validates :url_variables, json_schema: { filename: 'web_hooks_url_variables' }
|
||||
validate :no_missing_url_variables
|
||||
|
||||
after_initialize :initialize_url_variables
|
||||
enum branch_filter_strategy: {
|
||||
wildcard: 0,
|
||||
regex: 1,
|
||||
all_branches: 2
|
||||
}, _prefix: true
|
||||
|
||||
scope :executable, -> do
|
||||
next all unless Feature.enabled?(:web_hooks_disable_failed)
|
||||
|
|
@ -224,4 +235,12 @@ class WebHook < ApplicationRecord
|
|||
|
||||
errors.add(:url, "Invalid URL template. Missing keys: #{missing}")
|
||||
end
|
||||
|
||||
def enhanced_webhook_support_regex?
|
||||
Feature.enabled?(:enhanced_webhook_support_regex)
|
||||
end
|
||||
|
||||
def set_branch_filter_nil
|
||||
self.push_events_branch_filter = nil
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,37 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# BranchFilterValidator
|
||||
#
|
||||
# Custom validator for branch names. Squishes whitespace and ignores empty
|
||||
# string. This only checks that a string is a valid git branch name. It does
|
||||
# not check whether a branch already exists.
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# class Webhook < ActiveRecord::Base
|
||||
# validates :push_events_branch_filter, branch_name: true
|
||||
# end
|
||||
#
|
||||
class BranchFilterValidator < ActiveModel::EachValidator
|
||||
def validate_each(record, attribute, value)
|
||||
value.squish! unless value.nil?
|
||||
|
||||
if value.present?
|
||||
value_without_wildcards = value.tr('*', 'x')
|
||||
|
||||
unless Gitlab::GitRefValidator.validate(value_without_wildcards)
|
||||
record.errors.add(attribute, "is not a valid branch name")
|
||||
end
|
||||
|
||||
unless value.length <= 4000
|
||||
record.errors.add(attribute, "is longer than the allowed length of 4000 characters.")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def contains_wildcard?(value)
|
||||
value.include?('*')
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# WildcardBranchFilterValidator
|
||||
#
|
||||
# Custom validator for wildcard branch filter. Squishes whitespace and ignores
|
||||
# empty string. This only checks that a string is a valid wildcard git branch
|
||||
# like "feature/login" and "feature/*". It doesn't check whether a branch already
|
||||
# exists.
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# class Webhook < ActiveRecord::Base
|
||||
# validates :push_events_branch_filter, "web_hooks/wildcard_branch_filter": true
|
||||
# end
|
||||
#
|
||||
module WebHooks
|
||||
class WildcardBranchFilterValidator < ActiveModel::EachValidator
|
||||
def validate_each(record, attribute, value)
|
||||
value.squish! unless value.nil?
|
||||
|
||||
return unless value.present?
|
||||
|
||||
value_without_wildcards = value.tr('*', 'x')
|
||||
|
||||
unless Gitlab::GitRefValidator.validate(value_without_wildcards)
|
||||
record.errors.add(attribute, "is not a valid branch name")
|
||||
end
|
||||
|
||||
return if value.length <= 4000
|
||||
|
||||
record.errors.add(attribute, "is longer than the allowed length of 4000 characters.")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -9,6 +9,6 @@
|
|||
.col-lg-8.gl-mb-3
|
||||
= gitlab_ui_form_for @hook, as: :hook, url: polymorphic_path([@project, :hooks]) do |f|
|
||||
= render partial: 'shared/web_hooks/form', locals: { form: f, hook: @hook }
|
||||
= f.submit 'Add webhook', pajamas_button: true
|
||||
= f.submit _('Add webhook'), pajamas_button: true, data: { qa_selector: "create_webhook_button" }
|
||||
|
||||
= render 'shared/web_hooks/index', hooks: @hooks, hook_class: @hook.class
|
||||
|
|
|
|||
|
|
@ -19,12 +19,16 @@
|
|||
= form.label :url, s_('Webhooks|Trigger'), class: 'label-bold'
|
||||
%ul.list-unstyled
|
||||
%li.gl-pb-5
|
||||
= form.gitlab_ui_checkbox_component :push_events, s_('Webhooks|Push events')
|
||||
.gl-pl-6
|
||||
= form.text_field :push_events_branch_filter, class: 'form-control gl-form-input',
|
||||
placeholder: 'Branch name or wildcard pattern to trigger on (leave blank for all)'
|
||||
%p.form-text.text-muted.custom-control
|
||||
= s_('Webhooks|Push to the repository.')
|
||||
- if Feature.enabled?(:enhanced_webhook_support_regex)
|
||||
- is_new_hook = hook.id.nil?
|
||||
.js-vue-push-events{ data: { push_events: hook.push_events.to_s, strategy: hook.branch_filter_strategy, is_new_hook: is_new_hook.to_s, push_events_branch_filter: hook.push_events_branch_filter } }
|
||||
- else
|
||||
= form.gitlab_ui_checkbox_component :push_events, s_('Webhooks|Push events')
|
||||
.gl-pl-6
|
||||
= form.text_field :push_events_branch_filter, class: 'form-control gl-form-input',
|
||||
placeholder: 'Branch name or wildcard pattern to trigger on (leave blank for all)'
|
||||
%p.form-text.text-muted.custom-control
|
||||
= s_('Webhooks|Push to the repository.')
|
||||
%li.gl-pb-5
|
||||
= form.gitlab_ui_checkbox_component :tag_push_events,
|
||||
s_('Webhooks|Tag push events'),
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
---
|
||||
name: cascade_package_forwarding_settings
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/99285
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/375761
|
||||
milestone: '15.5'
|
||||
name: enhanced_webhook_support_regex
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/97235
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/375728
|
||||
milestone: '15.6'
|
||||
type: development
|
||||
group: group::package
|
||||
group: group::integrations
|
||||
default_enabled: false
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
- name: "File Type variable expansion in `.gitlab-ci.yml`" # (required) The name of the feature to be deprecated
|
||||
announcement_milestone: "15.5" # (required) The milestone when this feature was first announced as deprecated.
|
||||
announcement_date: "2022-10-22" # (required) The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
|
||||
removal_milestone: "15.7" # (required) The milestone when this feature is planned to be removed
|
||||
removal_date: # (required) The date of the milestone release when this feature is planned to be removed. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
|
||||
breaking_change: true # (required) If this deprecation is a breaking change, set this value to true
|
||||
reporter: DarrenEastman # (required) GitLab username of the person reporting the deprecation
|
||||
stage: Verify # (required) String value of the stage that the feature was created in. e.g., Growth
|
||||
issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/29407 # (required) Link to the deprecation issue in GitLab
|
||||
body: | # (required) Do not modify this line, instead modify the lines below.
|
||||
Previously, variables that referenced or applied alias file variables expanded the value of the `File` type variable. For example, the file contents. This behavior was incorrect because it did not comply with typical shell variable expansion rules. To leak secrets or sensitive information stored in `File` type variables, a user could run an $echo command with the variable as an input parameter.
|
||||
|
||||
This breaking change fixes this issue but could disrupt user workflows that work around the behavior. With this change, job variable expansions that reference or apply alias file variables, expand to the file name or path of the `File` type variable, instead of its value, such as the file contents.
|
||||
|
|
@ -97,6 +97,13 @@ Whenever a project is forked, it copies the settings of the jobs that relate
|
|||
to it. This means that if you have shared runners set up for a project and
|
||||
someone forks that project, the shared runners serve jobs of this project.
|
||||
|
||||
Because of a [known issue](https://gitlab.com/gitlab-org/gitlab/-/issues/364303), you might encounter the message `An error occurred while forking the project. Please try again.` if the runner settings of the project you are forking does not match the new project namespace.
|
||||
|
||||
To work around this issue, you should make sure that the shared runner settings are consistent in the forked project and the new namespace.
|
||||
|
||||
- If shared runners are **enabled** on the forked project, then this should also be **enabled** on the new namespace.
|
||||
- If shared runners are **disabled** on the forked project, then this should also be **disabled** on the new namespace.
|
||||
|
||||
### Attack vectors in runners
|
||||
|
||||
Mentioned briefly earlier, but the following things of runners can be exploited.
|
||||
|
|
|
|||
|
|
@ -8,23 +8,23 @@ info: To determine the technical writer assigned to the Stage/Group associated w
|
|||
|
||||
You accept and agree to the following terms and conditions for Your present and future Contributions submitted to GitLab B.V.. Except for the license granted herein to GitLab B.V. and recipients of software distributed by GitLab B.V., You reserve all right, title, and interest in and to Your Contributions.
|
||||
|
||||
- **Definitions:**
|
||||
"1." **Definitions:**
|
||||
|
||||
"You" (or "Your") shall mean the copyright owner or legal entity authorized by the copyright owner that is making this Agreement with GitLab B.V.. For legal entities, the entity making a Contribution and all other entities that control, are controlled by, or are under common control with that entity are considered to be a single Contributor. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"Contribution" shall mean the code, documentation or other original works of authorship, including any modifications or additions to an existing work, that is submitted by You to GitLab B.V. for inclusion in, or documentation of, any of the products owned or managed by GitLab B.V. (the "Work"). For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to GitLab B.V. or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, GitLab B.V. for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by You as "Not a Contribution."
|
||||
|
||||
- **Grant of Copyright License:**
|
||||
"2." **Grant of Copyright License:**
|
||||
|
||||
Subject to the terms and conditions of this Agreement, You hereby grant to GitLab B.V. and to recipients of software distributed by GitLab B.V. a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, sublicense, and distribute Your Contributions and such derivative works.
|
||||
|
||||
- **Grant of Patent License:**
|
||||
"3." **Grant of Patent License:**
|
||||
|
||||
Subject to the terms and conditions of this Agreement, You hereby grant to GitLab B.V. and to recipients of software distributed by GitLab B.V. a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by You that are necessarily infringed by Your Contribution(s) alone or by combination of Your Contribution(s) with the Work to which such Contribution(s) was submitted. If any entity institutes patent litigation against You or any other entity (including a cross-claim or counterclaim in a lawsuit) alleging that your Contribution, or the Work to which you have contributed, constitutes direct or contributory patent infringement, then any patent licenses granted to that entity under this Agreement for that Contribution or Work shall terminate as of the date such litigation is filed.
|
||||
|
||||
You represent that You are legally entitled to grant the above license. You represent further that each of Your employees is authorized to submit Contributions on Your behalf, but excluding employees that are designated in writing by You as "Not authorized to submit Contributions on behalf of (name of Your corporation here)." Such designations of exclusion for unauthorized employees are to be submitted via email to `legal@gitlab.com`. It is Your responsibility to notify GitLab B.V. when any change is required to the list of designated employees excluded from submitting Contributions on Your behalf. Such notification should also be sent via email to `legal@gitlab.com`.
|
||||
|
||||
- **Contributions:**
|
||||
"4." **Contributions:**
|
||||
|
||||
You represent that each of Your Contributions is Your original creation.
|
||||
|
||||
|
|
|
|||
|
|
@ -8,23 +8,23 @@ info: To determine the technical writer assigned to the Stage/Group associated w
|
|||
|
||||
You accept and agree to the following terms and conditions for Your present and future Contributions submitted to GitLab B.V.. Except for the license granted herein to GitLab B.V. and recipients of software distributed by GitLab B.V., You reserve all right, title, and interest in and to Your Contributions.
|
||||
|
||||
- **Definitions:**
|
||||
"1." **Definitions:**
|
||||
|
||||
"You" (or "Your") shall mean the copyright owner or legal entity authorized by the copyright owner that is making this Agreement with GitLab B.V.. For legal entities, the entity making a Contribution and all other entities that control, are controlled by, or are under common control with that entity are considered to be a single Contributor. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"Contribution" shall mean any original work of authorship, including any modifications or additions to an existing work, that is intentionally submitted by You to GitLab B.V. for inclusion in, or documentation of, any of the products owned or managed by GitLab B.V. (the "Work"). For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to GitLab B.V. or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, GitLab B.V. for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by You as "Not a Contribution."
|
||||
|
||||
- **Grant of Copyright License:**
|
||||
"2." **Grant of Copyright License:**
|
||||
|
||||
Subject to the terms and conditions of this Agreement, You hereby grant to GitLab B.V. and to recipients of software distributed by GitLab B.V. a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, sublicense, and distribute Your Contributions and such derivative works.
|
||||
|
||||
- **Grant of Patent License:**
|
||||
"3." **Grant of Patent License:**
|
||||
|
||||
Subject to the terms and conditions of this Agreement, You hereby grant to GitLab B.V. and to recipients of software distributed by GitLab B.V. a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by You that are necessarily infringed by Your Contribution(s) alone or by combination of Your Contribution(s) with the Work to which such Contribution(s) was submitted. If any entity institutes patent litigation against You or any other entity (including a cross-claim or counterclaim in a lawsuit) alleging that your Contribution, or the Work to which you have contributed, constitutes direct or contributory patent infringement, then any patent licenses granted to that entity under this Agreement for that Contribution or Work shall terminate as of the date such litigation is filed.
|
||||
|
||||
You represent that you are legally entitled to grant the above license. If your employer(s) has rights to intellectual property that you create that includes your Contributions, you represent that you have received permission to make Contributions on behalf of that employer, that your employer has waived such rights for your Contributions to GitLab B.V., or that your employer has executed a separate Corporate CLA with GitLab B.V..
|
||||
|
||||
- **Contributions:**
|
||||
"4." **Contributions:**
|
||||
|
||||
You represent that each of Your Contributions is Your original creation. You represent that Your Contribution submissions include complete details of any third-party license or other restriction (including, but not limited to, related patents and trademarks) of which you are personally aware and which are associated with any part of Your Contributions.
|
||||
|
||||
|
|
|
|||
|
|
@ -47,6 +47,22 @@ sole discretion of GitLab Inc.
|
|||
|
||||
## Announced in 15.5
|
||||
|
||||
<div class="deprecation removal-157 breaking-change">
|
||||
|
||||
### File Type variable expansion in `.gitlab-ci.yml`
|
||||
|
||||
Planned removal: GitLab <span class="removal-milestone">15.7</span> ()
|
||||
|
||||
WARNING:
|
||||
This is a [breaking change](https://docs.gitlab.com/ee/development/deprecation_guidelines/).
|
||||
Review the details carefully before upgrading.
|
||||
|
||||
Previously, variables that referenced or applied alias file variables expanded the value of the `File` type variable. For example, the file contents. This behavior was incorrect because it did not comply with typical shell variable expansion rules. To leak secrets or sensitive information stored in `File` type variables, a user could run an $echo command with the variable as an input parameter.
|
||||
|
||||
This breaking change fixes this issue but could disrupt user workflows that work around the behavior. With this change, job variable expansions that reference or apply alias file variables, expand to the file name or path of the `File` type variable, instead of its value, such as the file contents.
|
||||
|
||||
</div>
|
||||
|
||||
<div class="deprecation removal-160 breaking-change">
|
||||
|
||||
### GraphQL field `confidential` changed to `internal` on notes
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
|
|||
|
||||
Infrastructure as Code (IaC) Scanning scans your IaC configuration files for known vulnerabilities.
|
||||
|
||||
Currently, IaC scanning supports configuration files for Terraform, Ansible, AWS CloudFormation, and Kubernetes.
|
||||
IaC Scanning supports configuration files for Terraform, Ansible, AWS CloudFormation, and Kubernetes.
|
||||
|
||||
## Requirements
|
||||
|
||||
|
|
@ -18,13 +18,13 @@ IaC Scanning runs in the `test` stage, which is available by default. If you red
|
|||
|
||||
We recommend a minimum of 4GB RAM to ensure consistent performance.
|
||||
|
||||
To run IaC scanning jobs, by default, you need GitLab Runner with the
|
||||
To run IaC Scanning jobs, by default, you need GitLab Runner with the
|
||||
[`docker`](https://docs.gitlab.com/runner/executors/docker.html) or
|
||||
[`kubernetes`](https://docs.gitlab.com/runner/install/kubernetes.html) executor.
|
||||
If you're using the shared runners on GitLab.com, this is enabled by default.
|
||||
|
||||
WARNING:
|
||||
Our IaC scanning jobs require a Linux/amd64 container type. Windows containers are not yet supported.
|
||||
Our IaC Scanning jobs require a Linux/amd64 container type. Windows containers are not supported.
|
||||
|
||||
WARNING:
|
||||
If you use your own runners, make sure the Docker version installed
|
||||
|
|
@ -32,9 +32,9 @@ is **not** `19.03.0`. See [troubleshooting information](../sast/index.md#error-r
|
|||
|
||||
## Supported languages and frameworks
|
||||
|
||||
GitLab IaC scanning supports a variety of IaC configuration files. Our IaC security scanners also feature automatic language detection which works even for mixed-language projects. If any supported configuration files are detected in project source code we automatically run the appropriate IaC analyzers.
|
||||
GitLab IaC Scanning supports a variety of IaC configuration files. Our IaC security scanners also feature automatic language detection which works even for mixed-language projects. If any supported configuration files are detected in project source code we automatically run the appropriate IaC analyzers.
|
||||
|
||||
| Configuration File Type | Scan tool | Introduced in GitLab Version |
|
||||
| Configuration file type | Scan tool | Introduced in GitLab version |
|
||||
| ----------------------------------- | ------------------------ | ---------------------------- |
|
||||
| Ansible | [KICS](https://kics.io/) | 14.5 |
|
||||
| AWS CloudFormation | [KICS](https://kics.io/) | 14.5 |
|
||||
|
|
@ -45,7 +45,7 @@ GitLab IaC scanning supports a variety of IaC configuration files. Our IaC secur
|
|||
| OpenAPI | [KICS](https://kics.io/) | 14.5 |
|
||||
| Terraform <sup>2</sup> | [KICS](https://kics.io/) | 14.5 |
|
||||
|
||||
1. IaC scanning can analyze Azure Resource Manager templates in JSON format. If you write templates in the [Bicep](https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/overview) language, you must use [the bicep CLI](https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/bicep-cli) to convert your Bicep files into JSON before GitLab IaC scanning can analyze them.
|
||||
1. IaC Scanning can analyze Azure Resource Manager templates in JSON format. If you write templates in the [Bicep](https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/overview) language, you must use [the bicep CLI](https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/bicep-cli) to convert your Bicep files into JSON before GitLab IaC Scanning can analyze them.
|
||||
1. Terraform modules in a custom registry are not scanned for vulnerabilities. You can follow [this issue](https://gitlab.com/gitlab-org/gitlab/-/issues/357004) for the proposed feature.
|
||||
|
||||
### Supported distributions
|
||||
|
|
@ -107,7 +107,7 @@ include:
|
|||
- template: Jobs/SAST-IaC.gitlab-ci.yml
|
||||
```
|
||||
|
||||
The included template creates IaC scanning jobs in your CI/CD pipeline and scans
|
||||
The included template creates IaC Scanning jobs in your CI/CD pipeline and scans
|
||||
your project's configuration files for possible vulnerabilities.
|
||||
|
||||
The results are saved as a
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
stage: Plan
|
||||
group: Project Management
|
||||
group: Product Planning
|
||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ module API
|
|||
|
||||
raise ArgumentError, "Can't find application setting for package_type #{package_type}" unless application_setting_name
|
||||
|
||||
if target.present? && Feature.enabled?(:cascade_package_forwarding_settings, target)
|
||||
if target.present?
|
||||
target.public_send(application_setting_name) # rubocop:disable GitlabSecurity/PublicSend
|
||||
else
|
||||
::Gitlab::CurrentSettings
|
||||
|
|
|
|||
|
|
@ -157,13 +157,7 @@ module Gitlab
|
|||
signature_value: value
|
||||
)
|
||||
|
||||
if signature.valid?
|
||||
signature
|
||||
else
|
||||
e = SecurityReportParserError.new("Vulnerability tracking signature is not valid: #{signature}")
|
||||
Gitlab::ErrorTracking.track_exception(e)
|
||||
nil
|
||||
end
|
||||
signature if signature.valid?
|
||||
end.compact
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -23312,6 +23312,9 @@ msgstr ""
|
|||
msgid "Job|Download"
|
||||
msgstr ""
|
||||
|
||||
msgid "Job|Duration"
|
||||
msgstr ""
|
||||
|
||||
msgid "Job|Erase job log and artifacts"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -23351,6 +23354,9 @@ msgstr ""
|
|||
msgid "Job|Preparing"
|
||||
msgstr ""
|
||||
|
||||
msgid "Job|Queued"
|
||||
msgstr ""
|
||||
|
||||
msgid "Job|Retry"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
|||
|
|
@ -106,8 +106,9 @@ RSpec.describe Projects::HooksController do
|
|||
it 'sets all parameters' do
|
||||
hook_params = {
|
||||
enable_ssl_verification: true,
|
||||
token: "TEST TOKEN",
|
||||
url: "http://example.com",
|
||||
token: 'TEST TOKEN',
|
||||
url: 'http://example.com',
|
||||
branch_filter_strategy: 'regex',
|
||||
|
||||
push_events: true,
|
||||
tag_push_events: true,
|
||||
|
|
@ -124,13 +125,39 @@ RSpec.describe Projects::HooksController do
|
|||
url_variables: [{ key: 'token', value: 'some secret value' }]
|
||||
}
|
||||
|
||||
post :create, params: { namespace_id: project.namespace, project_id: project, hook: hook_params }
|
||||
params = { namespace_id: project.namespace, project_id: project, hook: hook_params }
|
||||
|
||||
expect { post :create, params: params }.to change(ProjectHook, :count).by(1)
|
||||
|
||||
project_hook = ProjectHook.order_id_desc.take
|
||||
|
||||
expect(project_hook).to have_attributes(
|
||||
**hook_params.merge(url_variables: { 'token' => 'some secret value' })
|
||||
)
|
||||
expect(response).to have_gitlab_http_status(:found)
|
||||
expect(flash[:alert]).to be_blank
|
||||
end
|
||||
|
||||
it 'ignores branch_filter_strategy when flag is disabled' do
|
||||
stub_feature_flags(enhanced_webhook_support_regex: false)
|
||||
hook_params = {
|
||||
url: 'http://example.com',
|
||||
branch_filter_strategy: 'regex',
|
||||
push_events: true
|
||||
}
|
||||
params = { namespace_id: project.namespace, project_id: project, hook: hook_params }
|
||||
|
||||
expect { post :create, params: params }.to change(ProjectHook, :count).by(1)
|
||||
|
||||
project_hook = ProjectHook.order_id_desc.take
|
||||
|
||||
expect(project_hook).to have_attributes(
|
||||
url: 'http://example.com',
|
||||
branch_filter_strategy: 'wildcard'
|
||||
)
|
||||
|
||||
expect(response).to have_gitlab_http_status(:found)
|
||||
expect(flash[:alert]).to be_blank
|
||||
expect(ProjectHook.count).to eq(1)
|
||||
expect(ProjectHook.first).to have_attributes(hook_params.except(:url_variables))
|
||||
expect(ProjectHook.first).to have_attributes(url_variables: { 'token' => 'some secret value' })
|
||||
end
|
||||
|
||||
it 'alerts the user if the new hook is invalid' do
|
||||
|
|
|
|||
|
|
@ -48,22 +48,47 @@ RSpec.describe 'Projects > Settings > Webhook Settings' do
|
|||
expect(page).to have_content('Releases events')
|
||||
end
|
||||
|
||||
it 'create webhook', :js do
|
||||
visit webhooks_path
|
||||
context 'when feature flag "enhanced_webhook_support_regex" is disabled' do
|
||||
before do
|
||||
stub_feature_flags(enhanced_webhook_support_regex: false)
|
||||
end
|
||||
|
||||
fill_in 'URL', with: url
|
||||
check 'Tag push events'
|
||||
fill_in 'hook_push_events_branch_filter', with: 'master'
|
||||
check 'Enable SSL verification'
|
||||
check 'Job events'
|
||||
it 'create webhook', :js do
|
||||
visit webhooks_path
|
||||
|
||||
click_button 'Add webhook'
|
||||
fill_in 'URL', with: url
|
||||
check 'Tag push events'
|
||||
fill_in 'hook_push_events_branch_filter', with: 'master'
|
||||
check 'Enable SSL verification'
|
||||
check 'Job events'
|
||||
|
||||
expect(page).to have_content(url)
|
||||
expect(page).to have_content('SSL Verification: enabled')
|
||||
expect(page).to have_content('Push events')
|
||||
expect(page).to have_content('Tag push events')
|
||||
expect(page).to have_content('Job events')
|
||||
click_button 'Add webhook'
|
||||
|
||||
expect(page).to have_content(url)
|
||||
expect(page).to have_content('SSL Verification: enabled')
|
||||
expect(page).to have_content('Tag push events')
|
||||
expect(page).to have_content('Job events')
|
||||
expect(page).to have_content('Push events')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when feature flag "enhanced_webhook_support_regex" is enabled' do
|
||||
it 'create webhook', :js do
|
||||
visit webhooks_path
|
||||
|
||||
fill_in 'URL', with: url
|
||||
check 'Tag push events'
|
||||
check 'Enable SSL verification'
|
||||
check 'Job events'
|
||||
|
||||
click_button 'Add webhook'
|
||||
|
||||
expect(page).to have_content(url)
|
||||
expect(page).to have_content('SSL Verification: enabled')
|
||||
expect(page).to have_content('Tag push events')
|
||||
expect(page).to have_content('Job events')
|
||||
expect(page).to have_selector('.js-vue-push-events', visible: :all)
|
||||
end
|
||||
end
|
||||
|
||||
it 'edit existing webhook', :js do
|
||||
|
|
|
|||
|
|
@ -61,6 +61,8 @@ describe('RunnerJobsTable', () => {
|
|||
__('Project'),
|
||||
__('Commit'),
|
||||
s__('Job|Finished at'),
|
||||
s__('Job|Duration'),
|
||||
s__('Job|Queued'),
|
||||
s__('Runners|Tags'),
|
||||
]);
|
||||
});
|
||||
|
|
@ -108,6 +110,22 @@ describe('RunnerJobsTable', () => {
|
|||
expect(findCell({ field: 'finished_at' }).text()).toBe('1 hour ago');
|
||||
});
|
||||
|
||||
it('Formats duration time', () => {
|
||||
mockJobsCopy[0].duration = 60;
|
||||
|
||||
createComponent({ props: { jobs: mockJobsCopy } }, mountExtended);
|
||||
|
||||
expect(findCell({ field: 'duration' }).text()).toBe('00:01:00');
|
||||
});
|
||||
|
||||
it('Formats queued time', () => {
|
||||
mockJobsCopy[0].queuedDuration = 30;
|
||||
|
||||
createComponent({ props: { jobs: mockJobsCopy } }, mountExtended);
|
||||
|
||||
expect(findCell({ field: 'queued' }).text()).toBe('00:00:30');
|
||||
});
|
||||
|
||||
it('Formats tags', () => {
|
||||
mockJobsCopy[0].tags = ['tag-1', 'tag-2'];
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ RSpec.describe API::Helpers::Packages::DependencyProxyHelpers do
|
|||
include_context 'dependency proxy helpers context'
|
||||
|
||||
let_it_be(:group) { create(:group) }
|
||||
let_it_be(:project) { create(:project, group: group) }
|
||||
let_it_be_with_reload(:project) { create(:project, group: group) }
|
||||
let_it_be_with_reload(:package_setting) { create(:namespace_package_setting, namespace: group) }
|
||||
|
||||
let(:target) { project }
|
||||
|
|
@ -76,19 +76,6 @@ RSpec.describe API::Helpers::Packages::DependencyProxyHelpers do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when cascade_package_forwarding_settings is disabled' do
|
||||
let(:package_type) { forwardable_package_type }
|
||||
let(:forward_to_registry) { true }
|
||||
|
||||
before do
|
||||
stub_feature_flags(cascade_package_forwarding_settings: false)
|
||||
allow_fetch_cascade_application_setting(attribute: "#{forwardable_package_type}_package_requests_forwarding", return_value: true)
|
||||
package_setting.update!("#{forwardable_package_type}_package_requests_forwarding" => false)
|
||||
end
|
||||
|
||||
it_behaves_like 'executing redirect'
|
||||
end
|
||||
|
||||
context 'when no target is present' do
|
||||
let(:package_type) { forwardable_package_type }
|
||||
let(:forward_to_registry) { true }
|
||||
|
|
|
|||
|
|
@ -400,28 +400,9 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Common do
|
|||
end
|
||||
|
||||
describe 'parsing tracking' do
|
||||
let(:tracking_data) do
|
||||
{
|
||||
'type' => 'source',
|
||||
'items' => [
|
||||
'signatures' => [
|
||||
{ 'algorithm' => 'hash', 'value' => 'hash_value' },
|
||||
{ 'algorithm' => 'location', 'value' => 'location_value' },
|
||||
{ 'algorithm' => 'scope_offset', 'value' => 'scope_offset_value' }
|
||||
]
|
||||
]
|
||||
}
|
||||
end
|
||||
|
||||
context 'with valid tracking information' do
|
||||
it 'creates signatures for each algorithm' do
|
||||
finding = report.findings.first
|
||||
expect(finding.signatures.size).to eq(3)
|
||||
expect(finding.signatures.map(&:algorithm_type).to_set).to eq(Set['hash', 'location', 'scope_offset'])
|
||||
end
|
||||
end
|
||||
|
||||
context 'with invalid tracking information' do
|
||||
let(:finding) { report.findings.first }
|
||||
let(:number_of_findings) { report.findings.length }
|
||||
let(:tracking_data) do
|
||||
{
|
||||
'type' => 'source',
|
||||
|
|
@ -435,14 +416,26 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Common do
|
|||
}
|
||||
end
|
||||
|
||||
it 'ignores invalid algorithm types' do
|
||||
finding = report.findings.first
|
||||
it 'ignores invalid algorithm types and logs warning' do
|
||||
expect(finding.signatures.size).to eq(2)
|
||||
expect(finding.signatures.map(&:algorithm_type).to_set).to eq(Set['hash', 'location'])
|
||||
end
|
||||
end
|
||||
|
||||
context 'with valid tracking information' do
|
||||
let(:tracking_data) do
|
||||
{
|
||||
'type' => 'source',
|
||||
'items' => [
|
||||
'signatures' => [
|
||||
{ 'algorithm' => 'hash', 'value' => 'hash_value' },
|
||||
{ 'algorithm' => 'location', 'value' => 'location_value' },
|
||||
{ 'algorithm' => 'scope_offset', 'value' => 'scope_offset_value' }
|
||||
]
|
||||
]
|
||||
}
|
||||
end
|
||||
|
||||
it 'creates signatures for each signature algorithm' do
|
||||
finding = report.findings.first
|
||||
expect(finding.signatures.size).to eq(3)
|
||||
|
|
|
|||
|
|
@ -53,8 +53,6 @@ RSpec.describe ChangePublicProjectsCostFactor, migration: :gitlab_ci do
|
|||
expect(shared_2.public_projects_minutes_cost_factor).to eq(0)
|
||||
expect(shared_3.public_projects_minutes_cost_factor).to eq(1)
|
||||
expect(group_1.public_projects_minutes_cost_factor).to eq(0)
|
||||
|
||||
schema_migrate_up!
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -6,65 +6,102 @@ RSpec.describe ActiveHookFilter do
|
|||
subject(:filter) { described_class.new(hook) }
|
||||
|
||||
describe '#matches?' do
|
||||
context 'for push event hooks' do
|
||||
let(:hook) do
|
||||
create(:project_hook, push_events: true, push_events_branch_filter: branch_filter)
|
||||
using RSpec::Parameterized::TableSyntax
|
||||
|
||||
context 'for various types of branch_filter' do
|
||||
let_it_be_with_reload(:hook) do
|
||||
create(:project_hook, push_events: true, issues_events: true)
|
||||
end
|
||||
|
||||
context 'branch filter is specified' do
|
||||
let(:branch_filter) { 'master' }
|
||||
|
||||
it 'returns true if branch matches' do
|
||||
expect(filter.matches?(:push_hooks, { ref: 'refs/heads/master' })).to be true
|
||||
end
|
||||
|
||||
it 'returns false if branch does not match' do
|
||||
expect(filter.matches?(:push_hooks, { ref: 'refs/heads/my_branch' })).to be false
|
||||
end
|
||||
|
||||
it 'returns false if ref is nil' do
|
||||
expect(filter.matches?(:push_hooks, {})).to be false
|
||||
end
|
||||
|
||||
context 'branch filter contains wildcard' do
|
||||
let(:branch_filter) { 'features/*' }
|
||||
|
||||
it 'returns true if branch matches' do
|
||||
expect(filter.matches?(:push_hooks, { ref: 'refs/heads/features/my-branch' })).to be true
|
||||
expect(filter.matches?(:push_hooks, { ref: 'refs/heads/features/my-branch/something' })).to be true
|
||||
end
|
||||
|
||||
it 'returns false if branch does not match' do
|
||||
expect(filter.matches?(:push_hooks, { ref: 'refs/heads/master' })).to be false
|
||||
end
|
||||
end
|
||||
where(:branch_filter_strategy, :branch_filter, :ref, :expected_matches?) do
|
||||
'all_branches' | 'master' | 'refs/heads/master' | true
|
||||
'all_branches' | '' | 'refs/heads/master' | true
|
||||
'all_branches' | nil | 'refs/heads/master' | true
|
||||
'all_branches' | '.*' | 'refs/heads/master' | true
|
||||
'wildcard' | 'master' | 'refs/heads/master' | true
|
||||
'wildcard' | 'master' | 'refs/heads/my_branch' | false
|
||||
'wildcard' | 'features/*' | 'refs/heads/features/my-branch' | true
|
||||
'wildcard' | 'features/*' | 'refs/heads/features/my-branch/something' | true
|
||||
'wildcard' | 'features/*' | 'refs/heads/master' | false
|
||||
'wildcard' | nil | 'refs/heads/master' | true
|
||||
'wildcard' | '' | 'refs/heads/master' | true
|
||||
'regex' | 'master' | 'refs/heads/master' | true
|
||||
'regex' | 'master' | 'refs/heads/my_branch' | false
|
||||
'regex' | 'features/*' | 'refs/heads/xxxx/features/my-branch' | true
|
||||
'regex' | 'features/*' | 'refs/heads/features/' | true
|
||||
'regex' | 'features/*' | 'refs/heads/features' | true
|
||||
'regex' | 'features/.*' | 'refs/heads/features/my-branch' | true
|
||||
'regex' | 'features/.*' | 'refs/heads/features/my-branch/something' | true
|
||||
'regex' | 'features/.*' | 'refs/heads/master' | false
|
||||
'regex' | '(feature|dev)' | 'refs/heads/feature' | true
|
||||
'regex' | '(feature|dev)' | 'refs/heads/dev' | true
|
||||
'regex' | '(feature|dev)' | 'refs/heads/master' | false
|
||||
'regex' | nil | 'refs/heads/master' | true
|
||||
'regex' | '' | 'refs/heads/master' | true
|
||||
end
|
||||
|
||||
context 'branch filter is not specified' do
|
||||
let(:branch_filter) { nil }
|
||||
|
||||
it 'returns true' do
|
||||
expect(filter.matches?(:push_hooks, { ref: 'refs/heads/master' })).to be true
|
||||
with_them do
|
||||
before do
|
||||
hook.assign_attributes(
|
||||
push_events_branch_filter: branch_filter,
|
||||
branch_filter_strategy: branch_filter_strategy
|
||||
)
|
||||
end
|
||||
|
||||
it { expect(filter.matches?(:push_hooks, { ref: ref })).to be expected_matches? }
|
||||
it { expect(filter.matches?(:issues_events, { ref: ref })).to be true }
|
||||
end
|
||||
|
||||
context 'branch filter is empty string' do
|
||||
let(:branch_filter) { '' }
|
||||
|
||||
it 'acts like branch is not specified' do
|
||||
expect(filter.matches?(:push_hooks, { ref: 'refs/heads/master' })).to be true
|
||||
context 'when the branch filter is a invalid regex' do
|
||||
let_it_be(:hook) do
|
||||
create(
|
||||
:project_hook,
|
||||
push_events: true,
|
||||
push_events_branch_filter: 'master',
|
||||
branch_filter_strategy: 'regex'
|
||||
)
|
||||
end
|
||||
|
||||
before do
|
||||
allow(hook).to receive(:push_events_branch_filter).and_return("invalid-regex[")
|
||||
end
|
||||
|
||||
it { expect(filter.matches?(:push_hooks, { ref: 'refs/heads/master' })).to be false }
|
||||
end
|
||||
|
||||
context 'when the branch filter is not properly set to nil' do
|
||||
let_it_be(:hook) do
|
||||
create(
|
||||
:project_hook,
|
||||
push_events: true,
|
||||
branch_filter_strategy: 'all_branches'
|
||||
)
|
||||
end
|
||||
|
||||
before do
|
||||
allow(hook).to receive(:push_events_branch_filter).and_return("master")
|
||||
end
|
||||
|
||||
it { expect(filter.matches?(:push_hooks, { ref: 'refs/heads/feature1' })).to be true }
|
||||
end
|
||||
end
|
||||
|
||||
context 'for non-push-events hooks' do
|
||||
let(:hook) do
|
||||
create(:project_hook, issues_events: true, push_events: false, push_events_branch_filter: '')
|
||||
context 'when feature flag is disabled' do
|
||||
before do
|
||||
stub_feature_flags(enhanced_webhook_support_regex: false)
|
||||
end
|
||||
|
||||
it 'returns true as branch filters are not yet supported for these' do
|
||||
expect(filter.matches?(:issues_events, { ref: 'refs/heads/master' })).to be true
|
||||
let_it_be(:hook) do
|
||||
create(
|
||||
:project_hook,
|
||||
push_events: true,
|
||||
push_events_branch_filter: '(master)',
|
||||
branch_filter_strategy: 'regex'
|
||||
)
|
||||
end
|
||||
|
||||
it { expect(filter.matches?(:push_hooks, { ref: 'refs/heads/master' })).to be false }
|
||||
it { expect(filter.matches?(:push_hooks, { ref: 'refs/heads/(master)' })).to be true }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -113,23 +113,80 @@ RSpec.describe WebHook do
|
|||
end
|
||||
|
||||
describe 'push_events_branch_filter' do
|
||||
it { is_expected.to allow_values("good_branch_name", "another/good-branch_name").for(:push_events_branch_filter) }
|
||||
it { is_expected.to allow_values("").for(:push_events_branch_filter) }
|
||||
it { is_expected.not_to allow_values("bad branch name", "bad~branchname").for(:push_events_branch_filter) }
|
||||
|
||||
it 'gets rid of whitespace' do
|
||||
hook.push_events_branch_filter = ' branch '
|
||||
hook.save!
|
||||
|
||||
expect(hook.push_events_branch_filter).to eq('branch')
|
||||
before do
|
||||
subject.branch_filter_strategy = strategy
|
||||
end
|
||||
|
||||
it 'stores whitespace only as empty' do
|
||||
hook.push_events_branch_filter = ' '
|
||||
hook.save!
|
||||
context 'with "all branches" strategy' do
|
||||
let(:strategy) { 'all_branches' }
|
||||
|
||||
expect(hook.push_events_branch_filter).to eq('')
|
||||
it {
|
||||
is_expected.to allow_values(
|
||||
"good_branch_name",
|
||||
"another/good-branch_name",
|
||||
"good branch name",
|
||||
"good~branchname",
|
||||
"good_branchname(",
|
||||
"good_branchname[",
|
||||
""
|
||||
).for(:push_events_branch_filter)
|
||||
}
|
||||
end
|
||||
|
||||
context 'with "wildcard" strategy' do
|
||||
let(:strategy) { 'wildcard' }
|
||||
|
||||
it {
|
||||
is_expected.to allow_values(
|
||||
"good_branch_name",
|
||||
"another/good-branch_name",
|
||||
"good_branch_name(",
|
||||
""
|
||||
).for(:push_events_branch_filter)
|
||||
}
|
||||
|
||||
it {
|
||||
is_expected.not_to allow_values(
|
||||
"bad branch name",
|
||||
"bad~branchname",
|
||||
"bad_branch_name["
|
||||
).for(:push_events_branch_filter)
|
||||
}
|
||||
|
||||
it 'gets rid of whitespace' do
|
||||
hook.push_events_branch_filter = ' branch '
|
||||
hook.save!
|
||||
|
||||
expect(hook.push_events_branch_filter).to eq('branch')
|
||||
end
|
||||
|
||||
it 'stores whitespace only as empty' do
|
||||
hook.push_events_branch_filter = ' '
|
||||
hook.save!
|
||||
expect(hook.push_events_branch_filter).to eq('')
|
||||
end
|
||||
end
|
||||
|
||||
context 'with "regex" strategy' do
|
||||
let(:strategy) { 'regex' }
|
||||
|
||||
it {
|
||||
is_expected.to allow_values(
|
||||
"good_branch_name",
|
||||
"another/good-branch_name",
|
||||
"good branch name",
|
||||
"good~branch~name",
|
||||
""
|
||||
).for(:push_events_branch_filter)
|
||||
}
|
||||
|
||||
it { is_expected.not_to allow_values("bad_branch_name(", "bad_branch_name[").for(:push_events_branch_filter) }
|
||||
end
|
||||
end
|
||||
|
||||
it "only consider these branch filter strategies are valid" do
|
||||
expected_valid_types = %w[all_branches regex wildcard]
|
||||
expect(described_class.branch_filter_strategies.keys).to contain_exactly(*expected_valid_types)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,15 @@
|
|||
|
||||
module Database
|
||||
module MultipleDatabases
|
||||
def run_and_cleanup(example)
|
||||
# Each example may call `migrate!`, so we must ensure we are migrated down every time
|
||||
schema_migrate_down!
|
||||
|
||||
example.run
|
||||
|
||||
delete_from_all_tables!(except: deletion_except_tables)
|
||||
end
|
||||
|
||||
def skip_if_multiple_databases_not_setup
|
||||
skip 'Skipping because multiple databases not set up' unless Gitlab::Database.has_config?(:ci)
|
||||
end
|
||||
|
|
@ -22,6 +31,21 @@ module Database
|
|||
model.establish_connection(new_db_config)
|
||||
end
|
||||
|
||||
def ensure_schema_and_empty_tables
|
||||
# Ensure all schemas for both databases are migrated back
|
||||
Gitlab::Database.database_base_models.each do |_, base_model|
|
||||
with_reestablished_active_record_base do
|
||||
reconfigure_db_connection(
|
||||
model: ActiveRecord::Base,
|
||||
config_model: base_model
|
||||
)
|
||||
|
||||
schema_migrate_up!
|
||||
delete_from_all_tables!(except: deletion_except_tables)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# The usage of this method switches temporarily used `connection_handler`
|
||||
# allowing full manipulation of ActiveRecord::Base connections without
|
||||
# having side effects like:
|
||||
|
|
@ -109,7 +133,13 @@ RSpec.configure do |config|
|
|||
end
|
||||
end
|
||||
|
||||
config.append_after(:context, :migration) do
|
||||
recreate_databases_and_seed_if_needed || ensure_schema_and_empty_tables
|
||||
end
|
||||
|
||||
config.around(:each, :migration) do |example|
|
||||
self.class.use_transactional_tests = false
|
||||
|
||||
migration_schema = example.metadata[:migration]
|
||||
migration_schema = :gitlab_main if migration_schema == true
|
||||
base_model = Gitlab::Database.schemas_to_base_models.fetch(migration_schema).first
|
||||
|
|
@ -122,11 +152,13 @@ RSpec.configure do |config|
|
|||
config_model: base_model
|
||||
)
|
||||
|
||||
example.run
|
||||
run_and_cleanup(example)
|
||||
end
|
||||
else
|
||||
example.run
|
||||
run_and_cleanup(example)
|
||||
end
|
||||
|
||||
self.class.use_transactional_tests = true
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -13,19 +13,6 @@ RSpec.configure do |config|
|
|||
DatabaseCleaner.clean_with(:deletion)
|
||||
end
|
||||
|
||||
config.append_after(:context, :migration) do
|
||||
delete_from_all_tables!(except: ['work_item_types'])
|
||||
|
||||
# Postgres maximum number of columns in a table is 1600 (https://github.com/postgres/postgres/blob/de41869b64d57160f58852eab20a27f248188135/src/include/access/htup_details.h#L23-L47).
|
||||
# We drop and recreate the database if any table has more than 1200 columns, just to be safe.
|
||||
if any_connection_class_with_more_than_allowed_columns?
|
||||
recreate_all_databases!
|
||||
|
||||
# Seed required data as recreating DBs will delete it
|
||||
TestEnv.seed_db
|
||||
end
|
||||
end
|
||||
|
||||
config.around(:each, :delete) do |example|
|
||||
self.class.use_transactional_tests = false
|
||||
|
||||
|
|
@ -35,14 +22,4 @@ RSpec.configure do |config|
|
|||
|
||||
self.class.use_transactional_tests = true
|
||||
end
|
||||
|
||||
config.around(:each, :migration) do |example|
|
||||
self.class.use_transactional_tests = false
|
||||
|
||||
example.run
|
||||
|
||||
delete_from_all_tables!(except: ['work_item_types'])
|
||||
|
||||
self.class.use_transactional_tests = true
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -78,6 +78,19 @@ module DbCleaner
|
|||
puts "Databases re-creation done in #{Gitlab::Metrics::System.monotonic_time - start}"
|
||||
end
|
||||
|
||||
def recreate_databases_and_seed_if_needed
|
||||
# Postgres maximum number of columns in a table is 1600 (https://github.com/postgres/postgres/blob/de41869b64d57160f58852eab20a27f248188135/src/include/access/htup_details.h#L23-L47).
|
||||
# We drop and recreate the database if any table has more than 1200 columns, just to be safe.
|
||||
return false unless any_connection_class_with_more_than_allowed_columns?
|
||||
|
||||
recreate_all_databases!
|
||||
|
||||
# Seed required data as recreating DBs will delete it
|
||||
TestEnv.seed_db
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
def force_disconnect_all_connections!
|
||||
cmd = <<~SQL
|
||||
SELECT pg_terminate_backend(pg_stat_activity.pid)
|
||||
|
|
|
|||
|
|
@ -87,6 +87,8 @@ module MigrationsHelpers
|
|||
[ApplicationSetting, SystemHook].each do |model|
|
||||
model.define_attribute_methods
|
||||
end
|
||||
|
||||
Gitlab.ee { License.define_attribute_methods }
|
||||
end
|
||||
|
||||
def reset_column_information(klass)
|
||||
|
|
|
|||
|
|
@ -19,13 +19,9 @@ RSpec.configure do |config|
|
|||
# Each example may call `migrate!`, so we must ensure we are migrated down every time
|
||||
config.before(:each, :migration) do
|
||||
use_fake_application_settings
|
||||
|
||||
schema_migrate_down!
|
||||
end
|
||||
|
||||
config.after(:context, :migration) do
|
||||
schema_migrate_up!
|
||||
|
||||
Gitlab::CurrentSettings.clear_in_memory_application_settings!
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe BranchFilterValidator do
|
||||
RSpec.describe WebHooks::WildcardBranchFilterValidator do
|
||||
let(:validator) { described_class.new(attributes: [:push_events_branch_filter]) }
|
||||
let(:hook) { build(:project_hook) }
|
||||
|
||||
Loading…
Reference in New Issue