Commit 74ab5b3c authored by Sashi's avatar Sashi

Add default scan execution policies stage to pipeline

This change adds 'scan-policies' stage to pipeline if
'test' stage is not present. The new stage will be added
after the 'build' stage if it exists. This change also adds
'dast' stage for policy with dast scan type.

EE: true
Changelog: fixed
parent 0433e99a
...@@ -5,6 +5,13 @@ module Gitlab ...@@ -5,6 +5,13 @@ module Gitlab
class Config class Config
module SecurityOrchestrationPolicies module SecurityOrchestrationPolicies
class Processor class Processor
DEFAULT_ON_DEMAND_STAGE = 'dast'
DEFAULT_SECURITY_JOB_STAGE = 'test'
DEFAULT_BUILD_STAGE = 'build'
DEFAULT_SCAN_POLICY_STAGE = 'scan-policies'
DEFAULT_STAGES = Gitlab::Ci::Config::Entry::Stages.default
def initialize(config, project, ref, source) def initialize(config, project, ref, source)
@config = config @config = config
@project = project @project = project
...@@ -18,9 +25,7 @@ module Gitlab ...@@ -18,9 +25,7 @@ module Gitlab
return @config if valid_security_orchestration_policy_configurations.blank? return @config if valid_security_orchestration_policy_configurations.blank?
return @config unless extend_configuration? return @config unless extend_configuration?
merged_config = @config merged_config = @config.deep_merge(merge_policies_with_stages(@config))
.deep_merge(on_demand_scans_template)
.deep_merge(pipeline_scan_template)
observe_processing_duration(Time.current - @start) observe_processing_duration(Time.current - @start)
...@@ -38,31 +43,79 @@ module Gitlab ...@@ -38,31 +43,79 @@ module Gitlab
all_security_orchestration_policy_configurations&.select(&:policy_configuration_valid?) all_security_orchestration_policy_configurations&.select(&:policy_configuration_valid?)
end end
def on_demand_scans_template def prepare_on_demand_scans_template
::Security::SecurityOrchestrationPolicies::OnDemandScanPipelineConfigurationService ::Security::SecurityOrchestrationPolicies::OnDemandScanPipelineConfigurationService
.new(project) .new(project)
.execute(on_demand_scan_actions) .execute(on_demand_scan_actions)
end end
def pipeline_scan_template def prepare_pipeline_scans_template
::Security::SecurityOrchestrationPolicies::ScanPipelineService ::Security::SecurityOrchestrationPolicies::ScanPipelineService
.new.execute(pipeline_scan_actions) .new.execute(pipeline_scan_actions)
end end
def on_demand_scan_actions ## Add `dast` to the end of stages if `dast` is not in stages already
return [] if valid_security_orchestration_policy_configurations.blank? ## For other scan types, add `scan-policies` stage after `build` stage if `test` stage is not defined
def merge_policies_with_stages(config)
merged_config = config
defined_stages = config[:stages].presence || DEFAULT_STAGES
valid_security_orchestration_policy_configurations merge_on_demand_scan_template(merged_config, defined_stages)
.flat_map { |security_orchestration_policy_configuration| security_orchestration_policy_configuration.on_demand_scan_actions(@ref) } merge_pipeline_scan_template(merged_config, defined_stages)
.compact
.uniq merged_config[:stages] = defined_stages.uniq if (defined_stages - DEFAULT_STAGES).present?
merged_config
end
def merge_on_demand_scan_template(merged_config, defined_stages)
on_demand_scan_template = prepare_on_demand_scans_template
if on_demand_scan_template.present?
defined_stages << DEFAULT_ON_DEMAND_STAGE
merged_config.deep_merge!(on_demand_scan_template)
end
end
def merge_pipeline_scan_template(merged_config, defined_stages)
pipeline_scan_template = prepare_pipeline_scans_template
if pipeline_scan_template.present?
unless defined_stages.include?(DEFAULT_SECURITY_JOB_STAGE)
insert_scan_policy_stage_after_build_stage_or_first(defined_stages)
pipeline_scan_template = pipeline_scan_template.transform_values { |job_config| job_config.merge(stage: DEFAULT_SCAN_POLICY_STAGE) }
end
merged_config.deep_merge!(pipeline_scan_template)
end
end
def insert_scan_policy_stage_after_build_stage_or_first(defined_stages)
build_stage_index = defined_stages.index(DEFAULT_BUILD_STAGE)
if build_stage_index.nil?
defined_stages.unshift(DEFAULT_SCAN_POLICY_STAGE)
else
defined_stages.insert(build_stage_index + 1, DEFAULT_SCAN_POLICY_STAGE)
end
defined_stages
end
def on_demand_scan_actions
scan_actions do |security_orchestration_policy_configuration|
security_orchestration_policy_configuration.on_demand_scan_actions(@ref)
end
end end
def pipeline_scan_actions def pipeline_scan_actions
scan_actions do |security_orchestration_policy_configuration|
security_orchestration_policy_configuration.pipeline_scan_actions(@ref)
end
end
def scan_actions
return [] if valid_security_orchestration_policy_configurations.blank? return [] if valid_security_orchestration_policy_configurations.blank?
valid_security_orchestration_policy_configurations valid_security_orchestration_policy_configurations
.flat_map { |security_orchestration_policy_configuration| security_orchestration_policy_configuration.pipeline_scan_actions(@ref) } .flat_map { |security_orchestration_policy_configuration| yield(security_orchestration_policy_configuration) }
.compact .compact
.uniq .uniq
end end
......
...@@ -11,6 +11,7 @@ RSpec.describe Gitlab::Ci::Config::SecurityOrchestrationPolicies::Processor do ...@@ -11,6 +11,7 @@ RSpec.describe Gitlab::Ci::Config::SecurityOrchestrationPolicies::Processor do
let(:ref) { 'refs/heads/master' } let(:ref) { 'refs/heads/master' }
let(:source) { 'pipeline' } let(:source) { 'pipeline' }
let(:scan_policy_stage) { 'test' }
let_it_be(:namespace) { create(:group) } let_it_be(:namespace) { create(:group) }
let_it_be(:namespace_policies_repository) { create(:project, :repository) } let_it_be(:namespace_policies_repository) { create(:project, :repository) }
...@@ -55,8 +56,44 @@ RSpec.describe Gitlab::Ci::Config::SecurityOrchestrationPolicies::Processor do ...@@ -55,8 +56,44 @@ RSpec.describe Gitlab::Ci::Config::SecurityOrchestrationPolicies::Processor do
end end
shared_examples 'with different scan type' do shared_examples 'with different scan type' do
it 'extends config with additional jobs' do context 'when test stage is available' do
expect(subject).to include(expected_configuration) let(:config) { { stages: %w[build test release], image: 'image:1.0.0' } }
it 'does not include scan-policies stage' do
expect(subject[:stages]).to eq(%w[build test release dast])
end
it 'extends config with additional jobs' do
expect(subject).to include(expected_configuration)
end
end
context 'when test stage is not available' do
let(:scan_policy_stage) { 'scan-policies' }
context 'when build stage is available' do
let(:config) { { stages: %w[build not-test release], image: 'image:1.0.0' } }
it 'includes scan-policies stage after build stage' do
expect(subject[:stages]).to eq(%w[build scan-policies not-test release dast])
end
it 'extends config with additional jobs' do
expect(subject).to include(expected_configuration)
end
end
context 'when build stage is not available' do
let(:config) { { stages: %w[not-test release], image: 'image:1.0.0' } }
it 'includes scan-policies stage as a first stage' do
expect(subject[:stages]).to eq(%w[scan-policies not-test release dast])
end
it 'extends config with additional jobs' do
expect(subject).to include(expected_configuration)
end
end
end end
end end
...@@ -160,7 +197,7 @@ RSpec.describe Gitlab::Ci::Config::SecurityOrchestrationPolicies::Processor do ...@@ -160,7 +197,7 @@ RSpec.describe Gitlab::Ci::Config::SecurityOrchestrationPolicies::Processor do
{ {
'secret-detection-0': hash_including( 'secret-detection-0': hash_including(
rules: [{ if: '$SECRET_DETECTION_DISABLED', when: 'never' }, { if: '$CI_COMMIT_BRANCH' }], rules: [{ if: '$SECRET_DETECTION_DISABLED', when: 'never' }, { if: '$CI_COMMIT_BRANCH' }],
stage: 'test', stage: scan_policy_stage,
image: '$SECURE_ANALYZERS_PREFIX/secrets:$SECRETS_ANALYZER_VERSION$SECRET_DETECTION_IMAGE_SUFFIX', image: '$SECURE_ANALYZERS_PREFIX/secrets:$SECRETS_ANALYZER_VERSION$SECRET_DETECTION_IMAGE_SUFFIX',
services: [], services: [],
allow_failure: true, allow_failure: true,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment