diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index a1d1c8d7c4c2d32fc60a34c0fe46265aaf1d51c5..310b9652c956193528e9d531a60971b1c807a749 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -15355,6 +15355,29 @@ The edge type for [`ProjectSavedReply`](#projectsavedreply).
 | <a id="projectsavedreplyedgecursor"></a>`cursor` | [`String!`](#string) | A cursor for use in pagination. |
 | <a id="projectsavedreplyedgenode"></a>`node` | [`ProjectSavedReply`](#projectsavedreply) | The item at the end of the edge. |
 
+#### `ProjectSecurityExclusionConnection`
+
+The connection type for [`ProjectSecurityExclusion`](#projectsecurityexclusion).
+
+##### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="projectsecurityexclusionconnectionedges"></a>`edges` | [`[ProjectSecurityExclusionEdge]`](#projectsecurityexclusionedge) | A list of edges. |
+| <a id="projectsecurityexclusionconnectionnodes"></a>`nodes` | [`[ProjectSecurityExclusion]`](#projectsecurityexclusion) | A list of nodes. |
+| <a id="projectsecurityexclusionconnectionpageinfo"></a>`pageInfo` | [`PageInfo!`](#pageinfo) | Information to aid in pagination. |
+
+#### `ProjectSecurityExclusionEdge`
+
+The edge type for [`ProjectSecurityExclusion`](#projectsecurityexclusion).
+
+##### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="projectsecurityexclusionedgecursor"></a>`cursor` | [`String!`](#string) | A cursor for use in pagination. |
+| <a id="projectsecurityexclusionedgenode"></a>`node` | [`ProjectSecurityExclusion`](#projectsecurityexclusion) | The item at the end of the edge. |
+
 #### `ProjectWikiRepositoryRegistryConnection`
 
 The connection type for [`ProjectWikiRepositoryRegistry`](#projectwikirepositoryregistry).
@@ -30931,6 +30954,28 @@ four standard [pagination arguments](#pagination-arguments):
 | <a id="projectscanresultpoliciesincludeunscoped"></a>`includeUnscoped` **{warning-solid}** | [`Boolean`](#boolean) | **Introduced** in GitLab 17.3. **Status**: Experiment. Filter policies that are scoped to the project. |
 | <a id="projectscanresultpoliciesrelationship"></a>`relationship` | [`SecurityPolicyRelationType`](#securitypolicyrelationtype) | Filter policies by the given policy relationship. |
 
+##### `Project.securityExclusions`
+
+Security exclusions of the project.
+
+DETAILS:
+**Introduced** in GitLab 17.4.
+**Status**: Experiment.
+
+Returns [`ProjectSecurityExclusionConnection`](#projectsecurityexclusionconnection).
+
+This field returns a [connection](#connections). It accepts the
+four standard [pagination arguments](#pagination-arguments):
+`before: String`, `after: String`, `first: Int`, and `last: Int`.
+
+###### Arguments
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="projectsecurityexclusionsactive"></a>`active` | [`Boolean`](#boolean) | Filter entries by active status. |
+| <a id="projectsecurityexclusionsscanner"></a>`scanner` | [`ExclusionScannerEnum`](#exclusionscannerenum) | Filter entries by scanner. |
+| <a id="projectsecurityexclusionstype"></a>`type` | [`ExclusionTypeEnum`](#exclusiontypeenum) | Filter entries by exclusion type. |
+
 ##### `Project.securityPolicyProjectSuggestions`
 
 Security policy project suggestions.
@@ -31448,6 +31493,21 @@ Representation of a project secrets manager.
 | <a id="projectsecretsmanagerproject"></a>`project` | [`Project!`](#project) | Project the secrets manager belong to. |
 | <a id="projectsecretsmanagerstatus"></a>`status` | [`ProjectSecretsManagerStatus`](#projectsecretsmanagerstatus) | Status of the project secrets manager. |
 
+### `ProjectSecurityExclusion`
+
+Represents a project-level security scanner exclusion.
+
+#### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="projectsecurityexclusionactive"></a>`active` | [`Boolean!`](#boolean) | Whether the exclusion is active. |
+| <a id="projectsecurityexclusiondescription"></a>`description` | [`String`](#string) | Optional description for the exclusion. |
+| <a id="projectsecurityexclusionid"></a>`id` | [`ID!`](#id) | ID of the exclusion. |
+| <a id="projectsecurityexclusionscanner"></a>`scanner` | [`ExclusionScannerEnum!`](#exclusionscannerenum) | Security scanner the exclusion will be used for. |
+| <a id="projectsecurityexclusiontype"></a>`type` | [`ExclusionTypeEnum!`](#exclusiontypeenum) | Type of the exclusion. |
+| <a id="projectsecurityexclusionvalue"></a>`value` | [`String!`](#string) | Value of the exclusion. |
+
 ### `ProjectSecurityPolicySource`
 
 Represents the source of a security policy belonging to a project.
@@ -36683,6 +36743,25 @@ Event action.
 | <a id="eventactionreopened"></a>`REOPENED` | Reopened action. |
 | <a id="eventactionupdated"></a>`UPDATED` | Updated action. |
 
+### `ExclusionScannerEnum`
+
+Enum for the security scanners used with exclusions.
+
+| Value | Description |
+| ----- | ----------- |
+| <a id="exclusionscannerenumsecret_push_protection"></a>`SECRET_PUSH_PROTECTION` | Secret Push Protection. |
+
+### `ExclusionTypeEnum`
+
+Enum for types of exclusion for a security scanner.
+
+| Value | Description |
+| ----- | ----------- |
+| <a id="exclusiontypeenumpath"></a>`PATH` | File or directory location. |
+| <a id="exclusiontypeenumraw_value"></a>`RAW_VALUE` | Raw value to ignore. |
+| <a id="exclusiontypeenumregex_pattern"></a>`REGEX_PATTERN` | Regex pattern matching rules. |
+| <a id="exclusiontypeenumrule"></a>`RULE` | Scanner rule identifier. |
+
 ### `ExtensionsMarketplaceOptInStatus`
 
 Values for status of the Web IDE Extension Marketplace opt-in for the user.
diff --git a/ee/app/finders/security/project_security_exclusions_finder.rb b/ee/app/finders/security/project_security_exclusions_finder.rb
new file mode 100644
index 0000000000000000000000000000000000000000..d0049b98077809eab4f7188f4255127c5775c38e
--- /dev/null
+++ b/ee/app/finders/security/project_security_exclusions_finder.rb
@@ -0,0 +1,64 @@
+# frozen_string_literal: true
+
+# Finder used to retrieve security scanners' exclusions for a project.
+#
+# Basic usage:
+#
+#     Security::ProjectSecurityExclusionsFinder.new(current_user, project: project).execute
+#
+# Filter by scopes:
+#
+#     Security::ProjectSecurityExclusionsFinder.new(current_user, project: project, params: { active: false }).execute
+#
+# Arguments:
+#   current_user - which user is requesting exclusions.
+#   project  -which project to scope to.
+#   params:
+#     scanner: string
+#     type: string
+#     status: string
+module Security
+  class ProjectSecurityExclusionsFinder
+    def initialize(current_user, project:, params: {})
+      @current_user = current_user
+      @project = project
+      @params = params
+    end
+
+    def execute
+      return ProjectSecurityExclusion.none unless can_read_project_security_exclusions?
+
+      exclusions = project.security_exclusions
+      exclusions = by_scanner(exclusions)
+      exclusions = by_type(exclusions)
+
+      by_status(exclusions)
+    end
+
+    private
+
+    attr_reader :current_user, :project, :params
+
+    def can_read_project_security_exclusions?
+      Ability.allowed?(current_user, :read_project_security_exclusions, project)
+    end
+
+    def by_scanner(exclusions)
+      return exclusions unless params[:scanner]
+
+      exclusions.by_scanner(params[:scanner])
+    end
+
+    def by_type(exclusions)
+      return exclusions unless params[:type]
+
+      exclusions.by_type(params[:type])
+    end
+
+    def by_status(exclusions)
+      return exclusions if params[:active].nil?
+
+      exclusions.by_status(params[:active])
+    end
+  end
+end
diff --git a/ee/app/graphql/ee/types/project_type.rb b/ee/app/graphql/ee/types/project_type.rb
index 9ae99b38ed113fb46175c7a71507d1bcd0413194..5aaa170de66951015a85439d5fb8be7e25fa21c4 100644
--- a/ee/app/graphql/ee/types/project_type.rb
+++ b/ee/app/graphql/ee/types/project_type.rb
@@ -465,6 +465,13 @@ module ProjectType
           alpha: { milestone: '17.4' },
           description: 'Traces attached to the project.',
           resolver: ::Resolvers::Observability::TracesResolver
+
+        field :security_exclusions,
+          ::Types::Security::ProjectSecurityExclusionType.connection_type,
+          null: true,
+          alpha: { milestone: '17.4' },
+          description: 'Security exclusions of the project.',
+          resolver: ::Resolvers::Security::ProjectSecurityExclusionResolver
       end
 
       def tracking_key
diff --git a/ee/app/graphql/resolvers/security/project_security_exclusion_resolver.rb b/ee/app/graphql/resolvers/security/project_security_exclusion_resolver.rb
new file mode 100644
index 0000000000000000000000000000000000000000..7a7ac3ed9af0ad2cc9a252ba8830f6f2caeac92d
--- /dev/null
+++ b/ee/app/graphql/resolvers/security/project_security_exclusion_resolver.rb
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+
+module Resolvers
+  module Security
+    class ProjectSecurityExclusionResolver < BaseResolver
+      include Gitlab::Graphql::Authorize::AuthorizeResource
+
+      type Types::Security::ProjectSecurityExclusionType.connection_type, null: true
+
+      authorize :read_project_security_exclusions
+
+      description 'Find security scanner exclusions for a project.'
+
+      argument :scanner, Types::Security::ExclusionScannerEnum, required: false,
+        description: 'Filter entries by scanner.'
+
+      argument :type, Types::Security::ExclusionTypeEnum, required: false,
+        description: 'Filter entries by exclusion type.'
+
+      argument :active, GraphQL::Types::Boolean, required: false,
+        description: 'Filter entries by active status.'
+
+      def resolve(**args)
+        raise_resource_not_available_error! unless object.licensed_feature_available?(:security_exclusions)
+
+        ::Security::ProjectSecurityExclusionsFinder.new(current_user, project: object, params: args).execute
+      end
+    end
+  end
+end
diff --git a/ee/app/graphql/resolvers/security_orchestration/pipeline_execution_policy_resolver.rb b/ee/app/graphql/resolvers/security_orchestration/pipeline_execution_policy_resolver.rb
index 094fd2f99175d389db15ef2c2caa08dc1e030e1d..125757b87f913e5098953679387fb0bd20ec0388 100644
--- a/ee/app/graphql/resolvers/security_orchestration/pipeline_execution_policy_resolver.rb
+++ b/ee/app/graphql/resolvers/security_orchestration/pipeline_execution_policy_resolver.rb
@@ -19,7 +19,7 @@ class PipelineExecutionPolicyResolver < BaseResolver
         default_value: true
 
       def resolve(**args)
-        policies = Security::PipelineExecutionPoliciesFinder.new(context[:current_user], project, args).execute
+        policies = ::Security::PipelineExecutionPoliciesFinder.new(context[:current_user], project, args).execute
         construct_pipeline_execution_policies(policies)
       end
     end
diff --git a/ee/app/graphql/resolvers/security_orchestration/policy_violations_resolver.rb b/ee/app/graphql/resolvers/security_orchestration/policy_violations_resolver.rb
index 75bd03cbecd57473557264ff66d01098a683ed2f..734538e457fbfaa40d57721a6f18ed5e9343c8db 100644
--- a/ee/app/graphql/resolvers/security_orchestration/policy_violations_resolver.rb
+++ b/ee/app/graphql/resolvers/security_orchestration/policy_violations_resolver.rb
@@ -16,7 +16,7 @@ def resolve(**_args)
         raise_resource_not_available_error! '`save_policy_violation_data` feature flag is disabled.' \
           if Feature.disabled?(:save_policy_violation_data, object.project)
 
-        Security::ScanResultPolicies::PolicyViolationDetails.new(object)
+        ::Security::ScanResultPolicies::PolicyViolationDetails.new(object)
       end
     end
   end
diff --git a/ee/app/graphql/resolvers/security_orchestration/scan_execution_policy_resolver.rb b/ee/app/graphql/resolvers/security_orchestration/scan_execution_policy_resolver.rb
index e5cc9eb02a36219b1c8fba53bdd6459bb70bcba7..196b5dcbeff6261871235234f2d3334cffb80263 100644
--- a/ee/app/graphql/resolvers/security_orchestration/scan_execution_policy_resolver.rb
+++ b/ee/app/graphql/resolvers/security_orchestration/scan_execution_policy_resolver.rb
@@ -24,7 +24,7 @@ class ScanExecutionPolicyResolver < BaseResolver
         default_value: true
 
       def resolve(**args)
-        policies = Security::ScanExecutionPoliciesFinder.new(context[:current_user], project, args).execute
+        policies = ::Security::ScanExecutionPoliciesFinder.new(context[:current_user], project, args).execute
         construct_scan_execution_policies(policies)
       end
     end
diff --git a/ee/app/graphql/resolvers/security_orchestration/scan_result_policy_resolver.rb b/ee/app/graphql/resolvers/security_orchestration/scan_result_policy_resolver.rb
index 248d9a743ec4219ffd1886fee705f9d40d006b9c..3025c9a93a752ad6c86b673bd3d7b5ef1480378f 100644
--- a/ee/app/graphql/resolvers/security_orchestration/scan_result_policy_resolver.rb
+++ b/ee/app/graphql/resolvers/security_orchestration/scan_result_policy_resolver.rb
@@ -19,7 +19,7 @@ class ScanResultPolicyResolver < BaseResolver
         default_value: true
 
       def resolve(**args)
-        policies = Security::ScanResultPoliciesFinder.new(context[:current_user], object,
+        policies = ::Security::ScanResultPoliciesFinder.new(context[:current_user], object,
           args.merge(include_invalid: true)).execute
         construct_scan_result_policies(policies)
       end
diff --git a/ee/app/graphql/resolvers/security_orchestration/security_policy_project_suggestions_resolver.rb b/ee/app/graphql/resolvers/security_orchestration/security_policy_project_suggestions_resolver.rb
index 4594711fefb3506404e25c86afe95a9ad75adf0e..c4ca4aee1d2cbbb841a88927cba2a682eea2fdec 100644
--- a/ee/app/graphql/resolvers/security_orchestration/security_policy_project_suggestions_resolver.rb
+++ b/ee/app/graphql/resolvers/security_orchestration/security_policy_project_suggestions_resolver.rb
@@ -23,7 +23,7 @@ class SecurityPolicyProjectSuggestionsResolver < BaseResolver
       def resolve(**args)
         args[:search_globally] = !gitlab_com_subscription?
 
-        Security::SecurityPolicyProjectsFinder
+        ::Security::SecurityPolicyProjectsFinder
           .new(container: object, current_user: current_user, params: args)
           .execute
       end
diff --git a/ee/app/graphql/resolvers/security_report/finding_resolver.rb b/ee/app/graphql/resolvers/security_report/finding_resolver.rb
index 91708b56de254b27214c736fd3555334b5817ee1..59b803bc5ee582c5ca77abba0d8312b341975557 100644
--- a/ee/app/graphql/resolvers/security_report/finding_resolver.rb
+++ b/ee/app/graphql/resolvers/security_report/finding_resolver.rb
@@ -13,9 +13,9 @@ class FindingResolver < BaseResolver
 
       def resolve(**args)
         if Feature.enabled?(:finding_resolver_use_pure_finder, pipeline.project)
-          Security::PureFindingsFinder.new(pipeline, params: { uuid: args[:uuid], scope: 'all' }).execute&.first
+          ::Security::PureFindingsFinder.new(pipeline, params: { uuid: args[:uuid], scope: 'all' }).execute&.first
         else
-          Security::FindingsFinder.new(pipeline, params: { uuid: args[:uuid], scope: 'all' }).execute&.findings&.first
+          ::Security::FindingsFinder.new(pipeline, params: { uuid: args[:uuid], scope: 'all' }).execute&.findings&.first
         end
       end
     end
diff --git a/ee/app/graphql/resolvers/security_report_summary_resolver.rb b/ee/app/graphql/resolvers/security_report_summary_resolver.rb
index 48b89dd52c3527f2fd7ed3b6d571a79125d1bf1d..e21fbd82d699e79a2004e25c3c7c1d622b31f2f4 100644
--- a/ee/app/graphql/resolvers/security_report_summary_resolver.rb
+++ b/ee/app/graphql/resolvers/security_report_summary_resolver.rb
@@ -13,7 +13,7 @@ class SecurityReportSummaryResolver < BaseResolver
     def resolve(lookahead:)
       return unless authorized_resource?(pipeline.project)
 
-      Security::ReportSummaryService.new(
+      ::Security::ReportSummaryService.new(
         pipeline,
         selection_information(lookahead)
       ).execute
diff --git a/ee/app/graphql/resolvers/vulnerability_severities_count_resolver.rb b/ee/app/graphql/resolvers/vulnerability_severities_count_resolver.rb
index 7e02c6d53264dcf2ea38440c498aa1c62cbc8573..ec9e60fd7a72a73a3218235c6653a2a6efa26d4d 100644
--- a/ee/app/graphql/resolvers/vulnerability_severities_count_resolver.rb
+++ b/ee/app/graphql/resolvers/vulnerability_severities_count_resolver.rb
@@ -94,7 +94,7 @@ def resolve(**args)
     private
 
     def vulnerabilities(filters)
-      Security::VulnerabilityReadsFinder.new(vulnerable, filters).execute
+      ::Security::VulnerabilityReadsFinder.new(vulnerable, filters).execute
     end
   end
 end
diff --git a/ee/app/graphql/types/security/exclusion_scanner_enum.rb b/ee/app/graphql/types/security/exclusion_scanner_enum.rb
new file mode 100644
index 0000000000000000000000000000000000000000..9726d03f5fba11812e90b0fddf86b92795a6906c
--- /dev/null
+++ b/ee/app/graphql/types/security/exclusion_scanner_enum.rb
@@ -0,0 +1,12 @@
+# frozen_string_literal: true
+
+module Types
+  module Security
+    class ExclusionScannerEnum < Types::BaseEnum
+      graphql_name 'ExclusionScannerEnum'
+      description 'Enum for the security scanners used with exclusions'
+
+      value 'SECRET_PUSH_PROTECTION', value: 'secret_push_protection', description: 'Secret Push Protection.'
+    end
+  end
+end
diff --git a/ee/app/graphql/types/security/exclusion_type_enum.rb b/ee/app/graphql/types/security/exclusion_type_enum.rb
new file mode 100644
index 0000000000000000000000000000000000000000..cf05cdfd8bc1451ec99c866f4806ee2320fdd21b
--- /dev/null
+++ b/ee/app/graphql/types/security/exclusion_type_enum.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+module Types
+  module Security
+    class ExclusionTypeEnum < Types::BaseEnum
+      graphql_name 'ExclusionTypeEnum'
+      description 'Enum for types of exclusion for a security scanner'
+
+      value 'PATH', value: 'path', description: 'File or directory location.'
+      value 'REGEX_PATTERN', value: 'regex_pattern', description: 'Regex pattern matching rules.'
+      value 'RAW_VALUE', value: 'raw_value', description: 'Raw value to ignore.'
+      value 'RULE', value: 'rule', description: 'Scanner rule identifier.'
+    end
+  end
+end
diff --git a/ee/app/graphql/types/security/project_security_exclusion_type.rb b/ee/app/graphql/types/security/project_security_exclusion_type.rb
new file mode 100644
index 0000000000000000000000000000000000000000..9f3eb142b0148192da5acdc39da1a92a16e14a20
--- /dev/null
+++ b/ee/app/graphql/types/security/project_security_exclusion_type.rb
@@ -0,0 +1,36 @@
+# frozen_string_literal: true
+
+module Types
+  module Security
+    # rubocop: disable Graphql/AuthorizeTypes -- The resolver authorizes the request
+    class ProjectSecurityExclusionType < BaseObject
+      graphql_name 'ProjectSecurityExclusion'
+      description 'Represents a project-level security scanner exclusion'
+
+      field :id, GraphQL::Types::ID,
+        null: false,
+        description: 'ID of the exclusion.'
+
+      field :scanner, Types::Security::ExclusionScannerEnum,
+        null: false,
+        description: 'Security scanner the exclusion will be used for.'
+
+      field :type, Types::Security::ExclusionTypeEnum,
+        null: false,
+        description: 'Type of the exclusion.'
+
+      field :value, GraphQL::Types::String,
+        null: false,
+        description: 'Value of the exclusion.'
+
+      field :description, GraphQL::Types::String,
+        null: true,
+        description: 'Optional description for the exclusion.'
+
+      field :active, GraphQL::Types::Boolean,
+        null: false,
+        description: 'Whether the exclusion is active.'
+    end
+    # rubocop: enable Graphql/AuthorizeTypes
+  end
+end
diff --git a/ee/app/models/gitlab_subscriptions/features.rb b/ee/app/models/gitlab_subscriptions/features.rb
index a4da6ffd030557a7a0380125941dfb05e30fde1c..22e454cbc742af4dbaaa583fcd7c37fa4ee4e526 100644
--- a/ee/app/models/gitlab_subscriptions/features.rb
+++ b/ee/app/models/gitlab_subscriptions/features.rb
@@ -265,7 +265,7 @@ class Features
       unique_project_download_limit
       vulnerability_finding_signatures
       container_scanning_for_registry
-      project_security_exclusions
+      security_exclusions
     ].freeze
 
     STARTER_FEATURES_WITH_USAGE_PING = %i[
diff --git a/ee/app/models/security/project_security_exclusion.rb b/ee/app/models/security/project_security_exclusion.rb
index f85fb91867243b593e48fc55c5ae86bee649dc56..2d0e15905d17b2c76e5caef969b766016d42af89 100644
--- a/ee/app/models/security/project_security_exclusion.rb
+++ b/ee/app/models/security/project_security_exclusion.rb
@@ -12,5 +12,9 @@ class ProjectSecurityExclusion < Gitlab::Database::SecApplicationRecord
     validates :scanner, :type, :value, :project, presence: true
     validates :active, inclusion: { in: [true, false] }
     validates :value, :description, length: { maximum: 255 }
+
+    scope :by_scanner, ->(scanner) { where(scanner: scanner) }
+    scope :by_type, ->(type) { where(type: type) }
+    scope :by_status, ->(status) { where(active: status) }
   end
 end
diff --git a/ee/lib/api/vulnerabilities.rb b/ee/lib/api/vulnerabilities.rb
index 204e3b8c6a94ef291a9de63c1436a01b7104d616..f1252151add73bea02f2f85f9c35d5febc1cd9b5 100644
--- a/ee/lib/api/vulnerabilities.rb
+++ b/ee/lib/api/vulnerabilities.rb
@@ -12,7 +12,7 @@ class Vulnerabilities < ::API::Base
 
     helpers do
       def vulnerabilities_by(project)
-        Security::VulnerabilityReadsFinder.new(project).execute.as_vulnerabilities
+        ::Security::VulnerabilityReadsFinder.new(project).execute.as_vulnerabilities
       end
 
       def find_vulnerability!
diff --git a/ee/lib/api/vulnerability_findings.rb b/ee/lib/api/vulnerability_findings.rb
index 5aff0c2b5c9d907df1068511f1f10fde0a8b7ceb..c63de9c73558f9b7f1681376a05fee60ff29d325 100644
--- a/ee/lib/api/vulnerability_findings.rb
+++ b/ee/lib/api/vulnerability_findings.rb
@@ -28,7 +28,7 @@ def vulnerability_findings
       end
 
       def findings
-        pure_finder = Security::PureFindingsFinder.new(pipeline, params: finder_params)
+        pure_finder = ::Security::PureFindingsFinder.new(pipeline, params: finder_params)
 
         pure_finder.execute
                    .tap { |findings| findings.each(&:remediations) } # initiates Batchloader
diff --git a/ee/spec/factories/security/project_security_exclusions.rb b/ee/spec/factories/security/project_security_exclusions.rb
index 428648dda78a1bdce7fc0f63f68cfdac75c7182a..a7d33e7299d0ab7cd1830628ab85c7d985b47e31 100644
--- a/ee/spec/factories/security/project_security_exclusions.rb
+++ b/ee/spec/factories/security/project_security_exclusions.rb
@@ -4,7 +4,7 @@
   factory :project_security_exclusion, class: 'Security::ProjectSecurityExclusion' do
     scanner { 'secret_push_protection' }
     description { 'basic exclusion with a specific value to exclude from scanning' }
-    type { 'raw_value' }
+    type { :raw_value }
     value { '01234567890123456789-glpat'.reverse }
     active { true }
   end
diff --git a/ee/spec/finders/security/project_security_exclusions_finder_spec.rb b/ee/spec/finders/security/project_security_exclusions_finder_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..90fd1a104ff4b4b1c06cc5b1d79ac7ee86aa51f4
--- /dev/null
+++ b/ee/spec/finders/security/project_security_exclusions_finder_spec.rb
@@ -0,0 +1,64 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Security::ProjectSecurityExclusionsFinder, feature_category: :secret_detection do
+  let_it_be(:project) { create(:project) }
+  let_it_be(:user) { create(:user) }
+
+  let(:exclusions) do
+    {
+      inactive: create(:project_security_exclusion, project: project, active: false),
+      raw_value: create(:project_security_exclusion, project: project),
+      path: create(:project_security_exclusion, project: project, type: :path, value: 'spec/models/project_spec.rb'),
+      regex: create(:project_security_exclusion, project: project, type: :regex_pattern, value: 'SK[0-9a-fA-F]{32}'),
+      rule: create(:project_security_exclusion, project: project, type: :rule, value: 'gitlab_personal_access_token')
+    }
+  end
+
+  let(:params) { {} }
+
+  subject(:finder) { described_class.new(user, project: project, params: params) }
+
+  shared_examples 'returns expected exclusions' do |expected_exclusions|
+    it 'returns the correct exclusions' do
+      expect(finder.execute).to contain_exactly(*expected_exclusions.map { |key| exclusions[key] })
+    end
+  end
+
+  describe '#execute' do
+    context 'with a role that can read security exclusions' do
+      before_all { project.add_maintainer(user) }
+
+      context 'without filters' do
+        include_examples 'returns expected exclusions', [:rule, :regex, :raw_value, :path, :inactive]
+      end
+
+      context 'when filtering by security scanner' do
+        let(:params) { { scanner: 'secret_push_protection' } }
+
+        include_examples 'returns expected exclusions', [:rule, :regex, :raw_value, :path, :inactive]
+      end
+
+      context 'when filtering by exclusion type' do
+        let(:params) { { type: 'rule' } }
+
+        include_examples 'returns expected exclusions', [:rule]
+      end
+
+      context 'when filtering by exclusion status' do
+        let(:params) { { active: true } }
+
+        include_examples 'returns expected exclusions', [:rule, :regex, :raw_value, :path]
+      end
+    end
+
+    context 'with a role that cannot read security exclusions' do
+      before_all { project.add_reporter(user) }
+
+      it 'returns no exclusions' do
+        expect(finder.execute).to be_empty
+      end
+    end
+  end
+end
diff --git a/ee/spec/graphql/resolvers/pipeline_security_report_findings_resolver_spec.rb b/ee/spec/graphql/resolvers/pipeline_security_report_findings_resolver_spec.rb
index a9d874dbfae0b9f6cae7489e06065986c3a59d93..67471b55a2cf019d830895e133159a1f3057406d 100644
--- a/ee/spec/graphql/resolvers/pipeline_security_report_findings_resolver_spec.rb
+++ b/ee/spec/graphql/resolvers/pipeline_security_report_findings_resolver_spec.rb
@@ -17,10 +17,10 @@
 
     let(:params) { {} }
 
-    let(:mock_pure_finder) { instance_double(Security::PureFindingsFinder, execute: returned_findings) }
+    let(:mock_pure_finder) { instance_double(::Security::PureFindingsFinder, execute: returned_findings) }
 
     before do
-      allow(Security::PureFindingsFinder).to receive(:new).and_return(mock_pure_finder)
+      allow(::Security::PureFindingsFinder).to receive(:new).and_return(mock_pure_finder)
     end
 
     context 'when given severities' do
diff --git a/ee/spec/graphql/resolvers/security/project_security_exclusion_resolver_spec.rb b/ee/spec/graphql/resolvers/security/project_security_exclusion_resolver_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..134c915416609c6a4573edbfae3032fcbeb3d7ea
--- /dev/null
+++ b/ee/spec/graphql/resolvers/security/project_security_exclusion_resolver_spec.rb
@@ -0,0 +1,101 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Resolvers::Security::ProjectSecurityExclusionResolver, feature_category: :secret_detection do
+  include GraphqlHelpers
+
+  describe '#resolve' do
+    let_it_be(:user) { create(:user) }
+    let_it_be(:project) { create(:project) }
+    let_it_be(:active_exclusion) { create(:project_security_exclusion, project: project) }
+    let_it_be(:inactive_exclusion) { create(:project_security_exclusion, project: project, active: false) }
+
+    let(:args) { {} }
+
+    subject(:resolver) { resolve(described_class, obj: project, ctx: { current_user: user }, args: args) }
+
+    context 'when the feature is not licensed' do
+      it 'raises a resource not available error' do
+        expect_graphql_error_to_be_created(Gitlab::Graphql::Errors::ResourceNotAvailable) do
+          resolver
+        end
+      end
+    end
+
+    context 'when the feature is licensed' do
+      before do
+        stub_licensed_features(security_exclusions: true)
+      end
+
+      context 'for a role that can read security exclusions' do
+        before_all do
+          project.add_maintainer(user)
+        end
+
+        it 'calls ProjectSecurityExclusionsFinder with correct arguments' do
+          finder = instance_double(
+            ::Security::ProjectSecurityExclusionsFinder,
+            execute: [active_exclusion, inactive_exclusion]
+          )
+
+          expect(::Security::ProjectSecurityExclusionsFinder).to receive(:new)
+            .with(user, project: project, params: args)
+            .and_return(finder)
+
+          resolver
+        end
+
+        it 'returns all exclusions when no arguments are provided' do
+          expect(resolver).to contain_exactly(active_exclusion, inactive_exclusion)
+        end
+
+        context 'when filtering by scanner' do
+          let(:args) { { scanner: 'secret_push_protection' } }
+
+          it 'passes the scanner argument to the finder' do
+            expect(::Security::ProjectSecurityExclusionsFinder).to receive(:new)
+              .with(user, project: project, params: hash_including(scanner: 'secret_push_protection'))
+              .and_call_original
+
+            resolver
+          end
+        end
+
+        context 'when filtering by type' do
+          let(:args) { { type: 'raw_value' } }
+
+          it 'passes the type argument to the finder' do
+            expect(::Security::ProjectSecurityExclusionsFinder).to receive(:new)
+              .with(user, project: project, params: hash_including(type: 'raw_value'))
+              .and_call_original
+
+            resolver
+          end
+        end
+
+        context 'when filtering by active status' do
+          let(:args) { { active: true } }
+
+          it 'passes the status argument to the finder' do
+            expect(::Security::ProjectSecurityExclusionsFinder).to receive(:new)
+              .with(user, project: project, params: hash_including(active: true))
+              .and_call_original
+
+            resolver
+          end
+        end
+      end
+
+      context 'for a role that cannot read security exclusions' do
+        before_all do
+          project.add_reporter(user)
+        end
+
+        it 'returns no exclusions' do
+          expect(resolver).to be_empty
+        end
+      end
+    end
+  end
+end
diff --git a/ee/spec/graphql/resolvers/security_orchestration/security_policy_project_suggestions_resolver_spec.rb b/ee/spec/graphql/resolvers/security_orchestration/security_policy_project_suggestions_resolver_spec.rb
index aa07e547895744b02a3cc85b508b473a5559a1ad..8117abc35976e0e3b37a9edffd8f1b0dbb7cc408 100644
--- a/ee/spec/graphql/resolvers/security_orchestration/security_policy_project_suggestions_resolver_spec.rb
+++ b/ee/spec/graphql/resolvers/security_orchestration/security_policy_project_suggestions_resolver_spec.rb
@@ -108,7 +108,7 @@
         let(:args) { { search: project.full_path } }
 
         before do
-          expect_next_instance_of(Security::SecurityPolicyProjectsFinder) do |service|
+          expect_next_instance_of(::Security::SecurityPolicyProjectsFinder) do |service|
             allow(service).to receive(:global_matching_projects) \
                                 .and_return(Project.none)
                                 .once
diff --git a/ee/spec/graphql/resolvers/security_report_summary_resolver_spec.rb b/ee/spec/graphql/resolvers/security_report_summary_resolver_spec.rb
index dfd090cb5c4a95513edea84b387c9ac6697dd6a3..ea7f8ec84a29a8477de8e53be353c96b98f4c582 100644
--- a/ee/spec/graphql/resolvers/security_report_summary_resolver_spec.rb
+++ b/ee/spec/graphql/resolvers/security_report_summary_resolver_spec.rb
@@ -31,7 +31,7 @@
 
       it 'returns calls the ReportSummaryService' do
         expect_next_instance_of(
-          Security::ReportSummaryService,
+          ::Security::ReportSummaryService,
           pipeline,
           expected_selection_info
         ) do |summary_service|
@@ -71,7 +71,7 @@
 
       it 'does not search for :__typename' do
         expect_next_instance_of(
-          Security::ReportSummaryService,
+          ::Security::ReportSummaryService,
           pipeline,
           expected_selection_info
         ) do |summary_service|
diff --git a/ee/spec/graphql/types/project_type_spec.rb b/ee/spec/graphql/types/project_type_spec.rb
index a736ab45a3d18c0aae59c392149eb88c20378bb8..41c3aaf89289cc7e624084ad709003c9a0c32a87 100644
--- a/ee/spec/graphql/types/project_type_spec.rb
+++ b/ee/spec/graphql/types/project_type_spec.rb
@@ -33,7 +33,7 @@
       runner_cloud_provisioning google_cloud_artifact_registry_repository marked_for_deletion_on
       is_adjourned_deletion_enabled permanent_deletion_date ai_metrics saved_reply merge_trains
       pending_member_approvals observability_logs_links observability_metrics_links
-      observability_traces_links dependencies
+      observability_traces_links dependencies security_exclusions
     ]
 
     expect(described_class).to include_graphql_fields(*expected_fields)
diff --git a/ee/spec/graphql/types/security/exclusion_scanner_enum_spec.rb b/ee/spec/graphql/types/security/exclusion_scanner_enum_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..6337dfdb853cbfc04c86553d50fc6dfaade893dc
--- /dev/null
+++ b/ee/spec/graphql/types/security/exclusion_scanner_enum_spec.rb
@@ -0,0 +1,8 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe GitlabSchema.types['ExclusionScannerEnum'], feature_category: :secret_detection do
+  it { expect(described_class.graphql_name).to eq('ExclusionScannerEnum') }
+  it { expect(described_class.values.keys).to include(*%w[SECRET_PUSH_PROTECTION]) }
+end
diff --git a/ee/spec/graphql/types/security/exclusion_type_enum_spec.rb b/ee/spec/graphql/types/security/exclusion_type_enum_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..0ec05d26c5a8c870d025d7e9226f8134c939d1e3
--- /dev/null
+++ b/ee/spec/graphql/types/security/exclusion_type_enum_spec.rb
@@ -0,0 +1,8 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe GitlabSchema.types['ExclusionTypeEnum'], feature_category: :secret_detection do
+  it { expect(described_class.graphql_name).to eq('ExclusionTypeEnum') }
+  it { expect(described_class.values.keys).to include(*%w[PATH REGEX_PATTERN RAW_VALUE RULE]) }
+end
diff --git a/ee/spec/graphql/types/security/project_security_exclusion_type_spec.rb b/ee/spec/graphql/types/security/project_security_exclusion_type_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..2c4bca04b413a9ec57229be4804f07f1c470ba7b
--- /dev/null
+++ b/ee/spec/graphql/types/security/project_security_exclusion_type_spec.rb
@@ -0,0 +1,8 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe GitlabSchema.types['ProjectSecurityExclusion'], feature_category: :secret_detection do
+  it { expect(described_class.graphql_name).to eq('ProjectSecurityExclusion') }
+  it { expect(described_class).to have_graphql_fields(:id, :scanner, :type, :value, :description, :active) }
+end
diff --git a/ee/spec/models/security/project_security_exclusion_spec.rb b/ee/spec/models/security/project_security_exclusion_spec.rb
index 31b29c5d2c76529c0ca3e958dbf96cd837dcadf1..01e9ef611f0993df886faa8f9b5819bc4c45f0d6 100644
--- a/ee/spec/models/security/project_security_exclusion_spec.rb
+++ b/ee/spec/models/security/project_security_exclusion_spec.rb
@@ -21,6 +21,32 @@
     it { is_expected.to define_enum_for(:type).with_values([:path, :regex_pattern, :raw_value, :rule]) }
   end
 
+  describe 'scopes' do
+    let_it_be(:project) { create(:project) }
+    let_it_be(:exclusion_1) { create(:project_security_exclusion, project: project) }
+    let_it_be(:exclusion_2) { create(:project_security_exclusion, project: project, active: false) }
+    let_it_be(:exclusion_3) { create(:project_security_exclusion, project: project, type: :path) }
+
+    describe '.by_scanner' do
+      it 'returns the correct records' do
+        expect(described_class.by_scanner(:secret_push_protection)).to match_array([exclusion_1, exclusion_2,
+          exclusion_3])
+      end
+    end
+
+    describe '.by_type' do
+      it 'returns the correct records' do
+        expect(described_class.by_type(:raw_value)).to match_array([exclusion_1, exclusion_2])
+      end
+    end
+
+    describe '.by_status' do
+      it 'returns the correct records' do
+        expect(described_class.by_status(true)).to match_array([exclusion_1, exclusion_3])
+      end
+    end
+  end
+
   context 'with loose foreign key on project_security_exclusions.project_id' do
     it_behaves_like 'cleanup by a loose foreign key' do
       let_it_be(:parent) { create(:project) }