Commit fe78ca30 authored by Rémy Coutable's avatar Rémy Coutable

Merge branch '210327-add-scanner-name-and-identifiers-to-graphql-vulns' into 'master'

Add scanner name and identifiers to Vulnerability Type

See merge request gitlab-org/gitlab!34766
parents 378b30e8 0f705fae
...@@ -13560,6 +13560,11 @@ type Vulnerability { ...@@ -13560,6 +13560,11 @@ type Vulnerability {
""" """
id: ID! id: ID!
"""
Identifiers of the vulnerability.
"""
identifiers: [VulnerabilityIdentifier!]!
""" """
List of issue links related to the vulnerability List of issue links related to the vulnerability
""" """
...@@ -13595,6 +13600,11 @@ type Vulnerability { ...@@ -13595,6 +13600,11 @@ type Vulnerability {
""" """
location: VulnerabilityLocation location: VulnerabilityLocation
"""
Primary identifier of the vulnerability.
"""
primaryIdentifier: VulnerabilityIdentifier
""" """
The project on which the vulnerability was found The project on which the vulnerability was found
""" """
...@@ -13606,6 +13616,11 @@ type Vulnerability { ...@@ -13606,6 +13616,11 @@ type Vulnerability {
""" """
reportType: VulnerabilityReportType reportType: VulnerabilityReportType
"""
Scanner metadata for the vulnerability.
"""
scanner: VulnerabilityScanner
""" """
Severity of the vulnerability (INFO, UNKNOWN, LOW, MEDIUM, HIGH, CRITICAL) Severity of the vulnerability (INFO, UNKNOWN, LOW, MEDIUM, HIGH, CRITICAL)
""" """
...@@ -13672,6 +13687,31 @@ type VulnerabilityEdge { ...@@ -13672,6 +13687,31 @@ type VulnerabilityEdge {
node: Vulnerability node: Vulnerability
} }
"""
Represents a vulnerability identifier.
"""
type VulnerabilityIdentifier {
"""
External ID of the vulnerability identifier
"""
externalId: String
"""
External type of the vulnerability identifier
"""
externalType: String
"""
Name of the vulnerability identifier
"""
name: String
"""
URL of the vulnerability identifier
"""
url: String
}
""" """
Represents an issue link of a vulnerability. Represents an issue link of a vulnerability.
""" """
...@@ -13916,6 +13956,21 @@ enum VulnerabilityReportType { ...@@ -13916,6 +13956,21 @@ enum VulnerabilityReportType {
SECRET_DETECTION SECRET_DETECTION
} }
"""
Represents a vulnerability scanner.
"""
type VulnerabilityScanner {
"""
External ID of the vulnerability scanner
"""
externalId: String
"""
Name of the vulnerability scanner
"""
name: String
}
""" """
Represents vulnerability counts by severity Represents vulnerability counts by severity
""" """
......
...@@ -39868,6 +39868,32 @@ ...@@ -39868,6 +39868,32 @@
"isDeprecated": false, "isDeprecated": false,
"deprecationReason": null "deprecationReason": null
}, },
{
"name": "identifiers",
"description": "Identifiers of the vulnerability.",
"args": [
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "VulnerabilityIdentifier",
"ofType": null
}
}
}
},
"isDeprecated": false,
"deprecationReason": null
},
{ {
"name": "issueLinks", "name": "issueLinks",
"description": "List of issue links related to the vulnerability", "description": "List of issue links related to the vulnerability",
...@@ -39949,6 +39975,20 @@ ...@@ -39949,6 +39975,20 @@
"isDeprecated": false, "isDeprecated": false,
"deprecationReason": null "deprecationReason": null
}, },
{
"name": "primaryIdentifier",
"description": "Primary identifier of the vulnerability.",
"args": [
],
"type": {
"kind": "OBJECT",
"name": "VulnerabilityIdentifier",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{ {
"name": "project", "name": "project",
"description": "The project on which the vulnerability was found", "description": "The project on which the vulnerability was found",
...@@ -39977,6 +40017,20 @@ ...@@ -39977,6 +40017,20 @@
"isDeprecated": false, "isDeprecated": false,
"deprecationReason": null "deprecationReason": null
}, },
{
"name": "scanner",
"description": "Scanner metadata for the vulnerability.",
"args": [
],
"type": {
"kind": "OBJECT",
"name": "VulnerabilityScanner",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{ {
"name": "severity", "name": "severity",
"description": "Severity of the vulnerability (INFO, UNKNOWN, LOW, MEDIUM, HIGH, CRITICAL)", "description": "Severity of the vulnerability (INFO, UNKNOWN, LOW, MEDIUM, HIGH, CRITICAL)",
...@@ -40189,6 +40243,75 @@ ...@@ -40189,6 +40243,75 @@
"enumValues": null, "enumValues": null,
"possibleTypes": null "possibleTypes": null
}, },
{
"kind": "OBJECT",
"name": "VulnerabilityIdentifier",
"description": "Represents a vulnerability identifier.",
"fields": [
{
"name": "externalId",
"description": "External ID of the vulnerability identifier",
"args": [
],
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "externalType",
"description": "External type of the vulnerability identifier",
"args": [
],
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "name",
"description": "Name of the vulnerability identifier",
"args": [
],
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "url",
"description": "URL of the vulnerability identifier",
"args": [
],
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [
],
"enumValues": null,
"possibleTypes": null
},
{ {
"kind": "OBJECT", "kind": "OBJECT",
"name": "VulnerabilityIssueLink", "name": "VulnerabilityIssueLink",
...@@ -40956,6 +41079,47 @@ ...@@ -40956,6 +41079,47 @@
], ],
"possibleTypes": null "possibleTypes": null
}, },
{
"kind": "OBJECT",
"name": "VulnerabilityScanner",
"description": "Represents a vulnerability scanner.",
"fields": [
{
"name": "externalId",
"description": "External ID of the vulnerability scanner",
"args": [
],
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "name",
"description": "Name of the vulnerability scanner",
"args": [
],
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [
],
"enumValues": null,
"possibleTypes": null
},
{ {
"kind": "OBJECT", "kind": "OBJECT",
"name": "VulnerabilitySeveritiesCount", "name": "VulnerabilitySeveritiesCount",
...@@ -2014,9 +2014,12 @@ Represents a vulnerability. ...@@ -2014,9 +2014,12 @@ Represents a vulnerability.
| --- | ---- | ---------- | | --- | ---- | ---------- |
| `description` | String | Description of the vulnerability | | `description` | String | Description of the vulnerability |
| `id` | ID! | GraphQL ID of the vulnerability | | `id` | ID! | GraphQL ID of the vulnerability |
| `identifiers` | VulnerabilityIdentifier! => Array | Identifiers of the vulnerability. |
| `location` | VulnerabilityLocation | Location metadata for the vulnerability. Its fields depend on the type of security scan that found the vulnerability | | `location` | VulnerabilityLocation | Location metadata for the vulnerability. Its fields depend on the type of security scan that found the vulnerability |
| `primaryIdentifier` | VulnerabilityIdentifier | Primary identifier of the vulnerability. |
| `project` | Project | The project on which the vulnerability was found | | `project` | Project | The project on which the vulnerability was found |
| `reportType` | VulnerabilityReportType | Type of the security report that found the vulnerability (SAST, DEPENDENCY_SCANNING, CONTAINER_SCANNING, DAST, SECRET_DETECTION) | | `reportType` | VulnerabilityReportType | Type of the security report that found the vulnerability (SAST, DEPENDENCY_SCANNING, CONTAINER_SCANNING, DAST, SECRET_DETECTION) |
| `scanner` | VulnerabilityScanner | Scanner metadata for the vulnerability. |
| `severity` | VulnerabilitySeverity | Severity of the vulnerability (INFO, UNKNOWN, LOW, MEDIUM, HIGH, CRITICAL) | | `severity` | VulnerabilitySeverity | Severity of the vulnerability (INFO, UNKNOWN, LOW, MEDIUM, HIGH, CRITICAL) |
| `state` | VulnerabilityState | State of the vulnerability (DETECTED, DISMISSED, RESOLVED, CONFIRMED) | | `state` | VulnerabilityState | State of the vulnerability (DETECTED, DISMISSED, RESOLVED, CONFIRMED) |
| `title` | String | Title of the vulnerability | | `title` | String | Title of the vulnerability |
...@@ -2024,6 +2027,17 @@ Represents a vulnerability. ...@@ -2024,6 +2027,17 @@ Represents a vulnerability.
| `userPermissions` | VulnerabilityPermissions! | Permissions for the current user on the resource | | `userPermissions` | VulnerabilityPermissions! | Permissions for the current user on the resource |
| `vulnerabilityPath` | String | URL to the vulnerability's details page | | `vulnerabilityPath` | String | URL to the vulnerability's details page |
## VulnerabilityIdentifier
Represents a vulnerability identifier.
| Name | Type | Description |
| --- | ---- | ---------- |
| `externalId` | String | External ID of the vulnerability identifier |
| `externalType` | String | External type of the vulnerability identifier |
| `name` | String | Name of the vulnerability identifier |
| `url` | String | URL of the vulnerability identifier |
## VulnerabilityIssueLink ## VulnerabilityIssueLink
Represents an issue link of a vulnerability. Represents an issue link of a vulnerability.
...@@ -2103,6 +2117,15 @@ Check permissions for the current user on a vulnerability ...@@ -2103,6 +2117,15 @@ 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 |
## VulnerabilityScanner
Represents a vulnerability scanner.
| Name | Type | Description |
| --- | ---- | ---------- |
| `externalId` | String | External ID of the vulnerability scanner |
| `name` | String | Name of the vulnerability scanner |
## VulnerabilitySeveritiesCount ## VulnerabilitySeveritiesCount
Represents vulnerability counts by severity Represents vulnerability counts by severity
......
# frozen_string_literal: true
module Types
# rubocop: disable Graphql/AuthorizeTypes
class VulnerabilityIdentifierType < BaseObject
graphql_name 'VulnerabilityIdentifier'
description 'Represents a vulnerability identifier.'
field :name, GraphQL::STRING_TYPE, null: true,
description: 'Name of the vulnerability identifier'
field :url, GraphQL::STRING_TYPE, null: true,
description: 'URL of the vulnerability identifier'
field :external_type, GraphQL::STRING_TYPE, null: true,
description: 'External type of the vulnerability identifier'
field :external_id, GraphQL::STRING_TYPE, null: true,
description: 'External ID of the vulnerability identifier'
end
# rubocop: enable Graphql/AuthorizeTypes
end
# frozen_string_literal: true
module Types
# rubocop: disable Graphql/AuthorizeTypes
class VulnerabilityScannerType < BaseObject
graphql_name 'VulnerabilityScanner'
description 'Represents a vulnerability scanner.'
field :name, GraphQL::STRING_TYPE, null: true,
description: 'Name of the vulnerability scanner'
field :external_id, GraphQL::STRING_TYPE, null: true,
description: 'External ID of the vulnerability scanner'
end
# rubocop: enable Graphql/AuthorizeTypes
end
...@@ -42,6 +42,18 @@ module Types ...@@ -42,6 +42,18 @@ module Types
description: 'Location metadata for the vulnerability. Its fields depend on the type of security scan that found the vulnerability', description: 'Location metadata for the vulnerability. Its fields depend on the type of security scan that found the vulnerability',
resolve: -> (obj, _args, _ctx) { obj.finding&.location&.merge(report_type: obj.report_type) } resolve: -> (obj, _args, _ctx) { obj.finding&.location&.merge(report_type: obj.report_type) }
field :scanner, VulnerabilityScannerType, null: true,
description: 'Scanner metadata for the vulnerability.',
resolve: -> (obj, _args, _ctx) { obj.finding&.scanner }
field :primary_identifier, VulnerabilityIdentifierType, null: true,
description: 'Primary identifier of the vulnerability.',
resolve: -> (obj, _args, _ctx) { obj.finding&.primary_identifier }
field :identifiers, [VulnerabilityIdentifierType], null: false,
description: 'Identifiers of the vulnerability.',
resolve: -> (obj, _args, _ctx) { obj.finding&.identifiers }
field :project, ::Types::ProjectType, null: true, field :project, ::Types::ProjectType, null: true,
description: 'The project on which the vulnerability was found', description: 'The project on which the vulnerability was found',
authorize: :read_project, authorize: :read_project,
......
---
title: Add scanner name and identifiers to VulnerabilityType
merge_request: 34766
author:
type: added
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe GitlabSchema.types['VulnerabilityIdentifier'] do
it { expect(described_class).to have_graphql_fields(:name, :url, :external_type, :external_id) }
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe GitlabSchema.types['VulnerabilityScanner'] do
it { expect(described_class).to have_graphql_fields(:name, :external_id) }
end
...@@ -8,7 +8,7 @@ RSpec.describe GitlabSchema.types['Vulnerability'] do ...@@ -8,7 +8,7 @@ RSpec.describe GitlabSchema.types['Vulnerability'] do
let_it_be(:vulnerability) { create(:vulnerability, project: project) } let_it_be(:vulnerability) { create(:vulnerability, project: project) }
let(:fields) do let(:fields) do
%i[userPermissions id title description user_notes_count state severity report_type vulnerability_path location project issueLinks] %i[userPermissions id title description user_notes_count state severity report_type vulnerability_path location scanner primary_identifier identifiers project issueLinks]
end end
before do before do
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'Query.vulnerabilities.identifiers' do
include GraphqlHelpers
let_it_be(:project) { create(:project) }
let_it_be(:user) { create(:user, security_dashboard_projects: [project]) }
let_it_be(:fields) do
<<~QUERY
identifiers {
name
externalType
externalId
url
}
QUERY
end
let_it_be(:query) do
graphql_query_for('vulnerabilities', {}, query_graphql_field('nodes', {}, fields))
end
let_it_be(:vulnerability) { create(:vulnerability, project: project, report_type: :container_scanning) }
let_it_be(:occurrence_identifier) do
create(
:vulnerabilities_identifier,
external_type: 'CVE',
external_id: 'CVE-2020-1211',
name: 'CVE-2020-1211',
url: 'http://cve.mitre.org/cgi-bin/cvename.cgi?name=2020-1211'
)
end
let_it_be(:finding) do
create(
:vulnerabilities_occurrence,
vulnerability: vulnerability
)
end
let_it_be(:vulnerabilities_occurrence_identifier) do
create(:vulnerabilities_occurrence_identifier, identifier: occurrence_identifier, occurrence: finding)
end
subject { graphql_data.dig('vulnerabilities', 'nodes') }
before do
project.add_developer(user)
stub_licensed_features(security_dashboard: true)
post_graphql(query, current_user: user)
end
it 'returns a vulnerability identifiers' do
identifier = subject.first['identifiers'].first
expect(identifier['name']).to eq(occurrence_identifier.name)
expect(identifier['externalType']).to eq(occurrence_identifier.external_type)
expect(identifier['externalId']).to eq(occurrence_identifier.external_id)
expect(identifier['url']).to eq(occurrence_identifier.url)
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'Query.vulnerabilities.primaryIdentifier' do
include GraphqlHelpers
let_it_be(:project) { create(:project) }
let_it_be(:user) { create(:user, security_dashboard_projects: [project]) }
let_it_be(:fields) do
<<~QUERY
primaryIdentifier {
name
externalType
externalId
url
}
QUERY
end
let_it_be(:query) do
graphql_query_for('vulnerabilities', {}, query_graphql_field('nodes', {}, fields))
end
let_it_be(:vulnerability) { create(:vulnerability, project: project, report_type: :container_scanning) }
let_it_be(:primary_identifier) do
create(
:vulnerabilities_identifier,
external_type: 'CVE',
external_id: 'CVE-2020-1211',
name: 'CVE-2020-1211',
url: 'http://cve.mitre.org/cgi-bin/cvename.cgi?name=2020-1211'
)
end
let_it_be(:finding) do
create(
:vulnerabilities_occurrence,
vulnerability: vulnerability,
primary_identifier: primary_identifier
)
end
subject { graphql_data.dig('vulnerabilities', 'nodes') }
before do
project.add_developer(user)
stub_licensed_features(security_dashboard: true)
post_graphql(query, current_user: user)
end
it 'returns a vulnerability identifiers' do
identifier = subject.first['primaryIdentifier']
expect(identifier['name']).to eq(primary_identifier.name)
expect(identifier['externalType']).to eq(primary_identifier.external_type)
expect(identifier['externalId']).to eq(primary_identifier.external_id)
expect(identifier['url']).to eq(primary_identifier.url)
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'Query.vulnerabilities.scanner' do
include GraphqlHelpers
let_it_be(:project) { create(:project) }
let_it_be(:user) { create(:user, security_dashboard_projects: [project]) }
let_it_be(:fields) do
<<~QUERY
scanner {
name
externalId
}
QUERY
end
let_it_be(:query) do
graphql_query_for('vulnerabilities', {}, query_graphql_field('nodes', {}, fields))
end
let_it_be(:vulnerability) { create(:vulnerability, project: project, report_type: :container_scanning) }
let_it_be(:vulnerabilities_scanner) do
create(
:vulnerabilities_scanner,
name: 'Vulnerability Scanner',
external_id: 'vulnerabilities_scanner',
project: project
)
end
let_it_be(:finding) do
create(
:vulnerabilities_occurrence,
vulnerability: vulnerability,
scanner: vulnerabilities_scanner
)
end
subject { graphql_data.dig('vulnerabilities', 'nodes') }
before do
project.add_developer(user)
stub_licensed_features(security_dashboard: true)
post_graphql(query, current_user: user)
end
it 'returns a vulnerability scanner' do
scanner = subject.first['scanner']
expect(scanner['name']).to eq(vulnerabilities_scanner.name)
expect(scanner['externalId']).to eq(vulnerabilities_scanner.external_id)
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