Add ability to Save Scans and Manage Saved Scans - Implementation
Implementation issue for design: https://gitlab.com/gitlab-org/gitlab/-/issues/283937 | Create a scan (saved scan) | Manage scans | | ------ | ------ | | ![mvc-saved-scans](/uploads/04ac0514980916748387f71e34e1f7e2/mvc-saved-scans.png) | ![manage-scans-SSOT](/uploads/76279d715e091b1e75636c681a12922d/manage-scans-SSOT.png) | # Implementation Plan * `dast_saved_scans` ~"feature flag" rollout issue: https://gitlab.com/gitlab-org/gitlab/-/issues/295252 | Task # | Issue | Description | Department | | ------ | ---------------------------------------------------- | --------------------------------------------------------- | -------------- | | 1 | https://gitlab.com/gitlab-org/gitlab/-/issues/295240 | Create feature flag and new **On-demand scans** routes | ~frontend | | 2 | https://gitlab.com/gitlab-org/gitlab/-/issues/295243 | Create the `DastScan` model | ~backend | | 3 | https://gitlab.com/gitlab-org/gitlab/-/issues/295241 | Add new inputs to the **On-demand scans** form | ~frontend | | 4 | https://gitlab.com/gitlab-org/gitlab/-/issues/295244 | Implement `dastProfileCreate` and `dastProfileUpdate` mutations | ~backend | | 5 | https://gitlab.com/gitlab-org/gitlab/-/issues/295242 | Add edit mode to the **On-demand scans** form | ~frontend | | 6 | https://gitlab.com/gitlab-org/gitlab/-/issues/296752 | Cleanup client-side mutations | ~frontend | | 7 | https://gitlab.com/gitlab-org/gitlab/-/issues/295245 | Update DAST profiles library UI | ~frontend | | 8 | https://gitlab.com/gitlab-org/gitlab/-/issues/295246 | Add **Saved scans** tab | ~frontend | | 9 | https://gitlab.com/gitlab-org/gitlab/-/issues/295248 | Create `project.dastProfiles` query | ~backend | | 10 | https://gitlab.com/gitlab-org/gitlab/-/issues/295247 | Implement **Run scan** and **Delete scan** actions | ~frontend | | 11 | https://gitlab.com/gitlab-org/gitlab/-/issues/295249 | Create `dastProfileRun` and `dastProfileDelete` mutation | ~backend | | 12 | https://gitlab.com/gitlab-org/gitlab/-/issues/295618 | Document DAST saved scans | ~documentation | ## Future iterations * Scan statuses in library. # Decisions ## GraphQL schemas ### `DastProfile` type This is the type whose fields may be selected in queries and mutations. ```graphql type DastProfile { id: DastProfileID name: String description: String dastSiteProfile: DastSiteProfile dastProfilenerProfile: DastProfilenerProfile editPath: String } ``` ### `dastProfileCreate` mutation This mutation will be used to create a new `DastProfile` entry in the DB. It will be triggered by the **On-demand scans** form. It accepts a scan's name, description, scanner & site profile, as well as a boolean to determine whether the scan should be run immediately. ```graphql mutation dastProfileCreate($fullPath: ID!, $name: String!, $description: String, $dastSiteProfileId: DastSiteProfileID!, $dastProfilenerProfileID: DastProfilenerProfileID!, $runAfterCreate: Boolean) { dastProfileCreate( input: { fullPath: $fullPath, name: $name, description: $description, dastSiteProfileId: $dastSiteProfileId, dastProfilenerProfileID: $dastProfilenerProfileID, runAfterCreate: $runAfterCreate } ) { dastProfile { id name description dastSiteProfile { id profileName } dastProfilenerProfile { id profileName } editPath } pipelineUrl errors } } ``` ### `dastProfileUpdate` mutation This mutation will be used to update an existing `DastProfile` entry. It accepts the same fields as `dastProfileCreate` as well as the ID of the scan being edited. ```graphql mutation dastProfileUpdate($fullPath: ID!, $id: DastProfileID!, $name: String, $description: String, $dastSiteProfileId: DastSiteProfileID, $dastProfilenerProfileID: $DastProfilenerProfileID, $runAfterUpdate: Boolean) { dastProfileUpdate( input: { id: $id, fullPath: $fullPath, name: $name, description: $description, dastSiteProfileId: $dastSiteProfileId, dastProfilenerProfileID: $dastProfilenerProfileID, runAfterUpdate: $runAfterUpdate } ) { dastProfile { id name description dastSiteProfile { id profileName } dastProfilenerProfile { id profileName } editPath } pipelineUrl errors } } ``` ### `project.dastProfiles` query This query will be used to query the paginated list of saved scans to be displayed in the DAST profiles library, under the **Saved scans** tab. ```graphql query project($fullPath: ID!, first: Int!) { project(fullPath: $fullPath) { dastProfiles(first: $first) { nodes { id name description dastSiteProfile { id profileName } dastProfilenerProfile { id profileName } editPath } } } } ``` ### `dastProfileRun` mutation This mutation will be used to run a scan directly from the **Saved scans** tab. It accepts a saved scan's ID. ```graphql mutation dastProfileRun($fullPath: ID!, $id: DastProfileID!) { dastProfileRun( input: { fullPath: $fullPath, id: $id, } ) { pipelineUrl errors } } ``` ### `dastProfileDelete` mutation This mutation will be used to delete a saved scans in the **Saved scans** tab. It accepts a saved scan's ID. ```graphql mutation dastProfileDelete($projectFullPath: ID!, $id: DastProfileID!) { dastProfileDelete( input: { fullPath: $projectFullPath, id: $id } ) { errors } } ```
epic