Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
83e4339a32
commit
f265a70317
|
|
@ -1,7 +1,7 @@
|
|||
<!--
|
||||
# Read me first!
|
||||
|
||||
Create this issue under https://gitlab.com/gitlab-org/security
|
||||
Create this issue under https://gitlab.com/gitlab-org/security/gitlab
|
||||
|
||||
Set the title to: `Description of the original issue`
|
||||
-->
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
6a06feda7fd01961bb332afce4d7f7b4ce4a5aad
|
||||
99f78e4d93d8c9ec23ef710ffde0fb4b75d786bb
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<script>
|
||||
import { GlLoadingIcon, GlIcon, GlIntersectionObserver } from '@gitlab/ui';
|
||||
import { GlLoadingIcon, GlIcon, GlIntersectionObserver, GlTooltipDirective } from '@gitlab/ui';
|
||||
import Timeago from '~/vue_shared/components/time_ago_tooltip.vue';
|
||||
import { n__, __ } from '~/locale';
|
||||
import { DESIGN_ROUTE_NAME } from '../../router/constants';
|
||||
|
|
@ -11,6 +11,9 @@ export default {
|
|||
GlIcon,
|
||||
Timeago,
|
||||
},
|
||||
directives: {
|
||||
GlTooltip: GlTooltipDirective,
|
||||
},
|
||||
props: {
|
||||
id: {
|
||||
type: [Number, String],
|
||||
|
|
@ -130,7 +133,7 @@ export default {
|
|||
<div
|
||||
class="card-body gl-p-0 gl-display-flex gl-align-items-center gl-justify-content-center gl-overflow-hidden gl-relative"
|
||||
>
|
||||
<div v-if="icon.name" data-testid="designEvent" class="design-event gl-absolute">
|
||||
<div v-if="icon.name" data-testid="design-event" class="design-event gl-absolute">
|
||||
<span :title="icon.tooltip" :aria-label="icon.tooltip">
|
||||
<gl-icon
|
||||
:name="icon.name"
|
||||
|
|
@ -153,9 +156,10 @@ export default {
|
|||
v-show="showImage"
|
||||
:src="imageLink"
|
||||
:alt="filename"
|
||||
class="gl-display-block gl-mx-auto gl-max-w-full mh-100 design-img"
|
||||
class="gl-display-block gl-mx-auto gl-max-w-full gl-max-h-full design-img"
|
||||
data-qa-selector="design_image"
|
||||
:data-qa-filename="filename"
|
||||
:data-testid="`design-img-${id}`"
|
||||
@load="onImageLoad"
|
||||
@error="onImageError"
|
||||
/>
|
||||
|
|
@ -163,9 +167,14 @@ export default {
|
|||
</div>
|
||||
<div class="card-footer gl-display-flex gl-w-full">
|
||||
<div class="gl-display-flex gl-flex-direction-column str-truncated-100">
|
||||
<span class="gl-font-weight-bold str-truncated-100" data-qa-selector="design_file_name">{{
|
||||
filename
|
||||
}}</span>
|
||||
<span
|
||||
v-gl-tooltip
|
||||
class="gl-font-weight-bold str-truncated-100"
|
||||
data-qa-selector="design_file_name"
|
||||
:data-testid="`design-img-filename-${id}`"
|
||||
:title="filename"
|
||||
>{{ filename }}</span
|
||||
>
|
||||
<span v-if="updatedAt" class="str-truncated-100">
|
||||
{{ __('Updated') }} <timeago :time="updatedAt" tooltip-placement="bottom" />
|
||||
</span>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,23 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Types
|
||||
module CiConfiguration
|
||||
module Sast
|
||||
# rubocop: disable Graphql/AuthorizeTypes
|
||||
class AnalyzersEntityInputType < BaseInputObject
|
||||
graphql_name 'SastCiConfigurationAnalyzersEntityInput'
|
||||
description 'Represents the analyzers entity in SAST CI configuration'
|
||||
|
||||
argument :name, GraphQL::STRING_TYPE, required: true,
|
||||
description: 'Name of analyzer.'
|
||||
|
||||
argument :enabled, GraphQL::BOOLEAN_TYPE, required: true,
|
||||
description: 'State of the analyzer.'
|
||||
|
||||
argument :variables, [::Types::CiConfiguration::Sast::EntityInputType],
|
||||
description: 'List of variables for the analyzer.',
|
||||
required: false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Types
|
||||
module CiConfiguration
|
||||
module Sast
|
||||
# rubocop: disable Graphql/AuthorizeTypes
|
||||
class AnalyzersEntityType < BaseObject
|
||||
graphql_name 'SastCiConfigurationAnalyzersEntity'
|
||||
description 'Represents an analyzer entity in SAST CI configuration'
|
||||
|
||||
field :name, GraphQL::STRING_TYPE, null: true,
|
||||
description: 'Name of the analyzer.'
|
||||
|
||||
field :label, GraphQL::STRING_TYPE, null: true,
|
||||
description: 'Analyzer label used in the config UI.'
|
||||
|
||||
field :enabled, GraphQL::BOOLEAN_TYPE, null: true,
|
||||
description: 'Indicates whether an analyzer is enabled.'
|
||||
|
||||
field :description, GraphQL::STRING_TYPE, null: true,
|
||||
description: 'Analyzer description that is displayed on the form.'
|
||||
|
||||
field :variables, ::Types::CiConfiguration::Sast::EntityType.connection_type, null: true,
|
||||
description: 'List of supported variables.'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Types
|
||||
module CiConfiguration
|
||||
module Sast
|
||||
# rubocop: disable Graphql/AuthorizeTypes
|
||||
class EntityInputType < BaseInputObject
|
||||
graphql_name 'SastCiConfigurationEntityInput'
|
||||
description 'Represents an entity in SAST CI configuration'
|
||||
|
||||
argument :field, GraphQL::STRING_TYPE, required: true,
|
||||
description: 'CI keyword of entity.'
|
||||
|
||||
argument :default_value, GraphQL::STRING_TYPE, required: true,
|
||||
description: 'Default value that is used if value is empty.'
|
||||
|
||||
argument :value, GraphQL::STRING_TYPE, required: true,
|
||||
description: 'Current value of the entity.'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Types
|
||||
module CiConfiguration
|
||||
module Sast
|
||||
# rubocop: disable Graphql/AuthorizeTypes
|
||||
class EntityType < BaseObject
|
||||
graphql_name 'SastCiConfigurationEntity'
|
||||
description 'Represents an entity in SAST CI configuration'
|
||||
|
||||
field :field, GraphQL::STRING_TYPE, null: true,
|
||||
description: 'CI keyword of entity.'
|
||||
|
||||
field :label, GraphQL::STRING_TYPE, null: true,
|
||||
description: 'Label for entity used in the form.'
|
||||
|
||||
field :type, GraphQL::STRING_TYPE, null: true,
|
||||
description: 'Type of the field value.'
|
||||
|
||||
field :options, ::Types::CiConfiguration::Sast::OptionsEntityType.connection_type, null: true,
|
||||
description: 'Different possible values of the field.'
|
||||
|
||||
field :default_value, GraphQL::STRING_TYPE, null: true,
|
||||
description: 'Default value that is used if value is empty.'
|
||||
|
||||
field :description, GraphQL::STRING_TYPE, null: true,
|
||||
description: 'Entity description that is displayed on the form.'
|
||||
|
||||
field :value, GraphQL::STRING_TYPE, null: true,
|
||||
description: 'Current value of the entity.'
|
||||
|
||||
field :size, ::Types::CiConfiguration::Sast::UiComponentSizeEnum, null: true,
|
||||
description: 'Size of the UI component.'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Types
|
||||
module CiConfiguration
|
||||
module Sast
|
||||
class InputType < BaseInputObject # rubocop:disable Graphql/AuthorizeTypes
|
||||
graphql_name 'SastCiConfigurationInput'
|
||||
description 'Represents a CI configuration of SAST'
|
||||
|
||||
argument :global, [::Types::CiConfiguration::Sast::EntityInputType],
|
||||
description: 'List of global entities related to SAST configuration.',
|
||||
required: false
|
||||
|
||||
argument :pipeline, [::Types::CiConfiguration::Sast::EntityInputType],
|
||||
description: 'List of pipeline entities related to SAST configuration.',
|
||||
required: false
|
||||
|
||||
argument :analyzers, [::Types::CiConfiguration::Sast::AnalyzersEntityInputType],
|
||||
description: 'List of analyzers and related variables for the SAST configuration.',
|
||||
required: false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Types
|
||||
module CiConfiguration
|
||||
module Sast
|
||||
# rubocop: disable Graphql/AuthorizeTypes
|
||||
class OptionsEntityType < BaseObject
|
||||
graphql_name 'SastCiConfigurationOptionsEntity'
|
||||
description 'Represents an entity for options in SAST CI configuration'
|
||||
|
||||
field :label, GraphQL::STRING_TYPE, null: true,
|
||||
description: 'Label of option entity.'
|
||||
|
||||
field :value, GraphQL::STRING_TYPE, null: true,
|
||||
description: 'Value of option entity.'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Types
|
||||
module CiConfiguration
|
||||
module Sast
|
||||
# rubocop: disable Graphql/AuthorizeTypes
|
||||
class Type < BaseObject
|
||||
graphql_name 'SastCiConfiguration'
|
||||
description 'Represents a CI configuration of SAST'
|
||||
|
||||
field :global, ::Types::CiConfiguration::Sast::EntityType.connection_type, null: true,
|
||||
description: 'List of global entities related to SAST configuration.'
|
||||
|
||||
field :pipeline, ::Types::CiConfiguration::Sast::EntityType.connection_type, null: true,
|
||||
description: 'List of pipeline entities related to SAST configuration.'
|
||||
|
||||
field :analyzers, ::Types::CiConfiguration::Sast::AnalyzersEntityType.connection_type, null: true,
|
||||
description: 'List of analyzers entities attached to SAST configuration.'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Types
|
||||
module CiConfiguration
|
||||
module Sast
|
||||
class UiComponentSizeEnum < BaseEnum
|
||||
graphql_name 'SastUiComponentSize'
|
||||
description 'Size of UI component in SAST configuration page'
|
||||
|
||||
value 'SMALL'
|
||||
value 'MEDIUM'
|
||||
value 'LARGE'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -16,6 +16,10 @@ module Types
|
|||
field :path, GraphQL::STRING_TYPE, null: false,
|
||||
description: 'Path of the project'
|
||||
|
||||
field :sast_ci_configuration, Types::CiConfiguration::Sast::Type, null: true,
|
||||
calls_gitaly: true,
|
||||
description: 'SAST CI configuration for the project'
|
||||
|
||||
field :name_with_namespace, GraphQL::STRING_TYPE, null: false,
|
||||
description: 'Full name of the project with its namespace'
|
||||
field :name, GraphQL::STRING_TYPE, null: false,
|
||||
|
|
@ -359,6 +363,12 @@ module Types
|
|||
project.container_repositories.size
|
||||
end
|
||||
|
||||
def sast_ci_configuration
|
||||
return unless Ability.allowed?(current_user, :download_code, object)
|
||||
|
||||
::Security::CiConfiguration::SastParserService.new(object).configuration
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def project
|
||||
|
|
|
|||
|
|
@ -995,13 +995,21 @@ module Ci
|
|||
end
|
||||
|
||||
def has_coverage_reports?
|
||||
pipeline_artifacts&.has_code_coverage?
|
||||
pipeline_artifacts&.has_report?(:code_coverage)
|
||||
end
|
||||
|
||||
def can_generate_coverage_reports?
|
||||
has_reports?(Ci::JobArtifact.coverage_reports)
|
||||
end
|
||||
|
||||
def has_codequality_reports?
|
||||
pipeline_artifacts&.has_report?(:code_quality)
|
||||
end
|
||||
|
||||
def can_generate_codequality_reports?
|
||||
has_reports?(Ci::JobArtifact.codequality_reports)
|
||||
end
|
||||
|
||||
def test_report_summary
|
||||
strong_memoize(:test_report_summary) do
|
||||
Gitlab::Ci::Reports::TestReportSummary.new(latest_builds_report_results)
|
||||
|
|
|
|||
|
|
@ -30,15 +30,18 @@ module Ci
|
|||
update_project_statistics project_statistics_name: :pipeline_artifacts_size
|
||||
|
||||
enum file_type: {
|
||||
code_coverage: 1
|
||||
code_coverage: 1,
|
||||
code_quality: 2
|
||||
}
|
||||
|
||||
def self.has_code_coverage?
|
||||
where(file_type: :code_coverage).exists?
|
||||
end
|
||||
class << self
|
||||
def has_report?(file_type)
|
||||
where(file_type: file_type).exists?
|
||||
end
|
||||
|
||||
def self.find_with_code_coverage
|
||||
find_by(file_type: :code_coverage)
|
||||
def find_by_file_type(file_type)
|
||||
find_by(file_type: file_type)
|
||||
end
|
||||
end
|
||||
|
||||
def present
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ module Ci
|
|||
{
|
||||
status: :parsed,
|
||||
key: key(base_pipeline, head_pipeline),
|
||||
data: head_pipeline.pipeline_artifacts.find_with_code_coverage.present.for_files(merge_request.new_paths)
|
||||
data: head_pipeline.pipeline_artifacts.find_by_file_type(:code_coverage).present.for_files(merge_request.new_paths)
|
||||
}
|
||||
rescue => e
|
||||
Gitlab::ErrorTracking.track_exception(e, project_id: project.id)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,128 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Security
|
||||
module CiConfiguration
|
||||
# This class parses SAST template file and .gitlab-ci.yml to populate default and current values into the JSON
|
||||
# read from app/validators/json_schemas/security_ci_configuration_schemas/sast_ui_schema.json
|
||||
class SastParserService < ::BaseService
|
||||
include Gitlab::Utils::StrongMemoize
|
||||
|
||||
SAST_UI_SCHEMA_PATH = 'app/validators/json_schemas/security_ci_configuration_schemas/sast_ui_schema.json'
|
||||
|
||||
def initialize(project)
|
||||
@project = project
|
||||
end
|
||||
|
||||
def configuration
|
||||
result = Gitlab::Json.parse(File.read(Rails.root.join(SAST_UI_SCHEMA_PATH))).with_indifferent_access
|
||||
populate_default_value_for(result, :global)
|
||||
populate_default_value_for(result, :pipeline)
|
||||
fill_current_value_with_default_for(result, :global)
|
||||
fill_current_value_with_default_for(result, :pipeline)
|
||||
populate_current_value_for(result, :global)
|
||||
populate_current_value_for(result, :pipeline)
|
||||
|
||||
fill_current_value_with_default_for_analyzers(result)
|
||||
populate_current_value_for_analyzers(result)
|
||||
|
||||
result
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def sast_template_content
|
||||
Gitlab::Template::GitlabCiYmlTemplate.find('SAST').content
|
||||
end
|
||||
|
||||
def populate_default_value_for(config, level)
|
||||
set_each(config[level], key: :default_value, with: sast_template_attributes)
|
||||
end
|
||||
|
||||
def populate_current_value_for(config, level)
|
||||
set_each(config[level], key: :value, with: gitlab_ci_yml_attributes)
|
||||
end
|
||||
|
||||
def fill_current_value_with_default_for(config, level)
|
||||
set_each(config[level], key: :value, with: sast_template_attributes)
|
||||
end
|
||||
|
||||
def set_each(config_attributes, key:, with:)
|
||||
config_attributes.each do |entity|
|
||||
entity[key] = with[entity[:field]] if with[entity[:field]]
|
||||
end
|
||||
end
|
||||
|
||||
def fill_current_value_with_default_for_analyzers(result)
|
||||
result[:analyzers].each do |analyzer|
|
||||
analyzer[:variables].each do |entity|
|
||||
entity[:value] = entity[:default_value] if entity[:default_value]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def populate_current_value_for_analyzers(result)
|
||||
result[:analyzers].each do |analyzer|
|
||||
analyzer[:enabled] = analyzer_enabled?(analyzer[:name])
|
||||
populate_current_value_for(analyzer, :variables)
|
||||
end
|
||||
end
|
||||
|
||||
def analyzer_enabled?(analyzer_name)
|
||||
# Unless explicitly listed in the excluded analyzers, consider it enabled
|
||||
sast_excluded_analyzers.exclude?(analyzer_name)
|
||||
end
|
||||
|
||||
def sast_excluded_analyzers
|
||||
strong_memoize(:sast_excluded_analyzers) do
|
||||
all_analyzers = Security::CiConfiguration::SastBuildActions::SAST_DEFAULT_ANALYZERS.split(', ') rescue []
|
||||
enabled_analyzers = sast_default_analyzers.split(',').map(&:strip) rescue []
|
||||
|
||||
excluded_analyzers = gitlab_ci_yml_attributes["SAST_EXCLUDED_ANALYZERS"] || sast_template_attributes["SAST_EXCLUDED_ANALYZERS"]
|
||||
excluded_analyzers = excluded_analyzers.split(',').map(&:strip) rescue []
|
||||
((all_analyzers - enabled_analyzers) + excluded_analyzers).uniq
|
||||
end
|
||||
end
|
||||
|
||||
def sast_default_analyzers
|
||||
@sast_default_analyzers ||= gitlab_ci_yml_attributes["SAST_DEFAULT_ANALYZERS"] || sast_template_attributes["SAST_DEFAULT_ANALYZERS"]
|
||||
end
|
||||
|
||||
def sast_template_attributes
|
||||
@sast_template_attributes ||= build_sast_attributes(sast_template_content)
|
||||
end
|
||||
|
||||
def gitlab_ci_yml_attributes
|
||||
@gitlab_ci_yml_attributes ||= begin
|
||||
config_content = @project.repository.blob_data_at(@project.repository.root_ref_sha, ci_config_file)
|
||||
|
||||
return {} unless config_content
|
||||
|
||||
build_sast_attributes(config_content)
|
||||
end
|
||||
end
|
||||
|
||||
def ci_config_file
|
||||
'.gitlab-ci.yml'
|
||||
end
|
||||
|
||||
def build_sast_attributes(content)
|
||||
options = { project: @project, user: current_user, sha: @project.repository.commit.sha }
|
||||
yaml_result = Gitlab::Ci::YamlProcessor.new(content, options).execute
|
||||
return {} unless yaml_result.valid?
|
||||
|
||||
sast_attributes = yaml_result.build_attributes(:sast)
|
||||
extract_required_attributes(sast_attributes)
|
||||
end
|
||||
|
||||
def extract_required_attributes(attributes)
|
||||
result = {}
|
||||
attributes[:yaml_variables].each do |variable|
|
||||
result[variable[:key]] = variable[:value]
|
||||
end
|
||||
|
||||
result[:stage] = attributes[:stage]
|
||||
result.with_indifferent_access
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -6,7 +6,7 @@
|
|||
%button.btn.js-settings-toggle
|
||||
= expanded ? _('Collapse') : _('Expand')
|
||||
%p
|
||||
= _('Select the branch you want to set as the default for this project. All merge requests and commits will automatically be made against this branch unless you specify a different one.')
|
||||
= _('Set the default branch for this project. All merge requests and commits are made against this branch unless you specify a different one.')
|
||||
|
||||
.settings-content
|
||||
= form_for @project, remote: true, html: { multipart: true, anchor: 'default-branch-settings' }, authenticity_token: true do |f|
|
||||
|
|
@ -25,7 +25,7 @@
|
|||
= f.label :autoclose_referenced_issues, class: 'form-check-label' do
|
||||
%strong= _("Auto-close referenced issues on default branch")
|
||||
.form-text.text-muted
|
||||
= _("Issues referenced by merge requests and commits within the default branch will be closed automatically")
|
||||
= _("When merge requests and commits in the default branch close, any issues they reference also close.")
|
||||
= link_to sprite_icon('question-o'), help_page_path('user/project/issues/managing_issues.md', anchor: 'disabling-automatic-issue-closing'), target: '_blank'
|
||||
|
||||
= f.submit _('Save changes'), class: "gl-button btn btn-success"
|
||||
|
|
|
|||
|
|
@ -9,6 +9,6 @@
|
|||
.note-form-actions.clearfix
|
||||
.settings-message.note-edit-warning.js-finish-edit-warning
|
||||
= _("Finish editing this message first!")
|
||||
= submit_tag _('Save comment'), class: 'btn btn-success js-comment-save-button', data: { qa_selector: 'save_comment_button' }
|
||||
%button.btn.btn-cancel.note-edit-cancel{ type: 'button' }
|
||||
= submit_tag _('Save comment'), class: 'gl-button btn btn-success js-comment-save-button', data: { qa_selector: 'save_comment_button' }
|
||||
%button.btn.gl-button.btn-cancel.note-edit-cancel{ type: 'button' }
|
||||
= _("Cancel")
|
||||
|
|
|
|||
|
|
@ -38,5 +38,5 @@
|
|||
.note-form-actions.clearfix
|
||||
= render partial: 'shared/notes/comment_button'
|
||||
|
||||
%a.btn.btn-cancel.js-close-discussion-note-form.hide{ role: "button", data: { cancel_text: _("Cancel") } }
|
||||
%a.btn.gl-button.btn-cancel.js-close-discussion-note-form.hide{ role: "button", data: { cancel_text: _("Cancel") } }
|
||||
= _('Cancel')
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Adds GitLab UI styles to button in _edit_form.html.haml
|
||||
merge_request: 51156
|
||||
author: nuwe1
|
||||
type: other
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Adds GitLab UI styles to button in _form.html.haml
|
||||
merge_request: 51160
|
||||
author: nuwe1
|
||||
type: other
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Display full design name in tooltip
|
||||
merge_request: 51421
|
||||
author:
|
||||
type: changed
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Updated UI text to match style guidelines
|
||||
merge_request: 51658
|
||||
author:
|
||||
type: other
|
||||
|
|
@ -1,5 +1,13 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
if Gitlab::Runtime.puma? && ::Puma.cli_config.options[:workers].to_i == 0
|
||||
return if allow_single_mode?
|
||||
|
||||
raise 'Puma is only supported in Cluster-mode: workers > 0'
|
||||
end
|
||||
|
||||
def allow_single_mode?
|
||||
return false if Gitlab.com?
|
||||
|
||||
Gitlab::Utils.to_boolean(ENV['PUMA_SKIP_CLUSTER_VALIDATION'])
|
||||
end
|
||||
|
|
|
|||
|
|
@ -157,3 +157,4 @@ exceptions:
|
|||
- XML
|
||||
- XSS
|
||||
- YAML
|
||||
- ZIP
|
||||
|
|
|
|||
|
|
@ -4,22 +4,24 @@ group: Health
|
|||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
|
||||
---
|
||||
|
||||
# GitLab Configuration
|
||||
# GitLab configuration
|
||||
|
||||
GitLab Performance Monitoring is disabled by default. To enable it and change any of its
|
||||
settings:
|
||||
|
||||
1. Navigate to **Admin Area > Settings > Metrics and profiling**
|
||||
(`/admin/application_settings/metrics_and_profiling`):
|
||||
|
||||

|
||||
|
||||
1. You must restart all GitLab processes for the changes to take effect:
|
||||
(`/admin/application_settings/metrics_and_profiling`).
|
||||
1. Add the necessary configuration changes.
|
||||
1. Restart all GitLab for the changes to take effect:
|
||||
|
||||
- For Omnibus GitLab installations: `sudo gitlab-ctl restart`
|
||||
- For installations from source: `sudo service gitlab restart`
|
||||
|
||||
## Pending Migrations
|
||||
NOTE:
|
||||
Removed [in GitLab 13.0](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/30786). Use the
|
||||
[Prometheus integration](../prometheus/index.md) instead.
|
||||
|
||||
## Pending migrations
|
||||
|
||||
When any migrations are pending, the metrics are disabled until the migrations
|
||||
have been performed.
|
||||
|
|
|
|||
|
|
@ -17,42 +17,38 @@ or Grafana supplies package repositories (Yum/Apt) for easy installation.
|
|||
See [Grafana installation documentation](https://grafana.com/docs/grafana/latest/installation/)
|
||||
for detailed steps.
|
||||
|
||||
Before starting Grafana for the first time, set the admin user
|
||||
Before starting Grafana for the first time, set the administration user
|
||||
and password in `/etc/grafana/grafana.ini`. If you don't, the default password
|
||||
is `admin`.
|
||||
|
||||
## Configuration
|
||||
|
||||
1. Log in to Grafana as the admin user.
|
||||
1. Expand the menu by clicking the Grafana logo in the top left corner.
|
||||
1. Choose **Data Sources** from the menu.
|
||||
1. Click **Add new** in the top bar:
|
||||

|
||||
1. Edit the data source to fit your needs:
|
||||

|
||||
1. Click **Save**.
|
||||
1. Log in to Grafana as the administration user.
|
||||
1. Select **Data Sources** from the **Configuration** menu.
|
||||
1. Select the **Add data source** button.
|
||||
1. Select the required data source type. For example, [Prometheus](../prometheus/index.md#prometheus-as-a-grafana-data-source).
|
||||
1. Complete the details for the data source and select the **Save & Test** button.
|
||||
|
||||
Grafana should indicate the data source is working.
|
||||
|
||||
## Import Dashboards
|
||||
|
||||
You can now import a set of default dashboards to start displaying useful information.
|
||||
GitLab has published a set of default
|
||||
[Grafana dashboards](https://gitlab.com/gitlab-org/grafana-dashboards) to get you started.
|
||||
Clone the repository, or download a ZIP file or tarball, then follow these steps to import each
|
||||
JSON file individually:
|
||||
[Grafana dashboards](https://gitlab.com/gitlab-org/grafana-dashboards) to get you started. To use
|
||||
them:
|
||||
|
||||
1. Log in to Grafana as the admin user.
|
||||
1. Open the dashboard dropdown menu and click **Import**:
|
||||

|
||||
1. Click **Choose file**, and browse to the location where you downloaded or
|
||||
cloned the dashboard repository. Select a JSON file to import:
|
||||

|
||||
1. After the dashboard is imported, click the **Save dashboard** icon in the top bar:
|
||||

|
||||
1. Clone the repository, or download a ZIP file or tarball.
|
||||
1. Follow these steps to import each JSON file individually:
|
||||
|
||||
If you don't save the dashboard after importing it, the dashboard is removed
|
||||
when you navigate away from the page.
|
||||
1. Log in to Grafana as the administration user.
|
||||
1. Select **Manage** from the **Dashboards** menu.
|
||||
1. Select the **Import** button, then the **Upload JSON file** button.
|
||||
1. Locate the JSON file to import and select **Choose for Upload**. Select the **Import** button.
|
||||
1. After the dashboard is imported, select the **Save dashboard** icon in the top bar.
|
||||
|
||||
Repeat this process for each dashboard you wish to import.
|
||||
If you don't save the dashboard after importing it, the dashboard is removed
|
||||
when you navigate away from the page. Repeat this process for each dashboard you wish to import.
|
||||
|
||||
Alternatively, you can import all the dashboards into your Grafana
|
||||
instance. For more information about this process, see the
|
||||
|
|
@ -103,7 +99,7 @@ sudo mv /var/opt/gitlab/grafana/data.bak.xxxx/ /var/opt/gitlab/grafana/data/
|
|||
|
||||
However, you should **not** reinstate your old data _except_ under one of the following conditions:
|
||||
|
||||
1. If you're certain that you changed your default admin password when you enabled Grafana.
|
||||
1. If you're certain that you changed your default administration password when you enabled Grafana.
|
||||
1. If you run GitLab in a private network, accessed only by trusted users, and your
|
||||
Grafana login page has not been exposed to the internet.
|
||||
|
||||
|
|
@ -121,8 +117,6 @@ existing data and dashboards.
|
|||
For more information and further mitigation details, please refer to our
|
||||
[blog post on the security release](https://about.gitlab.com/releases/2019/08/12/critical-security-release-gitlab-12-dot-1-dot-6-released/).
|
||||
|
||||
---
|
||||
|
||||
Read more on:
|
||||
|
||||
- [Introduction to GitLab Performance Monitoring](index.md)
|
||||
|
|
|
|||
Binary file not shown.
|
Before Width: | Height: | Size: 7.6 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 12 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 14 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 12 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 4.5 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 26 KiB |
|
|
@ -6,6 +6,11 @@ info: To determine the technical writer assigned to the Stage/Group associated w
|
|||
|
||||
# Monitoring GitLab with Prometheus
|
||||
|
||||
[Prometheus](https://prometheus.io) is a powerful time-series monitoring service, providing a flexible
|
||||
platform for monitoring GitLab and other software products.
|
||||
GitLab provides out-of-the-box monitoring with Prometheus, providing easy
|
||||
access to high quality time-series monitoring of GitLab services.
|
||||
|
||||
> **Notes:**
|
||||
>
|
||||
> - Prometheus and the various exporters listed in this page are bundled in the
|
||||
|
|
@ -16,11 +21,6 @@ info: To determine the technical writer assigned to the Stage/Group associated w
|
|||
> - Prometheus and its exporters don't authenticate users, and are available
|
||||
> to anyone who can access them.
|
||||
|
||||
[Prometheus](https://prometheus.io) is a powerful time-series monitoring service, providing a flexible
|
||||
platform for monitoring GitLab and other software products.
|
||||
GitLab provides out of the box monitoring with Prometheus, providing easy
|
||||
access to high quality time-series monitoring of GitLab services.
|
||||
|
||||
## Overview
|
||||
|
||||
Prometheus works by periodically connecting to data sources and collecting their
|
||||
|
|
|
|||
|
|
@ -222,7 +222,7 @@ control over how the Pages daemon runs and serves content in your environment.
|
|||
| `api_secret_key` | Full path to file with secret key used to authenticate with the GitLab API. Auto-generated when left unset.
|
||||
| `artifacts_server` | Enable viewing [artifacts](../job_artifacts.md) in GitLab Pages.
|
||||
| `artifacts_server_timeout` | Timeout (in seconds) for a proxied request to the artifacts server.
|
||||
| `artifacts_server_url` | API URL to proxy artifact requests to. Defaults to GitLab `external URL` + `/api/v4`, for example `https://gitlab.com/api/v4`.
|
||||
| `artifacts_server_url` | API URL to proxy artifact requests to. Defaults to GitLab `external URL` + `/api/v4`, for example `https://gitlab.com/api/v4`. When running a [separate Pages server](#running-gitlab-pages-on-a-separate-server), this URL must point to the main GitLab server's API.
|
||||
| `auth_redirect_uri` | Callback URL for authenticating with GitLab. Defaults to project's subdomain of `pages_external_url` + `/auth`.
|
||||
| `auth_secret` | Secret key for signing authentication requests. Leave blank to pull automatically from GitLab during OAuth registration.
|
||||
| `dir` | Working directory for configuration and secrets files.
|
||||
|
|
@ -940,6 +940,8 @@ Upgrading to an [officially supported operating system](https://about.gitlab.com
|
|||
This problem comes from the permissions of the GitLab Pages OAuth application. To fix it, go to
|
||||
**Admin > Applications > GitLab Pages** and edit the application. Under **Scopes**, ensure that the
|
||||
`api` scope is selected and save your changes.
|
||||
When running a [separate Pages server](#running-gitlab-pages-on-a-separate-server),
|
||||
this setting needs to be configured on the main GitLab server.
|
||||
|
||||
### Workaround in case no wildcard DNS entry can be set
|
||||
|
||||
|
|
|
|||
|
|
@ -22102,27 +22102,27 @@ Represents an analyzer entity in SAST CI configuration
|
|||
"""
|
||||
type SastCiConfigurationAnalyzersEntity {
|
||||
"""
|
||||
Analyzer description that is displayed on the form
|
||||
Analyzer description that is displayed on the form.
|
||||
"""
|
||||
description: String
|
||||
|
||||
"""
|
||||
Indicates whether an analyzer is enabled
|
||||
Indicates whether an analyzer is enabled.
|
||||
"""
|
||||
enabled: Boolean
|
||||
|
||||
"""
|
||||
Analyzer label used in the config UI
|
||||
Analyzer label used in the config UI.
|
||||
"""
|
||||
label: String
|
||||
|
||||
"""
|
||||
Name of the analyzer
|
||||
Name of the analyzer.
|
||||
"""
|
||||
name: String
|
||||
|
||||
"""
|
||||
List of supported variables
|
||||
List of supported variables.
|
||||
"""
|
||||
variables(
|
||||
"""
|
||||
|
|
@ -22187,17 +22187,17 @@ Represents the analyzers entity in SAST CI configuration
|
|||
"""
|
||||
input SastCiConfigurationAnalyzersEntityInput {
|
||||
"""
|
||||
State of the analyzer
|
||||
State of the analyzer.
|
||||
"""
|
||||
enabled: Boolean!
|
||||
|
||||
"""
|
||||
Name of analyzer
|
||||
Name of analyzer.
|
||||
"""
|
||||
name: String!
|
||||
|
||||
"""
|
||||
List of variables for the analyzer
|
||||
List of variables for the analyzer.
|
||||
"""
|
||||
variables: [SastCiConfigurationEntityInput!]
|
||||
}
|
||||
|
|
@ -22307,17 +22307,17 @@ Represents an entity in SAST CI configuration
|
|||
"""
|
||||
input SastCiConfigurationEntityInput {
|
||||
"""
|
||||
Default value that is used if value is empty
|
||||
Default value that is used if value is empty.
|
||||
"""
|
||||
defaultValue: String!
|
||||
|
||||
"""
|
||||
CI keyword of entity
|
||||
CI keyword of entity.
|
||||
"""
|
||||
field: String!
|
||||
|
||||
"""
|
||||
Current value of the entity
|
||||
Current value of the entity.
|
||||
"""
|
||||
value: String!
|
||||
}
|
||||
|
|
@ -22327,17 +22327,17 @@ Represents a CI configuration of SAST
|
|||
"""
|
||||
input SastCiConfigurationInput {
|
||||
"""
|
||||
List of analyzers and related variables for the SAST configuration
|
||||
List of analyzers and related variables for the SAST configuration.
|
||||
"""
|
||||
analyzers: [SastCiConfigurationAnalyzersEntityInput!]
|
||||
|
||||
"""
|
||||
List of global entities related to SAST configuration
|
||||
List of global entities related to SAST configuration.
|
||||
"""
|
||||
global: [SastCiConfigurationEntityInput!]
|
||||
|
||||
"""
|
||||
List of pipeline entities related to SAST configuration
|
||||
List of pipeline entities related to SAST configuration.
|
||||
"""
|
||||
pipeline: [SastCiConfigurationEntityInput!]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -63880,7 +63880,7 @@
|
|||
"fields": [
|
||||
{
|
||||
"name": "description",
|
||||
"description": "Analyzer description that is displayed on the form",
|
||||
"description": "Analyzer description that is displayed on the form.",
|
||||
"args": [
|
||||
|
||||
],
|
||||
|
|
@ -63894,7 +63894,7 @@
|
|||
},
|
||||
{
|
||||
"name": "enabled",
|
||||
"description": "Indicates whether an analyzer is enabled",
|
||||
"description": "Indicates whether an analyzer is enabled.",
|
||||
"args": [
|
||||
|
||||
],
|
||||
|
|
@ -63908,7 +63908,7 @@
|
|||
},
|
||||
{
|
||||
"name": "label",
|
||||
"description": "Analyzer label used in the config UI",
|
||||
"description": "Analyzer label used in the config UI.",
|
||||
"args": [
|
||||
|
||||
],
|
||||
|
|
@ -63922,7 +63922,7 @@
|
|||
},
|
||||
{
|
||||
"name": "name",
|
||||
"description": "Name of the analyzer",
|
||||
"description": "Name of the analyzer.",
|
||||
"args": [
|
||||
|
||||
],
|
||||
|
|
@ -63936,7 +63936,7 @@
|
|||
},
|
||||
{
|
||||
"name": "variables",
|
||||
"description": "List of supported variables",
|
||||
"description": "List of supported variables.",
|
||||
"args": [
|
||||
{
|
||||
"name": "after",
|
||||
|
|
@ -64115,7 +64115,7 @@
|
|||
"inputFields": [
|
||||
{
|
||||
"name": "name",
|
||||
"description": "Name of analyzer",
|
||||
"description": "Name of analyzer.",
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
|
|
@ -64129,7 +64129,7 @@
|
|||
},
|
||||
{
|
||||
"name": "enabled",
|
||||
"description": "State of the analyzer",
|
||||
"description": "State of the analyzer.",
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
|
|
@ -64143,7 +64143,7 @@
|
|||
},
|
||||
{
|
||||
"name": "variables",
|
||||
"description": "List of variables for the analyzer",
|
||||
"description": "List of variables for the analyzer.",
|
||||
"type": {
|
||||
"kind": "LIST",
|
||||
"name": null,
|
||||
|
|
@ -64448,7 +64448,7 @@
|
|||
"inputFields": [
|
||||
{
|
||||
"name": "field",
|
||||
"description": "CI keyword of entity",
|
||||
"description": "CI keyword of entity.",
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
|
|
@ -64462,7 +64462,7 @@
|
|||
},
|
||||
{
|
||||
"name": "defaultValue",
|
||||
"description": "Default value that is used if value is empty",
|
||||
"description": "Default value that is used if value is empty.",
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
|
|
@ -64476,7 +64476,7 @@
|
|||
},
|
||||
{
|
||||
"name": "value",
|
||||
"description": "Current value of the entity",
|
||||
"description": "Current value of the entity.",
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
|
|
@ -64501,7 +64501,7 @@
|
|||
"inputFields": [
|
||||
{
|
||||
"name": "global",
|
||||
"description": "List of global entities related to SAST configuration",
|
||||
"description": "List of global entities related to SAST configuration.",
|
||||
"type": {
|
||||
"kind": "LIST",
|
||||
"name": null,
|
||||
|
|
@ -64519,7 +64519,7 @@
|
|||
},
|
||||
{
|
||||
"name": "pipeline",
|
||||
"description": "List of pipeline entities related to SAST configuration",
|
||||
"description": "List of pipeline entities related to SAST configuration.",
|
||||
"type": {
|
||||
"kind": "LIST",
|
||||
"name": null,
|
||||
|
|
@ -64537,7 +64537,7 @@
|
|||
},
|
||||
{
|
||||
"name": "analyzers",
|
||||
"description": "List of analyzers and related variables for the SAST configuration",
|
||||
"description": "List of analyzers and related variables for the SAST configuration.",
|
||||
"type": {
|
||||
"kind": "LIST",
|
||||
"name": null,
|
||||
|
|
|
|||
|
|
@ -3183,11 +3183,11 @@ Represents an analyzer entity in SAST CI configuration.
|
|||
|
||||
| Field | Type | Description |
|
||||
| ----- | ---- | ----------- |
|
||||
| `description` | String | Analyzer description that is displayed on the form |
|
||||
| `enabled` | Boolean | Indicates whether an analyzer is enabled |
|
||||
| `label` | String | Analyzer label used in the config UI |
|
||||
| `name` | String | Name of the analyzer |
|
||||
| `variables` | SastCiConfigurationEntityConnection | List of supported variables |
|
||||
| `description` | String | Analyzer description that is displayed on the form. |
|
||||
| `enabled` | Boolean | Indicates whether an analyzer is enabled. |
|
||||
| `label` | String | Analyzer label used in the config UI. |
|
||||
| `name` | String | Name of the analyzer. |
|
||||
| `variables` | SastCiConfigurationEntityConnection | List of supported variables. |
|
||||
|
||||
### SastCiConfigurationEntity
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ group: Source Code
|
|||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
|
||||
---
|
||||
|
||||
# Approval Rules **(STARTER)**
|
||||
# Approval Rules development guide
|
||||
|
||||
This document explains the backend design and flow of all related functionality
|
||||
about [merge request approval rules](../user/project/merge_requests/merge_request_approvals.md).
|
||||
|
|
|
|||
|
|
@ -622,13 +622,6 @@ dropped and users get
|
|||
|
||||
To help avoid abuse, project and group imports, exports, and export downloads are rate limited. See [Project import/export rate limits](../../user/project/settings/import_export.md#rate-limits) and [Group import/export rate limits](../../user/group/settings/import_export.md#rate-limits) for details.
|
||||
|
||||
GitLab.com Import/Export Rate Limits are set to the default except:
|
||||
|
||||
| Setting | GitLab.com | Default |
|
||||
|:-------------------------------------------------|:-----------|:--------|
|
||||
| Max Project Export requests per minute per user | 1 | 6 |
|
||||
| Max Group Export requests per minute per user | 1 | 6 |
|
||||
|
||||
### Non-configurable limits
|
||||
|
||||
See [non-configurable limits](../../security/rate_limits.md#non-configurable-limits) for information on
|
||||
|
|
|
|||
|
|
@ -11,10 +11,8 @@ NOTE:
|
|||
Project access tokens are supported for self-managed instances on Core and above. They are also supported on GitLab.com Bronze and above (excluding [trial licenses](https://about.gitlab.com/free-trial/)).
|
||||
|
||||
> - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/2587) in GitLab 13.0.
|
||||
> - It was [deployed](https://gitlab.com/groups/gitlab-org/-/epics/2587) behind a feature flag, disabled by default.
|
||||
> - [Became enabled by default](https://gitlab.com/gitlab-org/gitlab/-/issues/218722) in GitLab 13.3.
|
||||
> - It's recommended for production use.
|
||||
> - [Became available on GitLab.com](https://gitlab.com/gitlab-org/gitlab/-/issues/235765) in 13.5 for paid groups only.
|
||||
> - [Became available on GitLab.com](https://gitlab.com/gitlab-org/gitlab/-/issues/235765) in GitLab 13.5 for paid groups only.
|
||||
> - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/235765) in GitLab 13.5.
|
||||
|
||||
WARNING:
|
||||
This feature might not be available to you. Check the **version history** note above for details.
|
||||
|
|
|
|||
|
|
@ -5,9 +5,10 @@ module Gitlab
|
|||
module Template
|
||||
module Finders
|
||||
class GlobalTemplateFinder < BaseTemplateFinder
|
||||
def initialize(base_dir, extension, categories = {}, excluded_patterns: [])
|
||||
def initialize(base_dir, extension, categories = {}, include_categories_for_file = {}, excluded_patterns: [])
|
||||
@categories = categories
|
||||
@extension = extension
|
||||
@include_categories_for_file = include_categories_for_file
|
||||
@excluded_patterns = excluded_patterns
|
||||
|
||||
super(base_dir)
|
||||
|
|
@ -47,7 +48,9 @@ module Gitlab
|
|||
end
|
||||
|
||||
def select_directory(file_name)
|
||||
@categories.keys.find do |category|
|
||||
categories = @categories
|
||||
categories.merge!(@include_categories_for_file[file_name]) if @include_categories_for_file[file_name].present?
|
||||
categories.keys.find do |category|
|
||||
File.exist?(File.join(category_directory(category), file_name))
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -25,6 +25,12 @@ module Gitlab
|
|||
}
|
||||
end
|
||||
|
||||
def include_categories_for_file
|
||||
{
|
||||
"SAST#{self.extension}" => { 'Security' => 'Security' }
|
||||
}
|
||||
end
|
||||
|
||||
def excluded_patterns
|
||||
strong_memoize(:excluded_patterns) do
|
||||
BASE_EXCLUDED_PATTERNS + additional_excluded_patterns
|
||||
|
|
@ -41,7 +47,11 @@ module Gitlab
|
|||
|
||||
def finder(project = nil)
|
||||
Gitlab::Template::Finders::GlobalTemplateFinder.new(
|
||||
self.base_dir, self.extension, self.categories, excluded_patterns: self.excluded_patterns
|
||||
self.base_dir,
|
||||
self.extension,
|
||||
self.categories,
|
||||
self.include_categories_for_file,
|
||||
excluded_patterns: self.excluded_patterns
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,170 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Security
|
||||
module CiConfiguration
|
||||
class SastBuildActions
|
||||
SAST_DEFAULT_ANALYZERS = 'bandit, brakeman, eslint, flawfinder, gosec, kubesec, nodejs-scan, phpcs-security-audit, pmd-apex, security-code-scan, sobelow, spotbugs'
|
||||
|
||||
def initialize(auto_devops_enabled, params, existing_gitlab_ci_content)
|
||||
@auto_devops_enabled = auto_devops_enabled
|
||||
@variables = variables(params)
|
||||
@existing_gitlab_ci_content = existing_gitlab_ci_content || {}
|
||||
@default_sast_values = default_sast_values(params)
|
||||
@default_values_overwritten = false
|
||||
end
|
||||
|
||||
def generate
|
||||
action = @existing_gitlab_ci_content.present? ? 'update' : 'create'
|
||||
|
||||
update_existing_content!
|
||||
|
||||
[{ action: action, file_path: '.gitlab-ci.yml', content: prepare_existing_content, default_values_overwritten: @default_values_overwritten }]
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def variables(params)
|
||||
# This early return is necessary for supporting REST API.
|
||||
# Will be removed during the implementation of
|
||||
# https://gitlab.com/gitlab-org/gitlab/-/issues/246737
|
||||
return params unless params['global'].present?
|
||||
|
||||
collect_values(params, 'value')
|
||||
end
|
||||
|
||||
def default_sast_values(params)
|
||||
collect_values(params, 'defaultValue')
|
||||
end
|
||||
|
||||
def collect_values(config, key)
|
||||
global_variables = config['global']&.to_h { |k| [k['field'], k[key]] } || {}
|
||||
pipeline_variables = config['pipeline']&.to_h { |k| [k['field'], k[key]] } || {}
|
||||
|
||||
analyzer_variables = collect_analyzer_values(config, key)
|
||||
|
||||
global_variables.merge!(pipeline_variables).merge!(analyzer_variables)
|
||||
end
|
||||
|
||||
def collect_analyzer_values(config, key)
|
||||
analyzer_variables = analyzer_variables_for(config, key)
|
||||
analyzer_variables['SAST_EXCLUDED_ANALYZERS'] = if key == 'value'
|
||||
config['analyzers']
|
||||
&.reject {|a| a['enabled'] }
|
||||
&.collect {|a| a['name'] }
|
||||
&.sort
|
||||
&.join(', ')
|
||||
else
|
||||
''
|
||||
end
|
||||
|
||||
analyzer_variables
|
||||
end
|
||||
|
||||
def analyzer_variables_for(config, key)
|
||||
config['analyzers']
|
||||
&.select {|a| a['enabled'] && a['variables'] }
|
||||
&.flat_map {|a| a['variables'] }
|
||||
&.collect {|v| [v['field'], v[key]] }.to_h
|
||||
end
|
||||
|
||||
def update_existing_content!
|
||||
@existing_gitlab_ci_content['stages'] = set_stages
|
||||
@existing_gitlab_ci_content['variables'] = set_variables(global_variables, @existing_gitlab_ci_content)
|
||||
@existing_gitlab_ci_content['sast'] = set_sast_block
|
||||
@existing_gitlab_ci_content['include'] = set_includes
|
||||
|
||||
@existing_gitlab_ci_content.select! { |k, v| v.present? }
|
||||
@existing_gitlab_ci_content['sast'].select! { |k, v| v.present? }
|
||||
end
|
||||
|
||||
def set_includes
|
||||
includes = @existing_gitlab_ci_content['include'] || []
|
||||
includes = includes.is_a?(Array) ? includes : [includes]
|
||||
includes << { 'template' => template }
|
||||
includes.uniq
|
||||
end
|
||||
|
||||
def set_stages
|
||||
existing_stages = @existing_gitlab_ci_content['stages'] || []
|
||||
base_stages = @auto_devops_enabled ? auto_devops_stages : ['test']
|
||||
(existing_stages + base_stages + [sast_stage]).uniq
|
||||
end
|
||||
|
||||
def auto_devops_stages
|
||||
auto_devops_template = YAML.safe_load( Gitlab::Template::GitlabCiYmlTemplate.find('Auto-DevOps').content )
|
||||
auto_devops_template['stages']
|
||||
end
|
||||
|
||||
def sast_stage
|
||||
@variables['stage'].presence ? @variables['stage'] : 'test'
|
||||
end
|
||||
|
||||
def set_variables(variables, hash_to_update = {})
|
||||
hash_to_update['variables'] ||= {}
|
||||
|
||||
variables.each do |key|
|
||||
if @variables[key].present? && @variables[key].to_s != @default_sast_values[key].to_s
|
||||
hash_to_update['variables'][key] = @variables[key]
|
||||
@default_values_overwritten = true
|
||||
else
|
||||
hash_to_update['variables'].delete(key)
|
||||
end
|
||||
end
|
||||
|
||||
hash_to_update['variables']
|
||||
end
|
||||
|
||||
def set_sast_block
|
||||
sast_content = @existing_gitlab_ci_content['sast'] || {}
|
||||
sast_content['variables'] = set_variables(sast_variables)
|
||||
sast_content['stage'] = sast_stage
|
||||
sast_content.select { |k, v| v.present? }
|
||||
end
|
||||
|
||||
def prepare_existing_content
|
||||
content = @existing_gitlab_ci_content.to_yaml
|
||||
content = remove_document_delimeter(content)
|
||||
|
||||
content.prepend(sast_comment)
|
||||
end
|
||||
|
||||
def remove_document_delimeter(content)
|
||||
content.gsub(/^---\n/, '')
|
||||
end
|
||||
|
||||
def sast_comment
|
||||
<<~YAML
|
||||
# You can override the included template(s) by including variable overrides
|
||||
# See https://docs.gitlab.com/ee/user/application_security/sast/#customizing-the-sast-settings
|
||||
# Note that environment variables can be set in several places
|
||||
# See https://docs.gitlab.com/ee/ci/variables/#priority-of-environment-variables
|
||||
YAML
|
||||
end
|
||||
|
||||
def template
|
||||
return 'Auto-DevOps.gitlab-ci.yml' if @auto_devops_enabled
|
||||
|
||||
'Security/SAST.gitlab-ci.yml'
|
||||
end
|
||||
|
||||
def global_variables
|
||||
%w(
|
||||
SECURE_ANALYZERS_PREFIX
|
||||
)
|
||||
end
|
||||
|
||||
def sast_variables
|
||||
%w(
|
||||
SAST_ANALYZER_IMAGE_TAG
|
||||
SAST_EXCLUDED_PATHS
|
||||
SEARCH_MAX_DEPTH
|
||||
SAST_EXCLUDED_ANALYZERS
|
||||
SAST_BRAKEMAN_LEVEL
|
||||
SAST_BANDIT_EXCLUDED_PATHS
|
||||
SAST_FLAWFINDER_LEVEL
|
||||
SAST_GOSEC_LEVEL
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -15899,9 +15899,6 @@ msgstr ""
|
|||
msgid "Issues closed"
|
||||
msgstr ""
|
||||
|
||||
msgid "Issues referenced by merge requests and commits within the default branch will be closed automatically"
|
||||
msgstr ""
|
||||
|
||||
msgid "Issues with comments, merge requests with diffs and comments, labels, milestones, snippets, and other project entities"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -25494,9 +25491,6 @@ msgstr ""
|
|||
msgid "Select target branch"
|
||||
msgstr ""
|
||||
|
||||
msgid "Select the branch you want to set as the default for this project. All merge requests and commits will automatically be made against this branch unless you specify a different one."
|
||||
msgstr ""
|
||||
|
||||
msgid "Select the custom project template source group."
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -25779,6 +25773,9 @@ msgstr ""
|
|||
msgid "Set target branch to %{branch_name}."
|
||||
msgstr ""
|
||||
|
||||
msgid "Set the default branch for this project. All merge requests and commits are made against this branch unless you specify a different one."
|
||||
msgstr ""
|
||||
|
||||
msgid "Set the default expiration time for each job's artifacts. 0 for unlimited. The default unit is in seconds, but you can define an alternative. For example: %{code_open}4 mins 2 sec%{code_close}, %{code_open}2h42min%{code_close}."
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -31911,6 +31908,9 @@ msgstr ""
|
|||
msgid "When leaving the URL blank, classification labels can still be specified without disabling cross project features or performing external authorization checks."
|
||||
msgstr ""
|
||||
|
||||
msgid "When merge requests and commits in the default branch close, any issues they reference also close."
|
||||
msgstr ""
|
||||
|
||||
msgid "When this merge request is accepted"
|
||||
msgid_plural "When these merge requests are accepted"
|
||||
msgstr[0] ""
|
||||
|
|
|
|||
|
|
@ -34,5 +34,15 @@ FactoryBot.define do
|
|||
|
||||
size { file.size }
|
||||
end
|
||||
|
||||
trait :codequality_report do
|
||||
file_type { :code_quality }
|
||||
size { 2.megabytes }
|
||||
|
||||
after(:build) do |artifact, _evaluator|
|
||||
artifact.file = fixture_file_upload(
|
||||
Rails.root.join('spec/fixtures/pipeline_artifacts/code_quality.json'), 'application/json')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -163,6 +163,12 @@ FactoryBot.define do
|
|||
end
|
||||
end
|
||||
|
||||
trait :with_codequality_report_artifact do
|
||||
after(:build) do |pipeline, evaluator|
|
||||
pipeline.pipeline_artifacts << build(:ci_pipeline_artifact, :codequality_report, pipeline: pipeline, project: pipeline.project)
|
||||
end
|
||||
end
|
||||
|
||||
trait :with_terraform_reports do
|
||||
status { :success }
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"files": {
|
||||
"demo.rb": [
|
||||
{
|
||||
"line": 5,
|
||||
"description": "Method `new_array` has 8 arguments (exceeds 4 allowed). Consider refactoring.",
|
||||
"severity": "major"
|
||||
},
|
||||
{
|
||||
"line": 5,
|
||||
"description": "Avoid parameter lists longer than 5 parameters. [8/5]",
|
||||
"severity": "minor"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -20,6 +20,10 @@ describe('~/api/api_utils.js', () => {
|
|||
);
|
||||
});
|
||||
|
||||
it('ensures the URL is prefixed with a /', () => {
|
||||
expect(apiUtils.buildApiUrl('api/:version/projects/:id')).toEqual('/api/v7/projects/:id');
|
||||
});
|
||||
|
||||
describe('when gon includes a relative_url_root property', () => {
|
||||
beforeEach(() => {
|
||||
window.gon.relative_url_root = '/relative/root';
|
||||
|
|
|
|||
|
|
@ -26,9 +26,10 @@ exports[`Design management list item component with notes renders item with mult
|
|||
|
||||
<img
|
||||
alt="test"
|
||||
class="gl-display-block gl-mx-auto gl-max-w-full mh-100 design-img"
|
||||
class="gl-display-block gl-mx-auto gl-max-w-full gl-max-h-full design-img"
|
||||
data-qa-filename="test"
|
||||
data-qa-selector="design_image"
|
||||
data-testid="design-img-1"
|
||||
src=""
|
||||
/>
|
||||
</gl-intersection-observer-stub>
|
||||
|
|
@ -43,6 +44,8 @@ exports[`Design management list item component with notes renders item with mult
|
|||
<span
|
||||
class="gl-font-weight-bold str-truncated-100"
|
||||
data-qa-selector="design_file_name"
|
||||
data-testid="design-img-filename-1"
|
||||
title="test"
|
||||
>
|
||||
test
|
||||
</span>
|
||||
|
|
@ -100,9 +103,10 @@ exports[`Design management list item component with notes renders item with sing
|
|||
|
||||
<img
|
||||
alt="test"
|
||||
class="gl-display-block gl-mx-auto gl-max-w-full mh-100 design-img"
|
||||
class="gl-display-block gl-mx-auto gl-max-w-full gl-max-h-full design-img"
|
||||
data-qa-filename="test"
|
||||
data-qa-selector="design_image"
|
||||
data-testid="design-img-1"
|
||||
src=""
|
||||
/>
|
||||
</gl-intersection-observer-stub>
|
||||
|
|
@ -117,6 +121,8 @@ exports[`Design management list item component with notes renders item with sing
|
|||
<span
|
||||
class="gl-font-weight-bold str-truncated-100"
|
||||
data-qa-selector="design_file_name"
|
||||
data-testid="design-img-filename-1"
|
||||
title="test"
|
||||
>
|
||||
test
|
||||
</span>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { createLocalVue, shallowMount } from '@vue/test-utils';
|
||||
import { GlIcon, GlLoadingIcon, GlIntersectionObserver } from '@gitlab/ui';
|
||||
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
|
||||
import VueRouter from 'vue-router';
|
||||
import Item from '~/design_management/components/list/item.vue';
|
||||
|
||||
|
|
@ -17,8 +18,11 @@ const DESIGN_VERSION_EVENT = {
|
|||
|
||||
describe('Design management list item component', () => {
|
||||
let wrapper;
|
||||
const imgId = 1;
|
||||
const imgFilename = 'test';
|
||||
|
||||
const findDesignEvent = () => wrapper.find('[data-testid="designEvent"]');
|
||||
const findDesignEvent = () => wrapper.findByTestId('design-event');
|
||||
const findImgFilename = (id = imgId) => wrapper.findByTestId(`design-img-filename-${id}`);
|
||||
const findEventIcon = () => findDesignEvent().find(GlIcon);
|
||||
const findLoadingIcon = () => wrapper.find(GlLoadingIcon);
|
||||
|
||||
|
|
@ -28,25 +32,27 @@ describe('Design management list item component', () => {
|
|||
isUploading = false,
|
||||
imageLoading = false,
|
||||
} = {}) {
|
||||
wrapper = shallowMount(Item, {
|
||||
localVue,
|
||||
router,
|
||||
propsData: {
|
||||
id: 1,
|
||||
filename: 'test',
|
||||
image: 'http://via.placeholder.com/300',
|
||||
isUploading,
|
||||
event,
|
||||
notesCount,
|
||||
updatedAt: '01-01-2019',
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
imageLoading,
|
||||
};
|
||||
},
|
||||
stubs: ['router-link'],
|
||||
});
|
||||
wrapper = extendedWrapper(
|
||||
shallowMount(Item, {
|
||||
localVue,
|
||||
router,
|
||||
propsData: {
|
||||
id: imgId,
|
||||
filename: imgFilename,
|
||||
image: 'http://via.placeholder.com/300',
|
||||
isUploading,
|
||||
event,
|
||||
notesCount,
|
||||
updatedAt: '01-01-2019',
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
imageLoading,
|
||||
};
|
||||
},
|
||||
stubs: ['router-link'],
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
afterEach(() => {
|
||||
|
|
@ -75,6 +81,10 @@ describe('Design management list item component', () => {
|
|||
return wrapper.vm.$nextTick();
|
||||
});
|
||||
|
||||
it('renders a tooltip', () => {
|
||||
expect(findImgFilename().attributes('title')).toEqual(imgFilename);
|
||||
});
|
||||
|
||||
describe('before image is loaded', () => {
|
||||
it('renders loading spinner', () => {
|
||||
expect(wrapper.find(GlLoadingIcon)).toExist();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe ::Types::CiConfiguration::Sast::AnalyzersEntityInputType do
|
||||
it { expect(described_class.graphql_name).to eq('SastCiConfigurationAnalyzersEntityInput') }
|
||||
|
||||
it { expect(described_class.arguments.keys).to match_array(%w[enabled name variables]) }
|
||||
end
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe GitlabSchema.types['SastCiConfigurationAnalyzersEntity'] do
|
||||
let(:fields) { %i[name label enabled description variables] }
|
||||
|
||||
it { expect(described_class.graphql_name).to eq('SastCiConfigurationAnalyzersEntity') }
|
||||
|
||||
it { expect(described_class).to have_graphql_fields(fields) }
|
||||
end
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe ::Types::CiConfiguration::Sast::EntityInputType do
|
||||
it { expect(described_class.graphql_name).to eq('SastCiConfigurationEntityInput') }
|
||||
|
||||
it { expect(described_class.arguments.keys).to match_array(%w[field defaultValue value]) }
|
||||
end
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe GitlabSchema.types['SastCiConfigurationEntity'] do
|
||||
let(:fields) { %i[field label description type options default_value value size] }
|
||||
|
||||
it { expect(described_class.graphql_name).to eq('SastCiConfigurationEntity') }
|
||||
|
||||
it { expect(described_class).to have_graphql_fields(fields) }
|
||||
end
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe ::Types::CiConfiguration::Sast::InputType do
|
||||
it { expect(described_class.graphql_name).to eq('SastCiConfigurationInput') }
|
||||
|
||||
it { expect(described_class.arguments.keys).to match_array(%w[global pipeline analyzers]) }
|
||||
end
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe GitlabSchema.types['SastCiConfigurationOptionsEntity'] do
|
||||
let(:fields) { %i[label value] }
|
||||
|
||||
it { expect(described_class.graphql_name).to eq('SastCiConfigurationOptionsEntity') }
|
||||
|
||||
it { expect(described_class).to have_graphql_fields(fields) }
|
||||
end
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe GitlabSchema.types['SastCiConfiguration'] do
|
||||
let(:fields) { %i[global pipeline analyzers] }
|
||||
|
||||
it { expect(described_class.graphql_name).to eq('SastCiConfiguration') }
|
||||
|
||||
it { expect(described_class).to have_graphql_fields(fields) }
|
||||
end
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Types::CiConfiguration::Sast::UiComponentSizeEnum do
|
||||
specify { expect(described_class.graphql_name).to eq('SastUiComponentSize') }
|
||||
|
||||
it 'exposes all sizes of ui components' do
|
||||
expect(described_class.values.keys).to include(*%w[SMALL MEDIUM LARGE])
|
||||
end
|
||||
end
|
||||
|
|
@ -31,12 +31,171 @@ RSpec.describe GitlabSchema.types['Project'] do
|
|||
container_expiration_policy service_desk_enabled service_desk_address
|
||||
issue_status_counts terraform_states alert_management_integrations
|
||||
container_repositories container_repositories_count
|
||||
pipeline_analytics squash_read_only
|
||||
pipeline_analytics squash_read_only sast_ci_configuration
|
||||
]
|
||||
|
||||
expect(described_class).to include_graphql_fields(*expected_fields)
|
||||
end
|
||||
|
||||
describe 'sast_ci_configuration' do
|
||||
let_it_be(:project) { create(:project) }
|
||||
let_it_be(:user) { create(:user) }
|
||||
|
||||
before do
|
||||
stub_licensed_features(security_dashboard: true)
|
||||
project.add_developer(user)
|
||||
allow(project.repository).to receive(:blob_data_at).and_return(gitlab_ci_yml_content)
|
||||
end
|
||||
|
||||
include_context 'read ci configuration for sast enabled project'
|
||||
|
||||
let(:query) do
|
||||
%(
|
||||
query {
|
||||
project(fullPath: "#{project.full_path}") {
|
||||
sastCiConfiguration {
|
||||
global {
|
||||
nodes {
|
||||
type
|
||||
options {
|
||||
nodes {
|
||||
label
|
||||
value
|
||||
}
|
||||
}
|
||||
field
|
||||
label
|
||||
defaultValue
|
||||
value
|
||||
size
|
||||
}
|
||||
}
|
||||
pipeline {
|
||||
nodes {
|
||||
type
|
||||
options {
|
||||
nodes {
|
||||
label
|
||||
value
|
||||
}
|
||||
}
|
||||
field
|
||||
label
|
||||
defaultValue
|
||||
value
|
||||
size
|
||||
}
|
||||
}
|
||||
analyzers {
|
||||
nodes {
|
||||
name
|
||||
label
|
||||
enabled
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
subject { GitlabSchema.execute(query, context: { current_user: user }).as_json }
|
||||
|
||||
it "returns the project's sast configuration for global variables" do
|
||||
secure_analyzers_prefix = subject.dig('data', 'project', 'sastCiConfiguration', 'global', 'nodes').first
|
||||
expect(secure_analyzers_prefix['type']).to eq('string')
|
||||
expect(secure_analyzers_prefix['field']).to eq('SECURE_ANALYZERS_PREFIX')
|
||||
expect(secure_analyzers_prefix['label']).to eq('Image prefix')
|
||||
expect(secure_analyzers_prefix['defaultValue']).to eq('registry.gitlab.com/gitlab-org/security-products/analyzers')
|
||||
expect(secure_analyzers_prefix['value']).to eq('registry.gitlab.com/gitlab-org/security-products/analyzers')
|
||||
expect(secure_analyzers_prefix['size']).to eq('LARGE')
|
||||
expect(secure_analyzers_prefix['options']).to be_nil
|
||||
end
|
||||
|
||||
it "returns the project's sast configuration for pipeline variables" do
|
||||
pipeline_stage = subject.dig('data', 'project', 'sastCiConfiguration', 'pipeline', 'nodes').first
|
||||
expect(pipeline_stage['type']).to eq('string')
|
||||
expect(pipeline_stage['field']).to eq('stage')
|
||||
expect(pipeline_stage['label']).to eq('Stage')
|
||||
expect(pipeline_stage['defaultValue']).to eq('test')
|
||||
expect(pipeline_stage['value']).to eq('test')
|
||||
expect(pipeline_stage['size']).to eq('MEDIUM')
|
||||
end
|
||||
|
||||
it "returns the project's sast configuration for analyzer variables" do
|
||||
analyzer = subject.dig('data', 'project', 'sastCiConfiguration', 'analyzers', 'nodes').first
|
||||
expect(analyzer['name']).to eq('brakeman')
|
||||
expect(analyzer['label']).to eq('Brakeman')
|
||||
expect(analyzer['enabled']).to eq(true)
|
||||
end
|
||||
|
||||
context "with guest user" do
|
||||
before do
|
||||
project.add_guest(user)
|
||||
end
|
||||
|
||||
context 'when project is private' do
|
||||
let(:project) { create(:project, :private, :repository) }
|
||||
|
||||
it "returns no configuration" do
|
||||
secure_analyzers_prefix = subject.dig('data', 'project', 'sastCiConfiguration')
|
||||
expect(secure_analyzers_prefix).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context 'when project is public' do
|
||||
let(:project) { create(:project, :public, :repository) }
|
||||
|
||||
context 'when repository is accessible by everyone' do
|
||||
it "returns the project's sast configuration for global variables" do
|
||||
secure_analyzers_prefix = subject.dig('data', 'project', 'sastCiConfiguration', 'global', 'nodes').first
|
||||
|
||||
expect(secure_analyzers_prefix['type']).to eq('string')
|
||||
expect(secure_analyzers_prefix['field']).to eq('SECURE_ANALYZERS_PREFIX')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "with non-member user" do
|
||||
before do
|
||||
project.team.truncate
|
||||
end
|
||||
|
||||
context 'when project is private' do
|
||||
let(:project) { create(:project, :private, :repository) }
|
||||
|
||||
it "returns no configuration" do
|
||||
secure_analyzers_prefix = subject.dig('data', 'project', 'sastCiConfiguration')
|
||||
expect(secure_analyzers_prefix).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context 'when project is public' do
|
||||
let(:project) { create(:project, :public, :repository) }
|
||||
|
||||
context 'when repository is accessible by everyone' do
|
||||
it "returns the project's sast configuration for global variables" do
|
||||
secure_analyzers_prefix = subject.dig('data', 'project', 'sastCiConfiguration', 'global', 'nodes').first
|
||||
expect(secure_analyzers_prefix['type']).to eq('string')
|
||||
expect(secure_analyzers_prefix['field']).to eq('SECURE_ANALYZERS_PREFIX')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when repository is accessible only by team members' do
|
||||
it "returns no configuration" do
|
||||
project.project_feature.update!(merge_requests_access_level: ProjectFeature::DISABLED,
|
||||
builds_access_level: ProjectFeature::DISABLED,
|
||||
repository_access_level: ProjectFeature::PRIVATE)
|
||||
|
||||
secure_analyzers_prefix = subject.dig('data', 'project', 'sastCiConfiguration')
|
||||
expect(secure_analyzers_prefix).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'issue field' do
|
||||
subject { described_class.fields['issue'] }
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,92 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe 'validate puma' do
|
||||
subject do
|
||||
load Rails.root.join('config/initializers/validate_puma.rb')
|
||||
end
|
||||
|
||||
before do
|
||||
stub_env('PUMA_SKIP_CLUSTER_VALIDATION', skip_validation)
|
||||
stub_const('Puma', double)
|
||||
allow(Gitlab::Runtime).to receive(:puma?).and_return(true)
|
||||
allow(Puma).to receive_message_chain(:cli_config, :options).and_return(workers: workers)
|
||||
end
|
||||
|
||||
context 'for .com' do
|
||||
before do
|
||||
allow(Gitlab).to receive(:com?).and_return(true)
|
||||
end
|
||||
|
||||
context 'when worker count is 0' do
|
||||
let(:workers) { 0 }
|
||||
|
||||
context 'and PUMA_SKIP_CLUSTER_VALIDATION is true' do
|
||||
let(:skip_validation) { true }
|
||||
|
||||
specify { expect { subject }.to raise_error(String) }
|
||||
end
|
||||
|
||||
context 'and PUMA_SKIP_CLUSTER_VALIDATION is false' do
|
||||
let(:skip_validation) { false }
|
||||
|
||||
specify { expect { subject }.to raise_error(String) }
|
||||
end
|
||||
end
|
||||
|
||||
context 'when worker count is > 0' do
|
||||
let(:workers) { 2 }
|
||||
|
||||
context 'and PUMA_SKIP_CLUSTER_VALIDATION is true' do
|
||||
let(:skip_validation) { true }
|
||||
|
||||
specify { expect { subject }.not_to raise_error }
|
||||
end
|
||||
|
||||
context 'and PUMA_SKIP_CLUSTER_VALIDATION is false' do
|
||||
let(:skip_validation) { false }
|
||||
|
||||
specify { expect { subject }.not_to raise_error }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'for other environments' do
|
||||
before do
|
||||
allow(Gitlab).to receive(:com?).and_return(false)
|
||||
end
|
||||
|
||||
context 'when worker count is 0' do
|
||||
let(:workers) { 0 }
|
||||
|
||||
context 'and PUMA_SKIP_CLUSTER_VALIDATION is true' do
|
||||
let(:skip_validation) { true }
|
||||
|
||||
specify { expect { subject }.not_to raise_error }
|
||||
end
|
||||
|
||||
context 'and PUMA_SKIP_CLUSTER_VALIDATION is false' do
|
||||
let(:skip_validation) { false }
|
||||
|
||||
specify { expect { subject }.to raise_error(String) }
|
||||
end
|
||||
end
|
||||
|
||||
context 'when worker count is > 0' do
|
||||
let(:workers) { 2 }
|
||||
|
||||
context 'and PUMA_SKIP_CLUSTER_VALIDATION is true' do
|
||||
let(:skip_validation) { true }
|
||||
|
||||
specify { expect { subject }.not_to raise_error }
|
||||
end
|
||||
|
||||
context 'and PUMA_SKIP_CLUSTER_VALIDATION is false' do
|
||||
let(:skip_validation) { false }
|
||||
|
||||
specify { expect { subject }.not_to raise_error }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -15,9 +15,19 @@ RSpec.describe Gitlab::Template::Finders::GlobalTemplateFinder do
|
|||
FileUtils.rm_rf(base_dir)
|
||||
end
|
||||
|
||||
subject(:finder) { described_class.new(base_dir, '', { 'General' => '', 'Bar' => 'Bar' }, excluded_patterns: excluded_patterns) }
|
||||
subject(:finder) do
|
||||
described_class.new(base_dir, '',
|
||||
{ 'General' => '', 'Bar' => 'Bar' },
|
||||
include_categories_for_file,
|
||||
excluded_patterns: excluded_patterns)
|
||||
end
|
||||
|
||||
let(:excluded_patterns) { [] }
|
||||
let(:include_categories_for_file) do
|
||||
{
|
||||
"SAST" => { "Security" => "Security" }
|
||||
}
|
||||
end
|
||||
|
||||
describe '.find' do
|
||||
context 'with a non-prefixed General template' do
|
||||
|
|
@ -60,6 +70,7 @@ RSpec.describe Gitlab::Template::Finders::GlobalTemplateFinder do
|
|||
context 'with a prefixed template' do
|
||||
before do
|
||||
create_template!('Bar/test-template')
|
||||
create_template!('Security/SAST')
|
||||
end
|
||||
|
||||
it 'finds the template with a prefix' do
|
||||
|
|
@ -76,6 +87,16 @@ RSpec.describe Gitlab::Template::Finders::GlobalTemplateFinder do
|
|||
expect { finder.find('../foo') }.to raise_error(/Invalid path/)
|
||||
end
|
||||
|
||||
context 'with include_categories_for_file being present' do
|
||||
it 'finds the template with a prefix' do
|
||||
expect(finder.find('SAST')).to be_present
|
||||
end
|
||||
|
||||
it 'does not find any template which is missing in include_categories_for_file' do
|
||||
expect(finder.find('DAST')).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context 'while listed as an exclusion' do
|
||||
let(:excluded_patterns) { [%r{^Bar/test-template$}] }
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,539 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Security::CiConfiguration::SastBuildActions do
|
||||
let(:default_sast_values) do
|
||||
{ 'global' =>
|
||||
[
|
||||
{ 'field' => 'SECURE_ANALYZERS_PREFIX', 'defaultValue' => 'registry.gitlab.com/gitlab-org/security-products/analyzers', 'value' => 'registry.gitlab.com/gitlab-org/security-products/analyzers' }
|
||||
],
|
||||
'pipeline' =>
|
||||
[
|
||||
{ 'field' => 'stage', 'defaultValue' => 'test', 'value' => 'test' },
|
||||
{ 'field' => 'SEARCH_MAX_DEPTH', 'defaultValue' => 4, 'value' => 4 },
|
||||
{ 'field' => 'SAST_ANALYZER_IMAGE_TAG', 'defaultValue' => 2, 'value' => 2 },
|
||||
{ 'field' => 'SAST_EXCLUDED_PATHS', 'defaultValue' => 'spec, test, tests, tmp', 'value' => 'spec, test, tests, tmp' }
|
||||
] }
|
||||
end
|
||||
|
||||
let(:params) do
|
||||
{ 'global' =>
|
||||
[
|
||||
{ 'field' => 'SECURE_ANALYZERS_PREFIX', 'defaultValue' => 'registry.gitlab.com/gitlab-org/security-products/analyzers', 'value' => 'new_registry' }
|
||||
],
|
||||
'pipeline' =>
|
||||
[
|
||||
{ 'field' => 'stage', 'defaultValue' => 'test', 'value' => 'security' },
|
||||
{ 'field' => 'SEARCH_MAX_DEPTH', 'defaultValue' => 4, 'value' => 1 },
|
||||
{ 'field' => 'SAST_ANALYZER_IMAGE_TAG', 'defaultValue' => 2, 'value' => 2 },
|
||||
{ 'field' => 'SAST_EXCLUDED_PATHS', 'defaultValue' => 'spec, test, tests, tmp', 'value' => 'spec,docs' }
|
||||
] }
|
||||
end
|
||||
|
||||
let(:params_with_analyzer_info) do
|
||||
params.merge( { 'analyzers' =>
|
||||
[
|
||||
{
|
||||
'name' => "bandit",
|
||||
'enabled' => false
|
||||
},
|
||||
{
|
||||
'name' => "brakeman",
|
||||
'enabled' => true,
|
||||
'variables' => [
|
||||
{ 'field' => "SAST_BRAKEMAN_LEVEL",
|
||||
'defaultValue' => "1",
|
||||
'value' => "2" }
|
||||
]
|
||||
},
|
||||
{
|
||||
'name' => "flawfinder",
|
||||
'enabled' => true,
|
||||
'variables' => [
|
||||
{ 'field' => "SAST_FLAWFINDER_LEVEL",
|
||||
'defaultValue' => "1",
|
||||
'value' => "1" }
|
||||
]
|
||||
}
|
||||
] }
|
||||
)
|
||||
end
|
||||
|
||||
let(:params_with_all_analyzers_enabled) do
|
||||
params.merge( { 'analyzers' =>
|
||||
[
|
||||
{
|
||||
'name' => "flawfinder",
|
||||
'enabled' => true
|
||||
},
|
||||
{
|
||||
'name' => "brakeman",
|
||||
'enabled' => true
|
||||
}
|
||||
] }
|
||||
)
|
||||
end
|
||||
|
||||
context 'with existing .gitlab-ci.yml' do
|
||||
let(:auto_devops_enabled) { false }
|
||||
|
||||
context 'sast has not been included' do
|
||||
context 'template includes are array' do
|
||||
let(:gitlab_ci_content) { existing_gitlab_ci_and_template_array_without_sast }
|
||||
|
||||
subject(:result) { described_class.new(auto_devops_enabled, params, gitlab_ci_content).generate }
|
||||
|
||||
it 'generates the correct YML' do
|
||||
expect(result.first[:action]).to eq('update')
|
||||
expect(result.first[:content]).to eq(sast_yaml_two_includes)
|
||||
end
|
||||
end
|
||||
|
||||
context 'template include is not an array' do
|
||||
let(:gitlab_ci_content) { existing_gitlab_ci_and_single_template_without_sast }
|
||||
|
||||
subject(:result) { described_class.new(auto_devops_enabled, params, gitlab_ci_content).generate }
|
||||
|
||||
it 'generates the correct YML' do
|
||||
expect(result.first[:action]).to eq('update')
|
||||
expect(result.first[:content]).to eq(sast_yaml_two_includes)
|
||||
end
|
||||
|
||||
it 'reports defaults have been overwritten' do
|
||||
expect(result.first[:default_values_overwritten]).to eq(true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'sast template include is not an array' do
|
||||
let(:gitlab_ci_content) { existing_gitlab_ci_and_single_template_with_sast_and_default_stage }
|
||||
|
||||
subject(:result) { described_class.new(auto_devops_enabled, params, gitlab_ci_content).generate }
|
||||
|
||||
it 'generates the correct YML' do
|
||||
expect(result.first[:action]).to eq('update')
|
||||
expect(result.first[:content]).to eq(sast_yaml_all_params)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with default values' do
|
||||
let(:params) { default_sast_values }
|
||||
let(:gitlab_ci_content) { existing_gitlab_ci_and_single_template_with_sast_and_default_stage }
|
||||
|
||||
subject(:result) { described_class.new(auto_devops_enabled, params, gitlab_ci_content).generate }
|
||||
|
||||
it 'generates the correct YML' do
|
||||
expect(result.first[:content]).to eq(sast_yaml_with_no_variables_set)
|
||||
end
|
||||
|
||||
it 'reports defaults have not been overwritten' do
|
||||
expect(result.first[:default_values_overwritten]).to eq(false)
|
||||
end
|
||||
|
||||
context 'analyzer section' do
|
||||
let(:gitlab_ci_content) { existing_gitlab_ci_and_single_template_with_sast_and_default_stage }
|
||||
|
||||
subject(:result) { described_class.new(auto_devops_enabled, params_with_analyzer_info, gitlab_ci_content).generate }
|
||||
|
||||
it 'generates the correct YML' do
|
||||
expect(result.first[:content]).to eq(sast_yaml_with_no_variables_set_but_analyzers)
|
||||
end
|
||||
|
||||
context 'analyzers are disabled' do
|
||||
let(:gitlab_ci_content) { existing_gitlab_ci_and_single_template_with_sast_and_default_stage }
|
||||
|
||||
subject(:result) { described_class.new(auto_devops_enabled, params_with_analyzer_info, gitlab_ci_content).generate }
|
||||
|
||||
it 'writes SAST_EXCLUDED_ANALYZERS' do
|
||||
stub_const('Security::CiConfiguration::SastBuildActions::SAST_DEFAULT_ANALYZERS', 'bandit, brakeman, flawfinder')
|
||||
|
||||
expect(result.first[:content]).to eq(sast_yaml_with_no_variables_set_but_analyzers)
|
||||
end
|
||||
end
|
||||
|
||||
context 'all analyzers are enabled' do
|
||||
let(:gitlab_ci_content) { existing_gitlab_ci_and_single_template_with_sast_and_default_stage }
|
||||
|
||||
subject(:result) { described_class.new(auto_devops_enabled, params_with_all_analyzers_enabled, gitlab_ci_content).generate }
|
||||
|
||||
it 'does not write SAST_DEFAULT_ANALYZERS or SAST_EXCLUDED_ANALYZERS' do
|
||||
stub_const('Security::CiConfiguration::SastBuildActions::SAST_DEFAULT_ANALYZERS', 'brakeman, flawfinder')
|
||||
|
||||
expect(result.first[:content]).to eq(sast_yaml_with_no_variables_set)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with update stage and SEARCH_MAX_DEPTH and set SECURE_ANALYZERS_PREFIX to default' do
|
||||
let(:params) do
|
||||
{ 'global' =>
|
||||
[
|
||||
{ 'field' => 'SECURE_ANALYZERS_PREFIX', 'defaultValue' => 'registry.gitlab.com/gitlab-org/security-products/analyzers', 'value' => 'registry.gitlab.com/gitlab-org/security-products/analyzers' }
|
||||
],
|
||||
'pipeline' =>
|
||||
[
|
||||
{ 'field' => 'stage', 'defaultValue' => 'test', 'value' => 'brand_new_stage' },
|
||||
{ 'field' => 'SEARCH_MAX_DEPTH', 'defaultValue' => 4, 'value' => 5 },
|
||||
{ 'field' => 'SAST_ANALYZER_IMAGE_TAG', 'defaultValue' => 2, 'value' => 2 },
|
||||
{ 'field' => 'SAST_EXCLUDED_PATHS', 'defaultValue' => 'spec, test, tests, tmp', 'value' => 'spec,docs' }
|
||||
] }
|
||||
end
|
||||
|
||||
let(:gitlab_ci_content) { existing_gitlab_ci }
|
||||
|
||||
subject(:result) { described_class.new(auto_devops_enabled, params, gitlab_ci_content).generate }
|
||||
|
||||
it 'generates the correct YML' do
|
||||
expect(result.first[:action]).to eq('update')
|
||||
expect(result.first[:content]).to eq(sast_yaml_updated_stage)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with no existing variables' do
|
||||
let(:gitlab_ci_content) { existing_gitlab_ci_with_no_variables }
|
||||
|
||||
subject(:result) { described_class.new(auto_devops_enabled, params, gitlab_ci_content).generate }
|
||||
|
||||
it 'generates the correct YML' do
|
||||
expect(result.first[:action]).to eq('update')
|
||||
expect(result.first[:content]).to eq(sast_yaml_variable_section_added)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with no existing sast config' do
|
||||
let(:gitlab_ci_content) { existing_gitlab_ci_with_no_sast_section }
|
||||
|
||||
subject(:result) { described_class.new(auto_devops_enabled, params, gitlab_ci_content).generate }
|
||||
|
||||
it 'generates the correct YML' do
|
||||
expect(result.first[:action]).to eq('update')
|
||||
expect(result.first[:content]).to eq(sast_yaml_sast_section_added)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with no existing sast variables' do
|
||||
let(:gitlab_ci_content) { existing_gitlab_ci_with_no_sast_variables }
|
||||
|
||||
subject(:result) { described_class.new(auto_devops_enabled, params, gitlab_ci_content).generate }
|
||||
|
||||
it 'generates the correct YML' do
|
||||
expect(result.first[:action]).to eq('update')
|
||||
expect(result.first[:content]).to eq(sast_yaml_sast_variables_section_added)
|
||||
end
|
||||
end
|
||||
|
||||
def existing_gitlab_ci_and_template_array_without_sast
|
||||
{ "stages" => %w(test security),
|
||||
"variables" => { "RANDOM" => "make sure this persists", "SECURE_ANALYZERS_PREFIX" => "localhost:5000/analyzers" },
|
||||
"sast" => { "variables" => { "SAST_ANALYZER_IMAGE_TAG" => 2, "SEARCH_MAX_DEPTH" => 1 }, "stage" => "security" },
|
||||
"include" => [{ "template" => "existing.yml" }] }
|
||||
end
|
||||
|
||||
def existing_gitlab_ci_and_single_template_with_sast_and_default_stage
|
||||
{ "stages" => %w(test),
|
||||
"variables" => { "SECURE_ANALYZERS_PREFIX" => "localhost:5000/analyzers" },
|
||||
"sast" => { "variables" => { "SAST_ANALYZER_IMAGE_TAG" => 2, "SEARCH_MAX_DEPTH" => 1 }, "stage" => "test" },
|
||||
"include" => { "template" => "Security/SAST.gitlab-ci.yml" } }
|
||||
end
|
||||
|
||||
def existing_gitlab_ci_and_single_template_without_sast
|
||||
{ "stages" => %w(test security),
|
||||
"variables" => { "RANDOM" => "make sure this persists", "SECURE_ANALYZERS_PREFIX" => "localhost:5000/analyzers" },
|
||||
"sast" => { "variables" => { "SAST_ANALYZER_IMAGE_TAG" => 2, "SEARCH_MAX_DEPTH" => 1 }, "stage" => "security" },
|
||||
"include" => { "template" => "existing.yml" } }
|
||||
end
|
||||
|
||||
def existing_gitlab_ci_with_no_variables
|
||||
{ "stages" => %w(test security),
|
||||
"sast" => { "variables" => { "SAST_ANALYZER_IMAGE_TAG" => 2, "SEARCH_MAX_DEPTH" => 1 }, "stage" => "security" },
|
||||
"include" => [{ "template" => "Security/SAST.gitlab-ci.yml" }] }
|
||||
end
|
||||
|
||||
def existing_gitlab_ci_with_no_sast_section
|
||||
{ "stages" => %w(test security),
|
||||
"variables" => { "RANDOM" => "make sure this persists", "SECURE_ANALYZERS_PREFIX" => "localhost:5000/analyzers" },
|
||||
"include" => [{ "template" => "Security/SAST.gitlab-ci.yml" }] }
|
||||
end
|
||||
|
||||
def existing_gitlab_ci_with_no_sast_variables
|
||||
{ "stages" => %w(test security),
|
||||
"variables" => { "RANDOM" => "make sure this persists", "SECURE_ANALYZERS_PREFIX" => "localhost:5000/analyzers" },
|
||||
"sast" => { "stage" => "security" },
|
||||
"include" => [{ "template" => "Security/SAST.gitlab-ci.yml" }] }
|
||||
end
|
||||
|
||||
def existing_gitlab_ci
|
||||
{ "stages" => %w(test security),
|
||||
"variables" => { "RANDOM" => "make sure this persists", "SECURE_ANALYZERS_PREFIX" => "bad_prefix" },
|
||||
"sast" => { "variables" => { "SAST_ANALYZER_IMAGE_TAG" => 2, "SEARCH_MAX_DEPTH" => 1 }, "stage" => "security" },
|
||||
"include" => [{ "template" => "Security/SAST.gitlab-ci.yml" }] }
|
||||
end
|
||||
end
|
||||
|
||||
context 'with no .gitlab-ci.yml' do
|
||||
let(:gitlab_ci_content) { nil }
|
||||
|
||||
context 'autodevops disabled' do
|
||||
let(:auto_devops_enabled) { false }
|
||||
|
||||
context 'with one empty parameter' do
|
||||
let(:params) do
|
||||
{ 'global' =>
|
||||
[
|
||||
{ 'field' => 'SECURE_ANALYZERS_PREFIX', 'defaultValue' => 'registry.gitlab.com/gitlab-org/security-products/analyzers', 'value' => '' }
|
||||
] }
|
||||
end
|
||||
|
||||
subject(:result) { described_class.new(auto_devops_enabled, params, gitlab_ci_content).generate }
|
||||
|
||||
it 'generates the correct YML' do
|
||||
expect(result.first[:content]).to eq(sast_yaml_with_no_variables_set)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with all parameters' do
|
||||
subject(:result) { described_class.new(auto_devops_enabled, params, gitlab_ci_content).generate }
|
||||
|
||||
it 'generates the correct YML' do
|
||||
expect(result.first[:content]).to eq(sast_yaml_all_params)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with autodevops enabled' do
|
||||
let(:auto_devops_enabled) { true }
|
||||
|
||||
subject(:result) { described_class.new(auto_devops_enabled, params, gitlab_ci_content).generate }
|
||||
|
||||
before do
|
||||
allow_next_instance_of(described_class) do |sast_build_actions|
|
||||
allow(sast_build_actions).to receive(:auto_devops_stages).and_return(fast_auto_devops_stages)
|
||||
end
|
||||
end
|
||||
|
||||
it 'generates the correct YML' do
|
||||
expect(result.first[:content]).to eq(auto_devops_with_custom_stage)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'Security::CiConfiguration::SastBuildActions::SAST_DEFAULT_ANALYZERS' do
|
||||
subject(:variable) {Security::CiConfiguration::SastBuildActions::SAST_DEFAULT_ANALYZERS}
|
||||
|
||||
it 'is sorted alphabetically' do
|
||||
sorted_variable = Security::CiConfiguration::SastBuildActions::SAST_DEFAULT_ANALYZERS
|
||||
.split(',')
|
||||
.map(&:strip)
|
||||
.sort
|
||||
.join(', ')
|
||||
|
||||
expect(variable).to eq(sorted_variable)
|
||||
end
|
||||
end
|
||||
|
||||
# stubbing this method allows this spec file to use fast_spec_helper
|
||||
def fast_auto_devops_stages
|
||||
auto_devops_template = YAML.safe_load( File.read('lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml') )
|
||||
auto_devops_template['stages']
|
||||
end
|
||||
|
||||
def sast_yaml_with_no_variables_set_but_analyzers
|
||||
<<-CI_YML.strip_heredoc
|
||||
# You can override the included template(s) by including variable overrides
|
||||
# See https://docs.gitlab.com/ee/user/application_security/sast/#customizing-the-sast-settings
|
||||
# Note that environment variables can be set in several places
|
||||
# See https://docs.gitlab.com/ee/ci/variables/#priority-of-environment-variables
|
||||
stages:
|
||||
- test
|
||||
sast:
|
||||
variables:
|
||||
SAST_EXCLUDED_ANALYZERS: bandit
|
||||
SAST_BRAKEMAN_LEVEL: '2'
|
||||
stage: test
|
||||
include:
|
||||
- template: Security/SAST.gitlab-ci.yml
|
||||
CI_YML
|
||||
end
|
||||
|
||||
def sast_yaml_with_no_variables_set
|
||||
<<-CI_YML.strip_heredoc
|
||||
# You can override the included template(s) by including variable overrides
|
||||
# See https://docs.gitlab.com/ee/user/application_security/sast/#customizing-the-sast-settings
|
||||
# Note that environment variables can be set in several places
|
||||
# See https://docs.gitlab.com/ee/ci/variables/#priority-of-environment-variables
|
||||
stages:
|
||||
- test
|
||||
sast:
|
||||
stage: test
|
||||
include:
|
||||
- template: Security/SAST.gitlab-ci.yml
|
||||
CI_YML
|
||||
end
|
||||
|
||||
def sast_yaml_all_params
|
||||
<<-CI_YML.strip_heredoc
|
||||
# You can override the included template(s) by including variable overrides
|
||||
# See https://docs.gitlab.com/ee/user/application_security/sast/#customizing-the-sast-settings
|
||||
# Note that environment variables can be set in several places
|
||||
# See https://docs.gitlab.com/ee/ci/variables/#priority-of-environment-variables
|
||||
stages:
|
||||
- test
|
||||
- security
|
||||
variables:
|
||||
SECURE_ANALYZERS_PREFIX: new_registry
|
||||
sast:
|
||||
variables:
|
||||
SAST_EXCLUDED_PATHS: spec,docs
|
||||
SEARCH_MAX_DEPTH: 1
|
||||
stage: security
|
||||
include:
|
||||
- template: Security/SAST.gitlab-ci.yml
|
||||
CI_YML
|
||||
end
|
||||
|
||||
def auto_devops_with_custom_stage
|
||||
<<-CI_YML.strip_heredoc
|
||||
# You can override the included template(s) by including variable overrides
|
||||
# See https://docs.gitlab.com/ee/user/application_security/sast/#customizing-the-sast-settings
|
||||
# Note that environment variables can be set in several places
|
||||
# See https://docs.gitlab.com/ee/ci/variables/#priority-of-environment-variables
|
||||
stages:
|
||||
- build
|
||||
- test
|
||||
- deploy
|
||||
- review
|
||||
- dast
|
||||
- staging
|
||||
- canary
|
||||
- production
|
||||
- incremental rollout 10%
|
||||
- incremental rollout 25%
|
||||
- incremental rollout 50%
|
||||
- incremental rollout 100%
|
||||
- performance
|
||||
- cleanup
|
||||
- security
|
||||
variables:
|
||||
SECURE_ANALYZERS_PREFIX: new_registry
|
||||
sast:
|
||||
variables:
|
||||
SAST_EXCLUDED_PATHS: spec,docs
|
||||
SEARCH_MAX_DEPTH: 1
|
||||
stage: security
|
||||
include:
|
||||
- template: Auto-DevOps.gitlab-ci.yml
|
||||
CI_YML
|
||||
end
|
||||
|
||||
def sast_yaml_two_includes
|
||||
<<-CI_YML.strip_heredoc
|
||||
# You can override the included template(s) by including variable overrides
|
||||
# See https://docs.gitlab.com/ee/user/application_security/sast/#customizing-the-sast-settings
|
||||
# Note that environment variables can be set in several places
|
||||
# See https://docs.gitlab.com/ee/ci/variables/#priority-of-environment-variables
|
||||
stages:
|
||||
- test
|
||||
- security
|
||||
variables:
|
||||
RANDOM: make sure this persists
|
||||
SECURE_ANALYZERS_PREFIX: new_registry
|
||||
sast:
|
||||
variables:
|
||||
SAST_EXCLUDED_PATHS: spec,docs
|
||||
SEARCH_MAX_DEPTH: 1
|
||||
stage: security
|
||||
include:
|
||||
- template: existing.yml
|
||||
- template: Security/SAST.gitlab-ci.yml
|
||||
CI_YML
|
||||
end
|
||||
|
||||
def sast_yaml_variable_section_added
|
||||
<<-CI_YML.strip_heredoc
|
||||
# You can override the included template(s) by including variable overrides
|
||||
# See https://docs.gitlab.com/ee/user/application_security/sast/#customizing-the-sast-settings
|
||||
# Note that environment variables can be set in several places
|
||||
# See https://docs.gitlab.com/ee/ci/variables/#priority-of-environment-variables
|
||||
stages:
|
||||
- test
|
||||
- security
|
||||
sast:
|
||||
variables:
|
||||
SAST_EXCLUDED_PATHS: spec,docs
|
||||
SEARCH_MAX_DEPTH: 1
|
||||
stage: security
|
||||
include:
|
||||
- template: Security/SAST.gitlab-ci.yml
|
||||
variables:
|
||||
SECURE_ANALYZERS_PREFIX: new_registry
|
||||
CI_YML
|
||||
end
|
||||
|
||||
def sast_yaml_sast_section_added
|
||||
<<-CI_YML.strip_heredoc
|
||||
# You can override the included template(s) by including variable overrides
|
||||
# See https://docs.gitlab.com/ee/user/application_security/sast/#customizing-the-sast-settings
|
||||
# Note that environment variables can be set in several places
|
||||
# See https://docs.gitlab.com/ee/ci/variables/#priority-of-environment-variables
|
||||
stages:
|
||||
- test
|
||||
- security
|
||||
variables:
|
||||
RANDOM: make sure this persists
|
||||
SECURE_ANALYZERS_PREFIX: new_registry
|
||||
include:
|
||||
- template: Security/SAST.gitlab-ci.yml
|
||||
sast:
|
||||
variables:
|
||||
SAST_EXCLUDED_PATHS: spec,docs
|
||||
SEARCH_MAX_DEPTH: 1
|
||||
stage: security
|
||||
CI_YML
|
||||
end
|
||||
|
||||
def sast_yaml_sast_variables_section_added
|
||||
<<-CI_YML.strip_heredoc
|
||||
# You can override the included template(s) by including variable overrides
|
||||
# See https://docs.gitlab.com/ee/user/application_security/sast/#customizing-the-sast-settings
|
||||
# Note that environment variables can be set in several places
|
||||
# See https://docs.gitlab.com/ee/ci/variables/#priority-of-environment-variables
|
||||
stages:
|
||||
- test
|
||||
- security
|
||||
variables:
|
||||
RANDOM: make sure this persists
|
||||
SECURE_ANALYZERS_PREFIX: new_registry
|
||||
sast:
|
||||
stage: security
|
||||
variables:
|
||||
SAST_EXCLUDED_PATHS: spec,docs
|
||||
SEARCH_MAX_DEPTH: 1
|
||||
include:
|
||||
- template: Security/SAST.gitlab-ci.yml
|
||||
CI_YML
|
||||
end
|
||||
|
||||
def sast_yaml_updated_stage
|
||||
<<-CI_YML.strip_heredoc
|
||||
# You can override the included template(s) by including variable overrides
|
||||
# See https://docs.gitlab.com/ee/user/application_security/sast/#customizing-the-sast-settings
|
||||
# Note that environment variables can be set in several places
|
||||
# See https://docs.gitlab.com/ee/ci/variables/#priority-of-environment-variables
|
||||
stages:
|
||||
- test
|
||||
- security
|
||||
- brand_new_stage
|
||||
variables:
|
||||
RANDOM: make sure this persists
|
||||
sast:
|
||||
variables:
|
||||
SAST_EXCLUDED_PATHS: spec,docs
|
||||
SEARCH_MAX_DEPTH: 5
|
||||
stage: brand_new_stage
|
||||
include:
|
||||
- template: Security/SAST.gitlab-ci.yml
|
||||
CI_YML
|
||||
end
|
||||
end
|
||||
|
|
@ -76,38 +76,98 @@ RSpec.describe Ci::PipelineArtifact, type: :model do
|
|||
end
|
||||
end
|
||||
|
||||
describe '.has_code_coverage?' do
|
||||
subject { Ci::PipelineArtifact.has_code_coverage? }
|
||||
describe '.has_report?' do
|
||||
subject(:pipeline_artifact) { Ci::PipelineArtifact.has_report?(file_type) }
|
||||
|
||||
context 'when pipeline artifact has a code coverage' do
|
||||
let!(:pipeline_artifact) { create(:ci_pipeline_artifact) }
|
||||
context 'when file_type is code_coverage' do
|
||||
let(:file_type) { :code_coverage }
|
||||
|
||||
it 'returns true' do
|
||||
expect(subject).to be_truthy
|
||||
context 'when pipeline artifact has a coverage report' do
|
||||
let!(:pipeline_artifact) { create(:ci_pipeline_artifact) }
|
||||
|
||||
it 'returns true' do
|
||||
expect(pipeline_artifact).to be_truthy
|
||||
end
|
||||
end
|
||||
|
||||
context 'when pipeline artifact does not have a coverage report' do
|
||||
it 'returns false' do
|
||||
expect(pipeline_artifact).to be_falsey
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when pipeline artifact does not have a code coverage' do
|
||||
context 'when file_type is code_quality' do
|
||||
let(:file_type) { :code_quality }
|
||||
|
||||
context 'when pipeline artifact has a quality report' do
|
||||
let!(:pipeline_artifact) { create(:ci_pipeline_artifact, :codequality_report) }
|
||||
|
||||
it 'returns true' do
|
||||
expect(pipeline_artifact).to be_truthy
|
||||
end
|
||||
end
|
||||
|
||||
context 'when pipeline artifact does not have a quality report' do
|
||||
it 'returns false' do
|
||||
expect(pipeline_artifact).to be_falsey
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when file_type is nil' do
|
||||
let(:file_type) { nil }
|
||||
|
||||
it 'returns false' do
|
||||
expect(subject).to be_falsey
|
||||
expect(pipeline_artifact).to be_falsey
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '.find_with_code_coverage' do
|
||||
subject { Ci::PipelineArtifact.find_with_code_coverage }
|
||||
describe '.find_by_file_type' do
|
||||
subject(:pipeline_artifact) { Ci::PipelineArtifact.find_by_file_type(file_type) }
|
||||
|
||||
context 'when pipeline artifact has a coverage report' do
|
||||
let!(:coverage_report) { create(:ci_pipeline_artifact) }
|
||||
context 'when file_type is code_coverage' do
|
||||
let(:file_type) { :code_coverage }
|
||||
|
||||
it 'returns a pipeline artifact with a code coverage' do
|
||||
expect(subject.file_type).to eq('code_coverage')
|
||||
context 'when pipeline artifact has a coverage report' do
|
||||
let!(:coverage_report) { create(:ci_pipeline_artifact) }
|
||||
|
||||
it 'returns a pipeline artifact with a coverage report' do
|
||||
expect(pipeline_artifact.file_type).to eq('code_coverage')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when pipeline artifact does not have a coverage report' do
|
||||
it 'returns nil' do
|
||||
expect(pipeline_artifact).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when pipeline artifact does not have a coverage report' do
|
||||
context 'when file_type is code_quality' do
|
||||
let(:file_type) { :code_quality }
|
||||
|
||||
context 'when pipeline artifact has a quality report' do
|
||||
let!(:coverage_report) { create(:ci_pipeline_artifact, :codequality_report) }
|
||||
|
||||
it 'returns a pipeline artifact with a quality report' do
|
||||
expect(pipeline_artifact.file_type).to eq('code_quality')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when pipeline artifact does not have a quality report' do
|
||||
it 'returns nil' do
|
||||
expect(pipeline_artifact).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when file_type is nil' do
|
||||
let(:file_type) { nil }
|
||||
|
||||
it 'returns nil' do
|
||||
expect(subject).to be_nil
|
||||
expect(pipeline_artifact).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -3489,6 +3489,54 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
|
|||
end
|
||||
end
|
||||
|
||||
describe '#has_codequality_reports?' do
|
||||
subject { pipeline.has_codequality_reports? }
|
||||
|
||||
context 'when pipeline has a codequality artifact' do
|
||||
let(:pipeline) { create(:ci_pipeline, :with_codequality_report_artifact, :running, project: project) }
|
||||
|
||||
it { expect(subject).to be_truthy }
|
||||
end
|
||||
|
||||
context 'when pipeline does not have a codequality artifact' do
|
||||
let(:pipeline) { create(:ci_pipeline, :success, project: project) }
|
||||
|
||||
it { expect(subject).to be_falsey }
|
||||
end
|
||||
end
|
||||
|
||||
describe '#can_generate_codequality_reports?' do
|
||||
subject { pipeline.can_generate_codequality_reports? }
|
||||
|
||||
context 'when pipeline has builds with codequality reports' do
|
||||
before do
|
||||
create(:ci_build, :codequality_reports, pipeline: pipeline, project: project)
|
||||
end
|
||||
|
||||
context 'when pipeline status is running' do
|
||||
let(:pipeline) { create(:ci_pipeline, :running, project: project) }
|
||||
|
||||
it { expect(subject).to be_falsey }
|
||||
end
|
||||
|
||||
context 'when pipeline status is success' do
|
||||
let(:pipeline) { create(:ci_pipeline, :success, project: project) }
|
||||
|
||||
it { expect(subject).to be_truthy }
|
||||
end
|
||||
end
|
||||
|
||||
context 'when pipeline does not have builds with codequality reports' do
|
||||
before do
|
||||
create(:ci_build, :artifacts, pipeline: pipeline, project: project)
|
||||
end
|
||||
|
||||
let(:pipeline) { create(:ci_pipeline, :success, project: project) }
|
||||
|
||||
it { expect(subject).to be_falsey }
|
||||
end
|
||||
end
|
||||
|
||||
describe '#test_report_summary' do
|
||||
subject { pipeline.test_report_summary }
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,76 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Security::CiConfiguration::SastParserService do
|
||||
describe '#configuration' do
|
||||
include_context 'read ci configuration for sast enabled project'
|
||||
|
||||
let(:configuration) { described_class.new(project).configuration }
|
||||
let(:secure_analyzers_prefix) { configuration['global'][0] }
|
||||
let(:sast_excluded_paths) { configuration['global'][1] }
|
||||
let(:sast_analyzer_image_tag) { configuration['global'][2] }
|
||||
let(:sast_pipeline_stage) { configuration['pipeline'][0] }
|
||||
let(:sast_search_max_depth) { configuration['pipeline'][1] }
|
||||
let(:brakeman) { configuration['analyzers'][0] }
|
||||
let(:bandit) { configuration['analyzers'][1] }
|
||||
let(:sast_brakeman_level) { brakeman['variables'][0] }
|
||||
|
||||
it 'parses the configuration for SAST' do
|
||||
expect(secure_analyzers_prefix['default_value']).to eql('registry.gitlab.com/gitlab-org/security-products/analyzers')
|
||||
expect(sast_excluded_paths['default_value']).to eql('spec, test, tests, tmp')
|
||||
expect(sast_analyzer_image_tag['default_value']).to eql('2')
|
||||
expect(sast_pipeline_stage['default_value']).to eql('test')
|
||||
expect(sast_search_max_depth['default_value']).to eql('4')
|
||||
expect(brakeman['enabled']).to be(true)
|
||||
expect(sast_brakeman_level['default_value']).to eql('1')
|
||||
end
|
||||
|
||||
context 'while populating current values of the entities' do
|
||||
context 'when .gitlab-ci.yml is present' do
|
||||
it 'populates the current values from the file' do
|
||||
allow(project.repository).to receive(:blob_data_at).and_return(gitlab_ci_yml_content)
|
||||
expect(secure_analyzers_prefix['value']).to eql('registry.gitlab.com/gitlab-org/security-products/analyzers2')
|
||||
expect(sast_excluded_paths['value']).to eql('spec, executables')
|
||||
expect(sast_analyzer_image_tag['value']).to eql('2')
|
||||
expect(sast_pipeline_stage['value']).to eql('our_custom_security_stage')
|
||||
expect(sast_search_max_depth['value']).to eql('8')
|
||||
expect(brakeman['enabled']).to be(false)
|
||||
expect(bandit['enabled']).to be(true)
|
||||
expect(sast_brakeman_level['value']).to eql('2')
|
||||
end
|
||||
|
||||
context 'SAST_DEFAULT_ANALYZERS is set' do
|
||||
it 'enables analyzers correctly' do
|
||||
allow(project.repository).to receive(:blob_data_at).and_return(gitlab_ci_yml_default_analyzers_content)
|
||||
|
||||
expect(brakeman['enabled']).to be(false)
|
||||
expect(bandit['enabled']).to be(true)
|
||||
end
|
||||
end
|
||||
|
||||
context 'SAST_EXCLUDED_ANALYZERS is set' do
|
||||
it 'enables analyzers correctly' do
|
||||
allow(project.repository).to receive(:blob_data_at).and_return(gitlab_ci_yml_excluded_analyzers_content)
|
||||
|
||||
expect(brakeman['enabled']).to be(false)
|
||||
expect(bandit['enabled']).to be(true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when .gitlab-ci.yml is absent' do
|
||||
it 'populates the current values with the default values' do
|
||||
allow(project.repository).to receive(:blob_data_at).and_return(nil)
|
||||
expect(secure_analyzers_prefix['value']).to eql('registry.gitlab.com/gitlab-org/security-products/analyzers')
|
||||
expect(sast_excluded_paths['value']).to eql('spec, test, tests, tmp')
|
||||
expect(sast_analyzer_image_tag['value']).to eql('2')
|
||||
expect(sast_pipeline_stage['value']).to eql('test')
|
||||
expect(sast_search_max_depth['value']).to eql('4')
|
||||
expect(brakeman['enabled']).to be(true)
|
||||
expect(sast_brakeman_level['value']).to eql('1')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
Loading…
Reference in New Issue