diff --git a/app/events/project_authorizations/authorizations_added_event.rb b/app/events/project_authorizations/authorizations_added_event.rb
new file mode 100644
index 0000000000000000000000000000000000000000..521a862218d307e53db6fb4a5d833e152e2957d9
--- /dev/null
+++ b/app/events/project_authorizations/authorizations_added_event.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+module ProjectAuthorizations
+  class AuthorizationsAddedEvent < ::Gitlab::EventStore::Event
+    def schema
+      {
+        'type' => 'object',
+        'required' => %w[project_id user_ids],
+        'properties' => {
+          'project_id' => { 'type' => 'integer' },
+          'user_ids' => { 'type' => 'array' }
+        }
+      }
+    end
+  end
+end
diff --git a/app/models/project_authorizations/changes.rb b/app/models/project_authorizations/changes.rb
index ac52bdfdb07dc75937a294a6f12b5bc7a2e68e7d..256207d2cc74aa0bed2863e754cc6ec98cd3a3cf 100644
--- a/app/models/project_authorizations/changes.rb
+++ b/app/models/project_authorizations/changes.rb
@@ -21,6 +21,7 @@ def initialize
       @authorizations_to_add = []
       @affected_project_ids = Set.new
       @removed_user_ids = Set.new
+      @added_user_ids = Set.new
       yield self
     end
 
@@ -61,6 +62,7 @@ def should_delete_authorizations_for_project?
     def add_authorizations
       insert_all_in_batches(authorizations_to_add)
       @affected_project_ids += authorizations_to_add.pluck(:project_id)
+      @added_user_ids += authorizations_to_add.pluck(:user_id)
     end
 
     def delete_authorizations_for_user
@@ -139,23 +141,52 @@ def user_ids
     end
 
     def publish_events
+      publish_changed_event
+      publish_removed_event
+      publish_added_event
+    end
+
+    def publish_changed_event
+      # This event is used to add policy approvers to approval rules by re-syncing all project policies which is costly.
+      # If the feature flag below is enabled, the policies won't be re-synced and
+      # the approvers will be added via `AuthorizationsAddedEvent`.
+      return if ::Feature.enabled?(:add_policy_approvers_to_rules)
+
       @affected_project_ids.each do |project_id|
         ::Gitlab::EventStore.publish(
           ::ProjectAuthorizations::AuthorizationsChangedEvent.new(data: { project_id: project_id })
         )
       end
-      return if ::Feature.disabled?(:user_approval_rules_removal) || @removed_user_ids.blank?
+    end
 
-      @affected_project_ids.each do |project_id|
-        @removed_user_ids.to_a.each_slice(EVENT_USER_BATCH_SIZE).each do |user_ids_batch|
-          ::Gitlab::EventStore.publish(
-            ::ProjectAuthorizations::AuthorizationsRemovedEvent.new(data: {
-              project_id: project_id,
-              user_ids: user_ids_batch
-            })
-          )
+    def publish_removed_event
+      return if ::Feature.disabled?(:user_approval_rules_removal)
+      return if @removed_user_ids.none?
+
+      events = @affected_project_ids.flat_map do |project_id|
+        @removed_user_ids.to_a.each_slice(EVENT_USER_BATCH_SIZE).map do |user_ids_batch|
+          ::ProjectAuthorizations::AuthorizationsRemovedEvent.new(data: {
+            project_id: project_id,
+            user_ids: user_ids_batch
+          })
+        end
+      end
+      ::Gitlab::EventStore.publish_group(events)
+    end
+
+    def publish_added_event
+      return if ::Feature.disabled?(:add_policy_approvers_to_rules)
+      return if @added_user_ids.none?
+
+      events = @affected_project_ids.flat_map do |project_id|
+        @added_user_ids.to_a.each_slice(EVENT_USER_BATCH_SIZE).map do |user_ids_batch|
+          ::ProjectAuthorizations::AuthorizationsAddedEvent.new(data: {
+            project_id: project_id,
+            user_ids: user_ids_batch
+          })
         end
       end
+      ::Gitlab::EventStore.publish_group(events)
     end
   end
 end
diff --git a/config/feature_flags/development/add_policy_approvers_to_rules.yml b/config/feature_flags/development/add_policy_approvers_to_rules.yml
new file mode 100644
index 0000000000000000000000000000000000000000..1d5584c518141a991afd4bd52d06b108b3576dd1
--- /dev/null
+++ b/config/feature_flags/development/add_policy_approvers_to_rules.yml
@@ -0,0 +1,8 @@
+---
+name: add_policy_approvers_to_rules
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/138809
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/434385
+milestone: '16.8'
+type: development
+group: group::security policies
+default_enabled: false
diff --git a/config/sidekiq_queues.yml b/config/sidekiq_queues.yml
index 34a41c39f3120d8443fbe14585fd62b79ee7f119..fc36a9d0b045a96a21d4a25e77b577a2e9a04b45 100644
--- a/config/sidekiq_queues.yml
+++ b/config/sidekiq_queues.yml
@@ -681,6 +681,8 @@
   - 1
 - - security_scan_execution_policies_rule_schedule
   - 1
+- - security_scan_result_policies_add_approvers_to_rules
+  - 1
 - - security_scan_result_policies_sync_any_merge_request_approval_rules
   - 1
 - - security_scan_result_policies_sync_project
diff --git a/ee/app/models/approval_project_rule.rb b/ee/app/models/approval_project_rule.rb
index 016f5b22a5019ec6ae406dee1f068f9665faa394..63543c554432c327c2a2a2547b41fe2c4c8e9c29 100644
--- a/ee/app/models/approval_project_rule.rb
+++ b/ee/app/models/approval_project_rule.rb
@@ -38,6 +38,7 @@ class ApprovalProjectRule < ApplicationRecord
   scope :report_approver_without_scan_finding, -> { report_approver.where.not(report_type: [:scan_finding, :license_scanning]) }
   scope :for_all_branches, -> { where.missing(:protected_branches).where(applies_to_all_protected_branches: false) }
   scope :for_all_protected_branches, -> { where.missing(:protected_branches).where(applies_to_all_protected_branches: true) }
