Skip to content
Snippets Groups Projects
Verified Commit d8732ca1 authored by Marcos Rocha's avatar Marcos Rocha
Browse files

Add cadence validation for sep creation

This MR adds a cadence validation for the scan
execution policy creation according to the
supported cadences in our documentation.

Changelog: added
EE: true
parent 77e55bb9
No related branches found
No related tags found
1 merge request!148096Add cadence validation for sep creation
# frozen_string_literal: true
module ScanExecutionPolicy
module CadenceHelper
VALID_CADENCE = /^((\*|\d{1,2})\s){2}(.+\s?){3}$/
def valid_cadence?(cadence)
return false if cadence == '* * * * *'
cadence.match?(VALID_CADENCE)
end
def log_invalid_cadence_error(project_id, cadence)
Gitlab::AppJsonLogger.info(event: 'scheduled_scan_execution_policy_validation',
message: 'Invalid cadence',
project_id: project_id,
cadence: cadence)
end
end
end
......@@ -4,6 +4,7 @@ module Security
module SecurityOrchestrationPolicies
class ValidatePolicyService < ::BaseContainerService
include ::Gitlab::Utils::StrongMemoize
include Security::SecurityOrchestrationPolicies::CadenceChecker
ValidationError = Struct.new(:field, :level, :message, :title)
......@@ -24,6 +25,10 @@ def execute
return error_with_title(s_('SecurityOrchestration|Required approvals exceed eligible approvers.'), title: s_('SecurityOrchestration|Logic error'), field: :approvers_ids) if required_approvals_exceed_eligible_approvers?
if Feature.enabled?(:scan_execution_policy_cadence_validation) && invalid_cadence?
return error_with_title(s_('SecurityOrchestration|Cadence is invalid'), field: :cadence)
end
success
end
......@@ -205,6 +210,15 @@ def approval_requiring_action
policy[:actions]&.find { |action| action[:type] == Security::ScanResultPolicy::REQUIRE_APPROVAL }
end
strong_memoize_attr :approval_requiring_action
def invalid_cadence?
return false if scan_result_policy?
policy[:rules].select { |rule| rule[:cadence] }
.any? do |rule|
!(Gitlab::Ci::CronParser.new(rule[:cadence]).cron_valid? && valid_cadence?(rule[:cadence]))
end
end
end
end
end
......@@ -4,7 +4,7 @@ module Security
class OrchestrationPolicyRuleScheduleNamespaceWorker
BATCH_SIZE = 50
include ApplicationWorker
include ::ScanExecutionPolicy::CadenceHelper
include Security::SecurityOrchestrationPolicies::CadenceChecker
feature_category :security_policy_management
......
......@@ -9,7 +9,7 @@ class OrchestrationPolicyRuleScheduleWorker # rubocop:disable Scalability/Idempo
# This worker does not perform work scoped to a context
include CronjobQueue
# rubocop:enable Scalability/CronWorkerContext
include ::ScanExecutionPolicy::CadenceHelper
include Security::SecurityOrchestrationPolicies::CadenceChecker
feature_category :security_policy_management
......
# frozen_string_literal: true
module Security
module SecurityOrchestrationPolicies
module CadenceChecker
VALID_CADENCE = /^((\*|\d{1,2})\s){2}(.+\s?){3}$/
def valid_cadence?(cadence)
return false if cadence == '* * * * *'
cadence.match?(VALID_CADENCE)
end
def log_invalid_cadence_error(project_id, cadence)
Gitlab::AppJsonLogger.info(event: 'scheduled_scan_execution_policy_validation',
message: 'Invalid cadence',
project_id: project_id,
cadence: cadence)
end
end
end
end
......@@ -2,10 +2,12 @@
require 'spec_helper'
RSpec.describe ScanExecutionPolicy::CadenceHelper, feature_category: :security_policy_management do
RSpec.describe Security::SecurityOrchestrationPolicies::CadenceChecker, feature_category: :security_policy_management do
using RSpec::Parameterized::TableSyntax
describe '#valid_cadence?' do
let_it_be(:cadence_checker) { Class.new { include Security::SecurityOrchestrationPolicies::CadenceChecker }.new }
where(:cadence, :expected_result) do
'* * * * *' | false
'*/30 * * * *' | false
......@@ -50,7 +52,7 @@
with_them do
it 'verifies if the cadence is allowed' do
expect(helper.valid_cadence?(cadence)).to eq expected_result
expect(cadence_checker.valid_cadence?(cadence)).to eq expected_result
end
end
end
......
......@@ -474,6 +474,41 @@
end
end
shared_examples 'checks if cadence is valid' do
context 'when cadence is provided' do
let(:rule) do
{
branches: ['master'],
cadence: cadence,
timezone: 'Europe/Amsterdam'
}
end
context 'when cadence is valid' do
let(:cadence) { '0 0 * * *' }
it { expect(result[:status]).to eq(:success) }
end
context 'when cadence is invalid' do
let(:cadence) { '* * * * *' }
it_behaves_like 'sets validation errors', field: :cadence, message: 'Cadence is invalid'
it { expect(result[:status]).to eq(:error) }
it { expect(result[:details]).to match_array(['Cadence is invalid']) }
context 'when the feature flag scan_execution_policy_cadence_validation is disabled' do
before do
stub_feature_flags(scan_execution_policy_cadence_validation: false)
end
it { expect(result[:status]).to eq(:success) }
end
end
end
end
shared_examples 'checks if vulnerability_age is valid' do
let(:new_states) { %w[new_needs_triage newly_detected] }
let(:new_and_previously_existing_states) { %w[detected new_needs_triage] }
......@@ -537,6 +572,7 @@
it_behaves_like 'checks policy name'
it_behaves_like 'checks if branches are provided in rule'
it_behaves_like 'checks if timezone is valid'
it_behaves_like 'checks if cadence is valid'
it_behaves_like 'checks if vulnerability_age is valid'
end
......@@ -588,6 +624,7 @@ def setup_repository(project, branches)
it_behaves_like 'checks if branches are defined in the project'
it_behaves_like 'checks if required approvals exceed eligible approvers'
it_behaves_like 'checks if timezone is valid'
it_behaves_like 'checks if cadence is valid'
it_behaves_like 'checks if vulnerability_age is valid'
it_behaves_like 'checks if branches exist for the provided branch_type' do
where(:policy_type, :branch_type, :status) do
......@@ -616,6 +653,7 @@ def setup_repository(project, branches)
it_behaves_like 'checks if branches are defined in the project'
it_behaves_like 'checks if required approvals exceed eligible approvers'
it_behaves_like 'checks if timezone is valid'
it_behaves_like 'checks if cadence is valid'
it_behaves_like 'checks if vulnerability_age is valid'
it_behaves_like 'checks if branches exist for the provided branch_type' do
where(:policy_type, :branch_type, :status) do
......@@ -677,6 +715,7 @@ def setup_repository(project, branches)
it_behaves_like 'checks if branches are provided in rule'
it_behaves_like 'checks if required approvals exceed eligible approvers'
it_behaves_like 'checks if timezone is valid'
it_behaves_like 'checks if cadence is valid'
it_behaves_like 'checks if vulnerability_age is valid'
context 'when policy_scope is present' do
......
......@@ -45751,6 +45751,9 @@ msgstr ""
msgid "SecurityOrchestration|Branch: %{boldStart}%{branchName}%{boldEnd} was not found in project: %{boldStart}%{projectName}%{boldEnd}. Edit or remove this entry."
msgstr ""
 
msgid "SecurityOrchestration|Cadence is invalid"
msgstr ""
msgid "SecurityOrchestration|Can't find project: %{boldStart}%{projectName}%{boldEnd}. Edit or remove this entry."
msgstr ""
 
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment