Commit 7846c3b1 authored by Zamir Martins's avatar Zamir Martins Committed by Heinrich Lee Yu

Create approval project rules

parent f410d5e9
...@@ -20,6 +20,8 @@ class ApprovalProjectRule < ApplicationRecord ...@@ -20,6 +20,8 @@ class ApprovalProjectRule < ApplicationRecord
any_approver: 3 any_approver: 3
} }
scope :report_approver_without_scan_finding, -> { report_approver.where.not(report_type: :scan_finding) }
alias_method :code_owner, :code_owner? alias_method :code_owner, :code_owner?
validate :validate_default_license_report_name, on: :update, if: :report_approver? validate :validate_default_license_report_name, on: :update, if: :report_approver?
...@@ -50,11 +52,8 @@ class ApprovalProjectRule < ApplicationRecord ...@@ -50,11 +52,8 @@ class ApprovalProjectRule < ApplicationRecord
end end
def apply_report_approver_rules_to(merge_request) def apply_report_approver_rules_to(merge_request)
rule = merge_request rule = merge_request_report_approver_rule(merge_request)
.approval_rules rule.update!(report_approver_attributes)
.report_approver
.find_or_initialize_by(report_type: report_type)
rule.update!(attributes_to_apply_for(report_type))
rule rule
end end
...@@ -68,7 +67,7 @@ class ApprovalProjectRule < ApplicationRecord ...@@ -68,7 +67,7 @@ class ApprovalProjectRule < ApplicationRecord
private private
def attributes_to_apply_for(report_type) def report_approver_attributes
attributes attributes
.slice('approvals_required', 'name') .slice('approvals_required', 'name')
.merge( .merge(
...@@ -86,4 +85,20 @@ class ApprovalProjectRule < ApplicationRecord ...@@ -86,4 +85,20 @@ class ApprovalProjectRule < ApplicationRecord
errors.add(:name, _("cannot be modified")) errors.add(:name, _("cannot be modified"))
end end
def merge_request_report_approver_rule(merge_request)
if scan_finding?
merge_request
.approval_rules
.report_approver
.joins(:approval_merge_request_rule_source)
.where(approval_merge_request_rule_source: { approval_project_rule_id: self.id })
.first_or_initialize
else
merge_request
.approval_rules
.report_approver
.find_or_initialize_by(report_type: report_type)
end
end
end end
...@@ -510,7 +510,7 @@ module EE ...@@ -510,7 +510,7 @@ module EE
def visible_approval_rules(target_branch: nil) def visible_approval_rules(target_branch: nil)
rules = strong_memoize(:visible_approval_rules) do rules = strong_memoize(:visible_approval_rules) do
Hash.new do |h, key| Hash.new do |h, key|
h[key] = visible_user_defined_rules(branch: key) + approval_rules.report_approver h[key] = visible_user_defined_rules(branch: key) + approval_rules.report_approver_without_scan_finding
end end
end end
......
...@@ -12,7 +12,6 @@ module Security ...@@ -12,7 +12,6 @@ module Security
def execute def execute
policy_configuration.delete_all_schedules policy_configuration.delete_all_schedules
create_new_schedule_rules create_new_schedule_rules
policy_configuration.update!(configured_at: Time.current)
end end
private private
......
...@@ -25,6 +25,17 @@ module Security ...@@ -25,6 +25,17 @@ module Security
.new(policy_configuration: configuration, policy_index: policy_index, policy: policy) .new(policy_configuration: configuration, policy_index: policy_index, policy: policy)
.execute .execute
end end
configuration.transaction do
configuration.approval_rules.scan_finding.delete_all
configuration.active_scan_result_policies.each do |policy|
Security::SecurityOrchestrationPolicies::ProcessScanResultPolicyService
.new(policy_configuration: configuration, policy: policy)
.execute
end
end
configuration.update!(configured_at: Time.current)
end end
end end
end end
......
...@@ -79,5 +79,11 @@ FactoryBot.define do ...@@ -79,5 +79,11 @@ FactoryBot.define do
rule_type { :report_approver } rule_type { :report_approver }
report_type { :code_coverage } report_type { :code_coverage }
end end
trait :scan_finding do
sequence(:name) { |n| "Scan finding #{n}" }
rule_type { :report_approver }
report_type { :scan_finding }
end
end end
end end
...@@ -128,6 +128,7 @@ RSpec.describe ApprovalProjectRule do ...@@ -128,6 +128,7 @@ RSpec.describe ApprovalProjectRule do
'Vulnerability-Check' | :vulnerability 'Vulnerability-Check' | :vulnerability
'License-Check' | :license_scanning 'License-Check' | :license_scanning
'Coverage-Check' | :code_coverage 'Coverage-Check' | :code_coverage
'Scan finding example' | :scan_finding
end end
context "when there is a project rule for each report type" do context "when there is a project rule for each report type" do
......
...@@ -3313,4 +3313,12 @@ RSpec.describe Project do ...@@ -3313,4 +3313,12 @@ RSpec.describe Project do
it { is_expected.to eql(vuln_rule) } it { is_expected.to eql(vuln_rule) }
end end
end end
describe '#visible_approval_rules' do
let(:scan_finding_rule) { create(:approval_project_rule, project: project, report_type: :scan_finding) }
subject { project.visible_approval_rules }
it { is_expected.not_to include(scan_finding_rule) }
end
end end
...@@ -32,7 +32,6 @@ RSpec.describe Security::SecurityOrchestrationPolicies::ProcessRuleService do ...@@ -32,7 +32,6 @@ RSpec.describe Security::SecurityOrchestrationPolicies::ProcessRuleService do
service.execute service.execute
new_schedule = Security::OrchestrationPolicyRuleSchedule.first new_schedule = Security::OrchestrationPolicyRuleSchedule.first
expect(policy_configuration.configured_at).not_to be_nil
expect(Security::OrchestrationPolicyRuleSchedule.count).to eq(1) expect(Security::OrchestrationPolicyRuleSchedule.count).to eq(1)
expect(new_schedule.id).not_to eq(schedule.id) expect(new_schedule.id).not_to eq(schedule.id)
expect(new_schedule.rule_index).to eq(1) expect(new_schedule.rule_index).to eq(1)
...@@ -45,7 +44,6 @@ RSpec.describe Security::SecurityOrchestrationPolicies::ProcessRuleService do ...@@ -45,7 +44,6 @@ RSpec.describe Security::SecurityOrchestrationPolicies::ProcessRuleService do
it 'deletes schedules' do it 'deletes schedules' do
expect { service.execute }.to change(Security::OrchestrationPolicyRuleSchedule, :count).by(-1) expect { service.execute }.to change(Security::OrchestrationPolicyRuleSchedule, :count).by(-1)
expect(policy_configuration.configured_at).not_to be_nil
end end
end end
end end
......
...@@ -4,12 +4,13 @@ require 'spec_helper' ...@@ -4,12 +4,13 @@ require 'spec_helper'
RSpec.describe Security::CreateOrchestrationPolicyWorker do RSpec.describe Security::CreateOrchestrationPolicyWorker do
describe '#perform' do describe '#perform' do
let_it_be(:configuration) { create(:security_orchestration_policy_configuration) } let_it_be(:configuration) { create(:security_orchestration_policy_configuration, configured_at: nil) }
let_it_be(:schedule) { create(:security_orchestration_policy_rule_schedule, security_orchestration_policy_configuration: configuration) } let_it_be(:schedule) { create(:security_orchestration_policy_rule_schedule, security_orchestration_policy_configuration: configuration) }
before do before do
allow_next_instance_of(Repository) do |repository| allow_next_instance_of(Repository) do |repository|
allow(repository).to receive(:blob_data_at).and_return({ scan_execution_policy: active_policies }.to_yaml) allow(repository).to receive(:blob_data_at).and_return(active_policies.to_yaml)
allow(repository).to receive(:last_commit_for_path)
end end
end end
...@@ -17,6 +18,8 @@ RSpec.describe Security::CreateOrchestrationPolicyWorker do ...@@ -17,6 +18,8 @@ RSpec.describe Security::CreateOrchestrationPolicyWorker do
context 'when policy is valid' do context 'when policy is valid' do
let(:active_policies) do let(:active_policies) do
{
scan_execution_policy:
[ [
{ {
name: 'Scheduled DAST 1', name: 'Scheduled DAST 1',
...@@ -36,35 +39,96 @@ RSpec.describe Security::CreateOrchestrationPolicyWorker do ...@@ -36,35 +39,96 @@ RSpec.describe Security::CreateOrchestrationPolicyWorker do
{ scan: 'dast', site_profile: 'Site Profile', scanner_profile: 'Scanner Profile' } { scan: 'dast', site_profile: 'Site Profile', scanner_profile: 'Scanner Profile' }
] ]
} }
],
scan_result_policy:
[
{
name: 'CS critical policy',
description: 'This policy with CS for critical policy',
enabled: true,
rules: [{ type: 'scan_finding', branches: %w[production], vulnerabilities_allowed: 0, severity_levels: %w[critical], scanners: %w[container_scanning] }],
actions: [
{ type: 'require_approval', approvals_required: 1, approvers: %w[admin] }
]
}
] ]
}
end end
it 'executes the process rule service' do it 'executes process services for all policies' do
active_policies.each_with_index do |policy, policy_index| active_policies[:scan_execution_policy].each_with_index do |policy, policy_index|
expect_next_instance_of(Security::SecurityOrchestrationPolicies::ProcessRuleService, expect_next_instance_of(Security::SecurityOrchestrationPolicies::ProcessRuleService,
policy_configuration: configuration, policy_index: policy_index, policy: policy) do |service| policy_configuration: configuration, policy_index: policy_index, policy: policy) do |service|
expect(service).to receive(:execute) expect(service).to receive(:execute)
end end
end end
active_policies[:scan_result_policy].each do |policy|
expect_next_instance_of(Security::SecurityOrchestrationPolicies::ProcessScanResultPolicyService,
policy_configuration: configuration, policy: policy) do |service|
expect(service).to receive(:execute)
end
end
expect(configuration.configured_at).to be_nil
expect { worker.perform }.not_to change(Security::OrchestrationPolicyRuleSchedule, :count) expect { worker.perform }.not_to change(Security::OrchestrationPolicyRuleSchedule, :count)
expect(configuration.reload.configured_at).not_to be_nil
end
context 'with existing project approval rules' do
let!(:approval_rule) { create(:approval_project_rule, :scan_finding, project: configuration.project )}
before do
allow_next_instance_of(Security::SecurityOrchestrationPolicies::ProcessRuleService) do |rule_service|
allow(rule_service).to receive(:execute)
end
allow_next_instance_of(Security::SecurityOrchestrationPolicies::ProcessScanResultPolicyService) do |rule_service|
allow(rule_service).to receive(:execute)
end
end
it 'deletes all approval_rules' do
expect { worker.perform }.to change(configuration.approval_rules, :count).by(-1)
end
end end
end end
context 'when policy is invalid' do context 'when policy is invalid' do
let(:active_policies) do let(:active_policies) do
{
scan_execution_policy:
[ [
{ {
key: 'invalid', key: 'invalid',
label: 'invalid' label: 'invalid'
} }
] ]
}
end end
it 'does not execute process rule service' do it 'does not execute process for any policy' do
expect(Security::SecurityOrchestrationPolicies::ProcessRuleService).not_to receive(:new) expect(Security::SecurityOrchestrationPolicies::ProcessRuleService).not_to receive(:new)
expect(Security::SecurityOrchestrationPolicies::ProcessScanResultPolicyService).not_to receive(:new)
expect { worker.perform }.to change(Security::OrchestrationPolicyRuleSchedule, :count).by(-1) expect { worker.perform }.to change(Security::OrchestrationPolicyRuleSchedule, :count).by(-1)
expect(configuration.reload.configured_at).to be_nil
end
context 'with existing project approval rules' do
let!(:approval_rule) { create(:approval_project_rule, :scan_finding, project: configuration.project )}
before do
allow_next_instance_of(Security::SecurityOrchestrationPolicies::ProcessRuleService) do |rule_service|
allow(rule_service).to receive(:execute)
end
allow_next_instance_of(Security::SecurityOrchestrationPolicies::ProcessScanResultPolicyService) do |rule_service|
allow(rule_service).to receive(:execute)
end
end
it 'does not delete the existing approval_rules' do
expect { worker.perform }.not_to change(configuration.approval_rules, :count)
end
end end
end end
end end
......
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