diff --git a/ee/lib/gitlab/ci/reports/security/vulnerability_reports_comparer.rb b/ee/lib/gitlab/ci/reports/security/vulnerability_reports_comparer.rb index b4312c0c1400034fb382f03b22f17633e819d8e1..4f7ed7b3611042adb4015489f00c2af5741e64ad 100644 --- a/ee/lib/gitlab/ci/reports/security/vulnerability_reports_comparer.rb +++ b/ee/lib/gitlab/ci/reports/security/vulnerability_reports_comparer.rb @@ -9,8 +9,8 @@ module Gitlab attr_reader :base_report, :head_report - def initialize(base_report, head_report) - @base_report = base_report || [] + def initialize(base_report = [], head_report = []) + @base_report = base_report @head_report = head_report end @@ -28,7 +28,8 @@ module Gitlab def existing strong_memoize(:existing) do - base_report & head_report + # Existing vulnerabilities should point to source report for most recent information + head_report & base_report end end end diff --git a/ee/spec/lib/gitlab/ci/reports/security/vulnerability_reports_comparer_spec.rb b/ee/spec/lib/gitlab/ci/reports/security/vulnerability_reports_comparer_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..02b5fd1d4a87ab9c164f229d58b2d8c33f0cd97a --- /dev/null +++ b/ee/spec/lib/gitlab/ci/reports/security/vulnerability_reports_comparer_spec.rb @@ -0,0 +1,63 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Gitlab::Ci::Reports::Security::VulnerabilityReportsComparer do + let!(:identifier) { create(:vulnerabilities_identifier) } + let!(:base_vulnerability) { build(:vulnerabilities_occurrence, report_type: :sast, identifiers: [identifier], location_fingerprint: '123') } + let!(:head_vulnerability) { build(:vulnerabilities_occurrence, report_type: :sast, identifiers: [identifier], location_fingerprint: '123') } + + before do + allow(base_vulnerability).to receive(:location).and_return({}) + allow(head_vulnerability).to receive(:location).and_return({}) + end + + describe '#existing' do + context 'with existing reports' do + let(:comparer) { described_class.new([base_vulnerability], [head_vulnerability]) } + + it 'points to source tree' do + allow(head_vulnerability).to receive(:raw_metadata).and_return('') + + expect(comparer.existing.count).to eq(1) + expect(comparer.existing).to eq([head_vulnerability]) + end + end + end + + describe '#added' do + let(:vuln) { build(:vulnerabilities_occurrence, report_type: :sast, identifiers: [identifier], location_fingerprint: '888') } + + context 'with new vulnerability' do + let(:comparer) { described_class.new([base_vulnerability], [head_vulnerability, vuln]) } + + it 'points to source tree' do + expect(comparer.added.count).to eq(1) + expect(comparer.added).to eq([vuln]) + end + end + end + + describe '#fixed' do + let(:vuln) { build(:vulnerabilities_occurrence, report_type: :sast, identifiers: [identifier], location_fingerprint: '888') } + + context 'with fixed vulnerability' do + let(:comparer) { described_class.new([base_vulnerability, vuln], [head_vulnerability]) } + + it 'points to base tree' do + expect(comparer.fixed.count).to eq(1) + expect(comparer.fixed).to eq([vuln]) + end + end + end + + describe 'with empty vulnerabilities' do + let(:comparer) { described_class.new } + + it 'returns empty array when reports are not present' do + expect(comparer.existing).to be_empty + expect(comparer.fixed).to be_empty + expect(comparer.added).to be_empty + end + end +end