134 lines
5.5 KiB
Ruby
134 lines
5.5 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require 'spec_helper'
|
|
|
|
RSpec.describe Gitlab::BackgroundMigration::PopulateFindingUuidForVulnerabilityFeedback, schema: 20201211090634 do
|
|
let(:namespaces) { table(:namespaces) }
|
|
let(:projects) { table(:projects) }
|
|
let(:users) { table(:users) }
|
|
let(:scanners) { table(:vulnerability_scanners) }
|
|
let(:identifiers) { table(:vulnerability_identifiers) }
|
|
let(:findings) { table(:vulnerability_occurrences) }
|
|
let(:vulnerability_feedback) { table(:vulnerability_feedback) }
|
|
|
|
let(:namespace) { namespaces.create!(name: 'gitlab', path: 'gitlab-org') }
|
|
let(:project) { projects.create!(namespace_id: namespace.id, name: 'foo') }
|
|
let(:user) { users.create!(username: 'john.doe', projects_limit: 5) }
|
|
let(:scanner) { scanners.create!(project_id: project.id, external_id: 'foo', name: 'bar') }
|
|
let(:identifier) { identifiers.create!(project_id: project.id, fingerprint: 'foo', external_type: 'bar', external_id: 'zoo', name: 'baz') }
|
|
let(:sast_report) { 0 }
|
|
let(:dependency_scanning_report) { 1 }
|
|
let(:dast_report) { 3 }
|
|
let(:secret_detection_report) { 4 }
|
|
let(:project_fingerprint) { Digest::SHA1.hexdigest(SecureRandom.uuid) }
|
|
let(:location_fingerprint_1) { Digest::SHA1.hexdigest(SecureRandom.uuid) }
|
|
let(:location_fingerprint_2) { Digest::SHA1.hexdigest(SecureRandom.uuid) }
|
|
let(:location_fingerprint_3) { Digest::SHA1.hexdigest(SecureRandom.uuid) }
|
|
let(:finding_1) { finding_creator.call(sast_report, location_fingerprint_1) }
|
|
let(:finding_2) { finding_creator.call(dast_report, location_fingerprint_2) }
|
|
let(:finding_3) { finding_creator.call(secret_detection_report, location_fingerprint_3) }
|
|
let(:expected_uuid_1) do
|
|
Security::VulnerabilityUUID.generate(
|
|
report_type: 'sast',
|
|
primary_identifier_fingerprint: identifier.fingerprint,
|
|
location_fingerprint: location_fingerprint_1,
|
|
project_id: project.id
|
|
)
|
|
end
|
|
|
|
let(:expected_uuid_2) do
|
|
Security::VulnerabilityUUID.generate(
|
|
report_type: 'dast',
|
|
primary_identifier_fingerprint: identifier.fingerprint,
|
|
location_fingerprint: location_fingerprint_2,
|
|
project_id: project.id
|
|
)
|
|
end
|
|
|
|
let(:expected_uuid_3) do
|
|
Security::VulnerabilityUUID.generate(
|
|
report_type: 'secret_detection',
|
|
primary_identifier_fingerprint: identifier.fingerprint,
|
|
location_fingerprint: location_fingerprint_3,
|
|
project_id: project.id
|
|
)
|
|
end
|
|
|
|
let(:finding_creator) do
|
|
-> (report_type, location_fingerprint) do
|
|
findings.create!(
|
|
project_id: project.id,
|
|
primary_identifier_id: identifier.id,
|
|
scanner_id: scanner.id,
|
|
report_type: report_type,
|
|
uuid: SecureRandom.uuid,
|
|
name: 'Foo',
|
|
location_fingerprint: Gitlab::Database::ShaAttribute.serialize(location_fingerprint),
|
|
project_fingerprint: Gitlab::Database::ShaAttribute.serialize(project_fingerprint),
|
|
metadata_version: '1',
|
|
severity: 0,
|
|
confidence: 5,
|
|
raw_metadata: '{}'
|
|
)
|
|
end
|
|
end
|
|
|
|
let(:feedback_creator) do
|
|
-> (category, project_fingerprint) do
|
|
vulnerability_feedback.create!(
|
|
project_id: project.id,
|
|
author_id: user.id,
|
|
feedback_type: 0,
|
|
category: category,
|
|
project_fingerprint: project_fingerprint
|
|
)
|
|
end
|
|
end
|
|
|
|
let!(:feedback_1) { feedback_creator.call(finding_1.report_type, project_fingerprint) }
|
|
let!(:feedback_2) { feedback_creator.call(finding_2.report_type, project_fingerprint) }
|
|
let!(:feedback_3) { feedback_creator.call(finding_3.report_type, project_fingerprint) }
|
|
let!(:feedback_4) { feedback_creator.call(finding_1.report_type, 'foo') }
|
|
let!(:feedback_5) { feedback_creator.call(dependency_scanning_report, project_fingerprint) }
|
|
|
|
subject(:populate_finding_uuids) { described_class.new.perform(feedback_1.id, feedback_5.id) }
|
|
|
|
before do
|
|
allow(Gitlab::BackgroundMigration::Logger).to receive(:info)
|
|
end
|
|
|
|
describe '#perform' do
|
|
it 'updates the `finding_uuid` attributes of the feedback records' do
|
|
expect { populate_finding_uuids }.to change { feedback_1.reload.finding_uuid }.from(nil).to(expected_uuid_1)
|
|
.and change { feedback_2.reload.finding_uuid }.from(nil).to(expected_uuid_2)
|
|
.and change { feedback_3.reload.finding_uuid }.from(nil).to(expected_uuid_3)
|
|
.and not_change { feedback_4.reload.finding_uuid }
|
|
.and not_change { feedback_5.reload.finding_uuid }
|
|
|
|
expect(Gitlab::BackgroundMigration::Logger).to have_received(:info).once
|
|
end
|
|
|
|
it 'preloads the finding and identifier records to prevent N+1 queries' do
|
|
# Load feedback records(1), load findings(2), load identifiers(3) and finally update feedback records one by one(6)
|
|
expect { populate_finding_uuids }.not_to exceed_query_limit(6)
|
|
end
|
|
|
|
context 'when setting the `finding_uuid` attribute of a feedback record fails' do
|
|
let(:expected_error) { RuntimeError.new }
|
|
|
|
before do
|
|
allow(Gitlab::ErrorTracking).to receive(:track_and_raise_for_dev_exception)
|
|
|
|
allow_next_found_instance_of(described_class::VulnerabilityFeedback) do |feedback|
|
|
allow(feedback).to receive(:update_column).and_raise(expected_error)
|
|
end
|
|
end
|
|
|
|
it 'captures the errors and does not crash entirely' do
|
|
expect { populate_finding_uuids }.not_to raise_error
|
|
|
|
expect(Gitlab::ErrorTracking).to have_received(:track_and_raise_for_dev_exception).with(expected_error).exactly(3).times
|
|
end
|
|
end
|
|
end
|
|
end
|