From e871cceb37f1e65b9bf384a2eb59e94d487f8914 Mon Sep 17 00:00:00 2001 From: mc_rocha Date: Mon, 23 Jun 2025 09:20:33 -0400 Subject: [PATCH 1/3] Add skipped security policies pipelines audit event Changelog: added EE: true --- .../types/policy_pipeline_skipped.yml | 10 ++ config/sidekiq_queues.yml | 2 + doc/user/compliance/audit_event_types.md | 1 + .../security/pipeline_execution_policy.rb | 4 + .../security/scan_execution_policy.rb | 4 + ee/app/models/ee/ci/pipeline.rb | 8 + ee/app/workers/all_queues.yml | 10 ++ .../policies/skip_pipelines_audit_worker.rb | 29 ++++ ..._policy_skipped_pipelines_audit_events.yml | 10 ++ .../pipeline_skipped_auditor.rb | 103 ++++++++++++ .../pipeline_skipped_auditor_spec.rb | 156 ++++++++++++++++++ ee/spec/models/ci/pipeline_spec.rb | 31 ++++ ...orchestration_policy_configuration_spec.rb | 32 +++- .../skip_pipelines_audit_worker_spec.rb | 58 +++++++ 14 files changed, 457 insertions(+), 1 deletion(-) create mode 100644 config/audit_events/types/policy_pipeline_skipped.yml create mode 100644 ee/app/workers/security/policies/skip_pipelines_audit_worker.rb create mode 100644 ee/config/feature_flags/gitlab_com_derisk/collect_security_policy_skipped_pipelines_audit_events.yml create mode 100644 ee/lib/security/security_orchestration_policies/pipeline_skipped_auditor.rb create mode 100644 ee/spec/lib/security/security_orchestration_policies/pipeline_skipped_auditor_spec.rb create mode 100644 ee/spec/workers/security/policies/skip_pipelines_audit_worker_spec.rb diff --git a/config/audit_events/types/policy_pipeline_skipped.yml b/config/audit_events/types/policy_pipeline_skipped.yml new file mode 100644 index 00000000000000..9f89393e342399 --- /dev/null +++ b/config/audit_events/types/policy_pipeline_skipped.yml @@ -0,0 +1,10 @@ +--- +name: policy_pipeline_skipped +description: A security policy pipeline is skipped +introduced_by_issue: https://gitlab.com/gitlab-org/gitlab/-/issues/539232 +introduced_by_mr: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/195325 +feature_category: security_policy_management +milestone: '18.2' +saved_to_database: false +streamed: true +scope: [Project] diff --git a/config/sidekiq_queues.yml b/config/sidekiq_queues.yml index 90ecc1cf996ee4..fe4fbf36e59d49 100644 --- a/config/sidekiq_queues.yml +++ b/config/sidekiq_queues.yml @@ -963,6 +963,8 @@ - 1 - - security_policies_project_transfer - 1 +- - security_policies_skip_pipelines_audit + - 1 - - security_process_scan_result_policy - 1 - - security_recreate_orchestration_configuration diff --git a/doc/user/compliance/audit_event_types.md b/doc/user/compliance/audit_event_types.md index 92e090c5b1fa20..cfa97ca3e2d47f 100644 --- a/doc/user/compliance/audit_event_types.md +++ b/doc/user/compliance/audit_event_types.md @@ -542,6 +542,7 @@ Audit event types belong to the following product categories. | [`merge_request_branch_bypassed_by_security_policy`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/195942) | The merge request's approval is bypassed by the branches configured in the security policy | {{< icon name="check-circle" >}} Yes | GitLab [18.2](https://gitlab.com/gitlab-org/gitlab/-/issues/549646) | Project | | [`merge_request_merged_with_policy_violations`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/195775) | A merge request merged with security policy violations | {{< icon name="check-circle" >}} Yes | GitLab [18.2](https://gitlab.com/gitlab-org/gitlab/-/work_items/549813) | Project | | [`policies_limit_exceeded`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/196005) | Enabled policies count exceeded the maximum allowed limit for policy type | {{< icon name="check-circle" >}} Yes | GitLab [18.2](https://gitlab.com/gitlab-org/gitlab/-/work_items/550891) | Project | +| [`policy_pipeline_skipped`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/195325) | A security policy pipeline is skipped | {{< icon name="dotted-circle" >}} No | GitLab [18.2](https://gitlab.com/gitlab-org/gitlab/-/issues/539232) | Project | | [`policy_violations_detected`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/193482) | Security policy violation is detected in the merge request | {{< icon name="dotted-circle" >}} No | GitLab [18.2](https://gitlab.com/gitlab-org/gitlab/-/work_items/549811) | Project | | [`policy_violations_resolved`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/193482) | Security policy violations are resolved in the merge request | {{< icon name="dotted-circle" >}} No | GitLab [18.2](https://gitlab.com/gitlab-org/gitlab/-/issues/549812) | Project | | [`policy_yaml_invalidated`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/196721) | The policy YAML is invalidated in security policy project | {{< icon name="check-circle" >}} Yes | GitLab [18.2](https://gitlab.com/gitlab-org/gitlab/-/work_items/550892) | Project | diff --git a/ee/app/models/concerns/security/pipeline_execution_policy.rb b/ee/app/models/concerns/security/pipeline_execution_policy.rb index 1622c75a2e6755..e8106ede1f9c45 100644 --- a/ee/app/models/concerns/security/pipeline_execution_policy.rb +++ b/ee/app/models/concerns/security/pipeline_execution_policy.rb @@ -8,6 +8,10 @@ def active_pipeline_execution_policies pipeline_execution_policy.select { |config| config[:enabled] }.first(pipeline_execution_policy_limit) end + def active_pipeline_execution_policy_names + active_pipeline_execution_policies.pluck(:name) # rubocop:disable Database/AvoidUsingPluckWithoutLimit -- not an ActiveRecord model and active_pipeline_execution_policies has limit + end + def pipeline_execution_policy policy_by_type(:pipeline_execution_policy) end diff --git a/ee/app/models/concerns/security/scan_execution_policy.rb b/ee/app/models/concerns/security/scan_execution_policy.rb index 7a1a0d2829f54d..8b4654c8dce83b 100644 --- a/ee/app/models/concerns/security/scan_execution_policy.rb +++ b/ee/app/models/concerns/security/scan_execution_policy.rb @@ -64,6 +64,10 @@ def active_policies_for_project(ref, project, pipeline_source = nil) .select { |policy| applicable_for_pipeline_source?(block_given? ? yield(policy[:rules]) : policy[:rules], pipeline_source) } end + def active_scan_execution_policy_names(ref, project) + active_policies_for_project(ref, project).pluck(:name) # rubocop:disable Database/AvoidUsingPluckWithoutLimit -- not an ActiveRecord model and active_scan_execution_policies has limit + end + def active_pipeline_policies_for_project(ref, project, pipeline_source = nil) active_policies_for_project(ref, project, pipeline_source) do |policy_rules| policy_rules.select { |rule| rule[:type] == RULE_TYPES[:pipeline] } diff --git a/ee/app/models/ee/ci/pipeline.rb b/ee/app/models/ee/ci/pipeline.rb index f5877c7e4f4524..5d48dccbe55d3d 100644 --- a/ee/app/models/ee/ci/pipeline.rb +++ b/ee/app/models/ee/ci/pipeline.rb @@ -127,6 +127,14 @@ def self.latest_limited_pipeline_ids_per_source(pipelines, sha) Security::PipelineAnalyzersStatusUpdateWorker.perform_async(pipeline.id) if pipeline.default_branch? end end + + after_transition any => :skipped do |pipeline| + pipeline.run_after_commit do + if ::Feature.enabled?(:collect_security_policy_skipped_pipelines_audit_events, pipeline.project) + Security::Policies::SkipPipelinesAuditWorker.perform_async(pipeline.id) + end + end + end end end diff --git a/ee/app/workers/all_queues.yml b/ee/app/workers/all_queues.yml index 0b60c6b4905d7e..505f2755dbcd49 100644 --- a/ee/app/workers/all_queues.yml +++ b/ee/app/workers/all_queues.yml @@ -3614,6 +3614,16 @@ :idempotent: true :tags: [] :queue_namespace: +- :name: security_policies_skip_pipelines_audit + :worker_name: Security::Policies::SkipPipelinesAuditWorker + :feature_category: :security_policy_management + :has_external_dependencies: true + :urgency: :low + :resource_boundary: :unknown + :weight: 1 + :idempotent: true + :tags: [] + :queue_namespace: - :name: security_process_scan_result_policy :worker_name: Security::ProcessScanResultPolicyWorker :feature_category: :security_policy_management diff --git a/ee/app/workers/security/policies/skip_pipelines_audit_worker.rb b/ee/app/workers/security/policies/skip_pipelines_audit_worker.rb new file mode 100644 index 00000000000000..8067b8ebb0881d --- /dev/null +++ b/ee/app/workers/security/policies/skip_pipelines_audit_worker.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +module Security + module Policies + class SkipPipelinesAuditWorker + include ApplicationWorker + + data_consistency :sticky + + feature_category :security_policy_management + urgency :low + idempotent! + deduplicate :until_executed + defer_on_database_health_signal :gitlab_main, [:project_audit_events], 1.minute + + # Audit stream to external destination with HTTP request if configured + worker_has_external_dependencies! + + def perform(pipeline_id) + pipeline = Ci::Pipeline.find_by_id(pipeline_id) + return unless pipeline + + return unless pipeline.project.licensed_feature_available?(:security_orchestration_policies) + + Security::SecurityOrchestrationPolicies::PipelineSkippedAuditor.new(pipeline: pipeline).audit + end + end + end +end diff --git a/ee/config/feature_flags/gitlab_com_derisk/collect_security_policy_skipped_pipelines_audit_events.yml b/ee/config/feature_flags/gitlab_com_derisk/collect_security_policy_skipped_pipelines_audit_events.yml new file mode 100644 index 00000000000000..6a6ce5e2103feb --- /dev/null +++ b/ee/config/feature_flags/gitlab_com_derisk/collect_security_policy_skipped_pipelines_audit_events.yml @@ -0,0 +1,10 @@ +--- +name: collect_security_policy_skipped_pipelines_audit_events +description: Collects audit events for skipped pipelines with security policy jobs in merge requests. +feature_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/539232 +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/195325 +rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/550772 +milestone: '18.2' +group: group::security policies +type: gitlab_com_derisk +default_enabled: false diff --git a/ee/lib/security/security_orchestration_policies/pipeline_skipped_auditor.rb b/ee/lib/security/security_orchestration_policies/pipeline_skipped_auditor.rb new file mode 100644 index 00000000000000..ca9edeb4794c7c --- /dev/null +++ b/ee/lib/security/security_orchestration_policies/pipeline_skipped_auditor.rb @@ -0,0 +1,103 @@ +# frozen_string_literal: true + +module Security + module SecurityOrchestrationPolicies + class PipelineSkippedAuditor + include Gitlab::Utils::StrongMemoize + + def initialize(pipeline:) + @pipeline = pipeline + end + + def audit + return unless pipeline + return unless security_orchestration_policy_configurations.present? + return unless skipped_policies.present? + + ::Gitlab::Audit::Auditor.audit(audit_context) + end + + private + + attr_reader :pipeline + + def audit_context + { + name: 'policy_pipeline_skipped', + author: pipeline.user, + scope: project, + target: pipeline, + message: "Pipeline: #{pipeline.id} with security policy jobs skipped", + additional_details: additional_details + } + end + + def additional_details + { + merge_request_title: merge_request&.title, + merge_request_id: merge_request&.id, + merge_request_iid: merge_request&.iid, + source_branch: merge_request&.source_branch, + target_branch: merge_request&.target_branch, + project_id: project.id, + project_name: project.name, + project_full_path: project.full_path, + skipped_policies: skipped_policies + }.compact + end + + def skipped_policies + pipeline_execution_policies, scan_execution_policies = active_execution_policies + + skipped_seps = format_skipped_policies(scan_execution_policies, 'scan_execution_policy') + skipped_peps = format_skipped_policies(pipeline_execution_policies, 'pipeline_execution_policy') + + skipped_seps + skipped_peps + end + + def format_skipped_policies(policies, type) + policies.map { |name| { name: name, policy_type: type } } + end + + def active_execution_policies + scan_execution_policies = Set.new + pipeline_execution_policies = Set.new + + security_orchestration_policy_configurations.each do |policy| + if target_branch_ref + active_seps_for_policy = policy.active_scan_execution_policy_names(target_branch_ref, project) + end + + active_peps_for_policy = policy.active_pipeline_execution_policy_names + + scan_execution_policies.merge(active_seps_for_policy) if active_seps_for_policy.present? + pipeline_execution_policies.merge(active_peps_for_policy) if active_peps_for_policy.present? + end + + [pipeline_execution_policies, scan_execution_policies] + end + + strong_memoize_attr :skipped_policies + + def security_orchestration_policy_configurations + project.all_security_orchestration_policy_configurations + end + strong_memoize_attr :security_orchestration_policy_configurations + + def project + pipeline.project + end + strong_memoize_attr :project + + def merge_request + pipeline.merge_request + end + strong_memoize_attr :merge_request + + def target_branch_ref + merge_request&.target_branch_ref + end + strong_memoize_attr :target_branch_ref + end + end +end diff --git a/ee/spec/lib/security/security_orchestration_policies/pipeline_skipped_auditor_spec.rb b/ee/spec/lib/security/security_orchestration_policies/pipeline_skipped_auditor_spec.rb new file mode 100644 index 00000000000000..d7201b1fa77aac --- /dev/null +++ b/ee/spec/lib/security/security_orchestration_policies/pipeline_skipped_auditor_spec.rb @@ -0,0 +1,156 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Security::SecurityOrchestrationPolicies::PipelineSkippedAuditor, feature_category: :security_policy_management do + let_it_be(:project) { build(:project) } + + describe '#audit' do + subject(:audit) { described_class.new(pipeline: pipeline).audit } + + shared_examples 'does not call Gitlab::Audit::Auditor' do + specify do + expect(::Gitlab::Audit::Auditor).not_to receive(:audit) + + audit + end + end + + context 'when pipeline is nil' do + let_it_be(:pipeline) { nil } + + it_behaves_like 'does not call Gitlab::Audit::Auditor' + end + + context 'when the pipeline is present' do + let_it_be(:pipeline) { build(:ci_pipeline, project: project) } + + context 'when there is no security_orchestration_policy_configuration assigned to project' do + it_behaves_like 'does not call Gitlab::Audit::Auditor' + end + + context 'when there is a security_orchestration_policy_configuration assigned to project' do + let_it_be(:security_orchestration_policy_configuration) do + build(:security_orchestration_policy_configuration, project: project) + end + + before do + allow(project).to receive(:all_security_orchestration_policy_configurations).and_return( + [security_orchestration_policy_configuration]) + + allow(security_orchestration_policy_configuration).to receive(:active_scan_execution_policy_names).with( + merge_request&.target_branch_ref, project).and_return(active_scan_execution_policy_names) + + allow(security_orchestration_policy_configuration).to receive(:active_pipeline_execution_policy_names) + .and_return(active_pipeline_execution_policy_names) + end + + context 'when there are no active policies' do + let(:active_scan_execution_policy_names) { [] } + let(:active_pipeline_execution_policy_names) { [] } + let(:merge_request) { nil } + + it_behaves_like 'does not call Gitlab::Audit::Auditor' + end + + context 'when there are active policies' do + shared_examples_for 'calls Gitlab::Audit::Auditor.audit with the expected context' do + specify do + expect(::Gitlab::Audit::Auditor).to receive(:audit) do |context| + expect(context[:name]).to eq('policy_pipeline_skipped') + expect(context[:author]).to eq(pipeline.user) + expect(context[:scope]).to eq(project) + expect(context[:target]).to eq(pipeline) + expect(context[:message]).to eq("Pipeline: #{pipeline.id} with security policy jobs skipped") + expect(context[:additional_details]).to eq(additional_details) + end + + audit + end + end + + shared_examples_for 'when the merge_request is present' do + context 'when the merge_request is present' do + let_it_be(:merge_request) do + build(:merge_request, id: 1, iid: 1, source_project: project, target_project: project) + end + + let(:additional_details) do + { + merge_request_title: merge_request.title, + merge_request_id: merge_request.id, + merge_request_iid: merge_request.iid, + source_branch: merge_request.source_branch, + target_branch: merge_request.target_branch, + project_id: project.id, + project_name: project.name, + project_full_path: project.full_path, + skipped_policies: skipped_policies_details + } + end + + before do + pipeline.merge_request = merge_request + end + + it_behaves_like 'calls Gitlab::Audit::Auditor.audit with the expected context' + end + end + + context 'when there are active scan_execution_policies policies' do + let(:skipped_policy_name) { 'Skipped sep policy' } + let(:skipped_policies_details) { [{ name: skipped_policy_name, policy_type: 'scan_execution_policy' }] } + + let(:active_scan_execution_policy_names) { [skipped_policy_name] } + let(:active_pipeline_execution_policy_names) { [] } + + it_behaves_like 'when the merge_request is present' + end + + context 'when there are active pipeline_execution_policies policies' do + let(:skipped_policy_name) { 'Skipped pep policy' } + let(:skipped_policies_details) { [{ name: skipped_policy_name, policy_type: 'pipeline_execution_policy' }] } + + let(:active_scan_execution_policy_names) { [] } + let(:active_pipeline_execution_policy_names) { [skipped_policy_name] } + + it_behaves_like 'when the merge_request is present' + + context 'when merge_request is nil' do + let(:merge_request) { nil } + let(:additional_details) do + { + project_id: project.id, + project_name: project.name, + project_full_path: project.full_path, + skipped_policies: skipped_policies_details + } + end + + before do + pipeline.merge_request = merge_request + end + + it_behaves_like 'calls Gitlab::Audit::Auditor.audit with the expected context' + end + end + + context 'when there are active scan_execution and pipeline_execution policies' do + let(:skipped_sep_policy_name) { 'Skipped sep policy' } + let(:skipped_pep_policy_name) { 'Skipped pep policy' } + let(:skipped_policies_details) do + [{ name: skipped_sep_policy_name, policy_type: 'scan_execution_policy' }, + { name: skipped_pep_policy_name, + policy_type: 'pipeline_execution_policy' }] + end + + let(:active_scan_execution_policy_names) { [skipped_sep_policy_name] } + let(:active_pipeline_execution_policy_names) { [skipped_pep_policy_name] } + + it_behaves_like 'when the merge_request is present' + end + end + end + end + end +end diff --git a/ee/spec/models/ci/pipeline_spec.rb b/ee/spec/models/ci/pipeline_spec.rb index e5d80af0b46c22..3c776abe3d9ff2 100644 --- a/ee/spec/models/ci/pipeline_spec.rb +++ b/ee/spec/models/ci/pipeline_spec.rb @@ -684,6 +684,8 @@ it 'does not schedule security status update worker' do expect(Security::PipelineAnalyzersStatusUpdateWorker).not_to receive(:perform_async).with(pipeline.id) + + transition_pipeline end end @@ -692,10 +694,39 @@ it 'does not schedule security status update worker' do expect(Security::PipelineAnalyzersStatusUpdateWorker).not_to receive(:perform_async).with(pipeline.id) + + transition_pipeline end end end end + + context 'Security::Policies::SkipPipelinesAuditWorker' do + let(:build) { create(:ci_empty_pipeline, project: project, status: from_status) } + let(:from_status) { Ci::HasStatus::ACTIVE_STATUSES[-1] } + + context 'on pipeline skipped' do + subject(:transition_pipeline) { pipeline.skip } + + context 'when the feature flag `collect_security_policy_skipped_pipelines_audit_events` is disabled' do + before do + stub_feature_flags(collect_security_policy_skipped_pipelines_audit_events: false) + end + + it 'does not enqueue SkipPipelinesAuditWorker' do + expect(Security::Policies::SkipPipelinesAuditWorker).not_to receive(:perform_async).with(pipeline.id) + + transition_pipeline + end + end + + it 'enqueue SkipPipelinesAuditWorker' do + expect(Security::Policies::SkipPipelinesAuditWorker).to receive(:perform_async).with(pipeline.id) + + transition_pipeline + end + end + end end describe '#latest_merged_result_pipeline?' do diff --git a/ee/spec/models/security/orchestration_policy_configuration_spec.rb b/ee/spec/models/security/orchestration_policy_configuration_spec.rb index 2a810b07cd3f4f..fb7308f993125f 100644 --- a/ee/spec/models/security/orchestration_policy_configuration_spec.rb +++ b/ee/spec/models/security/orchestration_policy_configuration_spec.rb @@ -3433,7 +3433,7 @@ end end - context 'with scan policies targetting specific pipeline source' do + context 'with scan policies targeting specific pipeline source' do let(:container_scanning_policy) do build( :scan_execution_policy, @@ -3461,6 +3461,17 @@ end end + describe '#active_scan_execution_policy_names' do + include_context 'for policies with pipeline and scheduled rules' + + subject(:active_scan_execution_policy_names) { security_orchestration_policy_configuration.active_scan_execution_policy_names('refs/heads/master', project) } + + it 'includes pipeline and scheduled policy names' do + expect(active_scan_execution_policy_names).to contain_exactly(dast_policy[:name], sast_policy_with_schedule[:name], + container_scanning_policy[:name]) + end + end + describe 'active_pipeline_policies_for_project' do include_context 'for policies with pipeline and scheduled rules' @@ -3701,6 +3712,25 @@ end end + describe '#active_pipeline_execution_policy_names' do + let(:policy_yaml) { fixture_file('security_orchestration.yml', dir: 'ee') } + + subject(:active_pipeline_execution_policy_names) { security_orchestration_policy_configuration.active_pipeline_execution_policy_names } + + before do + allow(security_policy_management_project).to receive(:repository).and_return(repository) + allow(repository).to receive(:blob_data_at).with(default_branch, Security::OrchestrationPolicyConfiguration::POLICY_PATH).and_return(policy_yaml) + end + + it 'returns active pipeline execution policy names' do + expect(active_pipeline_execution_policy_names).to contain_exactly('Run custom pipeline configuration', + 'Second pipeline execution policy', + 'Third pipeline execution policy', + 'Fourth pipeline execution policy', + 'Fifth pipeline execution policy') + end + end + describe '#active_pipeline_execution_schedule_policies' do let(:policy_yaml) { fixture_file('security_orchestration.yml', dir: 'ee') } diff --git a/ee/spec/workers/security/policies/skip_pipelines_audit_worker_spec.rb b/ee/spec/workers/security/policies/skip_pipelines_audit_worker_spec.rb new file mode 100644 index 00000000000000..334ac70b0cc78e --- /dev/null +++ b/ee/spec/workers/security/policies/skip_pipelines_audit_worker_spec.rb @@ -0,0 +1,58 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Security::Policies::SkipPipelinesAuditWorker, feature_category: :security_policy_management do + describe '#perform' do + let_it_be(:pipeline) { create(:ci_pipeline) } + + subject(:run_worker) { described_class.new.perform(pipeline_id) } + + shared_examples_for 'does not call PipelineSkippedAuditor' do + specify do + expect(Security::SecurityOrchestrationPolicies::PipelineSkippedAuditor).not_to receive(:new) + + run_worker + end + end + + context 'when pipeline is not found' do + let(:pipeline_id) { non_existing_record_id } + + it_behaves_like 'does not call PipelineSkippedAuditor' + end + + context 'when pipeline exist' do + let(:pipeline_id) { pipeline.id } + + context 'when security_orchestration_policies feature is available' do + before do + stub_licensed_features(security_orchestration_policies: true) + end + + it 'calls PipelineSkippedAuditor' do + expect_next_instance_of(Security::SecurityOrchestrationPolicies::PipelineSkippedAuditor, + pipeline: pipeline) do |auditor| + expect(auditor).to receive(:audit) + end + + run_worker + end + + it_behaves_like 'an idempotent worker' do + let(:job_args) { pipeline.id } + end + end + + context 'when security_orchestration_policies feature is not available' do + let(:pipeline_id) { pipeline.id } + + before do + stub_licensed_features(security_orchestration_policies: false) + end + + it_behaves_like 'does not call PipelineSkippedAuditor' + end + end + end +end -- GitLab From c1e19a148fe5bc6345d6aa5585e2a605e57d6ef2 Mon Sep 17 00:00:00 2001 From: mc_rocha Date: Thu, 3 Jul 2025 14:47:01 -0400 Subject: [PATCH 2/3] Address reviewer suggestion --- .../pipeline_skipped_auditor.rb | 2 ++ .../pipeline_skipped_auditor_spec.rb | 3 +++ 2 files changed, 5 insertions(+) diff --git a/ee/lib/security/security_orchestration_policies/pipeline_skipped_auditor.rb b/ee/lib/security/security_orchestration_policies/pipeline_skipped_auditor.rb index ca9edeb4794c7c..f86f31a3c2d1b2 100644 --- a/ee/lib/security/security_orchestration_policies/pipeline_skipped_auditor.rb +++ b/ee/lib/security/security_orchestration_policies/pipeline_skipped_auditor.rb @@ -27,6 +27,7 @@ def audit_context author: pipeline.user, scope: project, target: pipeline, + target_details: pipeline.id.to_s, message: "Pipeline: #{pipeline.id} with security policy jobs skipped", additional_details: additional_details } @@ -34,6 +35,7 @@ def audit_context def additional_details { + commit_sha: pipeline.sha, merge_request_title: merge_request&.title, merge_request_id: merge_request&.id, merge_request_iid: merge_request&.iid, diff --git a/ee/spec/lib/security/security_orchestration_policies/pipeline_skipped_auditor_spec.rb b/ee/spec/lib/security/security_orchestration_policies/pipeline_skipped_auditor_spec.rb index d7201b1fa77aac..61e00498c8b43e 100644 --- a/ee/spec/lib/security/security_orchestration_policies/pipeline_skipped_auditor_spec.rb +++ b/ee/spec/lib/security/security_orchestration_policies/pipeline_skipped_auditor_spec.rb @@ -61,6 +61,7 @@ expect(context[:author]).to eq(pipeline.user) expect(context[:scope]).to eq(project) expect(context[:target]).to eq(pipeline) + expect(context[:target_details]).to eq(pipeline.id.to_s) expect(context[:message]).to eq("Pipeline: #{pipeline.id} with security policy jobs skipped") expect(context[:additional_details]).to eq(additional_details) end @@ -77,6 +78,7 @@ let(:additional_details) do { + commit_sha: pipeline.sha, merge_request_title: merge_request.title, merge_request_id: merge_request.id, merge_request_iid: merge_request.iid, @@ -120,6 +122,7 @@ let(:merge_request) { nil } let(:additional_details) do { + commit_sha: pipeline.sha, project_id: project.id, project_name: project.name, project_full_path: project.full_path, -- GitLab From 1fc08a9094a9d7933f09f62cf56a3cba980f5a0a Mon Sep 17 00:00:00 2001 From: mc_rocha Date: Fri, 4 Jul 2025 11:24:35 -0400 Subject: [PATCH 3/3] Address reviewer suggestion --- .../pipeline_skipped_auditor.rb | 2 +- .../pipeline_skipped_auditor_spec.rb | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/ee/lib/security/security_orchestration_policies/pipeline_skipped_auditor.rb b/ee/lib/security/security_orchestration_policies/pipeline_skipped_auditor.rb index f86f31a3c2d1b2..c8b5c1717f074c 100644 --- a/ee/lib/security/security_orchestration_policies/pipeline_skipped_auditor.rb +++ b/ee/lib/security/security_orchestration_policies/pipeline_skipped_auditor.rb @@ -27,7 +27,7 @@ def audit_context author: pipeline.user, scope: project, target: pipeline, - target_details: pipeline.id.to_s, + target_details: pipeline.commit.present? ? pipeline.commit.title : pipeline.name, message: "Pipeline: #{pipeline.id} with security policy jobs skipped", additional_details: additional_details } diff --git a/ee/spec/lib/security/security_orchestration_policies/pipeline_skipped_auditor_spec.rb b/ee/spec/lib/security/security_orchestration_policies/pipeline_skipped_auditor_spec.rb index 61e00498c8b43e..07ebb22c997b73 100644 --- a/ee/spec/lib/security/security_orchestration_policies/pipeline_skipped_auditor_spec.rb +++ b/ee/spec/lib/security/security_orchestration_policies/pipeline_skipped_auditor_spec.rb @@ -23,7 +23,9 @@ end context 'when the pipeline is present' do - let_it_be(:pipeline) { build(:ci_pipeline, project: project) } + let_it_be(:commit_title) { '[skip ci]Add .gitlab-ci.yml' } + let_it_be(:pipeline) { build(:ci_pipeline, project: project, id: 42) } + let_it_be(:commit) { build(:commit, safe_message: commit_title) } context 'when there is no security_orchestration_policy_configuration assigned to project' do it_behaves_like 'does not call Gitlab::Audit::Auditor' @@ -43,6 +45,8 @@ allow(security_orchestration_policy_configuration).to receive(:active_pipeline_execution_policy_names) .and_return(active_pipeline_execution_policy_names) + + allow(pipeline).to receive(:commit).and_return(commit) end context 'when there are no active policies' do @@ -61,7 +65,7 @@ expect(context[:author]).to eq(pipeline.user) expect(context[:scope]).to eq(project) expect(context[:target]).to eq(pipeline) - expect(context[:target_details]).to eq(pipeline.id.to_s) + expect(context[:target_details]).to eq(commit_title) expect(context[:message]).to eq("Pipeline: #{pipeline.id} with security policy jobs skipped") expect(context[:additional_details]).to eq(additional_details) end -- GitLab