Skip to content

Add `branch_type` support to Scan execution policies

Summary

We are adding branch_type support to security policies (&9468 (closed)).

Our implementation needs to support group-level protected branches (&8679).

Implementation Plan

A proof-of-concept can be found on the 9468-branch-expressions branch.

  • backend Add or use the feature flag security_policies_branch_type. If disabled for an actor, branch_type rules are skipped from processing.

  • backend Extend the schema so that policy rules provide either branches or branch_types.

  • backend Change ValidatePolicyService to validate that either branches or branch_types are provided.

diff --git ee/app/services/security/security_orchestration_policies/validate_policy_service.rb
@@ -40,7 +40,7 @@ def blank_name?
       def blank_branch_for_rule?
         return false if policy_type == :scan_result_policy

-        policy[:rules].any? { |rule| rule[:agents].blank? && rule[:branches].blank? }
+        policy[:rules].any? { |rule| rule[:agents].blank? && rule[:branches].blank? && rule[:branch_type].blank? }
       end

       def missing_branch_for_rule?
  • backend Implement a service PolicyBranchesService that accepts as inputs (1) a list of policy rules and (2) a project. The output of the service is a list of all branch names present in the project's repository as matched by the provided rules.

type: pipeline

  • backend Change ScanExecutionPolicy#active_policies_scan_actions to use PolicyBranchesService to determine if a ref is in the set of matched branches.
diff --git ee/app/models/concerns/security/scan_execution_policy.rb b/ee/app/models/concerns/security/scan_execution_policy.rb
@@ -46,9 +46,9 @@ def scan_execution_policy
       policy_by_type(:scan_execution_policy)
     end

-    def active_policies_scan_actions(ref)
+    def active_policies_scan_actions(ref, project)
       active_scan_execution_policies
-        .select { |policy| applicable_for_ref?(policy, ref) }
+        .select { |policy| applicable_for_ref?(policy, ref, project) }
         .flat_map { |policy| policy[:actions] }
     end

@@ -71,14 +71,14 @@ def active_policy_names_with_dast_profiles
       end
     end

-    def applicable_for_ref?(policy, ref)
+    def applicable_for_ref?(policy, ref, project)
       return false unless Gitlab::Git.branch_ref?(ref)

-      branch_name = Gitlab::Git.ref_name(ref)
+      ref_name = Gitlab::Git.ref_name(ref)
+      pipeline_rules = policy[:rules].select { |rule| rule[:type] == RULE_TYPES[:pipeline] }

-      policy[:rules].any? do |rule|
-        rule[:type] == RULE_TYPES[:pipeline] && rule[:branches].any? { |branch| RefMatcher.new(branch).matches?(branch_name) }
-      end
+      applicable_branches = Security::SecurityOrchestrationPolicies::PolicyBranchesService.new(pipeline_rules, project).execute
+      ref_name.in?(applicable_branches)
     end
   end
 end
diff --git ee/lib/gitlab/ci/config/security_orchestration_policies/processor.rb
@@ -103,7 +103,7 @@ def insert_scan_policy_stage_after_build_stage_or_first(defined_stages)

           def active_scan_actions
             scan_actions do |configuration|
-              configuration.active_policies_scan_actions(@ref)
+              configuration.active_policies_scan_actions(@ref, project)
             end
           end

type: schedule

  • backend Change OrchestrationPolicyRuleSchedule#applicable_branches to use PolicyBranchesService to determine applicable branches.
diff --git ee/app/models/security/orchestration_policy_rule_schedule.rb b/ee/app/models/security/orchestration_policy_rule_schedule.rb
@@ -37,14 +37,11 @@ def policy
     end

     def applicable_branches(project = security_orchestration_policy_configuration.project)
-      configured_branches = policy&.dig(:rules, rule_index, :branches)
-      return [] if configured_branches.blank? || project.blank?
-
-      branch_names = project.repository.branches
-
-      configured_branches
-        .flat_map { |pattern| RefMatcher.new(pattern).matching(branch_names).map(&:name) }
-        .uniq
+      # rubocop: disable CodeReuse/ServiceClass
+      Security::SecurityOrchestrationPolicies::PolicyBranchesService
+        .new(policy[:rules], project)
+        .execute
+      # rubocop: enable CodeReuse/ServiceClass
     end

     def applicable_agents
Edited by Dominic Bauer