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 |
| ------ | ------ |
|  |  |
# 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