From 1a0cbaa828bfa5029036994c576580b733f9e64f Mon Sep 17 00:00:00 2001 From: Sashi Kumar Kumaresan Date: Fri, 1 Aug 2025 12:58:55 +0200 Subject: [PATCH] Add security policy bypass push option This change adds security policy bypass reason push option to provide a reason before bypassing branch protection. EE: true Changelog: added --- ee/lib/ee/gitlab/checks/security/policy_check.rb | 5 ++++- .../scan_result_policies/policy_bypass_checker.rb | 5 +++-- .../security/scan_result_policies/push_bypass_checker.rb | 8 +++++--- .../scan_result_policies/policy_bypass_checker_spec.rb | 6 +++++- .../scan_result_policies/push_bypass_checker_spec.rb | 9 ++++++++- lib/gitlab/checks/changes_access.rb | 3 ++- lib/gitlab/checks/single_change_access.rb | 6 +++--- lib/gitlab/push_options.rb | 3 +++ .../single_change_access_checks_shared_context.rb | 3 ++- 9 files changed, 35 insertions(+), 13 deletions(-) diff --git a/ee/lib/ee/gitlab/checks/security/policy_check.rb b/ee/lib/ee/gitlab/checks/security/policy_check.rb index 026451474178a5..d97d885877fb26 100644 --- a/ee/lib/ee/gitlab/checks/security/policy_check.rb +++ b/ee/lib/ee/gitlab/checks/security/policy_check.rb @@ -35,7 +35,10 @@ def branch_name_affected_by_policy? def policies_bypass_applied? ::Security::ScanResultPolicies::PushBypassChecker.new( - project: project, user_access: user_access, branch_name: branch_name + project: project, + user_access: user_access, + branch_name: branch_name, + push_options: change_access.push_options ).check_bypass! end diff --git a/ee/lib/security/scan_result_policies/policy_bypass_checker.rb b/ee/lib/security/scan_result_policies/policy_bypass_checker.rb index 7f2a4bd0ba7b81..7243f5873408b1 100644 --- a/ee/lib/security/scan_result_policies/policy_bypass_checker.rb +++ b/ee/lib/security/scan_result_policies/policy_bypass_checker.rb @@ -5,11 +5,12 @@ module ScanResultPolicies class PolicyBypassChecker include Gitlab::Utils::StrongMemoize - def initialize(security_policy:, project:, user_access:, branch_name:) + def initialize(security_policy:, project:, user_access:, branch_name:, push_options:) @security_policy = security_policy @project = project @user = user_access.user @branch_name = branch_name + @push_options = push_options end def bypass_allowed? @@ -44,7 +45,7 @@ def bypass_with_service_account? private - attr_reader :security_policy, :project, :user, :branch_name + attr_reader :security_policy, :project, :user, :branch_name, :push_options def log_bypass_audit!(type, id) message = <<~MSG.squish diff --git a/ee/lib/security/scan_result_policies/push_bypass_checker.rb b/ee/lib/security/scan_result_policies/push_bypass_checker.rb index 92d6dc600869d0..77a828e9ad3024 100644 --- a/ee/lib/security/scan_result_policies/push_bypass_checker.rb +++ b/ee/lib/security/scan_result_policies/push_bypass_checker.rb @@ -5,10 +5,11 @@ module ScanResultPolicies class PushBypassChecker include Gitlab::Utils::StrongMemoize - def initialize(project:, user_access:, branch_name:) + def initialize(project:, user_access:, branch_name:, push_options:) @project = project @user_access = user_access @branch_name = branch_name + @push_options = push_options end def check_bypass! @@ -22,14 +23,15 @@ def check_bypass! private - attr_reader :project, :user_access, :branch_name + attr_reader :project, :user_access, :branch_name, :push_options def bypass_allowed?(policy) Security::ScanResultPolicies::PolicyBypassChecker.new( security_policy: policy, project: project, user_access: user_access, - branch_name: branch_name + branch_name: branch_name, + push_options: push_options ).bypass_allowed? end end diff --git a/ee/spec/lib/security/scan_result_policies/policy_bypass_checker_spec.rb b/ee/spec/lib/security/scan_result_policies/policy_bypass_checker_spec.rb index a96bfa1482322e..fd6272242562bb 100644 --- a/ee/spec/lib/security/scan_result_policies/policy_bypass_checker_spec.rb +++ b/ee/spec/lib/security/scan_result_policies/policy_bypass_checker_spec.rb @@ -18,7 +18,11 @@ describe '#bypass_allowed?' do subject(:bypass_allowed?) do described_class.new( - security_policy: security_policy, project: project, user_access: user_access, branch_name: branch_name + security_policy: security_policy, + project: project, + user_access: user_access, + branch_name: branch_name, + push_options: {} ).bypass_allowed? end diff --git a/ee/spec/lib/security/scan_result_policies/push_bypass_checker_spec.rb b/ee/spec/lib/security/scan_result_policies/push_bypass_checker_spec.rb index 302a507c2bf88f..7a1802d41e2e37 100644 --- a/ee/spec/lib/security/scan_result_policies/push_bypass_checker_spec.rb +++ b/ee/spec/lib/security/scan_result_policies/push_bypass_checker_spec.rb @@ -8,7 +8,14 @@ let_it_be(:branch_name) { 'main' } let_it_be(:user) { create(:user, :project_bot) } let_it_be(:user_access) { Gitlab::UserAccess.new(user, container: project) } - let_it_be(:checker) { described_class.new(project: project, user_access: user_access, branch_name: branch_name) } + let_it_be(:checker) do + described_class.new( + project: project, + user_access: user_access, + branch_name: branch_name, + push_options: {} + ) + end describe '#check_bypass!' do context 'when the feature is not available' do diff --git a/lib/gitlab/checks/changes_access.rb b/lib/gitlab/checks/changes_access.rb index 4b0ba12de51198..0e5eaa7761c42b 100644 --- a/lib/gitlab/checks/changes_access.rb +++ b/lib/gitlab/checks/changes_access.rb @@ -104,7 +104,8 @@ def single_change_accesses protocol: protocol, logger: logger, commits: commits, - gitaly_context: gitaly_context + gitaly_context: gitaly_context, + push_options: push_options ) end end diff --git a/lib/gitlab/checks/single_change_access.rb b/lib/gitlab/checks/single_change_access.rb index 0640af9e6253e9..7d1822f5a8e1ff 100644 --- a/lib/gitlab/checks/single_change_access.rb +++ b/lib/gitlab/checks/single_change_access.rb @@ -5,13 +5,13 @@ module Checks class SingleChangeAccess ATTRIBUTES = %i[user_access project skip_authorization protocol oldrev newrev ref - branch_name tag_name logger commits gitaly_context].freeze + branch_name tag_name logger commits gitaly_context push_options].freeze attr_reader(*ATTRIBUTES) def initialize( change, user_access:, project:, - protocol:, logger:, commits: nil, gitaly_context: nil + protocol:, logger:, commits: nil, gitaly_context: nil, push_options: nil ) @oldrev, @newrev, @ref = change.values_at(:oldrev, :newrev, :ref) @branch_ref = Gitlab::Git.branch_ref?(@ref) @@ -23,7 +23,7 @@ def initialize( @protocol = protocol @commits = commits @gitaly_context = gitaly_context - + @push_options = push_options @logger = logger @logger.append_message("Running checks for ref: #{@branch_name || @tag_name}") end diff --git a/lib/gitlab/push_options.rb b/lib/gitlab/push_options.rb index d218b9c1177f8a..d7656e01e6cfa9 100644 --- a/lib/gitlab/push_options.rb +++ b/lib/gitlab/push_options.rb @@ -33,6 +33,9 @@ class PushOptions }, secret_push_protection: { keys: [:skip_all] + }, + security_policy: { + keys: [:bypass_reason] } }).freeze diff --git a/spec/support/shared_contexts/single_change_access_checks_shared_context.rb b/spec/support/shared_contexts/single_change_access_checks_shared_context.rb index 5945302824b8eb..26735d894d0ff8 100644 --- a/spec/support/shared_contexts/single_change_access_checks_shared_context.rb +++ b/spec/support/shared_contexts/single_change_access_checks_shared_context.rb @@ -17,7 +17,8 @@ project: project, user_access: user_access, protocol: protocol, - logger: logger + logger: logger, + push_options: Gitlab::PushOptions.new([]) ) end -- GitLab