Commit d4d98166 authored by Alan (Maciej) Paruszewski's avatar Alan (Maciej) Paruszewski Committed by Heinrich Lee Yu

Add mutation to Resolve Vulnerability in GraphQL

This change adds new mutation that allows user to resolve vulnerability
using GraphQL API.
parent 682ec79a
...@@ -10875,6 +10875,7 @@ type Mutation { ...@@ -10875,6 +10875,7 @@ type Mutation {
updateNote(input: UpdateNoteInput!): UpdateNotePayload updateNote(input: UpdateNoteInput!): UpdateNotePayload
updateRequirement(input: UpdateRequirementInput!): UpdateRequirementPayload updateRequirement(input: UpdateRequirementInput!): UpdateRequirementPayload
updateSnippet(input: UpdateSnippetInput!): UpdateSnippetPayload updateSnippet(input: UpdateSnippetInput!): UpdateSnippetPayload
vulnerabilityResolve(input: VulnerabilityResolveInput!): VulnerabilityResolvePayload
} }
""" """
...@@ -18825,6 +18826,11 @@ enum VulnerabilityGrade { ...@@ -18825,6 +18826,11 @@ enum VulnerabilityGrade {
F F
} }
"""
Identifier of Vulnerability
"""
scalar VulnerabilityID
""" """
Represents a vulnerability identifier Represents a vulnerability identifier
""" """
...@@ -19125,6 +19131,41 @@ enum VulnerabilityReportType { ...@@ -19125,6 +19131,41 @@ enum VulnerabilityReportType {
SECRET_DETECTION SECRET_DETECTION
} }
"""
Autogenerated input type of VulnerabilityResolve
"""
input VulnerabilityResolveInput {
"""
A unique identifier for the client performing the mutation.
"""
clientMutationId: String
"""
ID of the vulnerability to be resolveed
"""
id: VulnerabilityID!
}
"""
Autogenerated return type of VulnerabilityResolve
"""
type VulnerabilityResolvePayload {
"""
A unique identifier for the client performing the mutation.
"""
clientMutationId: String
"""
Errors encountered during execution of the mutation.
"""
errors: [String!]!
"""
The vulnerability after state change
"""
vulnerability: Vulnerability
}
""" """
Represents a vulnerability scanner Represents a vulnerability scanner
""" """
......
...@@ -32406,6 +32406,33 @@ ...@@ -32406,6 +32406,33 @@
}, },
"isDeprecated": false, "isDeprecated": false,
"deprecationReason": null "deprecationReason": null
},
{
"name": "vulnerabilityResolve",
"description": null,
"args": [
{
"name": "input",
"description": null,
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "INPUT_OBJECT",
"name": "VulnerabilityResolveInput",
"ofType": null
}
},
"defaultValue": null
}
],
"type": {
"kind": "OBJECT",
"name": "VulnerabilityResolvePayload",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
} }
], ],
"inputFields": null, "inputFields": null,
...@@ -55030,6 +55057,16 @@ ...@@ -55030,6 +55057,16 @@
], ],
"possibleTypes": null "possibleTypes": null
}, },
{
"kind": "SCALAR",
"name": "VulnerabilityID",
"description": "Identifier of Vulnerability",
"fields": null,
"inputFields": null,
"interfaces": null,
"enumValues": null,
"possibleTypes": null
},
{ {
"kind": "OBJECT", "kind": "OBJECT",
"name": "VulnerabilityIdentifier", "name": "VulnerabilityIdentifier",
...@@ -55960,6 +55997,108 @@ ...@@ -55960,6 +55997,108 @@
], ],
"possibleTypes": null "possibleTypes": null
}, },
{
"kind": "INPUT_OBJECT",
"name": "VulnerabilityResolveInput",
"description": "Autogenerated input type of VulnerabilityResolve",
"fields": null,
"inputFields": [
{
"name": "id",
"description": "ID of the vulnerability to be resolveed",
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "VulnerabilityID",
"ofType": null
}
},
"defaultValue": null
},
{
"name": "clientMutationId",
"description": "A unique identifier for the client performing the mutation.",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
}
],
"interfaces": null,
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "VulnerabilityResolvePayload",
"description": "Autogenerated return type of VulnerabilityResolve",
"fields": [
{
"name": "clientMutationId",
"description": "A unique identifier for the client performing the mutation.",
"args": [
],
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "errors",
"description": "Errors encountered during execution of the mutation.",
"args": [
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
}
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "vulnerability",
"description": "The vulnerability after state change",
"args": [
],
"type": {
"kind": "OBJECT",
"name": "Vulnerability",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [
],
"enumValues": null,
"possibleTypes": null
},
{ {
"kind": "OBJECT", "kind": "OBJECT",
"name": "VulnerabilityScanner", "name": "VulnerabilityScanner",
...@@ -2802,6 +2802,16 @@ Check permissions for the current user on a vulnerability. ...@@ -2802,6 +2802,16 @@ Check permissions for the current user on a vulnerability.
| `readVulnerabilityFeedback` | Boolean! | Indicates the user can perform `read_vulnerability_feedback` on this resource | | `readVulnerabilityFeedback` | Boolean! | Indicates the user can perform `read_vulnerability_feedback` on this resource |
| `updateVulnerabilityFeedback` | Boolean! | Indicates the user can perform `update_vulnerability_feedback` on this resource | | `updateVulnerabilityFeedback` | Boolean! | Indicates the user can perform `update_vulnerability_feedback` on this resource |
### VulnerabilityResolvePayload
Autogenerated return type of VulnerabilityResolve.
| Field | Type | Description |
| ----- | ---- | ----------- |
| `clientMutationId` | String | A unique identifier for the client performing the mutation. |
| `errors` | String! => Array | Errors encountered during execution of the mutation. |
| `vulnerability` | Vulnerability | The vulnerability after state change |
### VulnerabilityScanner ### VulnerabilityScanner
Represents a vulnerability scanner. Represents a vulnerability scanner.
......
...@@ -23,6 +23,7 @@ module EE ...@@ -23,6 +23,7 @@ module EE
mount_mutation ::Mutations::RequirementsManagement::CreateRequirement mount_mutation ::Mutations::RequirementsManagement::CreateRequirement
mount_mutation ::Mutations::RequirementsManagement::UpdateRequirement mount_mutation ::Mutations::RequirementsManagement::UpdateRequirement
mount_mutation ::Mutations::Vulnerabilities::Dismiss mount_mutation ::Mutations::Vulnerabilities::Dismiss
mount_mutation ::Mutations::Vulnerabilities::Resolve
mount_mutation ::Mutations::Boards::Update mount_mutation ::Mutations::Boards::Update
mount_mutation ::Mutations::Boards::Lists::UpdateLimitMetrics mount_mutation ::Mutations::Boards::Lists::UpdateLimitMetrics
mount_mutation ::Mutations::InstanceSecurityDashboard::AddProject mount_mutation ::Mutations::InstanceSecurityDashboard::AddProject
......
# frozen_string_literal: true
module Mutations
module Vulnerabilities
class Resolve < BaseMutation
graphql_name 'VulnerabilityResolve'
authorize :admin_vulnerability
field :vulnerability, Types::VulnerabilityType,
null: true,
description: 'The vulnerability after state change'
argument :id,
::Types::GlobalIDType[::Vulnerability],
required: true,
description: 'ID of the vulnerability to be resolveed'
def resolve(id:)
vulnerability = authorized_find!(id: id)
result = resolve_vulnerability(vulnerability)
{
vulnerability: result,
errors: result.errors.full_messages || []
}
end
private
def resolve_vulnerability(vulnerability)
::Vulnerabilities::ResolveService.new(current_user, vulnerability).execute
end
def find_object(id:)
GitlabSchema.find_by_gid(id)
end
end
end
end
---
title: Add mutation to Resolve Vulnerability in GraphQL
merge_request: 42500
author:
type: added
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Mutations::Vulnerabilities::Resolve do
let(:mutation) { described_class.new(object: nil, context: { current_user: user }, field: nil) }
describe '#resolve' do
let_it_be(:vulnerability) { create(:vulnerability, :with_findings) }
let_it_be(:user) { create(:user) }
let(:mutated_vulnerability) { subject[:vulnerability] }
subject { mutation.resolve(id: GitlabSchema.id_from_object(vulnerability)) }
context 'when the user can resolve the vulnerability' do
before do
stub_licensed_features(security_dashboard: true)
end
context 'when user doe not have access to the project' do
it 'raises an error' do
expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
end
end
context 'when user has access to the project' do
before do
vulnerability.project.add_developer(user)
end
it 'returns the resolveed vulnerability', :aggregate_failures do
expect(mutated_vulnerability).to eq(vulnerability)
expect(mutated_vulnerability).to be_resolved
expect(subject[:errors]).to be_empty
end
end
end
end
end
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