Commit b85c8792 authored by James Fargher's avatar James Fargher

Merge branch '343332_create_issue_links_on_ingestion' into 'master'

Ingest issue links for new findings

See merge request gitlab-org/gitlab!73859
parents d69c920a af54f7dd
......@@ -31,6 +31,8 @@ module Vulnerabilities
scope :with_associations, -> { includes(:pipeline, :issue, :merge_request, :author, :comment_author) }
scope :by_finding_uuid, -> (uuids) { where(finding_uuid: uuids) }
scope :by_project, -> (project) { where(project: project) }
scope :by_project_fingerprints, -> (project_fingerprints) { where(project_fingerprint: project_fingerprints) }
scope :all_preloaded, -> do
preload(:author, :comment_author, :project, :issue, :merge_request, :pipeline)
......
......@@ -16,6 +16,9 @@ module Security
attr_accessor :finding_id, :vulnerability_id, :new_record, :identifier_ids
delegate :uuid, :scanner_id, to: :security_finding
delegate :scan, to: :security_finding, private: true
delegate :project, to: :scan, private: true
delegate :project_fingerprint, to: :report_finding, private: true
def initialize(security_finding, report_finding)
@security_finding = security_finding
......@@ -31,6 +34,15 @@ module Security
@identifier_ids = identifiers.map { |identifier| fingerprint_id_map[identifier.fingerprint] }
end
def issue_feedback
BatchLoader.for([project.id, project_fingerprint]).batch do |tuples, loader|
Vulnerabilities::Feedback.for_issue
.by_project(tuples.first.first)
.by_project_fingerprints(tuples.map(&:second))
.each { |feedback| loader.call([feedback.project_id, feedback.project_fingerprint], feedback) }
end
end
def to_hash
# This was already an existing problem so we've used it here as well.
# TODO: https://gitlab.com/gitlab-org/gitlab/-/issues/342043
......@@ -40,7 +52,7 @@ module Security
.slice(*FINDING_ATTRIBUTES)
.merge(RAW_METADATA_PLACEHOLDER)
.merge(parsed_from_raw_metadata)
.merge(primary_identifier_id: identifier_ids.first, location_fingerprint: report_finding.location.fingerprint, project_fingerprint: report_finding.project_fingerprint)
.merge(primary_identifier_id: identifier_ids.first, location_fingerprint: report_finding.location.fingerprint, project_fingerprint: project_fingerprint)
.merge(uuid: uuid, scanner_id: scanner_id)
end
end
......
......@@ -17,6 +17,7 @@ module Security
IngestFindingLinks
IngestFindingSignatures
IngestVulnerabilityFlags
IngestIssueLinks
IngestRemediations
].freeze
......
# frozen_string_literal: true
module Security
module Ingestion
module Tasks
class IngestIssueLinks < AbstractTask
include BulkInsertableTask
self.model = Vulnerabilities::IssueLink
private
def attributes
new_finding_maps_with_issue_feedback.map do |finding_map|
{
issue_id: finding_map.issue_feedback.issue_id,
vulnerability_id: finding_map.vulnerability_id
}
end
end
def new_finding_maps_with_issue_feedback
finding_maps.select(&:new_record)
.each(&:issue_feedback) # This iteration is necessary to register BatchLoader.
.select { |finding_map| finding_map.issue_feedback.present? }
end
end
end
end
end
......@@ -30,6 +30,19 @@ RSpec.describe Security::Ingestion::FindingMap do
end
end
describe '#issue_feedback' do
let!(:feedback) do
create(:vulnerability_feedback,
:issue,
project: security_finding.scan.project,
project_fingerprint: report_finding.project_fingerprint)
end
subject { finding_map.issue_feedback }
it { is_expected.to eq(feedback) }
end
describe '#to_hash' do
let(:expected_hash) do
{
......
......@@ -30,6 +30,7 @@ RSpec.describe Security::Ingestion::IngestReportSliceService do
expect(Security::Ingestion::Tasks::IngestFindingLinks).to have_received(:execute).ordered.with(pipeline, finding_maps)
expect(Security::Ingestion::Tasks::IngestFindingSignatures).to have_received(:execute).ordered.with(pipeline, finding_maps)
expect(Security::Ingestion::Tasks::IngestVulnerabilityFlags).to have_received(:execute).ordered.with(pipeline, finding_maps)
expect(Security::Ingestion::Tasks::IngestIssueLinks).to have_received(:execute).ordered.with(pipeline, finding_maps)
expect(Security::Ingestion::Tasks::IngestRemediations).to have_received(:execute).ordered.with(pipeline, finding_maps)
end
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Security::Ingestion::Tasks::IngestIssueLinks do
describe '#execute' do
let(:pipeline) { create(:ci_pipeline) }
let(:report_finding_1) { create(:ci_reports_security_finding) }
let(:report_finding_2) { create(:ci_reports_security_finding) }
let(:finding_map_1) { create(:finding_map, :with_finding, report_finding: report_finding_1) }
let(:finding_map_2) { create(:finding_map, :new_record, report_finding: report_finding_2) }
let(:service_object) { described_class.new(pipeline, [finding_map_1, finding_map_2]) }
let(:feedback) do
create(:vulnerability_feedback,
:issue,
project: finding_map_2.security_finding.scan.project,
project_fingerprint: finding_map_2.report_finding.project_fingerprint)
end
subject(:ingest_issue_links) { service_object.execute }
before do
# There will be no issue link created for this record
# as this is related to an existing finding which means
# the issue link record should be already created before.
create(:vulnerability_feedback,
:issue,
project: finding_map_1.security_finding.scan.project,
project_fingerprint: finding_map_1.report_finding.project_fingerprint)
end
it 'ingests the issue links only for the new records' do
expect { ingest_issue_links }.to change { Vulnerabilities::IssueLink.for_issue(feedback.issue).count }.by(1)
end
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment