diff --git a/config/feature_flags/gitlab_com_derisk/policy_violations_es_filter.yml b/config/feature_flags/gitlab_com_derisk/policy_violations_es_filter.yml new file mode 100644 index 0000000000000000000000000000000000000000..07d346568e3ce217c3f56f540b16bdbcf6ad7472 --- /dev/null +++ b/config/feature_flags/gitlab_com_derisk/policy_violations_es_filter.yml @@ -0,0 +1,10 @@ +--- +name: policy_violations_es_filter +description: +feature_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/561739 +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/207947 +rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/577591 +milestone: '18.6' +group: group::security policies +type: gitlab_com_derisk +default_enabled: false diff --git a/doc/api/graphql/reference/_index.md b/doc/api/graphql/reference/_index.md index 5577fa57ae3c2b211f27bc0074522c1846904b2b..03639d2fd31318582447d693988ffd58f664b32d 100644 --- a/doc/api/graphql/reference/_index.md +++ b/doc/api/graphql/reference/_index.md @@ -1993,6 +1993,7 @@ four standard [pagination arguments](#pagination-arguments): | `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`. | | `owaspTopTen` | [`[VulnerabilityOwaspTop10!]`](#vulnerabilityowasptop10) | Filter vulnerabilities by OWASP Top 10 2017 category. Wildcard value `NONE` is also supported but it cannot be combined with other OWASP top 10 values. | | `owaspTopTen2021` {{< icon name="warning-solid" >}} | [`[VulnerabilityOwasp2021Top10!]`](#vulnerabilityowasp2021top10) | **Introduced** in GitLab 18.1. **Status**: Experiment. Filter vulnerabilities by OWASP Top 10 2021 category. Wildcard value `NONE` is also supported but it cannot be combined with other OWASP top 10 2021 values. To use this argument, you must have Elasticsearch configured and the `advanced_vulnerability_management` feature flag enabled. Not supported on Instance Security Dashboard queries. | +| `policyViolations` {{< icon name="warning-solid" >}} | [`[PolicyViolations!]`](#policyviolations) | **Introduced** in GitLab 18.6. **Status**: Experiment. Filter by security policy violations. To use this argument, you must have Elasticsearch configured and the `advanced_vulnerability_management` feature flag enabled. Not supported on Instance Security Dashboard queries. | | `projectId` | [`[ID!]`](#id) | Filter vulnerabilities by project. | | `reachability` {{< icon name="warning-solid" >}} | [`ReachabilityType`](#reachabilitytype) | **Introduced** in GitLab 18.2. **Status**: Experiment. Filter vulnerabilities by reachability. | | `reportType` | [`[VulnerabilityReportType!]`](#vulnerabilityreporttype) | Filter vulnerabilities by report type. | @@ -32876,6 +32877,7 @@ four standard [pagination arguments](#pagination-arguments): | `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`. | | `owaspTopTen` | [`[VulnerabilityOwaspTop10!]`](#vulnerabilityowasptop10) | Filter vulnerabilities by OWASP Top 10 2017 category. Wildcard value `NONE` is also supported but it cannot be combined with other OWASP top 10 values. | | `owaspTopTen2021` {{< icon name="warning-solid" >}} | [`[VulnerabilityOwasp2021Top10!]`](#vulnerabilityowasp2021top10) | **Introduced** in GitLab 18.1. **Status**: Experiment. Filter vulnerabilities by OWASP Top 10 2021 category. Wildcard value `NONE` is also supported but it cannot be combined with other OWASP top 10 2021 values. To use this argument, you must have Elasticsearch configured and the `advanced_vulnerability_management` feature flag enabled. Not supported on Instance Security Dashboard queries. | +| `policyViolations` {{< icon name="warning-solid" >}} | [`[PolicyViolations!]`](#policyviolations) | **Introduced** in GitLab 18.6. **Status**: Experiment. Filter by security policy violations. To use this argument, you must have Elasticsearch configured and the `advanced_vulnerability_management` feature flag enabled. Not supported on Instance Security Dashboard queries. | | `projectId` | [`[ID!]`](#id) | Filter vulnerabilities by project. | | `reachability` {{< icon name="warning-solid" >}} | [`ReachabilityType`](#reachabilitytype) | **Introduced** in GitLab 18.2. **Status**: Experiment. Filter vulnerabilities by reachability. | | `reportType` | [`[VulnerabilityReportType!]`](#vulnerabilityreporttype) | Filter vulnerabilities by report type. | @@ -32973,6 +32975,7 @@ Returns [`VulnerabilitySeveritiesCount`](#vulnerabilityseveritiescount). | `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`. | | `owaspTopTen` | [`[VulnerabilityOwaspTop10!]`](#vulnerabilityowasptop10) | Filter vulnerabilities by OWASP Top 10 2017 category. Wildcard value `NONE` is also supported but it cannot be combined with other OWASP top 10 values. | | `owaspTopTen2021` {{< icon name="warning-solid" >}} | [`[VulnerabilityOwasp2021Top10!]`](#vulnerabilityowasp2021top10) | **Introduced** in GitLab 18.1. **Status**: Experiment. Filter vulnerabilities by OWASP Top 10 2021 category. Wildcard value `NONE` is also supported but it cannot be combined with other OWASP top 10 2021 values. To use this argument, you must have Elasticsearch configured and the `advanced_vulnerability_management` feature flag enabled. Not supported on Instance Security Dashboard queries. | +| `policyViolations` {{< icon name="warning-solid" >}} | [`[PolicyViolations!]`](#policyviolations) | **Introduced** in GitLab 18.6. **Status**: Experiment. Filter by security policy violations. To use this argument, you must have Elasticsearch configured and the `advanced_vulnerability_management` feature flag enabled. Not supported on Instance Security Dashboard queries. | | `projectId` | [`[ID!]`](#id) | Filter vulnerabilities by project. | | `reachability` {{< icon name="warning-solid" >}} | [`ReachabilityType`](#reachabilitytype) | **Introduced** in GitLab 18.2. **Status**: Experiment. Filter vulnerabilities by reachability. | | `reportType` | [`[VulnerabilityReportType!]`](#vulnerabilityreporttype) | Filter vulnerabilities by report type. | @@ -34049,6 +34052,7 @@ Returns [`VulnerabilitySeveritiesCount`](#vulnerabilityseveritiescount). | `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`. | | `owaspTopTen` | [`[VulnerabilityOwaspTop10!]`](#vulnerabilityowasptop10) | Filter vulnerabilities by OWASP Top 10 2017 category. Wildcard value `NONE` is also supported but it cannot be combined with other OWASP top 10 values. | | `owaspTopTen2021` {{< icon name="warning-solid" >}} | [`[VulnerabilityOwasp2021Top10!]`](#vulnerabilityowasp2021top10) | **Introduced** in GitLab 18.1. **Status**: Experiment. Filter vulnerabilities by OWASP Top 10 2021 category. Wildcard value `NONE` is also supported but it cannot be combined with other OWASP top 10 2021 values. To use this argument, you must have Elasticsearch configured and the `advanced_vulnerability_management` feature flag enabled. Not supported on Instance Security Dashboard queries. | +| `policyViolations` {{< icon name="warning-solid" >}} | [`[PolicyViolations!]`](#policyviolations) | **Introduced** in GitLab 18.6. **Status**: Experiment. Filter by security policy violations. To use this argument, you must have Elasticsearch configured and the `advanced_vulnerability_management` feature flag enabled. Not supported on Instance Security Dashboard queries. | | `projectId` | [`[ID!]`](#id) | Filter vulnerabilities by project. | | `reachability` {{< icon name="warning-solid" >}} | [`ReachabilityType`](#reachabilitytype) | **Introduced** in GitLab 18.2. **Status**: Experiment. Filter vulnerabilities by reachability. | | `reportType` | [`[VulnerabilityReportType!]`](#vulnerabilityreporttype) | Filter vulnerabilities by report type. | @@ -41748,6 +41752,7 @@ four standard [pagination arguments](#pagination-arguments): | `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`. | | `owaspTopTen` | [`[VulnerabilityOwaspTop10!]`](#vulnerabilityowasptop10) | Filter vulnerabilities by OWASP Top 10 2017 category. Wildcard value `NONE` is also supported but it cannot be combined with other OWASP top 10 values. | | `owaspTopTen2021` {{< icon name="warning-solid" >}} | [`[VulnerabilityOwasp2021Top10!]`](#vulnerabilityowasp2021top10) | **Introduced** in GitLab 18.1. **Status**: Experiment. Filter vulnerabilities by OWASP Top 10 2021 category. Wildcard value `NONE` is also supported but it cannot be combined with other OWASP top 10 2021 values. To use this argument, you must have Elasticsearch configured and the `advanced_vulnerability_management` feature flag enabled. Not supported on Instance Security Dashboard queries. | +| `policyViolations` {{< icon name="warning-solid" >}} | [`[PolicyViolations!]`](#policyviolations) | **Introduced** in GitLab 18.6. **Status**: Experiment. Filter by security policy violations. To use this argument, you must have Elasticsearch configured and the `advanced_vulnerability_management` feature flag enabled. Not supported on Instance Security Dashboard queries. | | `projectId` | [`[ID!]`](#id) | Filter vulnerabilities by project. | | `reachability` {{< icon name="warning-solid" >}} | [`ReachabilityType`](#reachabilitytype) | **Introduced** in GitLab 18.2. **Status**: Experiment. Filter vulnerabilities by reachability. | | `reportType` | [`[VulnerabilityReportType!]`](#vulnerabilityreporttype) | Filter vulnerabilities by report type. | @@ -41832,6 +41837,7 @@ Returns [`VulnerabilitySeveritiesCount`](#vulnerabilityseveritiescount). | `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`. | | `owaspTopTen` | [`[VulnerabilityOwaspTop10!]`](#vulnerabilityowasptop10) | Filter vulnerabilities by OWASP Top 10 2017 category. Wildcard value `NONE` is also supported but it cannot be combined with other OWASP top 10 values. | | `owaspTopTen2021` {{< icon name="warning-solid" >}} | [`[VulnerabilityOwasp2021Top10!]`](#vulnerabilityowasp2021top10) | **Introduced** in GitLab 18.1. **Status**: Experiment. Filter vulnerabilities by OWASP Top 10 2021 category. Wildcard value `NONE` is also supported but it cannot be combined with other OWASP top 10 2021 values. To use this argument, you must have Elasticsearch configured and the `advanced_vulnerability_management` feature flag enabled. Not supported on Instance Security Dashboard queries. | +| `policyViolations` {{< icon name="warning-solid" >}} | [`[PolicyViolations!]`](#policyviolations) | **Introduced** in GitLab 18.6. **Status**: Experiment. Filter by security policy violations. To use this argument, you must have Elasticsearch configured and the `advanced_vulnerability_management` feature flag enabled. Not supported on Instance Security Dashboard queries. | | `projectId` | [`[ID!]`](#id) | Filter vulnerabilities by project. | | `reachability` {{< icon name="warning-solid" >}} | [`ReachabilityType`](#reachabilitytype) | **Introduced** in GitLab 18.2. **Status**: Experiment. Filter vulnerabilities by reachability. | | `reportType` | [`[VulnerabilityReportType!]`](#vulnerabilityreporttype) | Filter vulnerabilities by report type. | @@ -50732,6 +50738,12 @@ Types of security policy project created status. | `RUNNING` | Represents a running policy violation. | | `WARNING` | Represents a policy violation warning. | +### `PolicyViolations` + +| Value | Description | +| ----- | ----------- | +| `DISMISSED_IN_MR` | Dismissed in Merge request bypass reason. | + ### `PrincipalType` Types of principal that can have secret permissions. diff --git a/ee/app/finders/security/vulnerability_elastic_base_finder.rb b/ee/app/finders/security/vulnerability_elastic_base_finder.rb index 14fbb26559255d0389d8cf168282bf6aeac74e23..5c5d3d337992d34bdf8e66f4cad4cd39e93ea894 100644 --- a/ee/app/finders/security/vulnerability_elastic_base_finder.rb +++ b/ee/app/finders/security/vulnerability_elastic_base_finder.rb @@ -77,6 +77,7 @@ def initialize_search_params identifier_name: params[:identifier_name], reachability: reachability, validity_check: validity_check, + policy_violations: policy_violations, sort: sort } end @@ -142,5 +143,11 @@ def es_search_options def root_ancestor_ids [vulnerable.root_ancestor.id] end + + def policy_violations + return unless params[:policy_violations] + + ::Security::PolicyViolations::VIOLATIONS_TYPES.slice(*params[:policy_violations]).values + end end end diff --git a/ee/app/graphql/resolvers/vulnerabilities_resolver.rb b/ee/app/graphql/resolvers/vulnerabilities_resolver.rb index 9e232ee3669d8582b188b69648884b0cb61c2f9e..b98b220fb199cdaf67db0996cfafe9ae3fd9887a 100644 --- a/ee/app/graphql/resolvers/vulnerabilities_resolver.rb +++ b/ee/app/graphql/resolvers/vulnerabilities_resolver.rb @@ -110,6 +110,14 @@ class VulnerabilitiesResolver < VulnerabilitiesBaseResolver experiment: { milestone: '18.5' }, description: 'Filter vulnerabilities by validity check.' + argument :policy_violations, [::Types::SecurityOrchestration::PolicyViolationsEnum], + required: false, + experiment: { milestone: '18.6' }, + description: 'Filter by security policy violations. ' \ + 'To use this argument, you must have Elasticsearch configured and the ' \ + '`advanced_vulnerability_management` feature flag enabled. ' \ + 'Not supported on Instance Security Dashboard queries.' + def resolve_with_lookahead(**args) return Vulnerability.none unless vulnerable&.feature_available?(:security_dashboard) diff --git a/ee/app/graphql/resolvers/vulnerability_filterable.rb b/ee/app/graphql/resolvers/vulnerability_filterable.rb index 672a1f1efe517790330f0e484428e698cc0d2b9d..48a47dbb9656df5084e61ab4e6e7ca6f1c5c2ed7 100644 --- a/ee/app/graphql/resolvers/vulnerability_filterable.rb +++ b/ee/app/graphql/resolvers/vulnerability_filterable.rb @@ -8,7 +8,7 @@ module VulnerabilityFilterable private - ADVANCED_FILTERS = [:owasp_top_10_2021, :identifier_name, :reachability, :validity_check].freeze + ADVANCED_FILTERS = [:owasp_top_10_2021, :identifier_name, :reachability, :validity_check, :policy_violations].freeze def validate_filters(filters) # identifier_name is also supported on postgres @@ -19,6 +19,7 @@ def validate_filters(filters) validate_reachability!(vulnerable) if filters[:reachability].present? validate_validity_check!(vulnerable) if filters[:validity_check].present? + validate_policy_violations!(vulnerable) if filters[:policy_violations].present? # Identifier validation should only run for # 1. GitLab .com and Dedicated if ES is not available @@ -90,5 +91,19 @@ def validate_validity_check!(vulnerable) 'The \'validity_check\' argument is not currently supported on security center dashboard or ' \ 'the required migrations are not completed.' end + + def validate_policy_violations!(vulnerable) + group = + if vulnerable.is_a?(Project) + vulnerable.group + elsif vulnerable.is_a?(Group) + vulnerable + end + + return if group && Feature.enabled?(:policy_violations_es_filter, group) + + raise ::Gitlab::Graphql::Errors::ArgumentError, + 'The \'policy_violations\' argument is not currently supported on security center dashboard' + end end end diff --git a/ee/app/graphql/resolvers/vulnerability_severities_count_resolver.rb b/ee/app/graphql/resolvers/vulnerability_severities_count_resolver.rb index 6ca4bdcd851a3830e0118cfdecd55c53f6d60e2a..b2b5f84215c939fdeabe6c6b6092c0450a2c437b 100644 --- a/ee/app/graphql/resolvers/vulnerability_severities_count_resolver.rb +++ b/ee/app/graphql/resolvers/vulnerability_severities_count_resolver.rb @@ -107,6 +107,14 @@ class VulnerabilitySeveritiesCountResolver < VulnerabilitiesBaseResolver experiment: { milestone: '18.5' }, description: 'Filter vulnerabilities by token status.' + argument :policy_violations, [::Types::SecurityOrchestration::PolicyViolationsEnum], + required: false, + experiment: { milestone: '18.6' }, + description: 'Filter by security policy violations. ' \ + 'To use this argument, you must have Elasticsearch configured and the ' \ + '`advanced_vulnerability_management` feature flag enabled. ' \ + 'Not supported on Instance Security Dashboard queries.' + def resolve(**args) return Vulnerability.none unless vulnerable diff --git a/ee/app/graphql/types/security_orchestration/policy_violations_enum.rb b/ee/app/graphql/types/security_orchestration/policy_violations_enum.rb new file mode 100644 index 0000000000000000000000000000000000000000..0d8b71b3e20149fd045294a5986f3562aa7d9e7a --- /dev/null +++ b/ee/app/graphql/types/security_orchestration/policy_violations_enum.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module Types + module SecurityOrchestration # rubocop:disable Gitlab/BoundedContexts -- Existing module + class PolicyViolationsEnum < BaseEnum + graphql_name 'PolicyViolations' + + value 'DISMISSED_IN_MR', + description: 'Dismissed in Merge request bypass reason.', + value: :dismissed_in_mr + end + end +end diff --git a/ee/lib/search/elastic/vulnerability_filters.rb b/ee/lib/search/elastic/vulnerability_filters.rb index 93adeaa2b0fad19bf4c3d35cd1517e5c63a959d6..c33dfa278594134dc2f07c063fe51e47acf1b4a1 100644 --- a/ee/lib/search/elastic/vulnerability_filters.rb +++ b/ee/lib/search/elastic/vulnerability_filters.rb @@ -433,6 +433,26 @@ def by_validity_check(query_hash:, options:) end end + def by_policy_violations(query_hash:, options:) + policy_violations = options[:policy_violations] + return query_hash if policy_violations.blank? + + unless policy_violations.to_set.subset?(::Security::PolicyViolations::VIOLATIONS_TYPES.values.to_set) + return query_hash + end + + context.name(:filters) do + add_filter(query_hash, :query, :bool, :filter) do + { + terms: { + _name: context.name(:policy_violations), + policy_violations: policy_violations + } + } + end + end + end + private def valid_owasp_values?(owasp_values, regex_constant) diff --git a/ee/lib/search/elastic/vulnerability_query_builder.rb b/ee/lib/search/elastic/vulnerability_query_builder.rb index 81c703cee08273f598d8694db59f0f9e93a4d2ae..3f38aca86536a5a383d441362ec480a95ce85ccc 100644 --- a/ee/lib/search/elastic/vulnerability_query_builder.rb +++ b/ee/lib/search/elastic/vulnerability_query_builder.rb @@ -54,6 +54,11 @@ def build # rubocop:disable Metrics/AbcSize -- need all the filters in one place ) end + if ::Elastic::DataMigrationService.migration_has_finished?(:add_policy_violations_field_to_vulnerability) + query_hash = ::Search::Elastic::VulnerabilityFilters.by_policy_violations( + query_hash: query_hash, options: options) + end + query_hash = ::Search::Elastic::VulnerabilityAggregations.by_severity_counts( query_hash: query_hash, options: options) query_hash = ::Search::Elastic::VulnerabilityAggregations.by_identifiers_search( diff --git a/ee/spec/graphql/resolvers/vulnerabilities_resolver_spec.rb b/ee/spec/graphql/resolvers/vulnerabilities_resolver_spec.rb index a47fe6dbd48fa29a79ba55a68e8761ac374b780e..91e48f3f13a46794f1d73911ccb4fb171ee04f9d 100644 --- a/ee/spec/graphql/resolvers/vulnerabilities_resolver_spec.rb +++ b/ee/spec/graphql/resolvers/vulnerabilities_resolver_spec.rb @@ -290,6 +290,17 @@ end end end + + context 'when filtering vulnerabilities with policy_violations', :elastic do + let(:params) { { policy_violations: ['DISMISSED_IN_MR'] } } + let(:error_msg) { "Feature is not supported for InstanceSecurityDashboard" } + + it 'raises an error' do + expect_graphql_error_to_be_created(Gitlab::Graphql::Errors::ArgumentError, s_(error_msg)) do + resolved + end + end + end end context 'when image is given' do @@ -574,6 +585,64 @@ def create_vuln_with_status(severity:, status: nil) end end + context 'when filtering vulnerabilities with policy_violations', :elastic do + let(:params) { { policy_violations: ['DISMISSED_IN_MR'] } } + + context 'without elasticsearch' do + before do + allow(::Search::Elastic::VulnerabilityIndexingHelper).to receive(:vulnerability_indexing_allowed?).and_return(false) + end + + it_behaves_like 'raises ES errors' + end + + context 'with advanced_vulnerability_management FF disabled' do + before do + allow(::Search::Elastic::VulnerabilityIndexingHelper).to receive(:vulnerability_indexing_allowed?).and_return(true) + stub_feature_flags(advanced_vulnerability_management: false) + end + + it_behaves_like 'raises ES errors' + end + + shared_examples_for 'when elasticsearch is available' do + let_it_be(:dismissed_vulnerability_read) { create(:vulnerability_read, project: project) } + + let_it_be(:non_dismissed_vulnerability_read) { create(:vulnerability_read, project: project) } + + before do + stub_ee_application_setting(elasticsearch_search: true, elasticsearch_indexing: true) + + create(:policy_dismissal, :preserved, project: project, security_findings_uuids: [dismissed_vulnerability_read.uuid]) + + Elastic::ProcessBookkeepingService.track!(dismissed_vulnerability_read, non_dismissed_vulnerability_read) + ensure_elasticsearch_index! + + allow(current_user).to receive(:can?).with(:access_advanced_vulnerability_management, vulnerable).and_return(true) + end + + it 'only returns vulnerabilities with matching policy_violations types' do + expect(Gitlab::Search::Client).to receive(:execute_search).and_call_original + + results = resolved.to_a + + expect(results).to match_array([dismissed_vulnerability_read].map(&:vulnerability)) + end + end + + context 'when vulnerable is a project' do + let(:vulnerable) { project } + + it_behaves_like 'when elasticsearch is available' + end + + context 'when vulnerable is a group' do + let(:vulnerable) { group } + + it_behaves_like 'when elasticsearch is available' + end + end + context 'when identifer_name is given' do let_it_be(:identifier_name) { 'CVE-2024-1234' } diff --git a/ee/spec/graphql/resolvers/vulnerability_severities_count_resolver_spec.rb b/ee/spec/graphql/resolvers/vulnerability_severities_count_resolver_spec.rb index ad1bdc4156ae53484c19abb7549cf0730e2b1536..eba5f363e5ece0423d3f77cebdaa8f04188043c2 100644 --- a/ee/spec/graphql/resolvers/vulnerability_severities_count_resolver_spec.rb +++ b/ee/spec/graphql/resolvers/vulnerability_severities_count_resolver_spec.rb @@ -558,6 +558,32 @@ def create_vuln_with_status(severity:, status: nil) end end end + + context 'with policy_violations as parameter', :elastic do + let(:filters) { { policy_violations: ['DISMISSED_IN_MR'] } } + let(:expected_result) { { 'critical' => 1 } } + let_it_be(:dismissed_vulnerability_read) { create(:vulnerability_read, severity: :critical, project: project) } + let_it_be(:non_dismissed_vulnerability_read) { create(:vulnerability_read, severity: :low, project: project) } + + before do + stub_ee_application_setting(elasticsearch_search: true, elasticsearch_indexing: true) + + create(:policy_dismissal, :preserved, project: project, security_findings_uuids: [dismissed_vulnerability_read.uuid]) + + Elastic::ProcessBookkeepingService.track!(dismissed_vulnerability_read, non_dismissed_vulnerability_read) + ensure_elasticsearch_index! + + allow(current_user).to receive(:can?).with(:admin_all_resources).and_call_original + allow(current_user).to receive(:can?) + .with(:access_advanced_vulnerability_management, vulnerable) + .and_return(true) + end + + it 'only considers vulnerabilities with matching policy_violations values' do + expect(Gitlab::Search::Client).to receive(:execute_search).and_call_original + is_expected.to eq(expected_result) + end + end end context 'when resolving vulnerabilities for an instance security dashboard' do diff --git a/ee/spec/graphql/types/security_orchestration/policy_violations_enum_spec.rb b/ee/spec/graphql/types/security_orchestration/policy_violations_enum_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..6c7738f43dc83ec53e96ebc4cb294765ba648cc4 --- /dev/null +++ b/ee/spec/graphql/types/security_orchestration/policy_violations_enum_spec.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe GitlabSchema.types['PolicyViolations'], feature_category: :security_policy_management do + specify { expect(described_class.graphql_name).to eq('PolicyViolations') } + + it 'exposes all policy violations types' do + expect(described_class.values.keys).to include(*%w[DISMISSED_IN_MR]) + end +end diff --git a/ee/spec/lib/search/elastic/vulnerability_filters_spec.rb b/ee/spec/lib/search/elastic/vulnerability_filters_spec.rb index 4a695face28d8c3369e93a40db57271bf5b66888..ef3d3fb56d0ffb52b46f8fdf914790ba46b48b8f 100644 --- a/ee/spec/lib/search/elastic/vulnerability_filters_spec.rb +++ b/ee/spec/lib/search/elastic/vulnerability_filters_spec.rb @@ -867,4 +867,45 @@ end end end + + describe '.by_policy_violations' do + subject(:by_policy_violations) { described_class.by_policy_violations(query_hash: query_hash, options: options) } + + context 'when options[:policy_violations] is empty' do + let(:options) { {} } + + it_behaves_like 'does not modify the query_hash' + + context 'with invalid by_policy_violations value' do + let(:options) { { policy_violations: [::Security::PolicyViolations::VIOLATIONS_TYPES.values.last + 1] } } + + it_behaves_like 'does not modify the query_hash' + end + + context 'when options[:policy_violations] contains a valid policy violation' do + let(:options) { { policy_violations: [::Security::PolicyViolations::VIOLATIONS_TYPES.each_value.first] } } + let(:expected_query) do + { + query: { + bool: { + filter: [{ + terms: { + _name: "filters:policy_violations", + policy_violations: [0] + } + }], + must_not: [], + must: [], + should: [] + } + } + } + end + + it 'adds the policy_violations filter to query_hash' do + expect(by_policy_violations).to eq(expected_query) + end + end + end + end end diff --git a/ee/spec/lib/search/elastic/vulnerability_query_builder_spec.rb b/ee/spec/lib/search/elastic/vulnerability_query_builder_spec.rb index 28ebd7d905a31f4350278c239938d6e73909b9c7..945176ff9b35918c8ccc0185fb850ff3e2082afc 100644 --- a/ee/spec/lib/search/elastic/vulnerability_query_builder_spec.rb +++ b/ee/spec/lib/search/elastic/vulnerability_query_builder_spec.rb @@ -293,6 +293,37 @@ end end + describe 'policy_violations' do + let(:options) do + base_options.merge(policy_violations: [::Security::PolicyViolations::VIOLATIONS_TYPES[:dismissed_in_mr]]) + end + + context 'with finished migrations' do + before do + set_elasticsearch_migration_to(:add_policy_violations_field_to_vulnerability) + end + + it 'does add filter' do + assert_names_in_query(build, with: %w[ + filters:archived_projects + filters:policy_violations + ]) + end + end + + context 'without finished migrations' do + before do + set_elasticsearch_migration_to(:add_policy_violations_field_to_vulnerability, including: false) + end + + it 'does not add reachability filter' do + assert_names_in_query(build, with: %w[ + filters:archived_projects + ]) + end + end + end + describe 'by_vulnerabilities_over_time' do let(:current_day) { Time.current.beginning_of_day } let(:current_day_minus_1) { current_day - 1.day } diff --git a/ee/spec/support/shared_examples/graphql/resolvers/vulnerability_filterable_shared_examples.rb b/ee/spec/support/shared_examples/graphql/resolvers/vulnerability_filterable_shared_examples.rb index b5bf249009f75081b6061c7a7fea715dd2238a47..3b421f2b4d772d1b077fd5d245b9a0366210ff70 100644 --- a/ee/spec/support/shared_examples/graphql/resolvers/vulnerability_filterable_shared_examples.rb +++ b/ee/spec/support/shared_examples/graphql/resolvers/vulnerability_filterable_shared_examples.rb @@ -54,6 +54,22 @@ end end end + + context 'with policy_violations' do + let(filter_key) { { policy_violations: [Security::FindingTokenStatus.statuses.each_key.first.upcase] } } + + context 'when `policy_violations_es_filter` feature flag is disabled' do + before do + stub_feature_flags(policy_violations_es_filter: false) + end + + it 'raises an ArgumentError' do + expect_graphql_error_to_be_created(Gitlab::Graphql::Errors::ArgumentError) do + subject + end + end + end + end end end