[Backend] Security Scan Profiles: SPP - First Phase Implementation Plan
Suggested changes:
1. Database Tables
Introduce 3 new tables to support security scan profiles:
-
security_scan_profiles- Profile definitions scoped to root namespaces -
security_scan_profiles_projects- Many-to-many relationship between profiles and projects -
security_scan_profile_triggers- Trigger configurations for each profile
2. Permissions
- Introduce
admin_security_profilesability required for attach/detach profiles mutations. Since this mutation might persist default values to the DB, this permission should be checked against the root namespace in context. - Introduce
read_security_profilesability required for queryingavailableSecurityScanProfilesandattachedSecurityScanProfilesfields.
3. GraphQL Type Extensions
- Extend the
Grouptype withavailableSecurityScanProfilesfield that returns profiles available for attachment, sourced from the root group'ssecurity_scan_profileswherenamespace_idmatches the root namespace. When no profiles exist in the database, it returns virtual defaultgitlab-recommendedprofile templates (e.g., Secret Detection template). - Extend
Projecttype withattachedSecurityScanProfilesfield that returns profiles currently attached via thesecurity_scan_profiles_projectsjoin table.
Both fields require read_security_profiles permission. The profile resolution logic checks the database first, then falls back to predefined templates if the root namespace has no custom profiles.
4. GraphQL Mutations- Profile Attach and Detach
Make sure point 6 is implemented before adding a mutation to avoid data inconsistencies.
- Introduce
attachSecurityScanProfilebulk mutation that accepts target items (projects or groups, supporting mixed selection) and a profile identifier. For the profile identifier, consider two implementation approaches:
Option A: Separate parameters forscan_profile_typeandnamespace_idto explicitly request profile creation, for non-persisted profiles.-
Option B: Single
profile_idparameter accepting either real IDs or template identifiers (e.g.,gid://gitlab/Security::ScanProfile/SecretDetection)
The mutation behaves differently based on whether the profile exists:
- If the profile exists in the root namespace, it attaches to the specified projects.
- If it doesn't exist, it creates a new
gitlab-recommendedprofile for the root namespace and then attaches it. Profile creation includes automatic creation of an associated trigger record insecurity_scan_profile_triggerswith the default type.
For project selections, the mutation synchronously creates entries in security_scan_profiles_projects. For group selections, it enqueues SecurityScanProfiles::AttachWorker to handle asynchronous batch processing of groups and subgroups. The mutation requires admin_security_profiles permission on the target root namespace. The mutation should enforce an arbitrary limit of 10 profiles per project.
- Introduce a complementary bulk mutation
detachSecurityScanProfilewhich takes the same target items and a persisted profile id. It removes the specified profile from each target, using the same logic asattachSecurityScanProfile(synchronous for projects and asynchronous for groups).
5. Async Worker for Bulk Operations
Implement SecurityScanProfiles::AttachWorker to process group-level attachments. The worker should batch over the projects under those subgroups, and process them async, to avoid overwhelming the database. It creates entries in security_scan_profiles_projects for each project and handles cases where projects may already have the profile attached as an idempotent operation.
6. Cleanup Worker for Namespace Transfers
Implement SecurityScanProfiles::CleanupWorker to handle cleanup when projects or groups are transferred to another root group. The worker should subscribe to both ProjectTransferredEvent and GroupTransferredEvent. When triggered, it should remove any records in security_scan_profiles_projects that are linked to any namespaces no longer associated with the affected projects.
Note: We also need to handle a top-level group being attached to another group as a child group. The behavior must be defined by the product.
Verification steps
-
Verify mutation can attach existing profiles to individual projects -
Verify mutation creates new gitlab-recommendedprofiles when using template identifier -
Verify mutation handles mixed selection of projects and groups -
Verify bulk operation for groups enqueues worker and processes projects in subgroups asynchronously -
Verify worker batches over projects and handles already-attached profiles as idempotent operation -
Verify availableSecurityScanProfileson Group type returns database profiles when they exist -
Verify availableSecurityScanProfilesreturns template profiles as fallback when no database profiles exist -
Verify attachedSecurityScanProfileson Project type correctly shows attached profiles -
Verify cleanup worker removes profile attachments after project transfers to different root namespace -
Verify cleanup worker removes profile attachments after group transfers to different root namespace -
Verify admin_security_profilespermission is required for attach mutation -
Verify read_security_profilespermission is required for querying profile fields