summaryrefslogtreecommitdiff
path: root/spec/lib/gitlab/ci/reports/security/vulnerability_reports_comparer_spec.rb
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-08-19 09:08:42 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2021-08-19 09:08:42 +0000
commitb76ae638462ab0f673e5915986070518dd3f9ad3 (patch)
treebdab0533383b52873be0ec0eb4d3c66598ff8b91 /spec/lib/gitlab/ci/reports/security/vulnerability_reports_comparer_spec.rb
parent434373eabe7b4be9593d18a585fb763f1e5f1a6f (diff)
downloadgitlab-ce-8c890596f5d0792c467fe12805ab1b39f93bf140.tar.gz
Add latest changes from gitlab-org/gitlab@14-2-stable-eev14.2.0-rc42
Diffstat (limited to 'spec/lib/gitlab/ci/reports/security/vulnerability_reports_comparer_spec.rb')
-rw-r--r--spec/lib/gitlab/ci/reports/security/vulnerability_reports_comparer_spec.rb163
1 files changed, 163 insertions, 0 deletions
diff --git a/spec/lib/gitlab/ci/reports/security/vulnerability_reports_comparer_spec.rb b/spec/lib/gitlab/ci/reports/security/vulnerability_reports_comparer_spec.rb
new file mode 100644
index 00000000000..44e66fd9028
--- /dev/null
+++ b/spec/lib/gitlab/ci/reports/security/vulnerability_reports_comparer_spec.rb
@@ -0,0 +1,163 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Ci::Reports::Security::VulnerabilityReportsComparer do
+ let(:identifier) { build(:ci_reports_security_identifier) }
+
+ let_it_be(:project) { create(:project, :repository) }
+
+ let(:location_param) { build(:ci_reports_security_locations_sast, :dynamic) }
+ let(:vulnerability_params) { vuln_params(project.id, [identifier], confidence: :low, severity: :critical) }
+ let(:base_vulnerability) { build(:ci_reports_security_finding, location: location_param, **vulnerability_params) }
+ let(:base_report) { build(:ci_reports_security_aggregated_reports, findings: [base_vulnerability]) }
+
+ let(:head_vulnerability) { build(:ci_reports_security_finding, location: location_param, uuid: base_vulnerability.uuid, **vulnerability_params) }
+ let(:head_report) { build(:ci_reports_security_aggregated_reports, findings: [head_vulnerability]) }
+
+ shared_context 'comparing reports' do
+ let(:vul_params) { vuln_params(project.id, [identifier]) }
+ let(:base_vulnerability) { build(:ci_reports_security_finding, :dynamic, **vul_params) }
+ let(:head_vulnerability) { build(:ci_reports_security_finding, :dynamic, **vul_params) }
+ let(:head_vul_findings) { [head_vulnerability, vuln] }
+ end
+
+ subject { described_class.new(project, base_report, head_report) }
+
+ where(vulnerability_finding_signatures: [true, false])
+
+ with_them do
+ before do
+ stub_licensed_features(vulnerability_finding_signatures: vulnerability_finding_signatures)
+ end
+
+ describe '#base_report_out_of_date' do
+ context 'no base report' do
+ let(:base_report) { build(:ci_reports_security_aggregated_reports, reports: [], findings: []) }
+
+ it 'is not out of date' do
+ expect(subject.base_report_out_of_date).to be false
+ end
+ end
+
+ context 'base report older than one week' do
+ let(:report) { build(:ci_reports_security_report, created_at: 1.week.ago - 60.seconds) }
+ let(:base_report) { build(:ci_reports_security_aggregated_reports, reports: [report]) }
+
+ it 'is not out of date' do
+ expect(subject.base_report_out_of_date).to be true
+ end
+ end
+
+ context 'base report less than one week old' do
+ let(:report) { build(:ci_reports_security_report, created_at: 1.week.ago + 60.seconds) }
+ let(:base_report) { build(:ci_reports_security_aggregated_reports, reports: [report]) }
+
+ it 'is not out of date' do
+ expect(subject.base_report_out_of_date).to be false
+ end
+ end
+ end
+
+ describe '#added' do
+ let(:new_location) {build(:ci_reports_security_locations_sast, :dynamic) }
+ let(:vul_params) { vuln_params(project.id, [identifier], confidence: :high) }
+ let(:vuln) { build(:ci_reports_security_finding, severity: Enums::Vulnerability.severity_levels[:critical], location: new_location, **vul_params) }
+ let(:low_vuln) { build(:ci_reports_security_finding, severity: Enums::Vulnerability.severity_levels[:low], location: new_location, **vul_params) }
+
+ context 'with new vulnerability' do
+ let(:head_report) { build(:ci_reports_security_aggregated_reports, findings: [head_vulnerability, vuln]) }
+
+ it 'points to source tree' do
+ expect(subject.added).to eq([vuln])
+ end
+ end
+
+ context 'when comparing reports with different fingerprints' do
+ include_context 'comparing reports'
+
+ let(:head_report) { build(:ci_reports_security_aggregated_reports, findings: head_vul_findings) }
+
+ it 'does not find any overlap' do
+ expect(subject.added).to eq(head_vul_findings)
+ end
+ end
+
+ context 'order' do
+ let(:head_report) { build(:ci_reports_security_aggregated_reports, findings: [head_vulnerability, vuln, low_vuln]) }
+
+ it 'does not change' do
+ expect(subject.added).to eq([vuln, low_vuln])
+ end
+ end
+ end
+
+ describe '#fixed' do
+ let(:vul_params) { vuln_params(project.id, [identifier]) }
+ let(:vuln) { build(:ci_reports_security_finding, :dynamic, **vul_params ) }
+ let(:medium_vuln) { build(:ci_reports_security_finding, confidence: ::Enums::Vulnerability.confidence_levels[:high], severity: Enums::Vulnerability.severity_levels[:medium], uuid: vuln.uuid, **vul_params) }
+
+ context 'with fixed vulnerability' do
+ let(:base_report) { build(:ci_reports_security_aggregated_reports, findings: [base_vulnerability, vuln]) }
+
+ it 'points to base tree' do
+ expect(subject.fixed).to eq([vuln])
+ end
+ end
+
+ context 'when comparing reports with different fingerprints' do
+ include_context 'comparing reports'
+
+ let(:base_report) { build(:ci_reports_security_aggregated_reports, findings: [base_vulnerability, vuln]) }
+
+ it 'does not find any overlap' do
+ expect(subject.fixed).to eq([base_vulnerability, vuln])
+ end
+ end
+
+ context 'order' do
+ let(:vul_findings) { [vuln, medium_vuln] }
+ let(:base_report) { build(:ci_reports_security_aggregated_reports, findings: [*vul_findings, base_vulnerability]) }
+
+ it 'does not change' do
+ expect(subject.fixed).to eq(vul_findings)
+ end
+ end
+ end
+
+ describe 'with empty vulnerabilities' do
+ let(:empty_report) { build(:ci_reports_security_aggregated_reports, reports: [], findings: []) }
+
+ it 'returns empty array when reports are not present' do
+ comparer = described_class.new(project, empty_report, empty_report)
+
+ expect(comparer.fixed).to eq([])
+ expect(comparer.added).to eq([])
+ end
+
+ it 'returns added vulnerability when base is empty and head is not empty' do
+ comparer = described_class.new(project, empty_report, head_report)
+
+ expect(comparer.fixed).to eq([])
+ expect(comparer.added).to eq([head_vulnerability])
+ end
+
+ it 'returns fixed vulnerability when head is empty and base is not empty' do
+ comparer = described_class.new(project, base_report, empty_report)
+
+ expect(comparer.fixed).to eq([base_vulnerability])
+ expect(comparer.added).to eq([])
+ end
+ end
+ end
+
+ def vuln_params(project_id, identifiers, confidence: :high, severity: :critical)
+ {
+ project_id: project_id,
+ report_type: :sast,
+ identifiers: identifiers,
+ confidence: ::Enums::Vulnerability.confidence_levels[confidence],
+ severity: ::Enums::Vulnerability.severity_levels[severity]
+ }
+ end
+end