diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index d54afe84c25d9fda6be156e096592f837c727303..78f2f584a8a0a6cdce7410cca50ade91a6743c6d 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -5616,6 +5616,28 @@ Input type: `ProjectSetComplianceFrameworkInput`
 | <a id="mutationprojectsetcomplianceframeworkerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
 | <a id="mutationprojectsetcomplianceframeworkproject"></a>`project` | [`Project`](#project) | Project after mutation. |
 
+### `Mutation.projectSetContinuousVulnerabilityScanning`
+
+Enable/disable Continuous Vulnerability Scanning for the given project.
+
+Input type: `ProjectSetContinuousVulnerabilityScanningInput`
+
+#### Arguments
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="mutationprojectsetcontinuousvulnerabilityscanningclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
+| <a id="mutationprojectsetcontinuousvulnerabilityscanningenable"></a>`enable` | [`Boolean!`](#boolean) | Desired status for Continuous Vulnerability Scanning feature. |
+| <a id="mutationprojectsetcontinuousvulnerabilityscanningprojectpath"></a>`projectPath` | [`ID!`](#id) | Full path of the project. |
+
+#### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="mutationprojectsetcontinuousvulnerabilityscanningclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
+| <a id="mutationprojectsetcontinuousvulnerabilityscanningcontinuousvulnerabilityscanningenabled"></a>`continuousVulnerabilityScanningEnabled` | [`Boolean!`](#boolean) | Whether feature is enabled. |
+| <a id="mutationprojectsetcontinuousvulnerabilityscanningerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
+
 ### `Mutation.projectSetLocked`
 
 Input type: `ProjectSetLockedInput`
diff --git a/ee/app/assets/javascripts/security_configuration/graphql/project_set_continuous_vulnerability_scanning.graphql b/ee/app/assets/javascripts/security_configuration/graphql/project_set_continuous_vulnerability_scanning.graphql
new file mode 100644
index 0000000000000000000000000000000000000000..79f4316d106097d1779c275f086c5fd218cca6e6
--- /dev/null
+++ b/ee/app/assets/javascripts/security_configuration/graphql/project_set_continuous_vulnerability_scanning.graphql
@@ -0,0 +1,8 @@
+mutation ProjectSetContinuousVulnerabilityScanning(
+  $input: ProjectSetContinuousVulnerabilityScanningInput!
+) {
+  projectSetContinuousVulnerabilityScanning(input: $input) {
+    continuousVulnerabilityScanningEnabled
+    errors
+  }
+}
diff --git a/ee/app/graphql/ee/types/mutation_type.rb b/ee/app/graphql/ee/types/mutation_type.rb
index b80d0ea8eb6747b4f7cbff820bfb2df26ece85eb..6fa6ee1ae077b63befa7cd4249024e948f262ede 100644
--- a/ee/app/graphql/ee/types/mutation_type.rb
+++ b/ee/app/graphql/ee/types/mutation_type.rb
@@ -128,6 +128,7 @@ module MutationType
         mount_mutation ::Mutations::AuditEvents::Streaming::InstanceHeaders::Destroy
         mount_mutation ::Mutations::AuditEvents::Streaming::InstanceEventTypeFilters::Create
         mount_mutation ::Mutations::AuditEvents::Streaming::InstanceEventTypeFilters::Destroy
+        mount_mutation ::Mutations::Security::CiConfiguration::ProjectSetContinuousVulnerabilityScanning
 
         prepend(Types::DeprecatedMutations)
       end
diff --git a/ee/app/graphql/mutations/security/ci_configuration/project_set_continuous_vulnerability_scanning.rb b/ee/app/graphql/mutations/security/ci_configuration/project_set_continuous_vulnerability_scanning.rb
new file mode 100644
index 0000000000000000000000000000000000000000..a3955bfbe18c56d6362973c8fe98d4b3e9353ffa
--- /dev/null
+++ b/ee/app/graphql/mutations/security/ci_configuration/project_set_continuous_vulnerability_scanning.rb
@@ -0,0 +1,40 @@
+# frozen_string_literal: true
+
+module Mutations
+  module Security
+    module CiConfiguration
+      class ProjectSetContinuousVulnerabilityScanning < BaseMutation
+        graphql_name 'ProjectSetContinuousVulnerabilityScanning'
+
+        include FindsProject
+
+        description <<~DESC
+          Enable/disable Continuous Vulnerability Scanning for the given project.
+        DESC
+
+        argument :project_path, GraphQL::Types::ID,
+          required: true,
+          description: 'Full path of the project.'
+
+        argument :enable, GraphQL::Types::Boolean,
+          required: true,
+          description: 'Desired status for Continuous Vulnerability Scanning feature.'
+
+        field :continuous_vulnerability_scanning_enabled, GraphQL::Types::Boolean,
+          null: false,
+          description: 'Whether feature is enabled.'
+
+        authorize :enable_continuous_vulnerability_scans
+
+        def resolve(project_path:, enable:)
+          project = authorized_find!(project_path)
+
+          enabled = ::Security::Configuration::ProjectSetContinuousVulnerabilityScanningService
+            .execute(project: project, enable: enable)
+
+          { continuous_vulnerability_scanning_enabled: enabled, errors: [] }
+        end
+      end
+    end
+  end
+end
diff --git a/ee/app/models/project_security_setting.rb b/ee/app/models/project_security_setting.rb
index bfc9bae8b8c56a212126843eb15fd97682aec45c..a243f324675751bdcc4a092991a53bb171f83a63 100644
--- a/ee/app/models/project_security_setting.rb
+++ b/ee/app/models/project_security_setting.rb
@@ -23,9 +23,7 @@ def auto_fix_enabled_types
     end
   end
 
-  # TODO: the caller for this method is a graphql mutation implemented as part of
-  # https://gitlab.com/gitlab-org/gitlab/-/issues/424374
   def set_continuous_vulnerability_scans!(enabled:)
-    update!(continuous_vulnerability_scans_enabled: enabled)
+    enabled if update!(continuous_vulnerability_scans_enabled: enabled)
   end
 end
diff --git a/ee/app/policies/ee/project_policy.rb b/ee/app/policies/ee/project_policy.rb
index 5c44dae6bc1c8126f4a1f5f76224a49234a2ff18..938d70f7dd982e2d9986adc51e8652b8a4830876 100644
--- a/ee/app/policies/ee/project_policy.rb
+++ b/ee/app/policies/ee/project_policy.rb
@@ -570,6 +570,10 @@ module ProjectPolicy
           .default_project_deletion_protection
       end
 
+      condition(:continuous_vulnerability_scanning_available) do
+        ::Feature.enabled?(:dependency_scanning_on_advisory_ingestion)
+      end
+
       rule { needs_new_sso_session }.policy do
         prevent :read_project
       end
@@ -700,6 +704,10 @@ module ProjectPolicy
       rule do
         (maintainer | owner | admin) & pages_multiple_versions_available
       end.enable :pages_multiple_versions
+
+      rule { continuous_vulnerability_scanning_available & can?(:developer_access) }.policy do
+        enable :enable_continuous_vulnerability_scans
+      end
     end
 
     override :lookup_access_level!
diff --git a/ee/app/services/security/configuration/project_set_continuous_vulnerability_scanning_service.rb b/ee/app/services/security/configuration/project_set_continuous_vulnerability_scanning_service.rb
new file mode 100644
index 0000000000000000000000000000000000000000..334f7d6a47d2e3d7671405545804327b515bd2e2
--- /dev/null
+++ b/ee/app/services/security/configuration/project_set_continuous_vulnerability_scanning_service.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+module Security
+  module Configuration
+    class ProjectSetContinuousVulnerabilityScanningService
+      def self.execute(project:, enable:)
+        project.security_setting.set_continuous_vulnerability_scans!(enabled: enable)
+      end
+    end
+  end
+end
diff --git a/ee/spec/models/project_security_setting_spec.rb b/ee/spec/models/project_security_setting_spec.rb
index c19ba38f8311e8c4feb60bbdf88ad318bda2d55b..aa61f3c4efad527ebb38edd92f110eadb0267db6 100644
--- a/ee/spec/models/project_security_setting_spec.rb
+++ b/ee/spec/models/project_security_setting_spec.rb
@@ -67,8 +67,8 @@
     with_them do
       let(:setting) { create(:project_security_setting, continuous_vulnerability_scans_enabled: value_before) }
 
-      specify do
-        setting.set_continuous_vulnerability_scans!(enabled: enabled)
+      it 'updates the attribute and returns the new value' do
+        expect(setting.set_continuous_vulnerability_scans!(enabled: enabled)).to eq(value_after)
         expect(setting.reload.continuous_vulnerability_scans_enabled).to eq(value_after)
       end
     end
diff --git a/ee/spec/requests/api/graphql/mutations/security/configuration/project_set_continuous_vulnerability_scanning_spec.rb b/ee/spec/requests/api/graphql/mutations/security/configuration/project_set_continuous_vulnerability_scanning_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..f506b651045f01a954c3e7ce1a79d25bffae5e78
--- /dev/null
+++ b/ee/spec/requests/api/graphql/mutations/security/configuration/project_set_continuous_vulnerability_scanning_spec.rb
@@ -0,0 +1,75 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Setting Project Continuous Vulnerability Scanning', feature_category: :software_composition_analysis do
+  using RSpec::Parameterized::TableSyntax
+  include GraphqlHelpers
+
+  let(:current_user) { create(:user) }
+  let(:security_setting) { create(:project_security_setting, continuous_vulnerability_scans_enabled: value_before) }
+  let(:project) { security_setting.project }
+  let(:mutation_name) { :project_set_continuous_vulnerability_scanning }
+  let(:mutation) do
+    graphql_mutation(
+      mutation_name,
+      project_path: project.full_path,
+      enable: enable
+    )
+  end
+
+  let(:value_before) { false }
+  let(:enable) { true }
+
+  context 'when the user does not have permission' do
+    it_behaves_like 'a mutation that returns a top-level access error'
+
+    it 'does not enable cvs' do
+      expect { post_graphql_mutation(mutation, current_user: current_user) }
+        .not_to change { security_setting.reload.continuous_vulnerability_scans_enabled }
+    end
+  end
+
+  context 'when the user has permission' do
+    before do
+      project.add_developer(current_user)
+    end
+
+    context 'and feature is enabled' do
+      before do
+        stub_feature_flags(dependency_scanning_on_advisory_ingestion: true)
+      end
+
+      where(:value_before, :enable, :value_after) do
+        true  | false | false
+        true  | true  | true
+        false | true  | true
+        false | false | false
+      end
+
+      with_them do
+        it 'updates the project setting and returns the new value' do
+          post_graphql_mutation(mutation, current_user: current_user)
+
+          response = graphql_mutation_response(mutation_name)
+          expect(response).to include({ 'continuousVulnerabilityScanningEnabled' => value_after, 'errors' => [] })
+
+          expect(security_setting.reload.continuous_vulnerability_scans_enabled).to eq(value_after)
+        end
+      end
+    end
+
+    context 'and feature is disabled' do
+      before do
+        stub_feature_flags(dependency_scanning_on_advisory_ingestion: false)
+      end
+
+      it_behaves_like 'a mutation that returns a top-level access error'
+
+      it 'does not enable cvs' do
+        expect { post_graphql_mutation(mutation, current_user: current_user) }
+          .not_to change { security_setting.reload.continuous_vulnerability_scans_enabled }
+      end
+    end
+  end
+end
diff --git a/ee/spec/services/security/configuration/project_set_continuous_vulnerability_scanning_service_spec.rb b/ee/spec/services/security/configuration/project_set_continuous_vulnerability_scanning_service_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..027cf3d2fc08d1baca4d0f18fdcbe45402e52292
--- /dev/null
+++ b/ee/spec/services/security/configuration/project_set_continuous_vulnerability_scanning_service_spec.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Security::Configuration::ProjectSetContinuousVulnerabilityScanningService, feature_category: :software_composition_analysis do
+  describe '#execute' do
+    let_it_be(:security_setting) { create(:project_security_setting, continuous_vulnerability_scans_enabled: false) }
+    let_it_be(:project) { security_setting.project }
+
+    it 'returns attribute value' do
+      expect(described_class.execute(project: project, enable: true)).to eq(true)
+      expect(described_class.execute(project: project, enable: false)).to eq(false)
+    end
+
+    it 'changes the attribute' do
+      expect { described_class.execute(project: project, enable: true) }
+        .to change { security_setting.reload.continuous_vulnerability_scans_enabled }
+        .from(false).to(true)
+      expect { described_class.execute(project: project, enable: true) }
+        .not_to change { security_setting.reload.continuous_vulnerability_scans_enabled }
+      expect { described_class.execute(project: project, enable: false) }
+        .to change { security_setting.reload.continuous_vulnerability_scans_enabled }
+        .from(true).to(false)
+      expect { described_class.execute(project: project, enable: false) }
+        .not_to change { security_setting.reload.continuous_vulnerability_scans_enabled }
+    end
+  end
+end