diff --git a/app/assets/javascripts/security_configuration/components/constants.js b/app/assets/javascripts/security_configuration/components/constants.js index da213b0ed43072c07eff8a79704e53e17761d1c9..fd713a7a50439a0f6287ac42aa611797e83f31f2 100644 --- a/app/assets/javascripts/security_configuration/components/constants.js +++ b/app/assets/javascripts/security_configuration/components/constants.js @@ -1,6 +1,5 @@ import { helpPagePath } from '~/helpers/help_page_helper'; import { __, s__ } from '~/locale'; -import ContinuousVulnerabilityScan from '~/security_configuration/components/continuous_vulnerability_scan.vue'; import { REPORT_TYPE_SAST, @@ -211,7 +210,6 @@ export const securityFeatures = [ configurationHelpPath: DEPENDENCY_SCANNING_CONFIG_HELP_PATH, type: REPORT_TYPE_DEPENDENCY_SCANNING, anchor: 'dependency-scanning', - slotComponent: ContinuousVulnerabilityScan, }, { name: CONTAINER_SCANNING_NAME, diff --git a/app/assets/javascripts/security_configuration/components/continuous_vulnerability_scan.vue b/app/assets/javascripts/security_configuration/components/continuous_vulnerability_scan.vue deleted file mode 100644 index df648f665c7923ea4cb63da2e78c677c515b4ba1..0000000000000000000000000000000000000000 --- a/app/assets/javascripts/security_configuration/components/continuous_vulnerability_scan.vue +++ /dev/null @@ -1,133 +0,0 @@ -<script> -import { GlBadge, GlIcon, GlToggle, GlLink, GlSprintf, GlAlert } from '@gitlab/ui'; -import ProjectSetContinuousVulnerabilityScanning from '~/security_configuration/graphql/project_set_continuous_vulnerability_scanning.graphql'; -import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; -import { __, s__ } from '~/locale'; -import { helpPagePath } from '~/helpers/help_page_helper'; - -export default { - name: 'ContinuousVulnerabilityscan', - components: { GlBadge, GlIcon, GlToggle, GlLink, GlSprintf, GlAlert }, - mixins: [glFeatureFlagsMixin()], - inject: ['continuousVulnerabilityScansEnabled', 'projectFullPath'], - i18n: { - badgeLabel: __('Experiment'), - title: s__('CVS|Continuous Vulnerability Scan'), - description: s__( - 'CVS|Detect vulnerabilities outside a pipeline as new data is added to the GitLab Advisory Database.', - ), - learnMore: __('Learn more'), - testingAgreementMessage: s__( - 'CVS|By enabling this feature, you accept the %{linkStart}Testing Terms of Use%{linkEnd}', - ), - }, - props: { - feature: { - type: Object, - required: true, - }, - }, - data() { - return { - toggleValue: this.continuousVulnerabilityScansEnabled, - errorMessage: '', - isAlertDismissed: false, - }; - }, - computed: { - isFeatureConfigured() { - return this.feature.available && this.feature.configured; - }, - shouldShowAlert() { - return this.errorMessage && !this.isAlertDismissed; - }, - hasCvsSection() { - return ( - this.glFeatures.dependencyScanningOnAdvisoryIngestion && - !this.glFeatures.globalDependencyScanningOnAdvisoryIngestion - ); - }, - }, - methods: { - reportError(error) { - this.errorMessage = error; - this.isAlertDismissed = false; - }, - async toggleCVS(checked) { - try { - const { data } = await this.$apollo.mutate({ - mutation: ProjectSetContinuousVulnerabilityScanning, - variables: { - input: { - projectPath: this.projectFullPath, - enable: checked, - }, - }, - }); - - const { errors } = data.projectSetContinuousVulnerabilityScanning; - - if (errors.length > 0) { - this.reportError(errors[0].message); - } - if (data.projectSetContinuousVulnerabilityScanning !== null) { - this.toggleValue = checked; - } - } catch (error) { - this.reportError(error); - } - }, - }, - CVSHelpPagePath: helpPagePath( - 'user/application_security/continuous_vulnerability_scanning/index', - ), - experimentHelpPagePath: helpPagePath('policy/experiment-beta-support', { anchor: 'experiment' }), -}; -</script> - -<template> - <div v-if="hasCvsSection"> - <h4 class="gl-font-base gl-m-0 gl-mt-6"> - {{ $options.i18n.title }} - <gl-badge - ref="badge" - :href="$options.experimentHelpPagePath" - target="_blank" - size="sm" - variant="neutral" - class="gl-cursor-pointer" - >{{ $options.i18n.badgeLabel }}</gl-badge - > - </h4> - <gl-alert - v-if="shouldShowAlert" - class="gl-mb-5 gl-mt-2" - variant="danger" - @dismiss="isAlertDismissed = true" - >{{ errorMessage }}</gl-alert - > - <gl-toggle - class="gl-mt-5" - :disabled="!isFeatureConfigured" - :value="toggleValue" - :label="s__('CVS|Toggle CVS')" - label-position="hidden" - @change="toggleCVS" - /> - - <p class="gl-mb-0 gl-mt-5"> - {{ $options.i18n.description }} - <gl-link :href="$options.CVSHelpPagePath" target="_blank">{{ - $options.i18n.learnMore - }}</gl-link> - <br /> - <gl-sprintf :message="$options.i18n.testingAgreementMessage"> - <template #link="{ content }"> - <gl-link href="https://about.gitlab.com/handbook/legal/testing-agreement" target="_blank"> - {{ content }} <gl-icon name="external-link" /> - </gl-link> - </template> - </gl-sprintf> - </p> - </div> -</template> diff --git a/app/assets/javascripts/security_configuration/components/feature_card.vue b/app/assets/javascripts/security_configuration/components/feature_card.vue index 395bdad5dcc5599d3cb1876daee3d8cf4d0932c9..2100da78219a54d55c9c74d715fd9153d3642a19 100644 --- a/app/assets/javascripts/security_configuration/components/feature_card.vue +++ b/app/assets/javascripts/security_configuration/components/feature_card.vue @@ -73,9 +73,6 @@ export default { hasSecondary() { return Boolean(this.feature.secondary); }, - hasSlotComponent() { - return Boolean(this.feature.slotComponent); - }, // This condition is a temporary hack to not display any wrong information // until this BE Bug is fixed: https://gitlab.com/gitlab-org/gitlab/-/issues/350307. // More Information: https://gitlab.com/gitlab-org/gitlab/-/issues/350307#note_825447417 @@ -221,9 +218,5 @@ export default { {{ $options.i18n.configurationGuide }} </gl-button> </div> - - <div v-if="hasSlotComponent"> - <component :is="feature.slotComponent" :feature="feature" /> - </div> </gl-card> </template> diff --git a/app/assets/javascripts/security_configuration/index.js b/app/assets/javascripts/security_configuration/index.js index 4b4980911342cca896fdb371c7d9c47295bbebcb..aa3c9c876221d46d0253ad46378c2716428fdac6 100644 --- a/app/assets/javascripts/security_configuration/index.js +++ b/app/assets/javascripts/security_configuration/index.js @@ -26,7 +26,6 @@ export const initSecurityConfiguration = (el) => { autoDevopsHelpPagePath, autoDevopsPath, vulnerabilityTrainingDocsPath, - continuousVulnerabilityScansEnabled, } = el.dataset; const { augmentedSecurityFeatures } = augmentFeatures( @@ -44,7 +43,6 @@ export const initSecurityConfiguration = (el) => { autoDevopsHelpPagePath, autoDevopsPath, vulnerabilityTrainingDocsPath, - continuousVulnerabilityScansEnabled, }, render(createElement) { return createElement(SecurityConfigurationApp, { diff --git a/config/feature_flags/development/global_dependency_scanning_on_advisory_ingestion.yml b/config/feature_flags/development/global_dependency_scanning_on_advisory_ingestion.yml deleted file mode 100644 index fbede45e6655ef5709f7a7feb7718e950f48c9a8..0000000000000000000000000000000000000000 --- a/config/feature_flags/development/global_dependency_scanning_on_advisory_ingestion.yml +++ /dev/null @@ -1,8 +0,0 @@ ---- -name: global_dependency_scanning_on_advisory_ingestion -introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/135581 -rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/427424 -milestone: '16.6' -type: development -group: group::composition analysis -default_enabled: true diff --git a/config/sidekiq_queues.yml b/config/sidekiq_queues.yml index 88115fab689638a56a09c26493fcceffe2546ca6..77503814158d3b2245343d598f02e3fe35bfe408 100644 --- a/config/sidekiq_queues.yml +++ b/config/sidekiq_queues.yml @@ -511,8 +511,6 @@ - 1 - - package_cleanup - 1 -- - package_metadata_advisory_scan - - 1 - - package_metadata_global_advisory_scan - 1 - - package_repositories diff --git a/ee/app/controllers/ee/projects/security/configuration_controller.rb b/ee/app/controllers/ee/projects/security/configuration_controller.rb index 188477b80daca05244872266b0460745a2606e05..e8e1d556ef961a2f5e31fa78082b001c8642632c 100644 --- a/ee/app/controllers/ee/projects/security/configuration_controller.rb +++ b/ee/app/controllers/ee/projects/security/configuration_controller.rb @@ -14,8 +14,6 @@ module ConfigurationController before_action only: [:show] do push_frontend_feature_flag(:security_auto_fix, project) - push_frontend_feature_flag(:dependency_scanning_on_advisory_ingestion, project) - push_frontend_feature_flag(:global_dependency_scanning_on_advisory_ingestion, project) end before_action only: [:auto_fix] do diff --git a/ee/app/finders/sbom/possibly_affected_occurrences_finder.rb b/ee/app/finders/sbom/possibly_affected_occurrences_finder.rb index efab9e9eb77eeb6632304dec64156a19d5348726..1d3a69bbeca568118396de0b8b17c1c9b96e5a34 100644 --- a/ee/app/finders/sbom/possibly_affected_occurrences_finder.rb +++ b/ee/app/finders/sbom/possibly_affected_occurrences_finder.rb @@ -10,33 +10,24 @@ class PossiblyAffectedOccurrencesFinder # # @param purl_type [string] PURL type of the component to search for # @param package_name [string] Package name of the component to search for - # @param global [boolean] When true, search in all projects including those - # where Continuous Vulnerability Scanning isn't enabled. - def initialize(purl_type:, package_name:, global:) + def initialize(purl_type:, package_name:) @purl_type = purl_type @package_name = package_name - @global = global end def execute_in_batches(of: BATCH_SIZE) return unless component_id Sbom::Occurrence.filter_by_components(component_id).each_batch(of: of) do |batch| - components = batch + yield batch .with_component_source_version_project_and_pipeline .filter_by_non_nil_component_version - - if global - yield components - else - yield components.filter_by_cvs_enabled - end end end private - attr_reader :package_name, :purl_type, :global + attr_reader :package_name, :purl_type def component_id Sbom::Component diff --git a/ee/app/models/sbom/occurrence.rb b/ee/app/models/sbom/occurrence.rb index f88f9cce39aeea9032fe494b4aa2c047c2c60272..bf615490c850085366f3b7130cbb77fb5eb072dd 100644 --- a/ee/app/models/sbom/occurrence.rb +++ b/ee/app/models/sbom/occurrence.rb @@ -104,10 +104,6 @@ class Occurrence < ApplicationRecord includes(:component, :source, :component_version, :project).preload(:pipeline) end scope :filter_by_non_nil_component_version, -> { where.not(component_version: nil) } - scope :filter_by_cvs_enabled, -> do - joins(project: :security_setting) - .where(project_security_settings: { continuous_vulnerability_scans_enabled: true }) - end def location { diff --git a/ee/app/services/package_metadata/advisory_scan_service.rb b/ee/app/services/package_metadata/advisory_scan_service.rb index 910ae19f247039794d6e4ff91d45f764fe6a25c1..8b77f11c6d22abf2f8fcaac6f9b7e3836709d65e 100644 --- a/ee/app/services/package_metadata/advisory_scan_service.rb +++ b/ee/app/services/package_metadata/advisory_scan_service.rb @@ -2,8 +2,8 @@ module PackageMetadata class AdvisoryScanService - def self.execute(advisory, global:) - ::Gitlab::VulnerabilityScanning::AdvisoryScanner.scan_projects_for(advisory, global: global) + def self.execute(advisory) + ::Gitlab::VulnerabilityScanning::AdvisoryScanner.scan_projects_for(advisory) end end end diff --git a/ee/app/workers/all_queues.yml b/ee/app/workers/all_queues.yml index a483d1d19fab88d58ec6f7751a1b5f0cb46d3004..a3483cc61577465876f59c90d7ddcf8a4272427e 100644 --- a/ee/app/workers/all_queues.yml +++ b/ee/app/workers/all_queues.yml @@ -1803,15 +1803,6 @@ :weight: 1 :idempotent: true :tags: [] -- :name: package_metadata_advisory_scan - :worker_name: PackageMetadata::AdvisoryScanWorker - :feature_category: :software_composition_analysis - :has_external_dependencies: false - :urgency: :low - :resource_boundary: :unknown - :weight: 1 - :idempotent: true - :tags: [] - :name: package_metadata_global_advisory_scan :worker_name: PackageMetadata::GlobalAdvisoryScanWorker :feature_category: :software_composition_analysis diff --git a/ee/app/workers/package_metadata/advisory_scan_worker.rb b/ee/app/workers/package_metadata/advisory_scan_worker.rb deleted file mode 100644 index c9ed21d8dc9af9e063efd781b60c976b21c22f54..0000000000000000000000000000000000000000 --- a/ee/app/workers/package_metadata/advisory_scan_worker.rb +++ /dev/null @@ -1,23 +0,0 @@ -# frozen_string_literal: true - -module PackageMetadata - class AdvisoryScanWorker - include Gitlab::EventStore::Subscriber - - data_consistency :delayed - feature_category :software_composition_analysis - urgency :low - deduplicate :until_executed - idempotent! - - def handle_event(event) - advisory = Advisory.with_affected_packages.find_by_id(event.data[:advisory_id]) - - if advisory.nil? - return logger.error(structured_payload(message: 'Advisory not found.', advisory_id: event.data[:advisory_id])) - end - - AdvisoryScanService.execute(advisory, global: false) - end - end -end diff --git a/ee/app/workers/package_metadata/global_advisory_scan_worker.rb b/ee/app/workers/package_metadata/global_advisory_scan_worker.rb index 792b8b4aee18029399adce0be1b70a6da473a898..b58167bcb84169ead3aa92404c9a8b0959fe5460 100644 --- a/ee/app/workers/package_metadata/global_advisory_scan_worker.rb +++ b/ee/app/workers/package_metadata/global_advisory_scan_worker.rb @@ -17,7 +17,7 @@ def handle_event(event) return logger.error(structured_payload(message: 'Advisory not found.', advisory_id: event.data[:advisory_id])) end - AdvisoryScanService.execute(advisory, global: true) + AdvisoryScanService.execute(advisory) end end end diff --git a/ee/lib/ee/gitlab/event_store.rb b/ee/lib/ee/gitlab/event_store.rb index 484ad2347ec05fd2b051ae97f2a3da15d225a365..8d74fd31cf3625401e19aafab4bdbaa7d2f7b333 100644 --- a/ee/lib/ee/gitlab/event_store.rb +++ b/ee/lib/ee/gitlab/event_store.rb @@ -36,7 +36,6 @@ def configure!(store) to: ::Repositories::DefaultBranchChangedEvent, if: -> (_) { ::Gitlab::CurrentSettings.elasticsearch_indexing? } store.subscribe ::Search::Zoekt::DefaultBranchChangedWorker, to: ::Repositories::DefaultBranchChangedEvent - store.subscribe ::PackageMetadata::AdvisoryScanWorker, to: ::PackageMetadata::IngestedAdvisoryEvent store.subscribe ::PackageMetadata::GlobalAdvisoryScanWorker, to: ::PackageMetadata::IngestedAdvisoryEvent store.subscribe ::Llm::NamespaceAccessCacheResetWorker, to: ::NamespaceSettings::AiRelatedSettingsChangedEvent store.subscribe ::Llm::NamespaceAccessCacheResetWorker, to: ::Members::MembersAddedEvent diff --git a/ee/lib/gitlab/vulnerability_scanning/advisory_scanner.rb b/ee/lib/gitlab/vulnerability_scanning/advisory_scanner.rb index 0ee3a1792ecbe7b03f8f1a34cef4a15f150d8c76..f25f9860a2ebc0645d9d3656f1b6b5fc9be471cb 100644 --- a/ee/lib/gitlab/vulnerability_scanning/advisory_scanner.rb +++ b/ee/lib/gitlab/vulnerability_scanning/advisory_scanner.rb @@ -8,22 +8,17 @@ class AdvisoryScanner # project. # # @param advisory [PackageMetadata::Advisory] advisory - # @param global [boolean] When true, scan all projects including those - # where Continuous Vulnerability Scanning isn't enabled. - def self.scan_projects_for(advisory, global:) - new(advisory, global: global).execute + def self.scan_projects_for(advisory) + new(advisory).execute end # Create a new advisory scanner instance. # # @param advisory [PackageMetadata::Advisory] advisory - # @param global [boolean] When true, scan all projects including those - # where Continuous Vulnerability Scanning isn't enabled. # # @return [Gitlab::VulnerabilityScanning::AdvisoryScanner] - def initialize(advisory, global:) + def initialize(advisory) @advisory = advisory - @global = global @possibly_affected_sbom_occurrences_count = 0 @known_affected_sbom_occurrences_count = 0 @possibly_affected_projects = {} @@ -31,8 +26,6 @@ def initialize(advisory, global:) end def execute - return if global && !Feature.enabled?(:global_dependency_scanning_on_advisory_ingestion) - start_time = Time.current.iso8601 advisory.affected_packages.each do |affected_package| @@ -42,8 +35,7 @@ def execute ::Sbom::PossiblyAffectedOccurrencesFinder.new( purl_type: purl_type, - package_name: package_name, - global: global + package_name: package_name ).execute_in_batches do |batch| bulk_vulnerability_ingestion(affected_package, advisory_data_object, batch) end @@ -58,14 +50,13 @@ def execute known_affected_projects: known_affected_projects_count, possibly_affected_sbom_occurrences: possibly_affected_sbom_occurrences_count, known_affected_sbom_occurrences: known_affected_sbom_occurrences_count - }, - global: global + } ).execute end private - attr_reader :advisory, :global, :possibly_affected_sbom_occurrences_count, :known_affected_sbom_occurrences_count + attr_reader :advisory, :possibly_affected_sbom_occurrences_count, :known_affected_sbom_occurrences_count def occurrence_is_affected?(purl_type:, range:, version:) Gitlab::VulnerabilityScanning::DependencyScanning::AffectedVersionRangeMatcher.new( diff --git a/ee/lib/gitlab/vulnerability_scanning/track_cvs_service.rb b/ee/lib/gitlab/vulnerability_scanning/track_cvs_service.rb index a0ae617a4e9eab97cb261a01ec1f21d41abfadae..d64e5b7eeb24e2d4f2edf0ccca72ed02869ef6c9 100644 --- a/ee/lib/gitlab/vulnerability_scanning/track_cvs_service.rb +++ b/ee/lib/gitlab/vulnerability_scanning/track_cvs_service.rb @@ -10,40 +10,30 @@ module Gitlab module VulnerabilityScanning class TrackCvsService CVS_SCAN_SCHEMA_URL = 'iglu:com.gitlab/secure_cvs/jsonschema/1-0-0' - EVENT = 'VulnerabilityScanning::AdvisoryScanner' - SCOPE = 'scan' - GLOBAL_SCOPE = 'global_scan' - LABEL = 'secure_cvs' + EVENT_CATEGORY = 'VulnerabilityScanning::AdvisoryScanner' + EVENT_ACTION = 'global_scan' + EVENT_LABEL = 'secure_cvs' - def initialize(advisory:, start_time:, end_time:, counts:, global:) + def initialize(advisory:, start_time:, end_time:, counts:) @advisory = advisory @start_time = start_time @end_time = end_time @counts = counts - @global = global end def execute context = SnowplowTracker::SelfDescribingJson.new(CVS_SCAN_SCHEMA_URL, data_to_track) ::Gitlab::Tracking.event( - EVENT, - scope, + EVENT_CATEGORY, + EVENT_ACTION, context: [context], - label: LABEL + label: EVENT_LABEL ) end private - attr_reader :advisory, :counts, :start_time, :end_time, :global - - def scope - if global - GLOBAL_SCOPE - else - SCOPE - end - end + attr_reader :advisory, :counts, :start_time, :end_time def data_to_track { diff --git a/ee/spec/finders/sbom/possibly_affected_occurrences_finder_spec.rb b/ee/spec/finders/sbom/possibly_affected_occurrences_finder_spec.rb index 3997777df92e035015eca59a4cd2bd23476a79a0..35dd205d4b1eae160460a9ed257d08dfee27c331 100644 --- a/ee/spec/finders/sbom/possibly_affected_occurrences_finder_spec.rb +++ b/ee/spec/finders/sbom/possibly_affected_occurrences_finder_spec.rb @@ -30,12 +30,11 @@ let_it_be(:package_name) { matching_component.name } let_it_be(:purl_type) { matching_component.purl_type } - let_it_be(:global) { false } # use a method instead of a subject to avoid rspec memoization def possibly_affected_occurrences occurrences = [] - described_class.new(purl_type: purl_type, package_name: package_name, global: global).execute_in_batches do |batch| + described_class.new(purl_type: purl_type, package_name: package_name).execute_in_batches do |batch| batch.each do |possibly_affected_occurrence| occurrences << possibly_affected_occurrence end @@ -48,8 +47,8 @@ def possibly_affected_occurrences let_it_be(:package_name) { 'non-matching-package-name' } it 'returns nil' do - expect(described_class.new(purl_type: purl_type, package_name: package_name, - global: global).execute_in_batches).to be_nil + expect(described_class.new(purl_type: purl_type, package_name: package_name) + .execute_in_batches).to be_nil end it { expect(possibly_affected_occurrences).to be_empty } @@ -59,8 +58,8 @@ def possibly_affected_occurrences let_it_be(:purl_type) { 'non-matching-purl-type' } it 'returns nil' do - expect(described_class.new(purl_type: purl_type, package_name: package_name, - global: global).execute_in_batches).to be_nil + expect(described_class.new(purl_type: purl_type, package_name: package_name) + .execute_in_batches).to be_nil end it { expect(possibly_affected_occurrences).to be_empty } @@ -68,62 +67,42 @@ def possibly_affected_occurrences end context 'when a component matches the provided details' do - context 'and the project for the component does not have cvs enabled' do - context 'and the search is not global' do - let_it_be(:global) { false } - - it { expect(possibly_affected_occurrences).to be_empty } - end - - context 'and the search is global' do - let_it_be(:global) { true } - - it 'returns the possibly affected occurrences' do - expect(possibly_affected_occurrences).to match_array(matching_occurrences) - end - end + it 'returns the possibly affected occurrences' do + expect(possibly_affected_occurrences).to match_array(matching_occurrences) end - context 'and the project for the component has cvs enabled' do - let_it_be(:project) { create(:project, :with_cvs) } + it 'does not execute an N+1 query' do + control = ActiveRecord::QueryRecorder.new(skip_cached: false) { possibly_affected_occurrences.first } - it 'returns the possibly affected occurrences' do - expect(possibly_affected_occurrences).to match_array(matching_occurrences) - end + create(:sbom_component_version, component: matching_component, version: '1.0.5') + create(:sbom_component_version, component: matching_component, version: '1.0.6') + create(:sbom_component_version, component: matching_component, version: '1.0.7') - it 'does not execute an N+1 query' do - control = ActiveRecord::QueryRecorder.new(skip_cached: false) { possibly_affected_occurrences.first } - - create(:sbom_component_version, component: matching_component, version: '1.0.5') - create(:sbom_component_version, component: matching_component, version: '1.0.6') - create(:sbom_component_version, component: matching_component, version: '1.0.7') + expect { possibly_affected_occurrences.first }.not_to exceed_all_query_limit(control) + end - expect { possibly_affected_occurrences.first }.not_to exceed_all_query_limit(control) + context 'and an sbom occurrence exists without a version' do + let_it_be(:sbom_occurrence_without_component_version) do + create(:sbom_occurrence, component: matching_component, component_version: nil) end - context 'and an sbom occurrence exists without a version' do - let_it_be(:sbom_occurrence_without_component_version) do - create(:sbom_occurrence, component: matching_component, component_version: nil) - end - - it 'does not return the sbom occurrence without a component version' do - expect(possibly_affected_occurrences).not_to include(sbom_occurrence_without_component_version) - end + it 'does not return the sbom occurrence without a component version' do + expect(possibly_affected_occurrences).not_to include(sbom_occurrence_without_component_version) end + end - it 'pre-loads associations to avoid an N+1 query' do - described_class.new(purl_type: purl_type, package_name: package_name, - global: global).execute_in_batches do |batch| - batch.each do |record| - queries = ActiveRecord::QueryRecorder.new do - record.component - record.component_version - record.source - record.pipeline - record.project - end - expect(queries.count).to be_zero + it 'pre-loads associations to avoid an N+1 query' do + described_class.new(purl_type: purl_type, package_name: package_name + ).execute_in_batches do |batch| + batch.each do |record| + queries = ActiveRecord::QueryRecorder.new do + record.component + record.component_version + record.source + record.pipeline + record.project end + expect(queries.count).to be_zero end end end diff --git a/ee/spec/lib/gitlab/vulnerability_scanning/advisory_scanner_spec.rb b/ee/spec/lib/gitlab/vulnerability_scanning/advisory_scanner_spec.rb index 081e1215dfc089fc87ea570cfa74c9d51f8ac083..9fe83846bea0538e2a767195202fe860cf2bd00b 100644 --- a/ee/spec/lib/gitlab/vulnerability_scanning/advisory_scanner_spec.rb +++ b/ee/spec/lib/gitlab/vulnerability_scanning/advisory_scanner_spec.rb @@ -4,63 +4,56 @@ RSpec.describe Gitlab::VulnerabilityScanning::AdvisoryScanner, feature_category: :software_composition_analysis do let_it_be(:user) { create(:user) } - let_it_be(:user_2) { create(:user) } - let_it_be_with_reload(:affected_project) { create(:project) } let_it_be(:affected_pipeline) { create(:ci_pipeline, user: user, project: affected_project) } - let_it_be(:affected_version) { create(:sbom_component_version, version: '0.9.0') } - let_it_be(:affected_project_2) { create(:project) } + let_it_be(:user_2) { create(:user) } + let_it_be_with_reload(:affected_twice_project) { create(:project) } + let_it_be(:affected_twice_pipeline) { create(:ci_pipeline, user: user_2, project: affected_twice_project) } + + let_it_be(:invalid_affected_project) { create(:project) } # A user is required to attribute vulnerability creation to a user, hence the omission of a user # for the pipeline here. - let_it_be(:invalid_affected_pipeline) { create(:ci_pipeline, project: affected_project_2) } - - let_it_be_with_reload(:affected_project_3) { create(:project) } - let_it_be(:affected_pipeline_3) { create(:ci_pipeline, user: user_2, project: affected_project_3) } + let_it_be(:invalid_affected_pipeline) { create(:ci_pipeline, project: invalid_affected_project) } let_it_be(:unaffected_project) { create(:project) } let_it_be(:unaffected_pipeline) { create(:ci_pipeline, user: user, project: unaffected_project) } + + let_it_be(:affected_version) { create(:sbom_component_version, version: '0.8.0') } + let_it_be(:affected_version_2) { create(:sbom_component_version, version: '0.9.0') } let_it_be(:unaffected_version) { create(:sbom_component_version, version: '1.0.0') } - let_it_be(:affected_package) do - create(:pm_affected_package, package_name: 'eslint', purl_type: :npm, affected_range: '>=0.8.0 <1.0.0') + let(:affected_range) { '>=0.8.0 <1.0.0' } + + let(:affected_package) do + create(:pm_affected_package, package_name: 'eslint', purl_type: :npm, affected_range: affected_range) end before_all do eslint = create(:sbom_component, name: 'eslint', purl_type: :npm) create(:sbom_occurrence, component: eslint, component_version: affected_version, pipeline: affected_pipeline) - create(:sbom_occurrence, component: eslint, component_version: affected_version, pipeline: affected_pipeline_3) + create(:sbom_occurrence, component: eslint, component_version: affected_version, pipeline: affected_twice_pipeline) + create(:sbom_occurrence, component: eslint, component_version: affected_version, pipeline: affected_twice_pipeline) create(:sbom_occurrence, component: eslint, component_version: affected_version, pipeline: invalid_affected_pipeline) create(:sbom_occurrence, component: eslint, component_version: unaffected_version, pipeline: unaffected_pipeline) end describe '#execute' do - let_it_be(:global) { false } - let_it_be(:ga_flag_enabled) { true } - before do allow(Time).to receive(:current).and_return(Time.new(2023, 11, 14, 0, 0, 0, '+00:00')) allow(Gitlab::VulnerabilityScanning::TrackCvsService).to receive(:new).and_call_original allow(Gitlab::AppJsonLogger).to receive(:debug).and_call_original allow(Gitlab::AppJsonLogger).to receive(:error).and_call_original - stub_feature_flags(global_dependency_scanning_on_advisory_ingestion: ga_flag_enabled) - - described_class.scan_projects_for(affected_package.advisory, global: global) + described_class.scan_projects_for(affected_package.advisory) end - context 'when the projects have cvs_enabled' do - before_all do - affected_project.security_setting.update!(continuous_vulnerability_scans_enabled: true) - affected_project_2.security_setting.update!(continuous_vulnerability_scans_enabled: true) - end - - it 'creates a vulnerability in the affected project' do + context 'when the advisory matches component versions' do + it 'creates vulnerabilities in valid affected projects only' do advisory = affected_package.advisory - vulnerabilities = affected_project.vulnerabilities - expect(vulnerabilities).to match_array([ + expect(affected_project.vulnerabilities).to match_array([ have_attributes( author_id: user.id, project_id: affected_pipeline.project.id, @@ -75,147 +68,67 @@ ) ]) + expect(affected_twice_project.vulnerabilities).to match_array([ + have_attributes( + author_id: user_2.id, + project_id: affected_twice_pipeline.project.id, + state: 'detected', + confidence: 'unknown', + report_type: 'dependency_scanning', + present_on_default_branch: true, + title: advisory.title, + severity: advisory.cvss_v3.severity.downcase, + finding_description: advisory.description, + solution: affected_package.solution + ), + have_attributes( + author_id: user_2.id, + project_id: affected_twice_pipeline.project.id, + state: 'detected', + confidence: 'unknown', + report_type: 'dependency_scanning', + present_on_default_branch: true, + title: advisory.title, + severity: advisory.cvss_v3.severity.downcase, + finding_description: advisory.description, + solution: affected_package.solution + ) + ]) + + expect(unaffected_project.vulnerabilities).to be_empty + expect(invalid_affected_project.vulnerabilities).to be_empty + end + + it "logs a debug message for the scan" do expect(Gitlab::AppJsonLogger).to have_received(:debug).with( message: 'Successfully created vulnerabilities on advisory ingestion', - project_ids_with_upsert: [affected_project.id], - project_ids_with_error: [affected_project_2.id], - source_xid: 'glad', - advisory_xid: advisory.advisory_xid) - - expect(Gitlab::VulnerabilityScanning::TrackCvsService).to have_received(:new).with( - advisory: advisory, - start_time: Time.current.iso8601, - end_time: Time.current.iso8601, - counts: { - possibly_affected_projects: 2, - possibly_affected_sbom_occurrences: 2, - known_affected_projects: 2, - known_affected_sbom_occurrences: 2 - }, - global: false - ) + project_ids_with_upsert: [affected_project.id, affected_twice_project.id], + project_ids_with_error: [invalid_affected_project.id], + source_xid: 'glad', advisory_xid: affected_package.advisory.advisory_xid) end - it 'does not create a vulnerability in the unaffected project' do - vulnerabilities = unaffected_project.vulnerabilities - expect(vulnerabilities).to be_empty + it "tracks an event for the scan" do expect(Gitlab::VulnerabilityScanning::TrackCvsService).to have_received(:new).with( advisory: affected_package.advisory, start_time: Time.current.iso8601, end_time: Time.current.iso8601, counts: { - possibly_affected_projects: 2, - known_affected_projects: 2, - possibly_affected_sbom_occurrences: 2, - known_affected_sbom_occurrences: 2 - }, - global: false + possibly_affected_projects: 4, + possibly_affected_sbom_occurrences: 5, + known_affected_projects: 3, + known_affected_sbom_occurrences: 4 + } ) end end - context 'when the projects do not have cvs_enabled' do - context 'and the scan is global' do - let_it_be(:global) { true } - - context 'and global scans are enabled' do - let_it_be(:ga_flag_enabled) { true } - - it 'creates a vulnerability in valid affected projects' do - advisory = affected_package.advisory - - expect(affected_project.vulnerabilities).to match_array([ - have_attributes( - author_id: user.id, - project_id: affected_pipeline.project.id, - state: 'detected', - confidence: 'unknown', - report_type: 'dependency_scanning', - present_on_default_branch: true, - title: advisory.title, - severity: advisory.cvss_v3.severity.downcase, - finding_description: advisory.description, - solution: affected_package.solution - ) - ]) - expect(affected_project_3.vulnerabilities).to match_array([ - have_attributes( - author_id: user_2.id, - project_id: affected_pipeline_3.project.id, - state: 'detected', - confidence: 'unknown', - report_type: 'dependency_scanning', - present_on_default_branch: true, - title: advisory.title, - severity: advisory.cvss_v3.severity.downcase, - finding_description: advisory.description, - solution: affected_package.solution - ) - ]) - - expect(Gitlab::AppJsonLogger).to have_received(:debug).with( - message: 'Successfully created vulnerabilities on advisory ingestion', - project_ids_with_upsert: [affected_project.id, affected_project_3.id], - project_ids_with_error: [affected_project_2.id], - source_xid: 'glad', advisory_xid: advisory.advisory_xid) - - expect(Gitlab::VulnerabilityScanning::TrackCvsService).to have_received(:new).with( - advisory: advisory, - start_time: Time.current.iso8601, - end_time: Time.current.iso8601, - counts: { - possibly_affected_projects: 4, - possibly_affected_sbom_occurrences: 4, - known_affected_projects: 3, - known_affected_sbom_occurrences: 3 - }, - global: true - ) - end - - context 'when the advisory matches multiple unaffected components' do - # We override the affected package's range here so that it doesn't match - # any of the base components created. - # Reference: https://gitlab.com/gitlab-org/gitlab/-/issues/427424#note_1654487562 - let_it_be(:affected_package) do - create(:pm_affected_package, package_name: 'eslint', purl_type: :npm, affected_range: '<0.1.0') - end - - it 'does not attempt to create an empty list of vulnerabilities' do - expect(affected_project.vulnerabilities).to be_empty - expect(Gitlab::AppJsonLogger).not_to have_received(:error) - end - end - - context 'and global scans are disabled' do - let_it_be(:ga_flag_enabled) { false } - - it 'does not create a vulnerability in the affected project' do - expect(affected_project.vulnerabilities).to be_empty - expect(Gitlab::VulnerabilityScanning::TrackCvsService).not_to have_received(:new) - end - end - end - end + context 'when component matches but version is not in the affected range' do + let(:affected_range) { '<0.1.0' } - context 'and the scan is not global' do - let_it_be(:global) { false } - - it 'does not create a vulnerability in the affected project' do - expect(affected_project.vulnerabilities).to be_empty - expect(Gitlab::VulnerabilityScanning::TrackCvsService).to have_received(:new).with( - advisory: affected_package.advisory, - start_time: Time.current.iso8601, - end_time: Time.current.iso8601, - counts: { - possibly_affected_projects: 0, - possibly_affected_sbom_occurrences: 0, - known_affected_projects: 0, - known_affected_sbom_occurrences: 0 - }, - global: false - ) - end + it 'does not attempt to create an empty list of vulnerabilities' do + expect(affected_project.vulnerabilities).to be_empty + expect(affected_twice_project.vulnerabilities).to be_empty + expect(Gitlab::AppJsonLogger).not_to have_received(:error) end end end diff --git a/ee/spec/lib/gitlab/vulnerability_scanning/track_cvs_service_spec.rb b/ee/spec/lib/gitlab/vulnerability_scanning/track_cvs_service_spec.rb index ad713f1be76dc16ac8676ddc2a0c615fe87717af..1b49d1ca36efed63fe99c9330a335023c328a748 100644 --- a/ee/spec/lib/gitlab/vulnerability_scanning/track_cvs_service_spec.rb +++ b/ee/spec/lib/gitlab/vulnerability_scanning/track_cvs_service_spec.rb @@ -15,46 +15,35 @@ } end - let(:global) { false } - describe '#execute' do - using RSpec::Parameterized::TableSyntax - subject do - described_class.new(advisory: advisory, counts: counts, start_time: start_time, end_time: end_time, - global: global).execute + described_class.new(advisory: advisory, counts: counts, start_time: start_time, end_time: end_time) + .execute end - where(:global, :expected_action) do - false | described_class::SCOPE - true | described_class::GLOBAL_SCOPE - end - - with_them do - it 'tracks the scan event', :snowplow, :unlimited_max_formatted_output_length do - subject - - expect_snowplow_event( - category: described_class::EVENT, - action: expected_action, - context: [{ - schema: described_class::CVS_SCAN_SCHEMA_URL, - data: { - advisory_id: advisory.id, - advisory_xid: advisory.advisory_xid, - source_xid: advisory.source_xid, - start_time: start_time, - end_time: end_time, - counts: { - possibly_affected_projects: counts[:possibly_affected_projects], - known_affected_projects: counts[:known_affected_projects], - possibly_affected_sbom_occurrences: counts[:possibly_affected_sbom_occurrences], - known_affected_sbom_occurrences: counts[:known_affected_sbom_occurrences] - } + it 'tracks the scan event', :snowplow, :unlimited_max_formatted_output_length do + subject + + expect_snowplow_event( + category: described_class::EVENT_CATEGORY, + action: described_class::EVENT_ACTION, + context: [{ + schema: described_class::CVS_SCAN_SCHEMA_URL, + data: { + advisory_id: advisory.id, + advisory_xid: advisory.advisory_xid, + source_xid: advisory.source_xid, + start_time: start_time, + end_time: end_time, + counts: { + possibly_affected_projects: counts[:possibly_affected_projects], + known_affected_projects: counts[:known_affected_projects], + possibly_affected_sbom_occurrences: counts[:possibly_affected_sbom_occurrences], + known_affected_sbom_occurrences: counts[:known_affected_sbom_occurrences] } - }], - label: described_class::LABEL) - end + } + }], + label: described_class::EVENT_LABEL) end end end diff --git a/ee/spec/models/sbom/occurrence_spec.rb b/ee/spec/models/sbom/occurrence_spec.rb index 42603069b8288cfae1bbc5b2707a0b10d2f0da57..eeba24243f801602320355dff0f93a00e121bac1 100644 --- a/ee/spec/models/sbom/occurrence_spec.rb +++ b/ee/spec/models/sbom/occurrence_spec.rb @@ -160,23 +160,6 @@ end end - describe '.filter_by_cvs_enabled scope' do - let_it_be(:project_with_cvs) { create(:project, :with_cvs) } - let_it_be(:project_without_cvs) { create(:project) } - - let_it_be(:matching_occurrence) do - create(:sbom_occurrence, component: create(:sbom_component), project: project_with_cvs) - end - - let_it_be(:non_matching_occurrence) do - create(:sbom_occurrence, component: create(:sbom_component), project: project_without_cvs) - end - - it 'returns occurrences having a project where cvs is enabled' do - expect(described_class.filter_by_cvs_enabled).to eq([matching_occurrence]) - end - end - describe '.order_by_id' do let_it_be(:first) { create(:sbom_occurrence) } let_it_be(:second) { create(:sbom_occurrence) } diff --git a/ee/spec/services/package_metadata/advisory_scan_service_spec.rb b/ee/spec/services/package_metadata/advisory_scan_service_spec.rb index b0cb31ea60578c11c5b1389146fcc027b5ca1010..32bef068dccab9761f894b6d393386598fbfac32 100644 --- a/ee/spec/services/package_metadata/advisory_scan_service_spec.rb +++ b/ee/spec/services/package_metadata/advisory_scan_service_spec.rb @@ -5,13 +5,12 @@ RSpec.describe PackageMetadata::AdvisoryScanService, feature_category: :software_composition_analysis do describe '.execute' do let(:advisory) { build(:pm_advisory) } - let(:global) { false } it 'calls the advisory scanner execute method' do expect(::Gitlab::VulnerabilityScanning::AdvisoryScanner).to receive(:scan_projects_for) - .with(advisory, global: global) + .with(advisory) - described_class.execute(advisory, global: global) + described_class.execute(advisory) end end end diff --git a/ee/spec/workers/package_metadata/advisory_scan_worker_spec.rb b/ee/spec/workers/package_metadata/advisory_scan_worker_spec.rb deleted file mode 100644 index 1d8aede5f5de8ca6c6ce3a71da569bc6fe351657..0000000000000000000000000000000000000000 --- a/ee/spec/workers/package_metadata/advisory_scan_worker_spec.rb +++ /dev/null @@ -1,36 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe PackageMetadata::AdvisoryScanWorker, feature_category: :software_composition_analysis do - let_it_be(:advisory) { create(:pm_advisory) } - let(:event) { PackageMetadata::IngestedAdvisoryEvent.new(data: { advisory_id: advisory.id }) } - - before do - allow(PackageMetadata::AdvisoryScanService).to receive(:execute) - end - - it_behaves_like 'subscribes to event' - - context 'when advisory exists' do - it 'calls the advisory scanning service with the instantiated advisory' do - consume_event(subscriber: described_class, event: event) - expect(PackageMetadata::AdvisoryScanService).to have_received(:execute).with(advisory, global: false) - end - end - - context 'when advisory could not be found' do - before do - allow(PackageMetadata::Advisory).to receive(:find_by_id) - .with(event.data[:advisory_id]).and_return(nil) - end - - it 'logs the error and does not initiate a scan' do - expect(Sidekiq.logger).to receive(:error).with(hash_including({ 'message' => 'Advisory not found.', -'advisory_id' => advisory.id })) - - consume_event(subscriber: described_class, event: event) - expect(PackageMetadata::AdvisoryScanService).not_to have_received(:execute) - end - end -end diff --git a/ee/spec/workers/package_metadata/global_advisory_scan_worker_spec.rb b/ee/spec/workers/package_metadata/global_advisory_scan_worker_spec.rb index 51fa6000645a5d0d172c3892c0d48c31d0f6a046..26b22c2fe5af256aaaf6c2047a227320781fe74f 100644 --- a/ee/spec/workers/package_metadata/global_advisory_scan_worker_spec.rb +++ b/ee/spec/workers/package_metadata/global_advisory_scan_worker_spec.rb @@ -15,7 +15,7 @@ context 'when advisory exists' do it 'calls the advisory scanning service with the instantiated advisory' do consume_event(subscriber: described_class, event: event) - expect(PackageMetadata::AdvisoryScanService).to have_received(:execute).with(advisory, global: true) + expect(PackageMetadata::AdvisoryScanService).to have_received(:execute).with(advisory) end end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 63d17b99d1540ddad6e420408574a5e8988dcd8e..8e1c486afd75e763e7a21ff0d97c45872ecdcf1e 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -9450,18 +9450,6 @@ msgstr "" msgid "CVE|Why Request a CVE ID?" msgstr "" -msgid "CVS|By enabling this feature, you accept the %{linkStart}Testing Terms of Use%{linkEnd}" -msgstr "" - -msgid "CVS|Continuous Vulnerability Scan" -msgstr "" - -msgid "CVS|Detect vulnerabilities outside a pipeline as new data is added to the GitLab Advisory Database." -msgstr "" - -msgid "CVS|Toggle CVS" -msgstr "" - msgid "Cadence is not automated" msgstr "" diff --git a/spec/frontend/security_configuration/components/continuous_vulnerability_scan_spec.js b/spec/frontend/security_configuration/components/continuous_vulnerability_scan_spec.js deleted file mode 100644 index c395c91d8808a5711b1105b33aef41ad8561cd57..0000000000000000000000000000000000000000 --- a/spec/frontend/security_configuration/components/continuous_vulnerability_scan_spec.js +++ /dev/null @@ -1,132 +0,0 @@ -import { shallowMount } from '@vue/test-utils'; -import { GlBadge, GlToggle } from '@gitlab/ui'; -import VueApollo from 'vue-apollo'; -import Vue from 'vue'; -import ProjectSetContinuousVulnerabilityScanning from '~/security_configuration/graphql/project_set_continuous_vulnerability_scanning.graphql'; -import ContinuousVulnerabilityScan from '~/security_configuration/components/continuous_vulnerability_scan.vue'; -import createMockApollo from 'helpers/mock_apollo_helper'; - -Vue.use(VueApollo); - -const setCVSMockResponse = { - data: { - projectSetContinuousVulnerabilityScanning: { - continuousVulnerabilityScanningEnabled: true, - errors: [], - }, - }, -}; - -const defaultProvide = { - continuousVulnerabilityScansEnabled: true, - projectFullPath: 'project/full/path', -}; - -describe('ContinuousVulnerabilityScan', () => { - let wrapper; - let apolloProvider; - let requestHandlers; - - const createComponent = (options) => { - requestHandlers = { - setCVSMutationHandler: jest.fn().mockResolvedValue(setCVSMockResponse), - }; - - apolloProvider = createMockApollo([ - [ProjectSetContinuousVulnerabilityScanning, requestHandlers.setCVSMutationHandler], - ]); - - wrapper = shallowMount(ContinuousVulnerabilityScan, { - propsData: { - feature: { - available: true, - configured: true, - }, - }, - provide: { - glFeatures: { - dependencyScanningOnAdvisoryIngestion: true, - globalDependencyScanningOnAdvisoryIngestion: false, - }, - ...defaultProvide, - }, - apolloProvider, - ...options, - }); - }; - - beforeEach(() => { - createComponent(); - }); - - afterEach(() => { - apolloProvider = null; - }); - - const findBadge = () => wrapper.findComponent(GlBadge); - const findToggle = () => wrapper.findComponent(GlToggle); - - it('renders the component', () => { - expect(wrapper.exists()).toBe(true); - }); - - it('renders the correct title', () => { - expect(wrapper.text()).toContain('Continuous Vulnerability Scan'); - }); - - it('renders the badge and toggle component with correct values', () => { - expect(findBadge().exists()).toBe(true); - expect(findBadge().text()).toBe('Experiment'); - - expect(findToggle().exists()).toBe(true); - expect(findToggle().props('value')).toBe(defaultProvide.continuousVulnerabilityScansEnabled); - }); - - it('should disable toggle when feature is not configured', () => { - createComponent({ - propsData: { - feature: { - available: true, - configured: false, - }, - }, - }); - expect(findToggle().props('disabled')).toBe(true); - }); - - it('calls mutation on toggle change with correct payload', () => { - findToggle().vm.$emit('change', true); - - expect(requestHandlers.setCVSMutationHandler).toHaveBeenCalledWith({ - input: { - projectPath: 'project/full/path', - enable: true, - }, - }); - }); - - describe('when feature flag is disabled', () => { - it.each` - dependencyScanningOnAdvisoryIngestion | globalDependencyScanningOnAdvisoryIngestion - ${false} | ${false} - ${true} | ${true} - ${false} | ${true} - `( - 'when dependencyScanningOnAdvisoryIngestion: `$dependencyScanningOnAdvisoryIngestion` and globalDependencyScanningOnAdvisoryIngestion: `$globalDependencyScanningOnAdvisoryIngestion` should not render toggle and badge', - ({ dependencyScanningOnAdvisoryIngestion, globalDependencyScanningOnAdvisoryIngestion }) => { - createComponent({ - provide: { - glFeatures: { - dependencyScanningOnAdvisoryIngestion, - globalDependencyScanningOnAdvisoryIngestion, - }, - ...defaultProvide, - }, - }); - - expect(findToggle().exists()).toBe(false); - expect(findBadge().exists()).toBe(false); - }, - ); - }); -}); diff --git a/spec/frontend/security_configuration/components/feature_card_spec.js b/spec/frontend/security_configuration/components/feature_card_spec.js index c715d01dd581e9f2be26ac2de4e19d98680f92d3..983a66a7fd3dfdb9b127269ce502b13710385435 100644 --- a/spec/frontend/security_configuration/components/feature_card_spec.js +++ b/spec/frontend/security_configuration/components/feature_card_spec.js @@ -1,6 +1,5 @@ import { GlIcon } from '@gitlab/ui'; import { mount } from '@vue/test-utils'; -import Vue from 'vue'; import { extendedWrapper } from 'helpers/vue_test_utils_helper'; import { securityFeatures } from '~/security_configuration/components/constants'; import FeatureCard from '~/security_configuration/components/feature_card.vue'; @@ -14,10 +13,6 @@ import { import { manageViaMRErrorMessage } from '../constants'; import { makeFeature } from './utils'; -const MockComponent = Vue.component('MockComponent', { - render: (createElement) => createElement('span'), -}); - describe('FeatureCard component', () => { let feature; let wrapper; @@ -394,17 +389,4 @@ describe('FeatureCard component', () => { }); }); }); - - describe('when a slot component is passed', () => { - beforeEach(() => { - feature = makeFeature({ - slotComponent: MockComponent, - }); - createComponent({ feature }); - }); - - it('renders the component properly', () => { - expect(wrapper.findComponent(MockComponent).exists()).toBe(true); - }); - }); });