Commit 8635c0cd authored by Stan Hu's avatar Stan Hu

Merge branch '293680_add_dismissal_reason_to_graphql_api' into 'master'

Add `dismissal_reason` argument to the `VulnerabilityDismiss` mutation

See merge request gitlab-org/gitlab!50688
parents a6994188 ae529762
......@@ -7742,10 +7742,15 @@ input DismissVulnerabilityInput {
clientMutationId: String
"""
Reason why vulnerability should be dismissed
Comment why vulnerability should be dismissed
"""
comment: String
"""
Reason why vulnerability should be dismissed
"""
dismissalReason: VulnerabilityDismissalReason
"""
ID of the vulnerability to be dismissed
"""
......@@ -25997,10 +26002,15 @@ input VulnerabilityDismissInput {
clientMutationId: String
"""
Reason why vulnerability should be dismissed
Comment why vulnerability should be dismissed
"""
comment: String
"""
Reason why vulnerability should be dismissed
"""
dismissalReason: VulnerabilityDismissalReason
"""
ID of the vulnerability to be dismissed
"""
......@@ -26027,6 +26037,17 @@ type VulnerabilityDismissPayload {
vulnerability: Vulnerability
}
"""
The dismissal reason of the Vulnerability
"""
enum VulnerabilityDismissalReason {
ACCEPTABLE_RISK
FALSE_POSITIVE
MITIGATING_CONTROL
NOT_APPLICABLE
USED_IN_TESTS
}
"""
An edge in a connection.
"""
......
......@@ -21435,7 +21435,7 @@
},
{
"name": "comment",
"description": "Reason why vulnerability should be dismissed",
"description": "Comment why vulnerability should be dismissed",
"type": {
"kind": "SCALAR",
"name": "String",
......@@ -21443,6 +21443,16 @@
},
"defaultValue": null
},
{
"name": "dismissalReason",
"description": "Reason why vulnerability should be dismissed",
"type": {
"kind": "ENUM",
"name": "VulnerabilityDismissalReason",
"ofType": null
},
"defaultValue": null
},
{
"name": "clientMutationId",
"description": "A unique identifier for the client performing the mutation.",
......@@ -75380,7 +75390,7 @@
},
{
"name": "comment",
"description": "Reason why vulnerability should be dismissed",
"description": "Comment why vulnerability should be dismissed",
"type": {
"kind": "SCALAR",
"name": "String",
......@@ -75388,6 +75398,16 @@
},
"defaultValue": null
},
{
"name": "dismissalReason",
"description": "Reason why vulnerability should be dismissed",
"type": {
"kind": "ENUM",
"name": "VulnerabilityDismissalReason",
"ofType": null
},
"defaultValue": null
},
{
"name": "clientMutationId",
"description": "A unique identifier for the client performing the mutation.",
......@@ -75470,6 +75490,47 @@
"enumValues": null,
"possibleTypes": null
},
{
"kind": "ENUM",
"name": "VulnerabilityDismissalReason",
"description": "The dismissal reason of the Vulnerability",
"fields": null,
"inputFields": null,
"interfaces": null,
"enumValues": [
{
"name": "ACCEPTABLE_RISK",
"description": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "FALSE_POSITIVE",
"description": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "MITIGATING_CONTROL",
"description": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "USED_IN_TESTS",
"description": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "NOT_APPLICABLE",
"description": null,
"isDeprecated": false,
"deprecationReason": null
}
],
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "VulnerabilityEdge",
......@@ -4935,6 +4935,18 @@ Possible states of a user.
| `private` | |
| `public` | |
### VulnerabilityDismissalReason
The dismissal reason of the Vulnerability.
| Value | Description |
| ----- | ----------- |
| `ACCEPTABLE_RISK` | |
| `FALSE_POSITIVE` | |
| `MITIGATING_CONTROL` | |
| `NOT_APPLICABLE` | |
| `USED_IN_TESTS` | |
### VulnerabilityExternalIssueLinkExternalTracker
The external tracker of the external issue link related to a vulnerability.
......
......@@ -19,11 +19,16 @@ module Mutations
argument :comment,
GraphQL::STRING_TYPE,
required: false,
description: 'Comment why vulnerability should be dismissed'
argument :dismissal_reason,
Types::Vulnerabilities::DismissalReasonEnum,
required: false,
description: 'Reason why vulnerability should be dismissed'
def resolve(id:, comment: nil)
def resolve(id:, comment: nil, dismissal_reason: nil)
vulnerability = authorized_find!(id: id)
result = dismiss_vulnerability(vulnerability, comment)
result = dismiss_vulnerability(vulnerability, comment, dismissal_reason)
{
vulnerability: result,
......@@ -33,8 +38,8 @@ module Mutations
private
def dismiss_vulnerability(vulnerability, comment)
::Vulnerabilities::DismissService.new(current_user, vulnerability, comment).execute
def dismiss_vulnerability(vulnerability, comment, dismissal_reason)
::Vulnerabilities::DismissService.new(current_user, vulnerability, comment, dismissal_reason).execute
end
def find_object(id:)
......
# frozen_string_literal: true
module Types
module Vulnerabilities
class DismissalReasonEnum < BaseEnum
graphql_name 'VulnerabilityDismissalReason'
description 'The dismissal reason of the Vulnerability'
::Vulnerabilities::Feedback.dismissal_reasons.keys.each do |dismissal_reason|
value dismissal_reason.to_s.upcase, value: dismissal_reason.to_s
end
end
end
end
......@@ -6,9 +6,10 @@ module Vulnerabilities
class DismissService < BaseService
FindingsDismissResult = Struct.new(:ok?, :finding, :message)
def initialize(current_user, vulnerability, comment = nil, dismiss_findings: true)
def initialize(current_user, vulnerability, comment = nil, dismissal_reason = nil, dismiss_findings: true)
super(current_user, vulnerability)
@comment = comment
@dismissal_reason = dismissal_reason
@dismiss_findings = dismiss_findings
end
......@@ -45,6 +46,7 @@ module Vulnerabilities
feedback_type: 'dismissal',
project_fingerprint: finding.project_fingerprint,
comment: @comment,
dismissal_reason: @dismissal_reason,
pipeline: @project.latest_pipeline_with_security_reports(only_successful: true),
finding_uuid: finding.uuid_v5,
dismiss_vulnerability: false
......
---
title: Add `dismissal_reason` enum to `VulnerabilityDismiss` mutation on GraphQL API
merge_request: 50688
author:
type: added
......@@ -11,17 +11,17 @@ RSpec.describe Mutations::Vulnerabilities::Dismiss do
let_it_be(:user) { create(:user) }
let_it_be(:vulnerability_id) { GitlabSchema.id_from_object(vulnerability).to_s }
let(:comment) { 'Dismissal Feedbacl' }
let(:comment) { 'Dismissal Feedback' }
let(:mutated_vulnerability) { subject[:vulnerability] }
subject { mutation.resolve(id: vulnerability_id, comment: comment) }
subject { mutation.resolve(id: vulnerability_id, comment: comment, dismissal_reason: 'used_in_tests') }
context 'when the user can dismiss the vulnerability' do
before do
stub_licensed_features(security_dashboard: true)
end
context 'when user doe not have access to the project' do
context 'when user does not have access to the project' do
it 'raises an error' do
expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
end
......
......@@ -72,6 +72,19 @@ RSpec.describe Vulnerabilities::DismissService do
end
end
context 'when the dismissal_reason is added' do
let(:dismissal_reason) { 'used_in_tests' }
let(:service) { described_class.new(user, vulnerability, nil, dismissal_reason) }
it 'dismisses a vulnerability and its associated findings with comment', :aggregate_failures do
dismiss_vulnerability
expect(vulnerability.reload).to have_attributes(state: 'dismissed', dismissed_by: user)
expect(vulnerability.findings).to all have_vulnerability_dismissal_feedback
expect(vulnerability.findings.map(&:dismissal_feedback)).to all(have_attributes(dismissal_reason: dismissal_reason))
end
end
it 'creates note' do
expect(SystemNoteService).to receive(:change_vulnerability_state).with(vulnerability, user)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment