diff --git a/ee/app/models/security/policy.rb b/ee/app/models/security/policy.rb index ead1bdc6ce7899480095d1a8fd1ab565812165c2..6051c31ce5170183766843e236b5e44d884d328f 100644 --- a/ee/app/models/security/policy.rb +++ b/ee/app/models/security/policy.rb @@ -348,6 +348,41 @@ def bypass_settings end strong_memoize_attr :bypass_settings + def approval_policy + return unless type_approval_policy? + + Security::ScanResultPolicies::ApprovalPolicy.new(self) + end + strong_memoize_attr :approval_policy + + def scan_execution_policy + return unless type_scan_execution_policy? + + Security::ScanExecutionPolicies::ScanExecutionPolicy.new(self) + end + strong_memoize_attr :scan_execution_policy + + def vulnerability_management_policy + return unless type_vulnerability_management_policy? + + Security::VulnerabilityManagementPolicies::VulnerabilityManagementPolicy.new(self) + end + strong_memoize_attr :vulnerability_management_policy + + def pipeline_execution_policy + return unless type_pipeline_execution_policy? + + Security::PipelineExecutionPolicies::PipelineExecutionPolicy.new(self) + end + strong_memoize_attr :pipeline_execution_policy + + def pipeline_execution_schedule_policy + return unless type_pipeline_execution_schedule_policy? + + Security::PipelineExecutionSchedulePolicies::PipelineExecutionSchedulePolicy.new(self) + end + strong_memoize_attr :pipeline_execution_schedule_policy + def supports_policy_rules? Security::PolicyRule::SUPPORTED_POLICY_TYPES.include?(type.to_sym) end diff --git a/ee/lib/security/base_policy.rb b/ee/lib/security/base_policy.rb new file mode 100644 index 0000000000000000000000000000000000000000..57572cc005e2276d8533ee58c0b7b03717b5f6da --- /dev/null +++ b/ee/lib/security/base_policy.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +module Security + class BasePolicy + include Gitlab::Utils::StrongMemoize + + def initialize(policy_record) + @policy_record = policy_record + end + + def name + policy_record.name + end + strong_memoize_attr :name + + def description + policy_record.description + end + strong_memoize_attr :description + + def enabled + policy_record.enabled + end + strong_memoize_attr :enabled + + def policy_scope + Security::PolicyScope.new(policy_record.policy_scope || {}) + end + strong_memoize_attr :policy_scope + + private + + attr_reader :policy_record + end +end diff --git a/ee/lib/security/pipeline_execution_policies/content.rb b/ee/lib/security/pipeline_execution_policies/content.rb new file mode 100644 index 0000000000000000000000000000000000000000..8b6162fae9516d246b21f088e5a0f2a2d34f2665 --- /dev/null +++ b/ee/lib/security/pipeline_execution_policies/content.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +module Security + module PipelineExecutionPolicies + class Content + include Gitlab::Utils::StrongMemoize + + def initialize(content) + @content = content || {} + end + + def include + content[:include] || [] + end + strong_memoize_attr :include + + private + + attr_reader :content + end + end +end diff --git a/ee/lib/security/pipeline_execution_policies/pipeline_execution_policy.rb b/ee/lib/security/pipeline_execution_policies/pipeline_execution_policy.rb new file mode 100644 index 0000000000000000000000000000000000000000..94d0eeb919f96260246e5c5bfd578f2fe7627789 --- /dev/null +++ b/ee/lib/security/pipeline_execution_policies/pipeline_execution_policy.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +module Security + module PipelineExecutionPolicies + class PipelineExecutionPolicy < Security::BasePolicy + def pipeline_config_strategy + policy_content[:pipeline_config_strategy] + end + strong_memoize_attr :pipeline_config_strategy + + def suffix + policy_content[:suffix] + end + strong_memoize_attr :suffix + + def content + Security::PipelineExecutionPolicies::Content.new(policy_content[:content] || {}) + end + strong_memoize_attr :content + + def skip_ci + Security::PipelineExecutionPolicies::SkipCi.new(policy_content[:skip_ci] || {}) + end + strong_memoize_attr :skip_ci + + def variables_override + Security::PipelineExecutionPolicies::VariablesOverride.new(policy_content[:variables_override] || {}) + end + strong_memoize_attr :variables_override + + private + + def policy_content + policy_record.policy_content + end + end + end +end diff --git a/ee/lib/security/pipeline_execution_policies/skip_ci.rb b/ee/lib/security/pipeline_execution_policies/skip_ci.rb new file mode 100644 index 0000000000000000000000000000000000000000..eeb8eb3181b2bb2727f93ad33cef7aff0dbc8e72 --- /dev/null +++ b/ee/lib/security/pipeline_execution_policies/skip_ci.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +module Security + module PipelineExecutionPolicies + class SkipCi + include Gitlab::Utils::StrongMemoize + + def initialize(skip_ci) + @skip_ci = skip_ci || {} + end + + def allowed + skip_ci[:allowed] + end + + def allowlist + skip_ci[:allowlist] || {} + end + strong_memoize_attr :allowlist + + private + + attr_reader :skip_ci + end + end +end diff --git a/ee/lib/security/pipeline_execution_policies/variables_override.rb b/ee/lib/security/pipeline_execution_policies/variables_override.rb new file mode 100644 index 0000000000000000000000000000000000000000..adf4c171292e9d827b34809b8af0d80656750a87 --- /dev/null +++ b/ee/lib/security/pipeline_execution_policies/variables_override.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +module Security + module PipelineExecutionPolicies + class VariablesOverride + include Gitlab::Utils::StrongMemoize + + def initialize(variables_override) + @variables_override = variables_override || {} + end + + def allowed + variables_override[:allowed] + end + + def exceptions + variables_override[:exceptions] || [] + end + strong_memoize_attr :exceptions + + private + + attr_reader :variables_override + end + end +end diff --git a/ee/lib/security/pipeline_execution_schedule_policies/content.rb b/ee/lib/security/pipeline_execution_schedule_policies/content.rb new file mode 100644 index 0000000000000000000000000000000000000000..fbcb2e32499827ccbe7a6d6c1b202ff5b9bbee30 --- /dev/null +++ b/ee/lib/security/pipeline_execution_schedule_policies/content.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +module Security + module PipelineExecutionSchedulePolicies + class Content + include Gitlab::Utils::StrongMemoize + + def initialize(content) + @content = content || {} + end + + def include + content[:include] || [] + end + strong_memoize_attr :include + + private + + attr_reader :content + end + end +end diff --git a/ee/lib/security/pipeline_execution_schedule_policies/pipeline_execution_schedule_policy.rb b/ee/lib/security/pipeline_execution_schedule_policies/pipeline_execution_schedule_policy.rb new file mode 100644 index 0000000000000000000000000000000000000000..8621bc7c01533840f4e7b0378b76ababa2dcc1dd --- /dev/null +++ b/ee/lib/security/pipeline_execution_schedule_policies/pipeline_execution_schedule_policy.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module Security + module PipelineExecutionSchedulePolicies + class PipelineExecutionSchedulePolicy < Security::BasePolicy + def content + Security::PipelineExecutionSchedulePolicies::Content.new(policy_content[:content] || {}) + end + strong_memoize_attr :content + + def schedules + Security::PipelineExecutionSchedulePolicies::Schedules.new(policy_content[:schedules] || []) + end + strong_memoize_attr :schedules + + private + + def policy_content + policy_record.policy_content + end + end + end +end diff --git a/ee/lib/security/pipeline_execution_schedule_policies/schedule.rb b/ee/lib/security/pipeline_execution_schedule_policies/schedule.rb new file mode 100644 index 0000000000000000000000000000000000000000..1df582ac69c8cb76a4b8e2674ca0e665a4395e05 --- /dev/null +++ b/ee/lib/security/pipeline_execution_schedule_policies/schedule.rb @@ -0,0 +1,116 @@ +# frozen_string_literal: true + +module Security + module PipelineExecutionSchedulePolicies + class Schedule + include Gitlab::Utils::StrongMemoize + + def initialize(schedule) + @schedule = schedule || {} + end + + def type + schedule[:type] + end + + def start_time + schedule[:start_time] + end + + def timezone + schedule[:timezone] + end + + def branches + schedule[:branches] || [] + end + strong_memoize_attr :branches + + def time_window + schedule[:time_window] || {} + end + strong_memoize_attr :time_window + + def snooze + schedule[:snooze] || {} + end + strong_memoize_attr :snooze + + # Type-specific properties + def days + schedule[:days] || [] + end + strong_memoize_attr :days + + def days_of_month + schedule[:days_of_month] || [] + end + strong_memoize_attr :days_of_month + + # Time window helper methods + def time_window_value + time_window[:value] + end + strong_memoize_attr :time_window_value + + def time_window_distribution + time_window[:distribution] + end + strong_memoize_attr :time_window_distribution + + # Snooze helper methods + def snooze_until + snooze[:until] + end + strong_memoize_attr :snooze_until + + def snooze_reason + snooze[:reason] + end + strong_memoize_attr :snooze_reason + + # Type-specific helper methods + def daily_type? + type == 'daily' + end + + def weekly_type? + type == 'weekly' + end + + def monthly_type? + type == 'monthly' + end + + # Validation helper methods + def valid_daily_schedule? + daily_type? && start_time.present? && time_window.present? + end + + def valid_weekly_schedule? + weekly_type? && days.present? && start_time.present? && time_window.present? + end + + def valid_monthly_schedule? + monthly_type? && days_of_month.present? && start_time.present? && time_window.present? + end + + def valid_schedule? + case type + when 'daily' + valid_daily_schedule? + when 'weekly' + valid_weekly_schedule? + when 'monthly' + valid_monthly_schedule? + else + false + end + end + + private + + attr_reader :schedule + end + end +end diff --git a/ee/lib/security/pipeline_execution_schedule_policies/schedules.rb b/ee/lib/security/pipeline_execution_schedule_policies/schedules.rb new file mode 100644 index 0000000000000000000000000000000000000000..e5ff27c4535eeacafcff20d8252a372a05e014e2 --- /dev/null +++ b/ee/lib/security/pipeline_execution_schedule_policies/schedules.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +module Security + module PipelineExecutionSchedulePolicies + class Schedules + include Gitlab::Utils::StrongMemoize + + def initialize(schedules) + @schedules = schedules || [] + end + + def each(&block) + schedules.each(&block) + end + + def [](index) + Security::PipelineExecutionSchedulePolicies::Schedule.new(schedules[index]) + end + + def size + schedules.size + end + + def empty? + schedules.empty? + end + + def present? + schedules.present? + end + + private + + attr_reader :schedules + end + end +end diff --git a/ee/lib/security/policy_scope.rb b/ee/lib/security/policy_scope.rb new file mode 100644 index 0000000000000000000000000000000000000000..789372b77d65ee057f39bc0410322e63bcc6545d --- /dev/null +++ b/ee/lib/security/policy_scope.rb @@ -0,0 +1,97 @@ +# frozen_string_literal: true + +module Security + class PolicyScope + include Gitlab::Utils::StrongMemoize + + def initialize(policy_scope) + @policy_scope = policy_scope || {} + end + + def compliance_frameworks + @policy_scope[:compliance_frameworks] || [] + end + strong_memoize_attr :compliance_frameworks + + def projects + @policy_scope[:projects] || {} + end + strong_memoize_attr :projects + + def groups + @policy_scope[:groups] || {} + end + strong_memoize_attr :groups + + # Project-related helper methods + def project_ids_including + projects[:including]&.pluck(:id) || [] + end + strong_memoize_attr :project_ids_including + + def project_ids_excluding + projects[:excluding]&.pluck(:id) || [] + end + strong_memoize_attr :project_ids_excluding + + def has_project_inclusions? + project_ids_including.present? + end + strong_memoize_attr :has_project_inclusions? + + def has_project_exclusions? + project_ids_excluding.present? + end + strong_memoize_attr :has_project_exclusions? + + # Group-related helper methods + def group_ids_including + groups[:including]&.pluck(:id) || [] + end + strong_memoize_attr :group_ids_including + + def group_ids_excluding + groups[:excluding]&.pluck(:id) || [] + end + strong_memoize_attr :group_ids_excluding + + def has_group_inclusions? + group_ids_including.present? + end + strong_memoize_attr :has_group_inclusions? + + def has_group_exclusions? + group_ids_excluding.present? + end + strong_memoize_attr :has_group_exclusions? + + def compliance_framework_ids + # rubocop:disable CodeReuse/ActiveRecord -- pluck used on hash + compliance_frameworks.pluck(:id) + # rubocop:enable CodeReuse/ActiveRecord + end + strong_memoize_attr :compliance_framework_ids + + def has_compliance_frameworks? + compliance_frameworks.present? + end + strong_memoize_attr :has_compliance_frameworks? + + # Validation helper methods + def empty? + !has_project_inclusions? && !has_project_exclusions? && + !has_group_inclusions? && !has_group_exclusions? && + !has_compliance_frameworks? + end + strong_memoize_attr :empty? + + def present? + !empty? + end + strong_memoize_attr :present? + + private + + attr_reader :policy_scope + end +end diff --git a/ee/lib/security/scan_execution_policies/actions.rb b/ee/lib/security/scan_execution_policies/actions.rb new file mode 100644 index 0000000000000000000000000000000000000000..ed1f2afc321ce1d5a42db14dbe855ad712e551ae --- /dev/null +++ b/ee/lib/security/scan_execution_policies/actions.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +module Security + module ScanExecutionPolicies + class Actions + include Gitlab::Utils::StrongMemoize + + def initialize(actions) + @actions = actions || [] + end + + def each(&block) + actions.each(&block) + end + + def [](index) + actions[index] + end + + def size + actions.size + end + + def empty? + actions.empty? + end + + def present? + actions.present? + end + + private + + attr_reader :actions + end + end +end diff --git a/ee/lib/security/scan_execution_policies/rule.rb b/ee/lib/security/scan_execution_policies/rule.rb new file mode 100644 index 0000000000000000000000000000000000000000..c3080c110096110f47b637ad6b73540139e926db --- /dev/null +++ b/ee/lib/security/scan_execution_policies/rule.rb @@ -0,0 +1,112 @@ +# frozen_string_literal: true + +module Security + module ScanExecutionPolicies + class Rule + include Gitlab::Utils::StrongMemoize + + def initialize(rule) + @rule = rule || {} + end + + def type + rule[:type] + end + strong_memoize_attr :type + + def branches + rule[:branches] + end + strong_memoize_attr :branches + + def branch_type + rule[:branch_type] + end + strong_memoize_attr :branch_type + + def cadence + rule[:cadence] + end + strong_memoize_attr :cadence + + def timezone + rule[:timezone] + end + strong_memoize_attr :timezone + + def pipeline_sources + rule[:pipeline_sources] + end + strong_memoize_attr :pipeline_sources + + def branch_exceptions + rule[:branch_exceptions] || [] + end + strong_memoize_attr :branch_exceptions + + def time_window + rule[:time_window] || {} + end + strong_memoize_attr :time_window + + def agents + rule[:agents] || {} + end + strong_memoize_attr :agents + + # Helper methods for time window properties + def time_window_distribution + time_window[:distribution] + end + strong_memoize_attr :time_window_distribution + + def time_window_value + time_window[:value] + end + strong_memoize_attr :time_window_value + + # Helper methods for agents + def agent_namespaces + agents.each_value.first&.dig(:namespaces) || [] + end + strong_memoize_attr :agent_namespaces + + # Helper methods for pipeline sources + def pipeline_sources_including + pipeline_sources&.dig(:including) || [] + end + strong_memoize_attr :pipeline_sources_including + + # Type-specific helper methods + def pipeline_type? + type == 'pipeline' + end + + def schedule_type? + type == 'schedule' + end + + # Branch-related helper methods + def has_branches? + branches.present? + end + + def has_branch_type? + branch_type.present? + end + + # Validation helper methods + def valid_pipeline_rule? + pipeline_type? && (has_branches? || has_branch_type?) + end + + def valid_schedule_rule? + schedule_type? && cadence.present? + end + + private + + attr_reader :rule + end + end +end diff --git a/ee/lib/security/scan_execution_policies/rules.rb b/ee/lib/security/scan_execution_policies/rules.rb new file mode 100644 index 0000000000000000000000000000000000000000..b72d64c30a6a9d70fb2ed7ce6d0319b66d13ad84 --- /dev/null +++ b/ee/lib/security/scan_execution_policies/rules.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +module Security + module ScanExecutionPolicies + class Rules + include Gitlab::Utils::StrongMemoize + + def initialize(rules) + @rules = rules || [] + end + + def each(&block) + rules.each(&block) + end + + def [](index) + Security::ScanExecutionPolicies::Rule.new(rules[index]) + end + + def size + rules.size + end + + def empty? + rules.empty? + end + + def present? + rules.present? + end + + private + + attr_reader :rules + end + end +end diff --git a/ee/lib/security/scan_execution_policies/scan_execution_policy.rb b/ee/lib/security/scan_execution_policies/scan_execution_policy.rb new file mode 100644 index 0000000000000000000000000000000000000000..c18d8ec049214562f11f3a28bf7f1004faef47d2 --- /dev/null +++ b/ee/lib/security/scan_execution_policies/scan_execution_policy.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +module Security + module ScanExecutionPolicies + class ScanExecutionPolicy < Security::BasePolicy + def actions + Security::ScanExecutionPolicies::Actions.new(policy_content[:actions] || []) + end + strong_memoize_attr :actions + + def rules + Security::ScanExecutionPolicies::Rules.new(policy_content[:rules] || []) + end + strong_memoize_attr :rules + + def skip_ci + Security::ScanExecutionPolicies::SkipCi.new(policy_content[:skip_ci] || {}) + end + strong_memoize_attr :skip_ci + + private + + def policy_content + policy_record.policy_content + end + end + end +end diff --git a/ee/lib/security/scan_execution_policies/skip_ci.rb b/ee/lib/security/scan_execution_policies/skip_ci.rb new file mode 100644 index 0000000000000000000000000000000000000000..eee381ddbf3176def5c2a4f28c17e252b34539a4 --- /dev/null +++ b/ee/lib/security/scan_execution_policies/skip_ci.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +module Security + module ScanExecutionPolicies + class SkipCi + include Gitlab::Utils::StrongMemoize + + def initialize(skip_ci) + @skip_ci = skip_ci || {} + end + + def allowed + skip_ci[:allowed] + end + + def allowlist + skip_ci[:allowlist] || {} + end + strong_memoize_attr :allowlist + + private + + attr_reader :skip_ci + end + end +end diff --git a/ee/lib/security/scan_result_policies/action.rb b/ee/lib/security/scan_result_policies/action.rb new file mode 100644 index 0000000000000000000000000000000000000000..0f13f2eccba00d834f17240ed6331b079d78baa9 --- /dev/null +++ b/ee/lib/security/scan_result_policies/action.rb @@ -0,0 +1,54 @@ +# frozen_string_literal: true + +module Security + module ScanResultPolicies + class Action + include Gitlab::Utils::StrongMemoize + + def initialize(action) + @action = action || {} + end + + def type + action[:type] + end + + def approvals_required + action[:approvals_required] + end + + def enabled + action[:enabled] + end + + def user_approvers + action[:user_approvers] || [] + end + strong_memoize_attr :user_approvers + + def user_approvers_ids + action[:user_approvers_ids] || [] + end + strong_memoize_attr :user_approvers_ids + + def group_approvers + action[:group_approvers] || [] + end + strong_memoize_attr :group_approvers + + def group_approvers_ids + action[:group_approvers_ids] || [] + end + strong_memoize_attr :group_approvers_ids + + def role_approvers + action[:role_approvers] || [] + end + strong_memoize_attr :role_approvers + + private + + attr_reader :action + end + end +end diff --git a/ee/lib/security/scan_result_policies/actions.rb b/ee/lib/security/scan_result_policies/actions.rb new file mode 100644 index 0000000000000000000000000000000000000000..af498de2b1deeee16572dc8a4a03f51ce32a3d84 --- /dev/null +++ b/ee/lib/security/scan_result_policies/actions.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: true + +module Security + module ScanResultPolicies + class Actions + include Gitlab::Utils::StrongMemoize + + def initialize(actions) + @actions = actions || [] + end + + def require_approval_actions + actions.select { |action| action.type == 'require_approval' } + end + strong_memoize_attr :require_approval_actions + + def send_bot_message_actions + actions.select { |action| action.type == 'send_bot_message' } + end + strong_memoize_attr :send_bot_message_actions + + def each(&block) + actions.each(&block) + end + + def [](index) + Security::ScanResultPolicies::Action.new(actions[index]) + end + + def size + actions.size + end + + def empty? + actions.empty? + end + + def present? + actions.present? + end + + private + + attr_reader :actions + end + end +end diff --git a/ee/lib/security/scan_result_policies/approval_policy.rb b/ee/lib/security/scan_result_policies/approval_policy.rb new file mode 100644 index 0000000000000000000000000000000000000000..3020b6f9627a1acc081944fa16deebd0a6895a6d --- /dev/null +++ b/ee/lib/security/scan_result_policies/approval_policy.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +module Security + module ScanResultPolicies + class ApprovalPolicy < Security::BasePolicy + def fallback_behavior + policy_content[:fallback_behavior] + end + strong_memoize_attr :fallback_behavior + + def policy_tuning + policy_content[:policy_tuning] + end + strong_memoize_attr :policy_tuning + + def bypass_settings + policy_content[:bypass_settings] + end + strong_memoize_attr :bypass_settings + + def actions + Security::ScanResultPolicies::Actions.new(policy_content[:actions] || []) + end + strong_memoize_attr :actions + + def approval_settings + Security::ScanResultPolicies::ApprovalSettings.new(policy_content[:approval_settings] || {}) + end + strong_memoize_attr :approval_settings + + def rules + Security::ScanResultPolicies::Rules.new(policy_content[:rules] || []) + end + strong_memoize_attr :rules + + private + + def policy_content + policy_record.policy_content + end + end + end +end diff --git a/ee/lib/security/scan_result_policies/approval_settings.rb b/ee/lib/security/scan_result_policies/approval_settings.rb new file mode 100644 index 0000000000000000000000000000000000000000..1f29ae36ba255122ffb7781286f26cc18a565c5c --- /dev/null +++ b/ee/lib/security/scan_result_policies/approval_settings.rb @@ -0,0 +1,46 @@ +# frozen_string_literal: true + +module Security + module ScanResultPolicies + class ApprovalSettings + include Gitlab::Utils::StrongMemoize + + def initialize(approval_settings) + @approval_settings = approval_settings || {} + end + + def prevent_approval_by_author + approval_settings[:prevent_approval_by_author] + end + + def prevent_approval_by_commit_author + approval_settings[:prevent_approval_by_commit_author] + end + + def remove_approvals_with_new_commit + approval_settings[:remove_approvals_with_new_commit] + end + + def require_password_to_approve + approval_settings[:require_password_to_approve] + end + + def block_branch_modification + approval_settings[:block_branch_modification] + end + + def prevent_pushing_and_force_pushing + approval_settings[:prevent_pushing_and_force_pushing] + end + + def block_group_branch_modification + approval_settings[:block_group_branch_modification] || {} + end + strong_memoize_attr :block_group_branch_modification + + private + + attr_reader :approval_settings + end + end +end diff --git a/ee/lib/security/scan_result_policies/fallback_behavior.rb b/ee/lib/security/scan_result_policies/fallback_behavior.rb new file mode 100644 index 0000000000000000000000000000000000000000..bf79c2bd697123dae35837d6da8e02333faee450 --- /dev/null +++ b/ee/lib/security/scan_result_policies/fallback_behavior.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +module Security + module ScanResultPolicies + class FallbackBehavior + include Gitlab::Utils::StrongMemoize + + def initialize(fallback_behavior) + @fallback_behavior = fallback_behavior || {} + end + + def fail + fallback_behavior[:fail] + end + + private + + attr_reader :fallback_behavior + end + end +end diff --git a/ee/lib/security/scan_result_policies/policy_tuning.rb b/ee/lib/security/scan_result_policies/policy_tuning.rb index 2ed51d8a30bfe3d86f0de0eb4be803d50a2ffdbd..95754e3920435a322a299b97cc22c7ea78be55a5 100644 --- a/ee/lib/security/scan_result_policies/policy_tuning.rb +++ b/ee/lib/security/scan_result_policies/policy_tuning.rb @@ -11,6 +11,10 @@ def security_report_time_window policy_tuning[:security_report_time_window] end + def unblock_rules_using_execution_policies + policy_tuning[:unblock_rules_using_execution_policies] + end + private attr_reader :policy_tuning diff --git a/ee/lib/security/scan_result_policies/rule.rb b/ee/lib/security/scan_result_policies/rule.rb new file mode 100644 index 0000000000000000000000000000000000000000..241bc8f694a618adf220099cbc1622a2cf14396e --- /dev/null +++ b/ee/lib/security/scan_result_policies/rule.rb @@ -0,0 +1,84 @@ +# frozen_string_literal: true + +module Security + module ScanResultPolicies + class Rule + include Gitlab::Utils::StrongMemoize + + def initialize(rule) + @rule = rule || {} + end + + def type + rule[:type] + end + + def branches + rule[:branches] + end + + def branch_type + rule[:branch_type] + end + + def scanners + rule[:scanners] + end + + def vulnerabilities_allowed + rule[:vulnerabilities_allowed] + end + + def severity_levels + rule[:severity_levels] + end + + def vulnerability_states + rule[:vulnerability_states] + end + + def commits + rule[:commits] + end + + def branch_exceptions + rule[:branch_exceptions] || [] + end + strong_memoize_attr :branch_exceptions + + def vulnerability_attributes + rule[:vulnerability_attributes] || {} + end + strong_memoize_attr :vulnerability_attributes + + def vulnerability_age + rule[:vulnerability_age] || {} + end + strong_memoize_attr :vulnerability_age + + def match_on_inclusion_license + rule[:match_on_inclusion_license] + end + strong_memoize_attr :match_on_inclusion_license + + def license_types + rule[:license_types] || [] + end + strong_memoize_attr :license_types + + def license_states + rule[:license_states] || [] + end + strong_memoize_attr :license_states + + def licenses + rule[:licenses] || {} + end + strong_memoize_attr :licenses + + private + + attr_reader :rule + end + end +end diff --git a/ee/lib/security/scan_result_policies/rules.rb b/ee/lib/security/scan_result_policies/rules.rb new file mode 100644 index 0000000000000000000000000000000000000000..003f943637c2058f3dbaaaf78a5d1bd36c439998 --- /dev/null +++ b/ee/lib/security/scan_result_policies/rules.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +module Security + module ScanResultPolicies + class Rules + include Gitlab::Utils::StrongMemoize + + def initialize(rules) + @rules = rules || [] + end + + def each(&block) + rules.each(&block) + end + + def [](index) + Security::ScanResultPolicies::Rule.new(rules[index]) + end + + def size + rules.size + end + + def empty? + rules.empty? + end + + def present? + rules.present? + end + + private + + attr_reader :rules + end + end +end diff --git a/ee/lib/security/vulnerability_management_policies/actions.rb b/ee/lib/security/vulnerability_management_policies/actions.rb new file mode 100644 index 0000000000000000000000000000000000000000..ed1154a9d64f7d90ed8532d33f5272acde1b4b90 --- /dev/null +++ b/ee/lib/security/vulnerability_management_policies/actions.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +module Security + module VulnerabilityManagementPolicies + class Actions + include Gitlab::Utils::StrongMemoize + + def initialize(actions) + @actions = actions || [] + end + + def each(&block) + actions.each(&block) + end + + def [](index) + actions[index] + end + + def size + actions.size + end + + def empty? + actions.empty? + end + + def present? + actions.present? + end + + private + + attr_reader :actions + end + end +end diff --git a/ee/lib/security/vulnerability_management_policies/rule.rb b/ee/lib/security/vulnerability_management_policies/rule.rb new file mode 100644 index 0000000000000000000000000000000000000000..0a4c31b8412b4e4c6b83f79600691ade9022c9f9 --- /dev/null +++ b/ee/lib/security/vulnerability_management_policies/rule.rb @@ -0,0 +1,96 @@ +# frozen_string_literal: true + +module Security + module VulnerabilityManagementPolicies + class Rule + include Gitlab::Utils::StrongMemoize + + def initialize(rule) + @rule = rule || {} + end + + def type + rule[:type] + end + strong_memoize_attr :type + + def scanners + rule[:scanners] + end + strong_memoize_attr :scanners + + def severity_levels + rule[:severity_levels] + end + strong_memoize_attr :severity_levels + + # Type-specific helper methods + def no_longer_detected_type? + type == 'no_longer_detected' + end + + # Scanner-related helper methods + def has_sast_scanner? + scanners.include?('sast') + end + + def has_secret_detection_scanner? + scanners.include?('secret_detection') + end + + def has_dependency_scanning_scanner? + scanners.include?('dependency_scanning') + end + + def has_container_scanning_scanner? + scanners.include?('container_scanning') + end + + def has_dast_scanner? + scanners.include?('dast') + end + + def has_coverage_fuzzing_scanner? + scanners.include?('coverage_fuzzing') + end + + def has_api_fuzzing_scanner? + scanners.include?('api_fuzzing') + end + + # Severity-related helper methods + def has_critical_severity? + severity_levels.include?('critical') + end + + def has_high_severity? + severity_levels.include?('high') + end + + def has_medium_severity? + severity_levels.include?('medium') + end + + def has_low_severity? + severity_levels.include?('low') + end + + def has_info_severity? + severity_levels.include?('info') + end + + def has_unknown_severity? + severity_levels.include?('unknown') + end + + # Validation helper methods + def valid_rule? + type.present? && scanners.present? && severity_levels.present? + end + + private + + attr_reader :rule + end + end +end diff --git a/ee/lib/security/vulnerability_management_policies/rules.rb b/ee/lib/security/vulnerability_management_policies/rules.rb new file mode 100644 index 0000000000000000000000000000000000000000..da5eb88d782b195ec9d9aa32f8afc55680c34111 --- /dev/null +++ b/ee/lib/security/vulnerability_management_policies/rules.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +module Security + module VulnerabilityManagementPolicies + class Rules + include Gitlab::Utils::StrongMemoize + + def initialize(rules) + @rules = rules || [] + end + + def each(&block) + rules.each(&block) + end + + def [](index) + Security::VulnerabilityManagementPolicies::Rule.new(rules[index]) + end + + def size + rules.size + end + + def empty? + rules.empty? + end + + def present? + rules.present? + end + + private + + attr_reader :rules + end + end +end diff --git a/ee/lib/security/vulnerability_management_policies/vulnerability_management_policy.rb b/ee/lib/security/vulnerability_management_policies/vulnerability_management_policy.rb new file mode 100644 index 0000000000000000000000000000000000000000..d50b8f1679c6617d4a10736005743ffc0ac82dd5 --- /dev/null +++ b/ee/lib/security/vulnerability_management_policies/vulnerability_management_policy.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module Security + module VulnerabilityManagementPolicies + class VulnerabilityManagementPolicy < Security::BasePolicy + def actions + Security::VulnerabilityManagementPolicies::Actions.new(policy_content[:actions] || []) + end + strong_memoize_attr :actions + + def rules + Security::VulnerabilityManagementPolicies::Rules.new(policy_content[:rules] || []) + end + strong_memoize_attr :rules + + private + + def policy_content + policy_record.policy_content + end + end + end +end