diff --git a/ee/app/services/package_metadata/ingestion/advisory/advisory_ingestion_task.rb b/ee/app/services/package_metadata/ingestion/advisory/advisory_ingestion_task.rb index da47d66ee49bcdf285566389e108267749b0a2c0..5e0e66361ba725b4531afee0407aa2cfbbf59ced 100644 --- a/ee/app/services/package_metadata/ingestion/advisory/advisory_ingestion_task.rb +++ b/ee/app/services/package_metadata/ingestion/advisory/advisory_ingestion_task.rb @@ -17,10 +17,10 @@ def self.execute(import_data) def execute results = PackageMetadata::Advisory.bulk_upsert!(valid_advisories, unique_by: %w[advisory_xid source_xid], - returns: %w[advisory_xid id published_date]) + returns: %w[advisory_xid source_xid id published_date]) - map = results.each_with_object({}) do |(advisory_xid, id, published_date), acc| - acc[advisory_xid] = Hashie::Mash.new({ id: id, published_date: published_date }) + map = results.each_with_object({}) do |(advisory_xid, source_xid, id, published_date), acc| + acc[advisory_xid] = Hashie::Mash.new({ id: id, source_xid: source_xid, published_date: published_date }) end advisory_map.merge!(map) end diff --git a/ee/app/services/package_metadata/ingestion/advisory/ingestion_service.rb b/ee/app/services/package_metadata/ingestion/advisory/ingestion_service.rb index 74e0100eba989a90a4a3325871f2f6c61cfb363d..db52d8ba22665fb2cc565fda85ce2a501bce1cbd 100644 --- a/ee/app/services/package_metadata/ingestion/advisory/ingestion_service.rb +++ b/ee/app/services/package_metadata/ingestion/advisory/ingestion_service.rb @@ -34,9 +34,12 @@ def upsert_affected_package_data end def publish! - return unless Feature.enabled?(:dependency_scanning_on_advisory_ingestion) - publishable_advisories.each do |data_object| + next if data_object.source_xid == 'glad' && !Feature.enabled?(:dependency_scanning_on_advisory_ingestion) + next if data_object.source_xid == 'trivy-db' && + !Feature.enabled?(:container_scanning_continuous_vulnerability_scans, + Feature.current_request, type: :beta) + Gitlab::EventStore.publish( PackageMetadata::IngestedAdvisoryEvent.new(data: { advisory_id: data_object.id })) end diff --git a/ee/lib/gitlab/vulnerability_scanning/container_scanning/affected_version_range_matcher.rb b/ee/lib/gitlab/vulnerability_scanning/container_scanning/affected_version_range_matcher.rb index e262d24ea618c552030810a6ec087ba9f9eec6c6..69a239f92328adf9d1f88cc17c49adcd11bba095 100644 --- a/ee/lib/gitlab/vulnerability_scanning/container_scanning/affected_version_range_matcher.rb +++ b/ee/lib/gitlab/vulnerability_scanning/container_scanning/affected_version_range_matcher.rb @@ -15,7 +15,6 @@ def initialize(purl_type:, range:, version:, distro:, source:) end def affected? - return false unless Feature.enabled?(:container_scanning_continuous_vulnerability_scans, type: :beta) return false unless distro_matches? # a wildcard range means that all versions are affected diff --git a/ee/spec/lib/gitlab/vulnerability_scanning/container_scanning/affected_version_range_matcher_spec.rb b/ee/spec/lib/gitlab/vulnerability_scanning/container_scanning/affected_version_range_matcher_spec.rb index 0d51f0df6f5dab45c6ae010a04f3182394d7f66d..becef7187b93ea7bbd484675036c3cf82c889647 100644 --- a/ee/spec/lib/gitlab/vulnerability_scanning/container_scanning/affected_version_range_matcher_spec.rb +++ b/ee/spec/lib/gitlab/vulnerability_scanning/container_scanning/affected_version_range_matcher_spec.rb @@ -109,15 +109,5 @@ end end end - - context 'when the container_scanning_continuous_vulnerability_scans feature flag is disabled' do - before do - stub_feature_flags(container_scanning_continuous_vulnerability_scans: false) - end - - it 'returns false to indicate that the package is not affected' do - expect(affected).to be_falsey - end - end end end diff --git a/ee/spec/services/package_metadata/ingestion/advisory/advisory_ingestion_task_spec.rb b/ee/spec/services/package_metadata/ingestion/advisory/advisory_ingestion_task_spec.rb index 635c9c1a0bd5b10da237041025b549ee14aba9aa..a4f493a794360106b2d2bdee55c993d0a3928f57 100644 --- a/ee/spec/services/package_metadata/ingestion/advisory/advisory_ingestion_task_spec.rb +++ b/ee/spec/services/package_metadata/ingestion/advisory/advisory_ingestion_task_spec.rb @@ -36,7 +36,8 @@ expected_advisory_map = {} PackageMetadata::Advisory.all.each do |advisory| expected_advisory_map[advisory.advisory_xid] = - Hashie::Mash.new({ id: advisory.id, published_date: advisory.published_date }) + Hashie::Mash.new({ id: advisory.id, source_xid: advisory.source_xid, +published_date: advisory.published_date }) end expect(actual_advisory_map).to eq(expected_advisory_map) end diff --git a/ee/spec/services/package_metadata/ingestion/advisory/ingestion_service_spec.rb b/ee/spec/services/package_metadata/ingestion/advisory/ingestion_service_spec.rb index d3dd69d6f0bad2c8673c0f768fa708d064019137..dbc1bff3e677b5fa7a84fe7b3eadcc142ff9af54 100644 --- a/ee/spec/services/package_metadata/ingestion/advisory/ingestion_service_spec.rb +++ b/ee/spec/services/package_metadata/ingestion/advisory/ingestion_service_spec.rb @@ -3,108 +3,113 @@ require 'spec_helper' RSpec.describe PackageMetadata::Ingestion::Advisory::IngestionService, feature_category: :software_composition_analysis do - describe '.execute' do - subject(:execute) { described_class.execute(import_data) } + describe '.execute', :freeze_time do + using RSpec::Parameterized::TableSyntax - describe 'transaction' do - let(:import_data) { build_list(:pm_advisory_data_object, 10) } + subject(:execute) { described_class.execute(import_data) } - context 'when no errors' do - it 'uses package metadata application record' do - expect(PackageMetadata::ApplicationRecord).to receive(:transaction) - execute - end + let(:recent_advisories) { ds_advisories + cs_advisories } + let(:old_advisories) { build_list(:pm_advisory_data_object, 5, published_date: Time.zone.now - 14.days - 1.second) } + let(:import_data) { recent_advisories + old_advisories } - it 'adds new advisories and affected packages' do - expect { execute } - .to change { PackageMetadata::Advisory.count }.by(10) - .and change { PackageMetadata::AffectedPackage.count }.by(10) - end + where(:ds_ff_enabled, :cs_ff_enabled, :num_recent_ds_advisories, :num_recent_cs_advisories) do + true | true | 5 | 5 + true | false | 5 | 0 + false | true | 0 | 5 + false | false | 0 | 0 + end - context 'and advisory scanning is enabled', :freeze_time do - let(:recent) { build_list(:pm_advisory_data_object, 5, published_date: Time.zone.now - 13.days) } - let(:old) { build_list(:pm_advisory_data_object, 5, published_date: Time.zone.now - 14.days - 1.second) } - let(:import_data) { recent + old } + with_them do + let(:ds_advisories) do + build_list(:pm_advisory_data_object, num_recent_ds_advisories, source_xid: 'glad', + published_date: Time.zone.now - 13.days) + end - before do - stub_feature_flags(dependency_scanning_on_advisory_ingestion: true) - end + let(:cs_advisories) do + build_list(:pm_advisory_data_object, num_recent_cs_advisories, source_xid: 'trivy-db', + published_date: Time.zone.now - 13.days) + end - it 'publishes only recently ingested advisories to the event store' do - received_events = [] - allow(Gitlab::EventStore).to receive(:publish) do |event| - received_events << event - end + before do + stub_feature_flags(dependency_scanning_on_advisory_ingestion: ds_ff_enabled) + value = cs_ff_enabled ? 100 : 0 + Feature.enable_percentage_of_actors(:container_scanning_continuous_vulnerability_scans, value) + end - execute + it 'publishes only recently ingested advisories to the event store' do + received_events = [] + allow(Gitlab::EventStore).to receive(:publish) do |event| + received_events << event + end - received_advisory_ids = received_events.map { |event| event.data[:advisory_id] } - received_advisories = PackageMetadata::Advisory.where(id: received_advisory_ids) - .pluck(:source_xid, :advisory_xid).sort - recent_advisories = recent.map { |obj| [obj.source_xid, obj.advisory_xid] }.sort + execute - expect(received_advisories).to eq(recent_advisories) - end - end + received_advisory_ids = received_events.map { |event| event.data[:advisory_id] } + received_advisories = PackageMetadata::Advisory.where(id: received_advisory_ids) + .pluck(:source_xid, :advisory_xid).sort + expected = recent_advisories.map { |obj| [obj.source_xid, obj.advisory_xid] }.sort - context 'and advisory scanning is disabled' do - before do - stub_feature_flags(dependency_scanning_on_advisory_ingestion: false) - end + expect(received_advisories).to eq(expected) + end - it 'does not publish anything to the event store' do - expect(Gitlab::EventStore).not_to receive(:publish) + it 'uses package metadata application record transactions' do + expect(PackageMetadata::ApplicationRecord).to receive(:transaction) + execute + end - execute - end - end + it 'adds new advisories and affected packages' do + expect { execute } + .to change { PackageMetadata::Advisory.count }.by(import_data.size) + .and change { + PackageMetadata::AffectedPackage.count + }.by(import_data.size) end + end - context 'when error occurs' do - context 'when an advisory fails json validation but the affected packages are valid' do - let(:valid_advisory) do - build(:pm_advisory_data_object, advisory_xid: 'valid-advisory', - affected_packages: [build(:pm_affected_package_data_object, - package_name: 'package-with-valid-advisory')]) - end + context 'when error occurs' do + let(:valid_advisory) do + build(:pm_advisory_data_object, advisory_xid: 'valid-advisory', + affected_packages: [build(:pm_affected_package_data_object, + package_name: 'package-with-valid-advisory')]) + end - let(:invalid_advisory) do - build(:pm_advisory_data_object, identifiers: [{ key: 'invalid-json' }], advisory_xid: 'invalid-advisory', - affected_packages: [build(:pm_affected_package_data_object, - package_name: 'package-with-invalid-advisory')]) - end + let(:invalid_advisory) do + build(:pm_advisory_data_object, identifiers: [{ key: 'invalid-json' }], advisory_xid: 'invalid-advisory', + affected_packages: [build(:pm_affected_package_data_object, + package_name: 'package-with-invalid-advisory')]) + end - let(:import_data) { [invalid_advisory, valid_advisory] } + let(:import_data) { [invalid_advisory, valid_advisory] } - it 'does not create DB records for the affected package belonging to the invalid advisory' do - execute + context 'when an advisory fails json validation but the affected packages are valid' do + it 'does not create DB records for the affected package belonging to the invalid advisory' do + execute - expect(PackageMetadata::AffectedPackage.where(package_name: 'package-with-invalid-advisory')).not_to exist - end + expect(PackageMetadata::AffectedPackage.where(package_name: 'package-with-invalid-advisory')).not_to exist + end - it 'only adds a single advisory and affected package to the DB' do - expect { execute } - .to change { PackageMetadata::Advisory.count }.from(0).to(1) - .and change { PackageMetadata::AffectedPackage.count }.from(0).to(1) - end + it 'only adds a single advisory and affected package to the DB' do + expect { execute } + .to change { PackageMetadata::Advisory.count }.from(0).to(1) + .and change { PackageMetadata::AffectedPackage.count }.from(0).to(1) + end - it 'associates the affected package with the parent advisory' do - execute + it 'associates the affected package with the parent advisory' do + execute - advisory = PackageMetadata::Advisory.where(advisory_xid: valid_advisory.advisory_xid).first - expect(advisory.affected_packages.first.package_name).to eql('package-with-valid-advisory') - end + advisory = PackageMetadata::Advisory.where(advisory_xid: valid_advisory.advisory_xid).first + expect(advisory.affected_packages.first.package_name).to eql('package-with-valid-advisory') end + end - context 'when the error is unrecoverable' do - it 'rolls back changes' do - expect(PackageMetadata::Ingestion::Advisory::AdvisoryIngestionTask) - .to receive(:execute).and_raise(StandardError) - expect { execute } - .to raise_error(StandardError) - .and not_change(PackageMetadata::AffectedPackage, :count) - .and not_change(PackageMetadata::Advisory, :count) - end + context 'when the error is unrecoverable' do + it 'rolls back changes' do + expect(PackageMetadata::Ingestion::Advisory::AdvisoryIngestionTask) + .to receive(:execute).and_raise(StandardError) + expect { execute } + .to raise_error(StandardError) + .and not_change(PackageMetadata::AffectedPackage, :count) + .and not_change(PackageMetadata::Advisory, :count) end end end