Skip to content

BE: Support component filtering options for Merge Request Approval Policies

Implementation Plan

"components": {
  "type": "array",
  "description": "Specifies the components to match.",
  "minItems": 1,
  "uniqueItems": true,
  "additionalItems": false,
  "items": {
    "type": "string",
    "minLength": 1
  }
},
"match_on_inclusion_component": {
  "type": "boolean",
  "description": "Specifies whether to match components on inclusion or exclusion."
},
===================================================================
diff --git a/ee/app/validators/json_schemas/security_orchestration_policy.json b/ee/app/validators/json_schemas/security_orchestration_policy.json
--- a/ee/app/validators/json_schemas/security_orchestration_policy.json	(revision 6e5357b9e6289ba7857f8141e42f54b393779952)
+++ b/ee/app/validators/json_schemas/security_orchestration_policy.json	(date 1694119660540)
@@ -521,6 +521,21 @@
                     "any",
                     "unsigned"
                   ]
+                },
+                "components": {
+                  "type": "array",
+                  "description": "Specifies the components to match.",
+                  "minItems": 1,
+                  "uniqueItems": true,
+                  "additionalItems": false,
+                  "items": {
+                    "type": "string",
+                    "minLength": 1
+                  }
+                },
+                "match_on_inclusion_component": {
+                  "type": "boolean",
+                  "description": "Specifies whether to match components on inclusion or exclusion."
                 }
               },
               "oneOf": [
  • Add components columns to scan_result_policies table MR.
components text[] DEFAULT '{}'::text[],
match_on_inclusion_component boolean,
  • Add a method in Security::ScanResultPolicyRead to return the components attributes.
def components
  return {} unless components.present? && match_on_inclusion_component.present?

  { components_list: components, match_on_inclusion: match_on_inclusion_component }
end
  • Add a method to Gitlab::Ci::Reports::LicenseScanning::Report to get the components from the license scanning report.
def components
  found_licenses.values.flat_map { |license| license.dependencies.flat_map { |dependency| "#{dependency.name} (#{dependency.version})" } }.uniq
end

get the package names using this method.

Index: ee/app/services/security/sync_license_scanning_rules_service.rb
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/ee/app/services/security/sync_license_scanning_rules_service.rb b/ee/app/services/security/sync_license_scanning_rules_service.rb
--- a/ee/app/services/security/sync_license_scanning_rules_service.rb	(revision d5e0068910b948fd9c921dbcbb0091b5d22e70c9)
+++ b/ee/app/services/security/sync_license_scanning_rules_service.rb	(date 1695230565357)
@@ -44,7 +44,7 @@
       return if license_approval_rules.empty?
 
       violated_rules, unviolated_rules = license_approval_rules.partition do |rule|
-        violates_policy?(merge_request, rule)
+        violates_policy?(merge_request, rule) || violates_components_policy(rule)
       end
 
       update_required_approvals(merge_request, violated_rules, unviolated_rules)
@@ -90,6 +90,25 @@
       violates_license_policy
     end
 
+    def violates_components_policy(rule)
+      scan_result_policy_read = rule.scan_result_policy_read
+
+      components_on_report = report.dependency_names
+
+      components_to_check = scan_result_policy_read.components[:components_list]
+
+      return false unless components_on_report && components_to_check
+
+      check_specific_components = scan_result_policy_read.components[:match_on_inclusion]
+
+      if check_specific_components # components that cannot be present on the report
+        components_to_check.any? { |component| components_on_report.include?(component) }
+      else
+        # components that can be present on the report
+        (components_on_report - components_to_check).present?
+      end
+    end
+
     def licenses_to_check(target_branch_report, scan_result_policy_read)
       only_newly_detected = scan_result_policy_read.license_states == [ApprovalProjectRule::NEWLY_DETECTED]
 

Verification steps

Edited by Marcos Rocha