+  scope :for_project, ->(project_id) { where(project_id: project_id) }
 
   alias_method :code_owner, :code_owner?
 
diff --git a/ee/app/models/concerns/security/scan_result_policy.rb b/ee/app/models/concerns/security/scan_result_policy.rb
index 715b19695c75e545d4025b1bfaeeb1d93be1ad75..a079a15b7a8f6341ba509e70fe87c5612e4e878e 100644
--- a/ee/app/models/concerns/security/scan_result_policy.rb
+++ b/ee/app/models/concerns/security/scan_result_policy.rb
@@ -65,6 +65,13 @@ def active_scan_result_policies
         scan_result_policies&.select { |config| config[:enabled] }&.first(LIMIT)
       end
 
+      def applicable_scan_result_policies_for_project(project)
+        strong_memoize_with(:applicable_scan_result_policies_for_project, project) do
+          policy_scope_service = ::Security::SecurityOrchestrationPolicies::PolicyScopeService.new(project: project)
+          active_scan_result_policies.select { |policy| policy_scope_service.policy_applicable?(policy) }
+        end
+      end
+
       def scan_result_policies
         policy_by_type(:scan_result_policy)
       end
diff --git a/ee/app/services/security/scan_result_policies/add_approvers_to_rules_service.rb b/ee/app/services/security/scan_result_policies/add_approvers_to_rules_service.rb
new file mode 100644
index 0000000000000000000000000000000000000000..1e8f6469e848a4237d39ae161d952f2c06932f58
--- /dev/null
+++ b/ee/app/services/security/scan_result_policies/add_approvers_to_rules_service.rb
@@ -0,0 +1,107 @@
+# frozen_string_literal: true
+
+module Security
+  module ScanResultPolicies
+    class AddApproversToRulesService < BaseProjectService
+      INSERT_BATCH_SIZE = 100
+      APPROVAL_RULES_ATTRIBUTES = %i[id orchestration_policy_idx].freeze
+
+      def execute(user_ids)
+        params = build_bulk_insert_params(user_ids)
+        insert_users_to_approval_rules(params)
+      end
+
+      private
+
+      def build_bulk_insert_params(user_ids)
+        project_usernames = username_map(user_ids)
+
+        project.all_security_orchestration_policy_configurations.flat_map do |configuration|
+          policies = configuration.applicable_scan_result_policies_for_project(project)
+          next if policies.blank?
+
+          policy_indexes_for_users = policy_indexes_for_users(user_ids, policies, project_usernames)
+          referenced_policy_indexes = policy_indexes_for_users.values.flatten.uniq
+          next if referenced_policy_indexes.none?
+
+          build_approval_rules_for_users(configuration, policy_indexes_for_users, referenced_policy_indexes)
+        end.compact
+      end
+
+      def build_approval_rules_for_users(configuration, policy_indexes_for_users, referenced_policy_indexes)
+        project_rules = rules_by_idx(approval_project_rules(configuration, referenced_policy_indexes))
+        merge_request_rules = rules_by_idx(approval_merge_request_rules(configuration, referenced_policy_indexes))
+
+        policy_indexes_for_users.map do |user_id, policy_indexes|
+          project_rule_ids = project_rules.values_at(*policy_indexes).compact
+          merge_request_rule_ids = merge_request_rules.values_at(*policy_indexes).compact
+
+          build_user_approval_rules_params(user_id, project_rule_ids, merge_request_rule_ids)
+        end
+      end
+
+      def rules_by_idx(approval_rules)
+        approval_rules.to_h { |rule| [rule.orchestration_policy_idx, rule.id] }
+      end
+
+      def policy_indexes_for_users(user_ids, policies, project_usernames)
+        user_ids.select { |user_id| project_usernames[user_id].present? }.filter_map do |user_id|
+          policy_indexes = policy_indexes_for_user(policies, project_usernames, user_id)
+          next if policy_indexes.none?
+
+          [user_id, policy_indexes]
+        end.to_h
+      end
+
+      def policy_indexes_for_user(policies, usernames, user_id)
+        policies.filter_map.with_index do |policy, index|
+          index if policy[:actions]&.any? do |action|
+            action[:user_approvers_ids]&.include?(user_id) || action[:user_approvers]&.include?(usernames[user_id])
+          end
+        end
+      end
+
+      def username_map(user_ids)
+        project.team.users.id_in(user_ids).select(:id, :username).to_h { |user| [user.id, user.username] }
+      end
+
+      def build_user_approval_rules_params(user_id, project_rule_ids, merge_request_rule_ids)
+        project_rule_params = build_project_rule_params(project_rule_ids, user_id)
+        merge_request_rule_params = build_merge_request_rule_params(merge_request_rule_ids, user_id)
+
+        [project_rule_params, merge_request_rule_params]
+      end
+
+      def insert_users_to_approval_rules(bulk_insert_params)
+        ApplicationRecord.transaction do
+          bulk_insert_params.each_slice(INSERT_BATCH_SIZE) do |params_batch|
+            project_rule_params = params_batch.flat_map(&:first)
+            merge_request_rule_params = params_batch.flat_map(&:second)
+
+            ApprovalProjectRulesUser.insert_all(project_rule_params) if project_rule_params.present?
+            ApprovalMergeRequestRulesUser.insert_all(merge_request_rule_params) if merge_request_rule_params.present?
+          end
+        end
+      end
+
+      def approval_project_rules(configuration, policy_indexes)
+        configuration.approval_project_rules
+                     .for_project(project.id).for_policy_index(policy_indexes).select(*APPROVAL_RULES_ATTRIBUTES)
+      end
+
+      def approval_merge_request_rules(configuration, policy_indexes)
+        configuration.approval_merge_request_rules.for_unmerged_merge_requests
+                     .for_merge_request_project(project.id).for_policy_index(policy_indexes)
+                     .select(*APPROVAL_RULES_ATTRIBUTES)
+      end
+
+      def build_project_rule_params(approval_rule_ids, user_id)
+        approval_rule_ids.map { |rule_id| { approval_project_rule_id: rule_id, user_id: user_id } }
+      end
+
+      def build_merge_request_rule_params(approval_rule_ids, user_id)
+        approval_rule_ids.map { |rule_id| { approval_merge_request_rule_id: rule_id, user_id: user_id } }
+      end
+    end
+  end
+end
diff --git a/ee/app/workers/all_queues.yml b/ee/app/workers/all_queues.yml
index b32d344c50aae54fc48a35434e4f52d9437ee8ca..5b44994fd943c29ca6268ea67159a5a513d1f9db 100644
--- a/ee/app/workers/all_queues.yml
+++ b/ee/app/workers/all_queues.yml
@@ -1911,6 +1911,15 @@
   :weight: 1
   :idempotent: false
   :tags: []
+- :name: security_scan_result_policies_add_approvers_to_rules
+  :worker_name: Security::ScanResultPolicies::AddApproversToRulesWorker
+  :feature_category: :security_policy_management
+  :has_external_dependencies: false
+  :urgency: :low
+  :resource_boundary: :unknown
+  :weight: 1
+  :idempotent: true
+  :tags: []
 - :name: security_scan_result_policies_sync_any_merge_request_approval_rules
   :worker_name: Security::ScanResultPolicies::SyncAnyMergeRequestApprovalRulesWorker
   :feature_category: :security_policy_management
diff --git a/ee/app/workers/security/refresh_project_policies_worker.rb b/ee/app/workers/security/refresh_project_policies_worker.rb
index d04324d7a3f6a49da280c1c35d0e124f577aa8d2..0901f471bc697def98947920cfa36ba004f23694 100644
--- a/ee/app/workers/security/refresh_project_policies_worker.rb
+++ b/ee/app/workers/security/refresh_project_policies_worker.rb
@@ -15,6 +15,8 @@ class RefreshProjectPoliciesWorker
     DELAY_INTERVAL = 30.seconds.to_i
 
     def handle_event(event)
+      return if ::Feature.enabled?(:add_policy_approvers_to_rules)
+
       project = Project.find_by_id(event.data[:project_id])
       return unless project
 
diff --git a/ee/app/workers/security/scan_result_policies/add_approvers_to_rules_worker.rb b/ee/app/workers/security/scan_result_policies/add_approvers_to_rules_worker.rb
new file mode 100644
index 0000000000000000000000000000000000000000..b5af8f05208a62a4a5d77e992ff8a1e32583573d
--- /dev/null
+++ b/ee/app/workers/security/scan_result_policies/add_approvers_to_rules_worker.rb
@@ -0,0 +1,32 @@
+# frozen_string_literal: true
+
+module Security
+  module ScanResultPolicies
+    class AddApproversToRulesWorker
+      include Gitlab::EventStore::Subscriber
+
+      data_consistency :sticky
+      feature_category :security_policy_management
+      idempotent!
+
+      def handle_event(event)
+        return if ::Feature.disabled?(:add_policy_approvers_to_rules)
+
+        user_ids = event.data[:user_ids]
+        return if user_ids.blank?
+
+        project_id = event.data[:project_id]
+        project = Project.find_by_id(project_id)
+
+        unless project
+          logger.info(structured_payload(message: 'Project not found.', project_id: project_id))
+          return
+        end
+
+        return unless project.licensed_feature_available?(:security_orchestration_policies)
+
+        Security::ScanResultPolicies::AddApproversToRulesService.new(project: project).execute(user_ids)
+      end
+    end
+  end
+end
diff --git a/ee/lib/ee/gitlab/event_store.rb b/ee/lib/ee/gitlab/event_store.rb
index e0875d389f8e1a0611e1a9171364d77678b14964..d0a28c7f5c4144b4a55ddf29694b438bd4bc1f5b 100644
--- a/ee/lib/ee/gitlab/event_store.rb
+++ b/ee/lib/ee/gitlab/event_store.rb
@@ -44,6 +44,8 @@ def configure!(store)
             delay: 1.minute
           store.subscribe ::MergeRequests::RemoveUserApprovalRulesWorker,
             to: ::ProjectAuthorizations::AuthorizationsRemovedEvent
+          store.subscribe ::Security::ScanResultPolicies::AddApproversToRulesWorker,
+            to: ::ProjectAuthorizations::AuthorizationsAddedEvent
           store.subscribe ::Security::RefreshComplianceFrameworkSecurityPoliciesWorker,
             to: ::Projects::ComplianceFrameworkChangedEvent
         end
diff --git a/ee/spec/lib/ee/gitlab/event_store_spec.rb b/ee/spec/lib/ee/gitlab/event_store_spec.rb
index 53a4a3838d337b580913b7457b7496ff07680bff..5fc311900a5ae6db7e1d2351b80114fc3302c5a0 100644
--- a/ee/spec/lib/ee/gitlab/event_store_spec.rb
+++ b/ee/spec/lib/ee/gitlab/event_store_spec.rb
@@ -20,6 +20,7 @@
         ::Members::MembersAddedEvent,
         ::ProjectAuthorizations::AuthorizationsChangedEvent,
         ::ProjectAuthorizations::AuthorizationsRemovedEvent,
+        ::ProjectAuthorizations::AuthorizationsAddedEvent,
         ::Projects::ComplianceFrameworkChangedEvent
       )
     end
diff --git a/ee/spec/models/approval_project_rule_spec.rb b/ee/spec/models/approval_project_rule_spec.rb
index aab79ca80d21b1475bd2f39bc0c43e3ba31cf4c6..dd812051267b52b137af3bbdf00ed9937172af54 100644
--- a/ee/spec/models/approval_project_rule_spec.rb
+++ b/ee/spec/models/approval_project_rule_spec.rb
@@ -109,6 +109,18 @@
     end
   end
 
+  describe '.for_project' do
+    it 'returns approval rules belonging to a project' do
+      project1 = create(:project)
+      project2 = create(:project)
+
+      rule_for_project1 = create(:approval_project_rule, project: project1)
+      create(:approval_project_rule, project: project2)
+
+      expect(described_class.for_project(project1)).to contain_exactly(rule_for_project1)
+    end
+  end
+
   describe '.regular_or_any_approver scope' do
     it 'returns regular or any-approver rules' do
       any_approver_rule = create(:approval_project_rule, rule_type: :any_approver)
