Commit b090066a authored by Mayra Cabrera's avatar Mayra Cabrera

Merge branch 'sk/337926-add-cluster-filter' into 'master'

Add cluster_id filtering for vulnerabilites graphql query

See merge request gitlab-org/gitlab!71807
parents f02c40eb 073de687
......@@ -484,6 +484,7 @@ four standard [pagination arguments](#connection-pagination-arguments):
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="queryvulnerabilitiesclusterid"></a>`clusterId` | [`[ClustersClusterID!]`](#clustersclusterid) | Filter vulnerabilities by `cluster_id`. Vulnerabilities with a `reportType` of `cluster_image_scanning` are only included with this filter. |
| <a id="queryvulnerabilitieshasissues"></a>`hasIssues` | [`Boolean`](#boolean) | Returns only the vulnerabilities which have linked issues. |
| <a id="queryvulnerabilitieshasresolution"></a>`hasResolution` | [`Boolean`](#boolean) | Returns only the vulnerabilities which have been resolved on default branch. |
| <a id="queryvulnerabilitiesimage"></a>`image` | [`[String!]`](#string) | Filter vulnerabilities by location image. When this filter is present, the response only matches entries for a `reportType` that includes `container_scanning`, `cluster_image_scanning`. |
......@@ -10834,6 +10835,7 @@ four standard [pagination arguments](#connection-pagination-arguments):
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="groupvulnerabilitiesclusterid"></a>`clusterId` | [`[ClustersClusterID!]`](#clustersclusterid) | Filter vulnerabilities by `cluster_id`. Vulnerabilities with a `reportType` of `cluster_image_scanning` are only included with this filter. |
| <a id="groupvulnerabilitieshasissues"></a>`hasIssues` | [`Boolean`](#boolean) | Returns only the vulnerabilities which have linked issues. |
| <a id="groupvulnerabilitieshasresolution"></a>`hasResolution` | [`Boolean`](#boolean) | Returns only the vulnerabilities which have been resolved on default branch. |
| <a id="groupvulnerabilitiesimage"></a>`image` | [`[String!]`](#string) | Filter vulnerabilities by location image. When this filter is present, the response only matches entries for a `reportType` that includes `container_scanning`, `cluster_image_scanning`. |
......@@ -13627,6 +13629,7 @@ four standard [pagination arguments](#connection-pagination-arguments):
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="projectvulnerabilitiesclusterid"></a>`clusterId` | [`[ClustersClusterID!]`](#clustersclusterid) | Filter vulnerabilities by `cluster_id`. Vulnerabilities with a `reportType` of `cluster_image_scanning` are only included with this filter. |
| <a id="projectvulnerabilitieshasissues"></a>`hasIssues` | [`Boolean`](#boolean) | Returns only the vulnerabilities which have linked issues. |
| <a id="projectvulnerabilitieshasresolution"></a>`hasResolution` | [`Boolean`](#boolean) | Returns only the vulnerabilities which have been resolved on default branch. |
| <a id="projectvulnerabilitiesimage"></a>`image` | [`[String!]`](#string) | Filter vulnerabilities by location image. When this filter is present, the response only matches entries for a `reportType` that includes `container_scanning`, `cluster_image_scanning`. |
......
......@@ -37,6 +37,7 @@ module Security
filter_by_scanner_ids
filter_by_resolution
filter_by_issues
filter_by_cluster_id
sort(vulnerabilities)
end
......@@ -102,6 +103,12 @@ module Security
end
end
def filter_by_cluster_id
if params[:cluster_id].present?
@vulnerabilities = vulnerabilities.with_cluster_ids(params[:cluster_id])
end
end
def sort(items)
items.order_by(params[:sort])
end
......
......@@ -50,6 +50,12 @@ module Resolvers
"the response only matches entries for a `reportType` "\
"that includes #{::Vulnerabilities::Finding::REPORT_TYPES_WITH_LOCATION_IMAGE.map { |type| "`#{type}`" }.join(', ')}."
argument :cluster_id, [::Types::GlobalIDType[::Clusters::Cluster]],
prepare: ->(ids, _) { ids.map(&:model_id) },
required: false,
description: "Filter vulnerabilities by `cluster_id`. Vulnerabilities with a `reportType` "\
"of `cluster_image_scanning` are only included with this filter."
def resolve_with_lookahead(**args)
return Vulnerability.none unless vulnerable
......
......@@ -121,6 +121,9 @@ module EE
scope :with_container_image, -> (images) do
joins(:findings).merge(Vulnerabilities::Finding.by_location_image(images))
end
scope :with_cluster_ids, -> (cluster_ids) do
joins(:findings).merge(Vulnerabilities::Finding.by_location_cluster(cluster_ids))
end
delegate :scanner_name, :scanner_external_id, :scanner_id, :metadata, :message, :description, :details,
to: :finding, prefix: true, allow_nil: true
......
......@@ -99,6 +99,10 @@ module Vulnerabilities
where(report_type: REPORT_TYPES_WITH_LOCATION_IMAGE)
.where("vulnerability_occurrences.location -> 'image' ?| array[:images]", images: images)
end
scope :by_location_cluster, -> (cluster_ids) do
where(report_type: 'cluster_image_scanning')
.where("vulnerability_occurrences.location -> 'cluster_id' ?| array[:cluster_ids]", cluster_ids: cluster_ids)
end
def self.counted_by_severity
group(:severity).count.transform_keys do |severity|
......
......@@ -532,7 +532,8 @@ FactoryBot.define do
"version": "2.24-11+deb9u3"
},
"operating_system": "alpine 3.7",
"image": "alpine:3.7"
"image": "alpine:3.7",
"cluster_id": "1"
}
finding.raw_metadata = {
"category": "cluster_image_scanning",
......@@ -551,7 +552,8 @@ FactoryBot.define do
"version": "2.24-11+deb9u3"
},
"operating_system": "alpine 3.7",
"image": "alpine:3.7"
"image": "alpine:3.7",
"cluster_id": "1"
},
"identifiers": [{
"type": "cve",
......
......@@ -199,4 +199,23 @@ RSpec.describe Security::VulnerabilitiesFinder do
end
end
end
context 'when filtered by cluster_id' do
let_it_be(:cluster_vulnerability) { create(:vulnerability, :cluster_image_scanning, project: project) }
let_it_be(:finding) { create(:vulnerabilities_finding, :with_cluster_image_scanning_scanning_metadata, vulnerability: cluster_vulnerability) }
let(:filters) { { cluster_id: [finding.location['cluster_id']] } }
it 'only returns vulnerabilities matching the given cluster_id' do
is_expected.to contain_exactly(cluster_vulnerability)
end
context 'when different report_type is passed' do
let(:filters) { { report_type: %w[dast], cluster_id: [finding.location['cluster_id']] }}
it 'returns empty list' do
is_expected.to be_empty
end
end
end
end
......@@ -210,5 +210,25 @@ RSpec.describe Resolvers::VulnerabilitiesResolver do
end
end
end
context 'when cluster_id is given' do
let_it_be(:cluster_vulnerability) { create(:vulnerability, :cluster_image_scanning, project: project) }
let_it_be(:cluster_finding) { create(:vulnerabilities_finding, :with_cluster_image_scanning_scanning_metadata, vulnerability: cluster_vulnerability) }
let_it_be(:cluster_gid) { ::Gitlab::GlobalId.as_global_id(cluster_finding.location['cluster_id'].to_i, model_name: 'Clusters::Cluster') }
let(:params) { { cluster_id: [cluster_gid] } }
it 'only returns vulnerabilities with given cluster' do
is_expected.to contain_exactly(cluster_vulnerability)
end
context 'when different report_type is given along with cluster' do
let(:params) { { report_type: %w[sast], cluster_id: [cluster_gid] } }
it 'returns empty list' do
is_expected.to be_empty
end
end
end
end
end
......@@ -601,6 +601,36 @@ RSpec.describe Vulnerability do
end
end
describe '.with_cluster_ids' do
let_it_be(:vulnerability) { create(:vulnerability, project: project, report_type: 'cluster_image_scanning') }
let_it_be(:finding) { create(:vulnerabilities_finding, :with_cluster_image_scanning_scanning_metadata, vulnerability: vulnerability) }
let_it_be(:cluster_ids) { [finding.location['cluster_id']] }
before do
finding_with_different_cluster_id = create(
:vulnerabilities_finding,
:with_cluster_image_scanning_scanning_metadata,
vulnerability: create(:vulnerability, report_type: 'cluster_image_scanning')
)
finding_with_different_cluster_id.location['cluster_id'] = '2'
finding_with_different_cluster_id.save!
finding_without_cluster_id = create(
:vulnerabilities_finding,
:with_cluster_image_scanning_scanning_metadata,
vulnerability: create(:vulnerability, report_type: 'cluster_image_scanning')
)
finding_without_cluster_id.location['cluster_id'] = nil
finding_without_cluster_id.save!
end
subject(:cluster_vulnerabilities) { described_class.with_cluster_ids(cluster_ids) }
it 'returns vulnerabilities with given cluster_id' do
expect(cluster_vulnerabilities).to contain_exactly(vulnerability)
end
end
describe 'created_in_time_range' do
it 'returns vulnerabilities created in given time range', :aggregate_failures do
record1 = create(:vulnerability, created_at: 1.day.ago)
......
......@@ -363,6 +363,30 @@ RSpec.describe Vulnerabilities::Finding do
end
end
describe '.by_location_cluster' do
let_it_be(:vulnerability) { create(:vulnerability, report_type: 'cluster_image_scanning') }
let_it_be(:finding) { create(:vulnerabilities_finding, :with_cluster_image_scanning_scanning_metadata, vulnerability: vulnerability) }
let_it_be(:cluster_ids) { [finding.location['cluster_id']] }
before do
finding_with_different_cluster_id = create(
:vulnerabilities_finding,
:with_cluster_image_scanning_scanning_metadata,
vulnerability: create(:vulnerability, report_type: 'cluster_image_scanning')
)
finding_with_different_cluster_id.location['cluster_id'] = '2'
finding_with_different_cluster_id.save!
create(:vulnerabilities_finding, report_type: :dast)
end
subject(:cluster_findings) { described_class.by_location_cluster(cluster_ids) }
it 'returns findings with given cluster_id' do
expect(cluster_findings).to contain_exactly(finding)
end
end
describe '#false_positive?' do
let_it_be(:finding) { create(:vulnerabilities_finding) }
let_it_be(:finding_with_fp) { create(:vulnerabilities_finding, vulnerability_flags: [create(:vulnerabilities_flag)]) }
......
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