Add SecurityScanProfileDelete GraphQL mutation
What does this MR do and why?
Introduces Mutations::Security::ScanProfiles::Delete (GraphQL securityScanProfileDelete), which deletes a Security::ScanProfile.
A scan profile is namespace-scoped and can be attached to thousands of projects via Security::ScanProfileProject, so deletion has to stay cheap regardless of fan-out. The mutation soft-deletes the profile synchronously — profile.destroy sets deleted_at, which hides it from every read path immediately — and enqueues Security::ScanProfiles::DeleteScanProfilesWorker to clean up the join records and hard-delete the row asynchronously. This reuses the soft-delete infrastructure merged in Soft-delete Security::ScanProfile records (!247671 - merged) • rossfuhrman • 19.3. GitLab-recommended profiles are system-managed and cannot be deleted: the mutation short-circuits with the error Cannot delete a GitLab-recommended scan profile. and makes no state change.
Authorization goes through a new, dedicated delete_security_scan_profiles permission, used for both the DeclarativePolicy authorize check and the authorize_granular_token directive (group boundary). Because the ability is prevented when the security_scan_profiles licensed feature is off, the license gate is automatic. The mutation is mounted as an experiment (milestone: '19.3') behind the default-off configurable_security_scan_profiles feature flag, the same flag gating the sibling Create/Update mutations — so no changelog entry is required for this change.
The delete_scan_profile internal event is added in a separate follow-up MR to keep the Analytics Instrumentation review scoped.
Part of epic &20193 (Advanced Configuration Profiles for Security Scanners).
Related issue
Add SecurityScanProfileDelete GraphQL mutation ... (#607385) • rossfuhrman
How to set up and validate locally
-
Enable the feature flag (default-off) and make sure the group has the
security_scan_profileslicensed feature (Ultimate):Feature.enable(:configurable_security_scan_profiles) -
In a Rails console, create a non-recommended secret-detection profile attached to a project:
group = Group.roots.first project = group.all_projects.first profile = Security::ScanProfile.create!(namespace: group, scan_type: :secret_detection, name: 'Delete demo', gitlab_recommended: false) profile.projects << project -
Run the mutation in GraphiQL (
/-/graphql-explorer) or via curl, using the profile's Global ID:mutation { securityScanProfileDelete(input: { id: "gid://gitlab/Security::ScanProfile/<ID>" }) { deletedScanProfileId errors } }Expect
errors: []anddeletedScanProfileIdechoing the profile's GID. -
Confirm the profile is soft-deleted immediately (hidden from reads, row still present):
Security::ScanProfile.not_deleted.exists?(profile.id) # => false Security::ScanProfile.unscoped.exists?(profile.id) # => true (until the worker runs) -
After
Security::ScanProfiles::DeleteScanProfilesWorkerruns (processed automatically in GDK, or run inline), confirm the hard delete cascaded:Security::ScanProfile.unscoped.exists?(profile.id) # => false Security::ScanProfileProject.where(security_scan_profile_id: profile.id).count # => 0 -
Confirm a GitLab-recommended profile cannot be deleted: the mutation returns
Cannot delete a GitLab-recommended scan profile.and the row is untouched.
MR acceptance checklist
Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.