diff --git a/ee/spec/models/security/orchestration_policy_configuration_spec.rb b/ee/spec/models/security/orchestration_policy_configuration_spec.rb
index 1af5d1b2a50c66a4698b38eeb591de5b30e8a628..57cec4f5954f2fc2664465dddaf0148c87ccb9f7 100644
--- a/ee/spec/models/security/orchestration_policy_configuration_spec.rb
+++ b/ee/spec/models/security/orchestration_policy_configuration_spec.rb
@@ -1436,6 +1436,35 @@
     end
   end
 
+  describe '#applicable_scan_result_policies_for_project' do
+    let_it_be(:namespace_settings) do
+      create(:namespace_settings, toggle_security_policies_policy_scope: true)
+    end
+
+    let_it_be(:group) { create(:group, namespace_settings: namespace_settings) }
+    let_it_be(:project) { create(:project, :repository, group: group) }
+    let(:policy_yaml) do
+      build(:orchestration_policy_yaml, scan_result_policy: [
+        build(:scan_result_policy, name: 'Active policy'),
+        build(:scan_result_policy, name: 'Disabled policy', enabled: false),
+        build(:scan_result_policy, name: 'Not applicable policy', policy_scope: {
+          projects: {
+            excluding: [{ id: project.id }]
+          }
+        })
+      ])
+    end
+
+    subject(:applicable_policies) do
+      security_orchestration_policy_configuration.applicable_scan_result_policies_for_project(project)
+    end
+
+    it 'returns only active applicable policies' do
+      expect(applicable_policies).to be_one
+      expect(applicable_policies.first[:name]).to eq('Active policy')
+    end
+  end
+
   describe '#scan_result_policies' do
     let(:policy_yaml) { fixture_file('security_orchestration.yml', dir: 'ee') }
 
