diff --git a/ee/app/services/package_metadata/advisory_scan_service.rb b/ee/app/services/package_metadata/advisory_scan_service.rb index b9d8a80e45be30a3bbec63a93161acede07de622..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) - raise NoMethodError, 'To be implemented in https://gitlab.com/gitlab-org/gitlab/-/issues/371065' + def self.execute(advisory) + ::Gitlab::VulnerabilityScanning::AdvisoryScanner.scan_projects_for(advisory) end end end diff --git a/ee/app/services/security/vulnerability_scanning/create_vulnerability_service.rb b/ee/app/services/security/vulnerability_scanning/create_vulnerability_service.rb index abf3dd16e06f721faf57ee6307214a7fa44da206..80ea5929541ed042a3ceae76ddf5371ebdcb46e5 100644 --- a/ee/app/services/security/vulnerability_scanning/create_vulnerability_service.rb +++ b/ee/app/services/security/vulnerability_scanning/create_vulnerability_service.rb @@ -10,6 +10,7 @@ class CreateVulnerabilityService # @param params [Hash] # @option params [::Gitlab::Ci::Reports::Sbom::Source, Sbom::Source] :sbom_source # @option params [::Gitlab::VulnerabilityScanning::Advisory] :advisory + # @option params [Array<::Gitlab::VulnerabilityScanning::AffectedComponent>] :affected_components # # @return [ServiceResponse] If successful, the service response payload contains # a `vulnerability_id` key with the id of the new vulnerability. Otherwise, the @@ -22,7 +23,8 @@ def initialize(project:, pipeline:, params:) @project = project @pipeline = pipeline @sbom_source = params[:sbom_source] - @advisories = params[:advisories] + @advisory = params[:advisory] + @affected_components = params[:affected_components] end def execute @@ -40,7 +42,7 @@ def execute private - attr_reader :project, :pipeline, :sbom_source, :advisories + attr_reader :project, :pipeline, :sbom_source, :advisory, :affected_components def report_scanner ::Gitlab::VulnerabilityScanning::SecurityScanner.fabricate @@ -50,20 +52,21 @@ def project_scanner ::Gitlab::VulnerabilityScanning::SecurityScanner.find_or_create_for_project!(project) end - def finding(advisory) - purl_type = advisory.affected_component.purl_type + def finding(affected_component) + purl_type = affected_component.purl_type builder = ::Gitlab::VulnerabilityScanning::FindingBuilder.for_purl_type!(purl_type) builder.new( project: project, pipeline: pipeline, sbom_source: sbom_source, scanner: report_scanner, - advisory: advisory + advisory: advisory, + affected_component: affected_component ).finding end def finding_maps - advisories.map { |advisory| FindingMap.new(finding(advisory), project_scanner.id) } + affected_components.map { |affected_component| FindingMap.new(finding(affected_component), project_scanner.id) } end def process_error(error) diff --git a/ee/lib/gitlab/vulnerability_scanning/advisory.rb b/ee/lib/gitlab/vulnerability_scanning/advisory.rb index 2a4bb0eeda60cb6e4893cd5743d287904fb54e05..6282719cb4f0a5ee99fc6458ccc72dc0df398c5b 100644 --- a/ee/lib/gitlab/vulnerability_scanning/advisory.rb +++ b/ee/lib/gitlab/vulnerability_scanning/advisory.rb @@ -3,7 +3,7 @@ module Gitlab module VulnerabilityScanning class Advisory - attr_reader :xid, :title, :description, :solution, :identifiers, :urls, :affected_component, :cvss_v2, :cvss_v3 + attr_reader :xid, :title, :description, :solution, :identifiers, :urls, :cvss_v2, :cvss_v3 # rubocop:disable Metrics/ParameterLists # Creates a new advisory object that can be used to create findings @@ -17,21 +17,16 @@ class Advisory # @param solution [String] The solution of the advisory. # @param identifiers [Array<String>] A list of identifiers for the advisory, e.g. ["CVE-2023-00001"]. # @param urls [Array<String>] A list of URLs with resources related to the advisory, e.g. ["https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2023-00001"]. - # @param affected_component [Gitlab::VulnerabilityScanning::AffectedComponent] - # The component affected by the advisory. # @param cvss_v3 [String, CvssSuite::Cvss3, CvssSuite::Cvss31, nil] A cvss vector string, CvssSuite::Cvss3 object, # or CvssSuite::Cvss31 object. # @param cvss_v2 [String, CvssSuite::Cvss2, nil] A cvss vector string or CvssSuite::Cvss2 object. - def initialize( - xid:, title:, description:, solution:, identifiers:, urls:, affected_component:, cvss_v3: nil, - cvss_v2: nil) + def initialize(xid:, title:, description:, solution:, identifiers:, urls:, cvss_v3: nil, cvss_v2: nil) @xid = xid @title = title @description = description @solution = solution @identifiers = identifiers @urls = urls - @affected_component = affected_component @cvss_v3 = init_cvss_v3(cvss_v3) @cvss_v2 = init_cvss_v2(cvss_v2) end diff --git a/ee/lib/gitlab/vulnerability_scanning/advisory_scanner.rb b/ee/lib/gitlab/vulnerability_scanning/advisory_scanner.rb new file mode 100644 index 0000000000000000000000000000000000000000..c76479b16a5e6ddd1a63b35d14266f1ce02b6da4 --- /dev/null +++ b/ee/lib/gitlab/vulnerability_scanning/advisory_scanner.rb @@ -0,0 +1,93 @@ +# frozen_string_literal: true + +module Gitlab + module VulnerabilityScanning + class AdvisoryScanner + include Gitlab::Utils::StrongMemoize + + # Scans eligible projects that contain software components affected + # by an advisory. If affected, it creates new vulnerabilities in the + # project. + # + # @param advisory [PackageMetadata::Advisory] + def self.scan_projects_for(advisory) + new(advisory).execute + end + + # Create a new advisory scanner instance. + # + # @param advisory [PackageMetadata::Advisory] The new advisory to scan projects for. + # + # @return [Gitlab::VulnerabilityScanning::AdvisoryScanner] + def initialize(advisory) + @advisory = advisory + end + + def execute + advisory.affected_packages.each do |affected_package| + advisory_data_object = vulnerability_scanning_advisory(solution: affected_package.solution) + ::Sbom::PossiblyAffectedOccurrencesFinder.new(purl_type: affected_package.purl_type, + package_name: affected_package.package_name).execute_in_batches do |batch| + batch.each do |occurrence| + next unless occurrence_is_affected?( + purl_type: affected_package.purl_type, + range: affected_package.affected_range, + version: occurrence.version) + + affected_component = Gitlab::VulnerabilityScanning::PossiblyAffectedComponent + .from_sbom_occurrence(occurrence) + + create_vulnerability(advisory_data_object, affected_component) + end + end + end + end + + private + + attr_reader :advisory + + def occurrence_is_affected?(purl_type:, range:, version:) + Gitlab::VulnerabilityScanning::AffectedVersionRangeMatcher.affected?( + purl_type: purl_type, range: range, version: version) + end + + def vulnerability_scanning_advisory(solution:) + Gitlab::VulnerabilityScanning::Advisory.new( + xid: advisory.advisory_xid, + title: advisory.title, + description: advisory.description, + identifiers: advisory.identifiers, + urls: advisory.urls, + cvss_v2: advisory.cvss_v2, + cvss_v3: advisory.cvss_v3, + solution: solution + ) + end + + def create_vulnerability(advisory, affected_component) + response = ::Security::VulnerabilityScanning::CreateVulnerabilityService.execute( + project: affected_component.project, + pipeline: affected_component.pipeline, + params: { sbom_source: affected_component.source, advisory: advisory, + affected_components: [affected_component] }) + + if response.success? + log_success(affected_component.project.id) + else + log_error(response.payload[:error], affected_component.project.id) + end + end + + def log_success(project_id) + Gitlab::AppJsonLogger.debug(message: "Successfully created vulnerability on advisory ingestion", + project_id: project_id, source_xid: advisory.source_xid, advisory_xid: advisory.advisory_xid) + end + + def log_error(error, project_id) + Gitlab::AppJsonLogger.error(message: "Failed to create vulnerability on advisory ingestion", error: error, + project_id: project_id, source_xid: advisory.source_xid, advisory_xid: advisory.advisory_xid) + end + end + end +end diff --git a/ee/lib/gitlab/vulnerability_scanning/affected_version_range_matcher.rb b/ee/lib/gitlab/vulnerability_scanning/affected_version_range_matcher.rb index 66f0b2dfa9982fae6b3c1bfe03f3027e646767d3..2b98b50af29878f58a5f49ebb32390ddbe2e29e7 100644 --- a/ee/lib/gitlab/vulnerability_scanning/affected_version_range_matcher.rb +++ b/ee/lib/gitlab/vulnerability_scanning/affected_version_range_matcher.rb @@ -5,6 +5,10 @@ module VulnerabilityScanning class AffectedVersionRangeMatcher attr_reader :purl_type, :range, :version + def self.affected?(...) + new(...).affected? + end + def initialize(purl_type:, range:, version:) @purl_type = purl_type @range = range diff --git a/ee/lib/gitlab/vulnerability_scanning/finding_builder.rb b/ee/lib/gitlab/vulnerability_scanning/finding_builder.rb index a9bd6dd386d3c47f89c474669cbc0a12ccb0c0e5..5e17c99d551797cf0d3635324643617eded1c383 100644 --- a/ee/lib/gitlab/vulnerability_scanning/finding_builder.rb +++ b/ee/lib/gitlab/vulnerability_scanning/finding_builder.rb @@ -5,6 +5,12 @@ module VulnerabilityScanning class FindingBuilder include Gitlab::Utils::StrongMemoize + def self.for_report_type(report_type) + return unless report_type == "dependency_scanning" + + ::Gitlab::VulnerabilityScanning::DependencyScanning::FindingBuilder + end + # .for_purl_type will return a builder for the given purl type if it exists. def self.for_purl_type(purl_type) return unless DEPENDENCY_SCANNING_PURL_TYPES.include?(purl_type) @@ -25,12 +31,13 @@ def self.for_purl_type!(purl_type) builder_class end - def initialize(project:, pipeline:, sbom_source:, scanner:, advisory:) + def initialize(project:, pipeline:, sbom_source:, scanner:, advisory:, affected_component:) @project = project @pipeline = pipeline @sbom_source = sbom_source @scanner = scanner @advisory = advisory + @affected_component = affected_component end def finding @@ -70,16 +77,12 @@ def finding pypi ].freeze - attr_reader :project, :pipeline, :sbom_source, :scanner, :advisory + attr_reader :project, :pipeline, :sbom_source, :scanner, :advisory, :affected_component def validate! raise NoMethodError, "#{self.class}#validate! is not implemented" end - def affected_component - advisory.affected_component - end - def report_type raise NoMethodError, "#{self.class}#report_type is not implemented" end diff --git a/ee/lib/gitlab/vulnerability_scanning/security_report_builder.rb b/ee/lib/gitlab/vulnerability_scanning/security_report_builder.rb index afa717f5b8165e01b6b55c9a7c71fdb3919ed573..15dc3cb53bd3de763f8a5d968347100a2c732e93 100644 --- a/ee/lib/gitlab/vulnerability_scanning/security_report_builder.rb +++ b/ee/lib/gitlab/vulnerability_scanning/security_report_builder.rb @@ -22,9 +22,12 @@ def initialize(report_type:, project:, pipeline:, sbom:) # Add advisories affecting a component to the security report. # - # @param advisories [Array<Gitlab::VulnerabilityScanning::Advisory>] - def add_advisories(advisories) - advisories.each { |a| add_advisory(a) } + # @param advisories [Array<[Gitlab::VulnerabilityScanning::Advisory, + # Gitlab::VulnerabilityScanning::AffectedComponent]>] + def add_affections(affections) + affections.each do |advisory, affected_component| + add_affection(advisory, affected_component) + end end private @@ -36,16 +39,14 @@ def scanner end strong_memoize_attr :scanner - def add_advisory(advisory) - builder = case report_type - when "dependency_scanning" - DependencyScanning::FindingBuilder.new(project: project, pipeline: pipeline, - sbom_source: sbom.source, scanner: scanner, advisory: advisory) - end + def add_affection(advisory, affected_component) + builder_class = Gitlab::VulnerabilityScanning::FindingBuilder.for_report_type(report_type) - return unless builder + return unless builder_class begin + builder = builder_class.new(project: project, pipeline: pipeline, sbom_source: sbom.source, scanner: scanner, + advisory: advisory, affected_component: affected_component) finding = builder.finding report.add_finding(finding) finding.identifiers.each { |ident| report.add_identifier(ident) } diff --git a/ee/spec/factories/vulnerability_scanning/advisories.rb b/ee/spec/factories/vulnerability_scanning/advisories.rb index b0537001730b6c01723ea04933dfc8e1ac45c3e9..623c4c42843a29b746701fb1459f3d7dbbf243fa 100644 --- a/ee/spec/factories/vulnerability_scanning/advisories.rb +++ b/ee/spec/factories/vulnerability_scanning/advisories.rb @@ -17,7 +17,6 @@ urls { Array.new(2) { FFaker::Internet.uri("https") } } cvss_v2 { "AV:N/AC:M/Au:N/C:N/I:P/A:N" } cvss_v3 { "CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:C/C:N/I:L/A:L" } - affected_component { association :vs_possibly_affected_component } skip_create @@ -30,8 +29,7 @@ identifiers: identifiers, urls: urls, cvss_v2: cvss_v2, - cvss_v3: cvss_v3, - affected_component: affected_component + cvss_v3: cvss_v3 ) 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 new file mode 100644 index 0000000000000000000000000000000000000000..7829012ef2795365ebfad19ca53b3bbf44d0458e --- /dev/null +++ b/ee/spec/lib/gitlab/vulnerability_scanning/advisory_scanner_spec.rb @@ -0,0 +1,76 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::VulnerabilityScanning::AdvisoryScanner, feature_category: :software_composition_analysis do + let_it_be(:user) { create(:user) } + + let_it_be(: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) } + # A user is required to attribuet 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(:unaffected_project) { create(:project) } + let_it_be(:unaffected_pipeline) { create(:ci_pipeline, user: user, project: unaffected_project) } + 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') + 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: invalid_affected_pipeline) + create(:sbom_occurrence, component: eslint, component_version: unaffected_version, pipeline: unaffected_pipeline) + end + + describe '#execute' do + before do + allow(Gitlab::AppJsonLogger).to receive(:debug).and_call_original + allow(Gitlab::AppJsonLogger).to receive(:error).and_call_original + + described_class.scan_projects_for(affected_package.advisory) + end + + it 'creates a vulnerability in the affected project' do + advisory = affected_package.advisory + vulnerabilities = affected_project.vulnerabilities + expect(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(Gitlab::AppJsonLogger).to have_received(:debug).with( + message: 'Successfully created vulnerability on advisory ingestion', + project_id: affected_project.id, source_xid: 'glad', advisory_xid: advisory.advisory_xid) + end + + it 'does not create a vulnerability in the unaffected project' do + vulnerabilities = unaffected_project.vulnerabilities + expect(vulnerabilities).to be_empty + end + + it 'logs error message when it encounters an error' do + expect(Gitlab::AppJsonLogger).to have_received(:error).with( + message: 'Failed to create vulnerability on advisory ingestion', + error: anything, project_id: affected_project_2.id, source_xid: 'glad', + advisory_xid: affected_package.advisory.advisory_xid) + end + end +end diff --git a/ee/spec/lib/gitlab/vulnerability_scanning/advisory_spec.rb b/ee/spec/lib/gitlab/vulnerability_scanning/advisory_spec.rb index 3997f5b15270da35d5df5c6794d583fef560d915..be0e6f0268e37f68d50f304c5d21f3cf2caa6e6f 100644 --- a/ee/spec/lib/gitlab/vulnerability_scanning/advisory_spec.rb +++ b/ee/spec/lib/gitlab/vulnerability_scanning/advisory_spec.rb @@ -17,8 +17,7 @@ identifiers: ['CVE-2023-00001'], urls: ["https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2023-00001"], cvss_v3: cvss_v3, - cvss_v2: cvss_v2, - affected_component: affected_component) + cvss_v2: cvss_v2) end describe '#cvss_v3' do diff --git a/ee/spec/lib/gitlab/vulnerability_scanning/dependency_scanning/finding_builder_spec.rb b/ee/spec/lib/gitlab/vulnerability_scanning/dependency_scanning/finding_builder_spec.rb index 800f84767e660a6205b78de8e4ba1db0b9caf138..ff11dc63d2b9498e563a00b80dfee23175744f28 100644 --- a/ee/spec/lib/gitlab/vulnerability_scanning/dependency_scanning/finding_builder_spec.rb +++ b/ee/spec/lib/gitlab/vulnerability_scanning/dependency_scanning/finding_builder_spec.rb @@ -8,6 +8,11 @@ let(:sbom_source) { build(:ci_reports_sbom_source) } let(:security_scanner) { Gitlab::VulnerabilityScanning::SecurityScanner.fabricate } + let(:affected_component) do + build(:vs_possibly_affected_component, name: 'github.com/minio/minio', + version: 'v0.0.0-20180419184637-5a16671f721f') + end + let(:advisory) do build(:vs_advisory, title: "Allocation of File Descriptors or Handles Without Limits or Throttling", @@ -19,15 +24,13 @@ build(:pm_identifier, type: "cve", name: "CVE-2018-1000538", url: "https://nvd.nist.gov/vuln/detail/CVE-2018-1000538", value: "CVE-2018-1000538") ], - solution: "Unfortunately, there is no solution available yet.", - affected_component: build(:vs_possibly_affected_component, name: 'github.com/minio/minio', - version: 'v0.0.0-20180419184637-5a16671f721f') + solution: "Unfortunately, there is no solution available yet." ) end subject(:builder) do described_class.new(project: ci_build.project, pipeline: ci_build.pipeline, sbom_source: sbom_source, - scanner: security_scanner, advisory: advisory) + scanner: security_scanner, advisory: advisory, affected_component: affected_component) end describe "#finding" do diff --git a/ee/spec/lib/gitlab/vulnerability_scanning/finding_builder_spec.rb b/ee/spec/lib/gitlab/vulnerability_scanning/finding_builder_spec.rb index ed1ae06d37143bf782da04ac0281a1d6ef220d0e..a1e4699c85410829f835f5b91afd31a2883d8e5f 100644 --- a/ee/spec/lib/gitlab/vulnerability_scanning/finding_builder_spec.rb +++ b/ee/spec/lib/gitlab/vulnerability_scanning/finding_builder_spec.rb @@ -23,9 +23,11 @@ ) end + let_it_be(:affected_component) { build(:vs_possibly_affected_component) } + subject(:builder) do described_class.new(project: pipeline.project, pipeline: pipeline, - sbom_source: sbom_source, scanner: scanner, advisory: advisory) + sbom_source: sbom_source, scanner: scanner, advisory: advisory, affected_component: affected_component) end describe "#finding" do diff --git a/ee/spec/lib/gitlab/vulnerability_scanning/security_report_builder_spec.rb b/ee/spec/lib/gitlab/vulnerability_scanning/security_report_builder_spec.rb index ffbcd223dc9fd8be0bb37dc193b7952e7d821c1a..5eb2a249795d151dc341244bf4c58568aa27757b 100644 --- a/ee/spec/lib/gitlab/vulnerability_scanning/security_report_builder_spec.rb +++ b/ee/spec/lib/gitlab/vulnerability_scanning/security_report_builder_spec.rb @@ -26,8 +26,7 @@ url: "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-1000538", value: "CVE-2018-1000538") ], urls: ["https://github.com/minio/minio/pull/5957", "https://nvd.nist.gov/vuln/detail/CVE-2018-1000538"], - solution: "Unfortunately, there is no solution available yet.", - affected_component: minio_component + solution: "Unfortunately, there is no solution available yet." ) end @@ -45,8 +44,7 @@ url: "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-11012", value: "CVE-2020-11012") ], urls: ["https://nvd.nist.gov/vuln/detail/CVE-2020-11012"], - solution: "Upgrade to version RELEASE.2020-04-23T00-58-49Z or above.", - affected_component: minio_component + solution: "Upgrade to version RELEASE.2020-04-23T00-58-49Z or above." ) end @@ -65,7 +63,7 @@ before do Gitlab::Ci::Parsers::Security::DependencyScanning.parse!(dependency_scanning_json_report, expected, validate: true) - builder.add_advisories([cve_2020_11012, cve_2018_1000538]) + builder.add_affections([[cve_2020_11012, minio_component], [cve_2018_1000538, minio_component]]) end describe "#report" do 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 a031ccaaff7f43c2993e3e066be2028a9e2f810b..1a4f3d90987396b1e2188cdb2dcc9d5b00f3769f 100644 --- a/ee/spec/services/package_metadata/advisory_scan_service_spec.rb +++ b/ee/spec/services/package_metadata/advisory_scan_service_spec.rb @@ -3,11 +3,13 @@ require 'spec_helper' RSpec.describe PackageMetadata::AdvisoryScanService, feature_category: :software_composition_analysis do - describe '#execute' do + describe '.execute' do let(:advisory) { build(:pm_advisory) } - it 'is not implemented' do - expect { described_class.execute(advisory) }.to raise_error(NoMethodError) + it 'calls the advisory scanner execute method' do + expect(::Gitlab::VulnerabilityScanning::AdvisoryScanner).to receive(:scan_projects_for).with(advisory) + + described_class.execute(advisory) end end end diff --git a/ee/spec/services/security/vulnerability_scanning/create_vulnerability_service_spec.rb b/ee/spec/services/security/vulnerability_scanning/create_vulnerability_service_spec.rb index 3d5fd3718310154bd13a919e9f6831e2a9109815..013ee0e02c26e16152ef1c03ca89620d41572899 100644 --- a/ee/spec/services/security/vulnerability_scanning/create_vulnerability_service_spec.rb +++ b/ee/spec/services/security/vulnerability_scanning/create_vulnerability_service_spec.rb @@ -7,7 +7,7 @@ let_it_be(:pipeline) { create(:ci_pipeline, user: user) } let(:sbom_source) { build(:ci_reports_sbom_source) } let(:affected_component) { build(:vs_possibly_affected_component, purl_type: 'npm') } - let(:advisories) { build_list(:vs_advisory, 3, affected_component: affected_component) } + let(:advisory) { build(:vs_advisory) } subject(:service_response) do described_class.execute( @@ -15,7 +15,8 @@ pipeline: pipeline, params: { sbom_source: sbom_source, - advisories: advisories + advisory: advisory, + affected_components: [affected_component] } ) end @@ -30,7 +31,7 @@ expect(vuln_ids).not_to be_empty vulns_created = Vulnerability.find(vuln_ids) - expected_vulns = advisories.map do |advisory| + expect(vulns_created).to match_array([ have_attributes( author_id: user.id, project_id: pipeline.project_id, @@ -43,8 +44,7 @@ finding_description: advisory.description, solution: advisory.solution ) - end - expect(vulns_created).to match_array(expected_vulns) + ]) end end