diff --git a/ee/spec/services/security/scan_result_policies/add_approvers_to_rules_service_spec.rb b/ee/spec/services/security/scan_result_policies/add_approvers_to_rules_service_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..598388135274d28685b65219c6fafce70afa37a5
--- /dev/null
+++ b/ee/spec/services/security/scan_result_policies/add_approvers_to_rules_service_spec.rb
@@ -0,0 +1,221 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Security::ScanResultPolicies::AddApproversToRulesService, feature_category: :security_policy_management do
+  let_it_be(:user1) { create(:user) }
+  let_it_be(:user2) { create(:user) }
+  let_it_be(:user_not_referenced_in_policy) { create(:user) }
+
+  let_it_be(:namespace_settings) do
+    create(:namespace_settings, toggle_security_policies_policy_scope: true)
+  end
+
+  let_it_be(:namespace) do
+    create(:group, :with_security_orchestration_policy_configuration, namespace_settings: namespace_settings)
+  end
+
+  let_it_be(:project1) { create(:project, :repository, group: namespace) }
+  let_it_be(:project2) { create(:project, :repository, group: namespace) }
+  let_it_be_with_reload(:merge_request1) { create(:merge_request, source_project: project1, target_project: project1) }
+
+  let(:user_ids) { [user1.id, user2.id, user_not_referenced_in_policy.id] }
+  let(:configuration) { namespace.security_orchestration_policy_configuration }
+  let(:policies_yaml) do
+    build(:orchestration_policy_yaml, scan_result_policy: [
+      build(:scan_result_policy, name: 'Active policy', actions: [
+        { type: 'require_approval', approvals_required: 1,
+          user_approvers_ids: [user1.id], user_approvers: [user2.username] }
+      ]),
+      build(:scan_result_policy, name: 'Disabled policy', enabled: false, actions: [
+        { type: 'require_approval', approvals_required: 1, user_approvers_ids: [user1.id] }
+      ])
+    ])
+  end
+
+  let(:policies_not_applicable_yaml) do
+    build(:orchestration_policy_yaml, scan_result_policy: [
+      build(:scan_result_policy, name: 'Active policy',
+        actions: [
+          { type: 'require_approval', approvals_required: 1,
+            user_approvers_ids: [user1.id], user_approvers: [user2.username] }
+        ],
+        policy_scope: {
+          projects: {
+            excluding: [{ id: project1.id }]
+          }
+        })
+    ])
+  end
+
+  before do
+    allow_next_instance_of(Repository) do |repository|
+      allow(repository).to receive(:blob_data_at).and_return(policies_yaml)
+    end
+  end
+
+  describe '#execute' do
+    let(:project_rule_expected_to_change) do
+      create(:approval_project_rule, project: project1, security_orchestration_policy_configuration: configuration,
+        orchestration_policy_idx: 0)
+    end
+
+    let!(:merge_request_rule_expected_to_change) do
+      create(:approval_merge_request_rule, merge_request: merge_request1,
+        security_orchestration_policy_configuration: configuration, orchestration_policy_idx: 0)
+    end
+
+    subject(:execute) { described_class.new(project: project1).execute(user_ids) }
+
+    shared_examples_for 'adds users to the project rule' do
+      it 'adds users to the project rule' do
+        expect { execute }.to change { project_rule.users.count }.by(2)
+        expect(project_rule.reload.users).to contain_exactly(user1, user2)
+      end
+    end
+
+    shared_examples_for 'does not add users to the project rule' do
+      it 'does not add users to the project rule' do
+        expect { execute }.not_to change { project_rule.users.count }
+      end
+    end
+
+    shared_examples_for 'adds users to the merge request rule' do
+      it 'adds users to the merge request rule' do
+        expect { execute }.to change { merge_request_rule.users.count }.by(2)
+        expect(merge_request_rule.reload.users).to contain_exactly(user1, user2)
+      end
+    end
+
+    shared_examples_for 'does not add users to the merge request rule' do
+      it 'does not add users to the merge request rule' do
+        expect { execute }.not_to change { merge_request_rule.users.count }
+      end
+    end
+
+    context 'when users have access to the project' do
+      before_all do
+        project1.add_developer(user1)
+        project1.add_developer(user2)
+        project1.add_developer(user_not_referenced_in_policy)
+      end
+
+      describe 'project rules' do
+        let!(:project_rule) { project_rule_expected_to_change }
+
+        it_behaves_like 'adds users to the project rule'
+
+        context 'when project rule belongs to a different policy index' do
+          let!(:project_rule) do
+            create(:approval_project_rule, project: project1,
+              security_orchestration_policy_configuration: configuration, orchestration_policy_idx: 1)
+          end
+
+          it_behaves_like 'does not add users to the project rule'
+        end
+
+        context 'when project rule belongs to other configuration' do
+          let_it_be(:configuration_other) do
+            create(:security_orchestration_policy_configuration, project: project2)
+          end
+
+          let!(:project_rule) do
+            create(:approval_project_rule, project: project1,
+              security_orchestration_policy_configuration: configuration_other, orchestration_policy_idx: 0)
+          end
+
+          it_behaves_like 'does not add users to the project rule'
+        end
+
+        context 'when project rule belongs to other project' do
+          let!(:project_rule) do
+            create(:approval_project_rule, project: project2,
+              security_orchestration_policy_configuration: configuration, orchestration_policy_idx: 0)
+          end
+
+          it_behaves_like 'does not add users to the project rule'
+        end
+
+        context 'when policy is not applicable for the project' do
+          let!(:project_rule) { project_rule_expected_to_change }
+          let(:policies_yaml) { policies_not_applicable_yaml }
+
+          it_behaves_like 'does not add users to the project rule'
+        end
+      end
+
+      describe 'merge request rules' do
+        let!(:merge_request_rule) { merge_request_rule_expected_to_change }
+
+        it_behaves_like 'adds users to the merge request rule'
+
+        context 'when merge request rule belongs to the project rule' do
+          let!(:merge_request_rule) do
+            create(:approval_merge_request_rule, merge_request: merge_request1,
+              security_orchestration_policy_configuration: configuration, orchestration_policy_idx: 0,
+              approval_project_rule: project_rule_expected_to_change)
+          end
+
+          it_behaves_like 'adds users to the merge request rule'
+        end
+
+        context 'when merge request is merged' do
+          let!(:merge_request_rule) { merge_request_rule_expected_to_change }
+
+          before do
+            merge_request1.mark_as_merged!
+          end
+
+          it_behaves_like 'does not add users to the merge request rule'
+        end
+
+        context 'when policies are not applicable to the project' do
+          let!(:merge_request_rule) { merge_request_rule_expected_to_change }
+          let(:policies_yaml) { policies_not_applicable_yaml }
+
+          it_behaves_like 'does not add users to the merge request rule'
+        end
+
+        context 'when merge request rule belongs to a different policy index' do
+          let!(:merge_request_rule) do
+            create(:approval_merge_request_rule, merge_request: merge_request1,
+              security_orchestration_policy_configuration: configuration, orchestration_policy_idx: 1)
+          end
+
+          it_behaves_like 'does not add users to the merge request rule'
+        end
+
+        context 'when merge request rule belongs to other configuration' do
+          let_it_be(:configuration_other) do
+            create(:security_orchestration_policy_configuration, project: project2)
+          end
+
+          let!(:merge_request_rule) do
+            create(:approval_merge_request_rule, merge_request: merge_request1,
+              security_orchestration_policy_configuration: configuration_other, orchestration_policy_idx: 0)
+          end
+
+          it_behaves_like 'does not add users to the merge request rule'
+        end
+
+        context 'when merge request rule belongs to a merge request in other project' do
+          let_it_be(:merge_request2) { create(:merge_request, source_project: project2, target_project: project2) }
+          let!(:merge_request_rule) do
+            create(:approval_merge_request_rule, merge_request: merge_request2,
+              security_orchestration_policy_configuration: configuration, orchestration_policy_idx: 0)
+          end
+
+          it_behaves_like 'does not add users to the merge request rule'
+        end
+      end
+    end
+
+    context 'when users do not have access to the project' do
+      let!(:project_rule) { project_rule_expected_to_change }
+      let!(:merge_request_rule) { merge_request_rule_expected_to_change }
+
+      it_behaves_like 'does not add users to the project rule'
+      it_behaves_like 'does not add users to the merge request rule'
+    end
+  end
+end
diff --git a/ee/spec/workers/security/refresh_project_policies_worker_spec.rb b/ee/spec/workers/security/refresh_project_policies_worker_spec.rb
index dc22a2952b5c5d21b0c13e7a23201352035be51d..29bed67de0ebafd6d3974bd3c2d841c842ba5162 100644
--- a/ee/spec/workers/security/refresh_project_policies_worker_spec.rb
+++ b/ee/spec/workers/security/refresh_project_policies_worker_spec.rb
@@ -31,13 +31,9 @@
     end
   end
 
-  context 'when skip_refresh_project_policies is enabled' do
-    before do
-      stub_feature_flags(skip_refresh_project_policies: true)
-    end
-
-    it 'does not invoke Security::ProcessScanResultPolicyWorker' do
-      expect(worker).not_to receive(:perform_async)
+  shared_examples_for 'does not invoke the worker' do
+    it 'does not invoke Security::ProcessScanResultPolicyWorker worker' do
+      expect(worker).not_to receive(:perform_in)
 
       consume_event(subscriber: described_class, event: project_member_changed_event)
     end
@@ -53,10 +49,26 @@
       end
     end
 
-    it 'invokes Security::ProcessScanResultPolicyWorker' do
-      expect(worker).to receive(:perform_in).with(0, project.id, configuration.id)
+    it_behaves_like 'does not invoke the worker'
 
-      consume_event(subscriber: described_class, event: project_member_changed_event)
+    context 'when feature flag "add_policy_approvers_to_rules" is disabled' do
+      before do
+        stub_feature_flags(add_policy_approvers_to_rules: false)
+      end
+
+      it 'invokes Security::ProcessScanResultPolicyWorker' do
+        expect(worker).to receive(:perform_in).with(0, project.id, configuration.id)
+
+        consume_event(subscriber: described_class, event: project_member_changed_event)
+      end
+
+      context 'when skip_refresh_project_policies is enabled' do
+        before do
+          stub_feature_flags(skip_refresh_project_policies: true)
+        end
+
+        it_behaves_like 'does not invoke the worker'
+      end
     end
   end
 
@@ -77,11 +89,19 @@
       end
     end
 
-    it 'invokes Security::ProcessScanResultPolicyWorker with incremental delay' do
-      expect(worker).to receive(:perform_in).with(0, project.id, configuration.id).ordered
-      expect(worker).to receive(:perform_in).with(30, project.id, inherited_configuration.id).ordered
+    it_behaves_like 'does not invoke the worker'
 
-      consume_event(subscriber: described_class, event: project_member_changed_event)
+    context 'when feature flag "add_policy_approvers_to_rules" is disabled' do
+      before do
+        stub_feature_flags(add_policy_approvers_to_rules: false)
+      end
+
+      it 'invokes Security::ProcessScanResultPolicyWorker with incremental delay' do
+        expect(worker).to receive(:perform_in).with(0, project.id, configuration.id).ordered
+        expect(worker).to receive(:perform_in).with(30, project.id, inherited_configuration.id).ordered
+
+        consume_event(subscriber: described_class, event: project_member_changed_event)
+      end
     end
   end
 
@@ -101,10 +121,18 @@
       end
     end
 
-    it 'invokes Security::ProcessScanResultPolicyWorker with incremental delay' do
-      expect(worker).to receive(:perform_in).with(0, project.id, inherited_configuration.id)
+    it_behaves_like 'does not invoke the worker'
 
-      consume_event(subscriber: described_class, event: project_member_changed_event)
+    context 'when feature flag "add_policy_approvers_to_rules" is disabled' do
+      before do
+        stub_feature_flags(add_policy_approvers_to_rules: false)
+      end
+
+      it 'invokes Security::ProcessScanResultPolicyWorker with incremental delay' do
+        expect(worker).to receive(:perform_in).with(0, project.id, inherited_configuration.id)
+
+        consume_event(subscriber: described_class, event: project_member_changed_event)
+      end
     end
   end
 end
diff --git a/ee/spec/workers/security/scan_result_policies/add_approvers_to_rules_worker_spec.rb b/ee/spec/workers/security/scan_result_policies/add_approvers_to_rules_worker_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..90c2fa544c5d701a7476c9d165f78d8b41353c96
--- /dev/null
+++ b/ee/spec/workers/security/scan_result_policies/add_approvers_to_rules_worker_spec.rb
@@ -0,0 +1,78 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Security::ScanResultPolicies::AddApproversToRulesWorker, feature_category: :security_policy_management do
+  let_it_be(:user) { create(:user) }
+  let_it_be(:project) { create(:project) }
+
+  let(:project_id) { project.id }
+  let(:user_ids) { [user.id] }
+  let(:data) { { project_id: project_id, user_ids: user_ids } }
+  let(:authorizations_event) { ProjectAuthorizations::AuthorizationsAddedEvent.new(data: data) }
+  let(:licensed_feature) { true }
+
+  before do
+    stub_licensed_features(security_orchestration_policies: licensed_feature)
+  end
+
+  it_behaves_like 'subscribes to event' do
+    let(:event) { authorizations_event }
+
+    it 'calls Security::ScanResultPolicies::AddApproversToRulesService' do
+      expect_next_instance_of(
+        Security::ScanResultPolicies::AddApproversToRulesService,
+        project: project
+      ) do |service|
+        expect(service).to receive(:execute).with([user.id])
+      end
+
+      consume_event(subscriber: described_class, event: authorizations_event)
+    end
+  end
+
+  context 'when the project does not exist' do
+    let(:project_id) { non_existing_record_id }
+
+    it 'logs and does not call Security::ScanResultPolicies::AddApproversToRulesService' do
+      expect(Sidekiq.logger).to receive(:info).with(
+        hash_including('message' => 'Project not found.', 'project_id' => project_id)
+      )
+      expect(Security::ScanResultPolicies::AddApproversToRulesService).not_to receive(:new)
+
+      expect { consume_event(subscriber: described_class, event: authorizations_event) }.not_to raise_exception
+    end
+  end
+
+  context 'when the user_ids are empty' do
+    let(:user_ids) { [] }
+
+    it 'does not call Security::ScanResultPolicies::AddApproversToRulesService' do
+      expect(Security::ScanResultPolicies::AddApproversToRulesService).not_to receive(:new)
+
+      expect { consume_event(subscriber: described_class, event: authorizations_event) }.not_to raise_exception
+    end
+  end
+
+  context 'when the feature flag "add_policy_approvers_to_rules" is disabled' do
+    before do
+      stub_feature_flags(add_policy_approvers_to_rules: false)
+    end
+
+    it 'does not call Security::ScanResultPolicies::AddApproversToRulesService' do
+      expect(Security::ScanResultPolicies::AddApproversToRulesService).not_to receive(:new)
+
+      expect { consume_event(subscriber: described_class, event: authorizations_event) }.not_to raise_exception
+    end
+  end
+
+  context 'when the feature is not licensed' do
+    let(:licensed_feature) { false }
+
+    it 'does not call Security::ScanResultPolicies::AddApproversToRulesService' do
+      expect(Security::ScanResultPolicies::AddApproversToRulesService).not_to receive(:new)
+
+      expect { consume_event(subscriber: described_class, event: authorizations_event) }.not_to raise_exception
+    end
+  end
+end
diff --git a/spec/models/project_authorizations/changes_spec.rb b/spec/models/project_authorizations/changes_spec.rb
index d6ccfccbcbe1123fd8ebbaecdd0f3b2512e74a3e..714144841fbbaf8e6c46900d156b3caa85edde2c 100644
--- a/spec/models/project_authorizations/changes_spec.rb
+++ b/spec/models/project_authorizations/changes_spec.rb
@@ -28,36 +28,49 @@
     end
 
     shared_examples_for 'publishes AuthorizationsChangedEvent' do
-      it 'publishes a AuthorizationsChangedEvent event with project id' do
-        project_ids.each do |project_id|
-          project_data = { project_id: project_id }
-          project_event = instance_double('::ProjectAuthorizations::AuthorizationsChangedEvent', data: project_data)
+      it 'does not publish a AuthorizationsChangedEvent event' do
+        expect(::Gitlab::EventStore).not_to receive(:publish)
+                                              .with(an_instance_of(::ProjectAuthorizations::AuthorizationsChangedEvent))
 
-          allow(::ProjectAuthorizations::AuthorizationsChangedEvent).to receive(:new)
-                                                                          .with(data: project_data)
-                                                                          .and_return(project_event)
+        apply_project_authorization_changes
+      end
 
-          allow(::Gitlab::EventStore).to receive(:publish)
-          expect(::Gitlab::EventStore).to receive(:publish).with(project_event)
+      context 'when feature flag "add_policy_approvers_to_rules" is disabled' do
+        before do
+          stub_feature_flags(add_policy_approvers_to_rules: false)
         end
 
-        apply_project_authorization_changes
+        it 'publishes a AuthorizationsChangedEvent event with project id' do
+          allow(::Gitlab::EventStore).to receive(:publish)
+          project_ids.each do |project_id|
+            project_data = { project_id: project_id }
+            project_event = instance_double('::ProjectAuthorizations::AuthorizationsChangedEvent', data: project_data)
+
+            allow(::ProjectAuthorizations::AuthorizationsChangedEvent).to receive(:new)
+                                                                            .with(data: project_data)
+                                                                            .and_return(project_event)
+
+            expect(::Gitlab::EventStore).to receive(:publish).with(project_event)
+          end
+
+          apply_project_authorization_changes
+        end
       end
     end
 
     shared_examples_for 'publishes AuthorizationsRemovedEvent' do
       it 'publishes a AuthorizationsRemovedEvent event with project id' do
-        project_ids.each do |project_id|
+        allow(::Gitlab::EventStore).to receive(:publish_group)
+        project_events = project_ids.map do |project_id|
           project_data = { project_id: project_id, user_ids: user_ids }
           project_event = instance_double('::ProjectAuthorizations::AuthorizationsRemovedEvent', data: project_data)
 
           allow(::ProjectAuthorizations::AuthorizationsRemovedEvent).to receive(:new)
                                                                           .with(data: project_data)
                                                                           .and_return(project_event)
-
-          allow(::Gitlab::EventStore).to receive(:publish)
-          expect(::Gitlab::EventStore).to receive(:publish).with(project_event)
+          project_event
         end
+        expect(::Gitlab::EventStore).to receive(:publish_group).with(project_events)
 
         apply_project_authorization_changes
       end
@@ -69,7 +82,43 @@
 
         it 'does not publish a AuthorizationsRemovedEvent event' do
           expect(::Gitlab::EventStore).not_to(
-            receive(:publish).with(an_instance_of(::ProjectAuthorizations::AuthorizationsRemovedEvent))
+            receive(:publish_group).with(
+              array_including(an_instance_of(::ProjectAuthorizations::AuthorizationsRemovedEvent))
+            )
+          )
+
+          apply_project_authorization_changes
+        end
+      end
+    end
+
+    shared_examples_for 'publishes AuthorizationsAddedEvent' do
+      it 'publishes a AuthorizationsAddedEvent event with project id' do
+        allow(::Gitlab::EventStore).to receive(:publish_group)
+        project_events = project_ids.map do |project_id|
+          project_data = { project_id: project_id, user_ids: user_ids }
+          project_event = instance_double('::ProjectAuthorizations::AuthorizationsAddedEvent', data: project_data)
+
+          allow(::ProjectAuthorizations::AuthorizationsAddedEvent).to receive(:new)
+                                                                          .with(data: project_data)
+                                                                          .and_return(project_event)
+          project_event
+        end
+        expect(::Gitlab::EventStore).to receive(:publish_group).with(project_events)
+
+        apply_project_authorization_changes
+      end
+
+      context 'when feature flag "add_policy_approvers_to_rules" is disabled' do
+        before do
+          stub_feature_flags(add_policy_approvers_to_rules: false)
+        end
+
+        it 'does not publish a AuthorizationsAddedEvent event' do
+          expect(::Gitlab::EventStore).not_to(
+            receive(:publish_group).with(array_including(
+              an_instance_of(::ProjectAuthorizations::AuthorizationsAddedEvent))
+                                        )
           )
 
           apply_project_authorization_changes
@@ -88,8 +137,23 @@
 
     shared_examples_for 'does not publish AuthorizationsRemovedEvent' do
       it 'does not publish a AuthorizationsRemovedEvent event' do
-        expect(::Gitlab::EventStore).not_to receive(:publish)
-                                              .with(an_instance_of(::ProjectAuthorizations::AuthorizationsRemovedEvent))
+        expect(::Gitlab::EventStore).not_to(
+          receive(:publish_group).with(
+            array_including(an_instance_of(::ProjectAuthorizations::AuthorizationsRemovedEvent))
+          )
+        )
+
+        apply_project_authorization_changes
+      end
+    end
+
+    shared_examples_for 'does not publish AuthorizationsAddedEvent' do
+      it 'does not publish a AuthorizationsAddedEvent event' do
+        expect(::Gitlab::EventStore).not_to(
+          receive(:publish_group).with(
+            array_including(an_instance_of(::ProjectAuthorizations::AuthorizationsAddedEvent))
+          )
+        )
 
         apply_project_authorization_changes
       end
@@ -101,6 +165,7 @@
       let_it_be(:project_2) { create(:project) }
       let_it_be(:project_3) { create(:project) }
       let(:project_ids) { [project_1.id, project_2.id, project_3.id] }
+      let(:user_ids) { [user.id] }
 
       let(:authorizations_to_add) do
         [
@@ -155,6 +220,7 @@
 
         it_behaves_like 'logs the detail', batch_size: 2
         it_behaves_like 'publishes AuthorizationsChangedEvent'
+        it_behaves_like 'publishes AuthorizationsAddedEvent'
         it_behaves_like 'does not publish AuthorizationsRemovedEvent'
 
         context 'when the GitLab installation does not have a replica database configured' do
@@ -166,6 +232,7 @@
           it_behaves_like 'inserts the rows in batches, as per the `per_batch` size, without a delay between batches'
           it_behaves_like 'does not log any detail'
           it_behaves_like 'publishes AuthorizationsChangedEvent'
+          it_behaves_like 'publishes AuthorizationsAddedEvent'
           it_behaves_like 'does not publish AuthorizationsRemovedEvent'
         end
       end
@@ -178,6 +245,7 @@
         it_behaves_like 'inserts the rows in batches, as per the `per_batch` size, without a delay between batches'
         it_behaves_like 'does not log any detail'
         it_behaves_like 'publishes AuthorizationsChangedEvent'
+        it_behaves_like 'publishes AuthorizationsAddedEvent'
         it_behaves_like 'does not publish AuthorizationsRemovedEvent'
       end
     end
@@ -242,6 +310,7 @@
         it_behaves_like 'logs the detail', batch_size: 2
         it_behaves_like 'publishes AuthorizationsChangedEvent'
         it_behaves_like 'publishes AuthorizationsRemovedEvent'
+        it_behaves_like 'does not publish AuthorizationsAddedEvent'
 
         context 'when the GitLab installation does not have a replica database configured' do
           before do
@@ -253,6 +322,7 @@
           it_behaves_like 'does not log any detail'
           it_behaves_like 'publishes AuthorizationsChangedEvent'
           it_behaves_like 'publishes AuthorizationsRemovedEvent'
+          it_behaves_like 'does not publish AuthorizationsAddedEvent'
         end
       end
 
@@ -265,6 +335,7 @@
         it_behaves_like 'does not log any detail'
         it_behaves_like 'publishes AuthorizationsChangedEvent'
         it_behaves_like 'publishes AuthorizationsRemovedEvent'
+        it_behaves_like 'does not publish AuthorizationsAddedEvent'
       end
 
       context 'when the user_ids list is empty' do
@@ -273,6 +344,7 @@
         it_behaves_like 'does not removes project authorizations of the users in the current project'
         it_behaves_like 'does not publish AuthorizationsChangedEvent'
         it_behaves_like 'does not publish AuthorizationsRemovedEvent'
+        it_behaves_like 'does not publish AuthorizationsAddedEvent'
       end
 
       context 'when the user_ids list is nil' do
@@ -281,6 +353,7 @@
         it_behaves_like 'does not removes project authorizations of the users in the current project'
         it_behaves_like 'does not publish AuthorizationsChangedEvent'
         it_behaves_like 'does not publish AuthorizationsRemovedEvent'
+        it_behaves_like 'does not publish AuthorizationsAddedEvent'
       end
     end
 
@@ -344,6 +417,7 @@
         it_behaves_like 'logs the detail', batch_size: 2
         it_behaves_like 'publishes AuthorizationsChangedEvent'
         it_behaves_like 'publishes AuthorizationsRemovedEvent'
+        it_behaves_like 'does not publish AuthorizationsAddedEvent'
 
         context 'when the GitLab installation does not have a replica database configured' do
           before do
@@ -355,6 +429,7 @@
           it_behaves_like 'does not log any detail'
           it_behaves_like 'publishes AuthorizationsChangedEvent'
           it_behaves_like 'publishes AuthorizationsRemovedEvent'
+          it_behaves_like 'does not publish AuthorizationsAddedEvent'
         end
       end
 
@@ -367,6 +442,7 @@
         it_behaves_like 'does not log any detail'
         it_behaves_like 'publishes AuthorizationsChangedEvent'
         it_behaves_like 'publishes AuthorizationsRemovedEvent'
+        it_behaves_like 'does not publish AuthorizationsAddedEvent'
       end
 
       context 'when the project_ids list is empty' do
@@ -375,6 +451,7 @@
         it_behaves_like 'does not removes any project authorizations from the current user'
         it_behaves_like 'does not publish AuthorizationsChangedEvent'
         it_behaves_like 'does not publish AuthorizationsRemovedEvent'
+        it_behaves_like 'does not publish AuthorizationsAddedEvent'
       end
 
       context 'when the user_ids list is nil' do
@@ -383,6 +460,7 @@
         it_behaves_like 'does not removes any project authorizations from the current user'
         it_behaves_like 'does not publish AuthorizationsChangedEvent'
         it_behaves_like 'does not publish AuthorizationsRemovedEvent'
+        it_behaves_like 'does not publish AuthorizationsAddedEvent'
       end
     end
   end
diff --git a/spec/services/members/create_service_spec.rb b/spec/services/members/create_service_spec.rb
index af151f93dc7c82a09ad5f40782de8a7990827c16..c08b40e9528dbce8ae6cbc59d2e357f885b535ee 100644
--- a/spec/services/members/create_service_spec.rb
+++ b/spec/services/members/create_service_spec.rb
@@ -119,14 +119,34 @@
       before do
         # validations will fail because we try to invite them to the project as a guest
         source.group.add_developer(member)
+        allow(Gitlab::EventStore).to receive(:publish)
       end
 
-      it 'triggers the members added and authorizations changed events' do
+      it 'triggers the authorizations changed events' do
         expect(Gitlab::EventStore)
-          .to receive(:publish)
-                .with(an_instance_of(ProjectAuthorizations::AuthorizationsChangedEvent))
+          .to receive(:publish_group)
+                .with(array_including(an_instance_of(ProjectAuthorizations::AuthorizationsAddedEvent)))
                 .and_call_original
 
+        execute_service
+      end
+
+      context 'when feature flag "add_policy_approvers_to_rules" is disabled' do
+        before do
+          stub_feature_flags(add_policy_approvers_to_rules: false)
+        end
+
+        it 'triggers the authorizations changed event' do
+          expect(Gitlab::EventStore)
+            .to receive(:publish)
+                  .with(an_instance_of(ProjectAuthorizations::AuthorizationsChangedEvent))
+                  .and_call_original
+
+          execute_service
+        end
+      end
+
+      it 'triggers the members added event' do
         expect(Gitlab::EventStore)
           .to receive(:publish)
           .with(an_instance_of(Members::MembersAddedEvent))