Commit c1a794aa authored by Michał Zając's avatar Michał Zając

Rename Occurrence to Finding

After introducing the first-class vulnerabilities, only the REST API
endpoints, model associations, and controller endpoints were renamed:
former Vulnerabilities became Vulnerability Findings.

https://gitlab.com/gitlab-org/gitlab/issues/10252#terminology
parent e30e95e9
......@@ -3,7 +3,7 @@
# Security::PipelineVulnerabilitiesFinder
#
# Used to retrieve security vulnerabilities from an associated Pipeline,
# This involves normalizing Report::Occurrence POROs to Vulnerabilities::Occurrence
# This involves normalizing Report::Occurrence POROs to Vulnerabilities::Finding
#
# Arguments:
# pipeline - object to filter vulnerabilities
......@@ -26,23 +26,23 @@ module Security
def execute
requested_reports = pipeline_reports.select { |report_type| requested_type?(report_type) }
occurrences = requested_reports.each_with_object([]) do |(type, report), occurrences|
findings = requested_reports.each_with_object([]) do |(type, report), findings|
raise ParseError, 'JSON parsing failed' if report.error.is_a?(Gitlab::Ci::Parsers::Security::Common::SecurityReportParserError)
normalized_occurrences = normalize_report_occurrences(
report.occurrences,
normalized_findings = normalize_report_findings(
report.findings,
vulnerabilities_by_finding_fingerprint(type, report))
filtered_occurrences = filter(normalized_occurrences)
filtered_findings = filter(normalized_findings)
occurrences.concat(filtered_occurrences)
findings.concat(filtered_findings)
end
Gitlab::Ci::Reports::Security::AggregatedReport.new(requested_reports.values, sort_occurrences(occurrences))
Gitlab::Ci::Reports::Security::AggregatedReport.new(requested_reports.values, sort_findings(findings))
end
private
def sort_occurrences(occurrences)
def sort_findings(findings)
# This sort is stable (see https://en.wikipedia.org/wiki/Sorting_algorithm#Stability) contrary to the bare
# Ruby sort_by method. Using just sort_by leads to instability across different platforms (e.g., x86_64-linux and
# x86_64-darwin18) which in turn leads to different sorting results for the equal elements across these platforms.
......@@ -51,7 +51,7 @@ module Security
# This is easier to address from within the class rather than from tests because this leads to bad class design
# and exposing too much of its implementation details to the test suite.
# See also https://stackoverflow.com/questions/15442298/is-sort-in-ruby-stable.
Gitlab::Utils.stable_sort_by(occurrences) { |x| [-x.severity_value, -x.confidence_value] }
Gitlab::Utils.stable_sort_by(findings) { |x| [-x.severity_value, -x.confidence_value] }
end
def pipeline_reports
......@@ -59,49 +59,49 @@ module Security
end
def vulnerabilities_by_finding_fingerprint(report_type, report)
Vulnerabilities::Occurrence
Vulnerabilities::Finding
.with_vulnerabilities_for_state(
project: pipeline.project,
report_type: report_type,
project_fingerprints: report.occurrences.map(&:project_fingerprint))
.each_with_object({}) do |occurrence, hash|
hash[occurrence.project_fingerprint] = occurrence.vulnerability
project_fingerprints: report.findings.map(&:project_fingerprint))
.each_with_object({}) do |finding, hash|
hash[finding.project_fingerprint] = finding.vulnerability
end
end
# This finder is used for fetching vulnerabilities for any pipeline, if we used it to fetch
# vulnerabilities for a non-default-branch, the occurrences will be unpersisted, so we
# vulnerabilities for a non-default-branch, the findings will be unpersisted, so we
# coerce the POROs into unpersisted AR records to give them a common object.
# See https://gitlab.com/gitlab-org/gitlab/issues/33588#note_291849433 for more context
# on why this happens.
def normalize_report_occurrences(report_occurrences, vulnerabilities)
report_occurrences.map do |report_occurrence|
occurrence_hash = report_occurrence.to_hash
def normalize_report_findings(report_findings, vulnerabilities)
report_findings.map do |report_finding|
finding_hash = report_finding.to_hash
.except(:compare_key, :identifiers, :location, :scanner)
occurrence = Vulnerabilities::Occurrence.new(occurrence_hash)
finding = Vulnerabilities::Finding.new(finding_hash)
# assigning Vulnerabilities to Findings to enable the computed state
occurrence.location_fingerprint = report_occurrence.location.fingerprint
occurrence.vulnerability = vulnerabilities[occurrence.project_fingerprint]
occurrence.project = pipeline.project
occurrence.sha = pipeline.sha
occurrence.build_scanner(report_occurrence.scanner&.to_hash)
occurrence.identifiers = report_occurrence.identifiers.map do |identifier|
finding.location_fingerprint = report_finding.location.fingerprint
finding.vulnerability = vulnerabilities[finding.project_fingerprint]
finding.project = pipeline.project
finding.sha = pipeline.sha
finding.build_scanner(report_finding.scanner&.to_hash)
finding.identifiers = report_finding.identifiers.map do |identifier|
Vulnerabilities::Identifier.new(identifier.to_hash)
end
occurrence
finding
end
end
def filter(occurrences)
occurrences.select do |occurrence|
next if !include_dismissed? && dismissal_feedback?(occurrence)
next unless confidence_levels.include?(occurrence.confidence)
next unless severity_levels.include?(occurrence.severity)
next if scanners.present? && !scanners.include?(occurrence.scanner.external_id)
def filter(findings)
findings.select do |finding|
next if !include_dismissed? && dismissal_feedback?(finding)
next unless confidence_levels.include?(finding.confidence)
next unless severity_levels.include?(finding.severity)
next if scanners.present? && !scanners.include?(finding.scanner.external_id)
occurrence
finding
end
end
......@@ -113,8 +113,8 @@ module Security
params[:scope] == 'all'
end
def dismissal_feedback?(occurrence)
dismissal_feedback_by_fingerprint[occurrence.project_fingerprint]
def dismissal_feedback?(finding)
dismissal_feedback_by_fingerprint[finding.project_fingerprint]
end
def dismissal_feedback_by_fingerprint
......@@ -127,15 +127,15 @@ module Security
end
def confidence_levels
Array(params.fetch(:confidence, Vulnerabilities::Occurrence.confidences.keys))
Array(params.fetch(:confidence, Vulnerabilities::Finding.confidences.keys))
end
def report_types
Array(params.fetch(:report_type, Vulnerabilities::Occurrence.report_types.keys))
Array(params.fetch(:report_type, Vulnerabilities::Finding.report_types.keys))
end
def severity_levels
Array(params.fetch(:severity, Vulnerabilities::Occurrence.severities.keys))
Array(params.fetch(:severity, Vulnerabilities::Finding.severities.keys))
end
def scanners
......
......@@ -2,7 +2,7 @@
# Security::VulnerabilityFindingsFinder
#
# Used to filter Vulnerabilities::Occurrences by set of params for Security Dashboard
# Used to filter Vulnerabilities::Findings by set of params for Security Dashboard
#
# Arguments:
# pipeline_ids - IDs for the pipelines to fetch vulnerabilities for
......@@ -40,7 +40,7 @@ module Security
return items unless params[:report_type].present?
items.by_report_types(
Vulnerabilities::Occurrence::REPORT_TYPES.values_at(
Vulnerabilities::Finding::REPORT_TYPES.values_at(
*params[:report_type]).compact)
end
......@@ -54,7 +54,7 @@ module Security
return items unless params[:severity].present?
items.by_severities(
Vulnerabilities::Occurrence::SEVERITY_LEVELS.values_at(
Vulnerabilities::Finding::SEVERITY_LEVELS.values_at(
*params[:severity]).compact)
end
......@@ -62,7 +62,7 @@ module Security
return items unless params[:confidence].present?
items.by_confidences(
Vulnerabilities::Occurrence::CONFIDENCE_LEVELS.values_at(
Vulnerabilities::Finding::CONFIDENCE_LEVELS.values_at(
*params[:confidence]).compact)
end
......@@ -76,9 +76,9 @@ module Security
def init_collection
if include_sha
::Vulnerabilities::Occurrence.for_pipelines_with_sha(pipeline_ids)
::Vulnerabilities::Finding.for_pipelines_with_sha(pipeline_ids)
else
::Vulnerabilities::Occurrence.for_pipelines(pipeline_ids)
::Vulnerabilities::Finding.for_pipelines(pipeline_ids)
end
end
end
......
......@@ -17,7 +17,7 @@ module Security
attr_reader :projects
def vulnerabilities
::Vulnerabilities::Occurrence
::Vulnerabilities::Finding
.select(1)
.undismissed
.scoped_project
......
......@@ -11,7 +11,7 @@ module Representation
attr_reader :raw_entry
def report_type
::Vulnerabilities::Occurrence::REPORT_TYPES.key(@report_type) || @report_type
::Vulnerabilities::Finding::REPORT_TYPES.key(@report_type) || @report_type
end
def ==(other)
......
......@@ -6,7 +6,7 @@ module Types
graphql_name 'SecurityReportSummary'
description 'Represents summary of a security report'
::Vulnerabilities::Occurrence::REPORT_TYPES.keys.each do |report_type|
::Vulnerabilities::Finding::REPORT_TYPES.keys.each do |report_type|
field report_type, ::Types::SecurityReportSummarySectionType, null: true,
description: "Aggregated counts for the #{report_type} scan"
end
......
......@@ -29,7 +29,7 @@ module Types
when 'coverage_fuzzing'
VulnerabilityLocation::CoverageFuzzingType
else
raise UnexpectedReportType, "Report type must be one of #{::Vulnerabilities::Occurrence::REPORT_TYPES.keys}"
raise UnexpectedReportType, "Report type must be one of #{::Vulnerabilities::Finding::REPORT_TYPES.keys}"
end
end
end
......
......@@ -5,7 +5,7 @@ module Types
graphql_name 'VulnerabilityReportType'
description 'The type of the security scan that found the vulnerability.'
::Vulnerabilities::Occurrence::REPORT_TYPES.keys.each do |report_type|
::Vulnerabilities::Finding::REPORT_TYPES.keys.each do |report_type|
value report_type.to_s.upcase, value: report_type.to_s
end
end
......
......@@ -6,7 +6,7 @@ module Types
graphql_name 'VulnerabilitySeveritiesCount'
description 'Represents vulnerability counts by severity'
::Vulnerabilities::Occurrence::SEVERITY_LEVELS.keys.each do |severity|
::Vulnerabilities::Finding::SEVERITY_LEVELS.keys.each do |severity|
field severity, GraphQL::INT_TYPE, null: true,
description: "Number of vulnerabilities of #{severity.upcase} severity of the project"
end
......
......@@ -5,7 +5,7 @@ module Types
graphql_name 'VulnerabilitySeverity'
description 'The severity of the vulnerability.'
::Vulnerabilities::Occurrence::SEVERITY_LEVELS.keys.each do |severity|
::Vulnerabilities::Finding::SEVERITY_LEVELS.keys.each do |severity|
value severity.to_s.upcase, value: severity.to_s
end
end
......
......@@ -22,10 +22,10 @@ module Types
description: "State of the vulnerability (#{::Vulnerability.states.keys.join(', ').upcase})"
field :severity, VulnerabilitySeverityEnum, null: true,
description: "Severity of the vulnerability (#{::Vulnerabilities::Occurrence::SEVERITY_LEVELS.keys.join(', ').upcase})"
description: "Severity of the vulnerability (#{::Vulnerabilities::Finding::SEVERITY_LEVELS.keys.join(', ').upcase})"
field :report_type, VulnerabilityReportTypeEnum, null: true,
description: "Type of the security report that found the vulnerability (#{::Vulnerabilities::Occurrence::REPORT_TYPES.keys.join(', ').upcase})"
description: "Type of the security report that found the vulnerability (#{::Vulnerabilities::Finding::REPORT_TYPES.keys.join(', ').upcase})"
field :user_notes_count, GraphQL::INT_TYPE, null: false,
description: 'Number of user notes attached to the vulnerability'
......
......@@ -14,8 +14,8 @@ module EE
prepended do
include UsageStatistics
has_many :vulnerabilities_finding_pipelines, class_name: 'Vulnerabilities::FindingPipeline'
has_many :vulnerability_findings, source: :occurrence, through: :vulnerabilities_finding_pipelines, class_name: 'Vulnerabilities::Occurrence'
has_many :vulnerabilities_finding_pipelines, class_name: 'Vulnerabilities::FindingPipeline', inverse_of: :pipeline
has_many :vulnerability_findings, source: :finding, through: :vulnerabilities_finding_pipelines, class_name: 'Vulnerabilities::Finding'
has_many :auto_canceled_pipelines, class_name: 'Ci::Pipeline', foreign_key: 'auto_canceled_by_id'
has_many :auto_canceled_jobs, class_name: 'CommitStatus', foreign_key: 'auto_canceled_by_id'
......
......@@ -63,7 +63,7 @@ module EE
has_many :vulnerabilities
has_many :vulnerability_feedback, class_name: 'Vulnerabilities::Feedback'
has_many :vulnerability_historical_statistics, class_name: 'Vulnerabilities::HistoricalStatistic'
has_many :vulnerability_findings, class_name: 'Vulnerabilities::Occurrence' do
has_many :vulnerability_findings, class_name: 'Vulnerabilities::Finding', inverse_of: :project do
def lock_for_confirmation!(id)
where(vulnerability_id: nil).lock.find(id)
end
......@@ -129,7 +129,7 @@ module EE
scope :with_protected_branches, -> { joins(:protected_branches) }
scope :with_repositories_enabled, -> { joins(:project_feature).where(project_features: { repository_access_level: ::ProjectFeature::ENABLED }) }
scope :with_security_reports_stored, -> { where('EXISTS (?)', ::Vulnerabilities::Occurrence.scoped_project.select(1)) }
scope :with_security_reports_stored, -> { where('EXISTS (?)', ::Vulnerabilities::Finding.scoped_project.select(1)) }
scope :with_security_reports, -> { where('EXISTS (?)', ::Ci::JobArtifact.security_reports.scoped_project.select(1)) }
scope :with_github_service_pipeline_events, -> { joins(:github_service).merge(GithubService.pipeline_hooks) }
scope :with_active_prometheus_service, -> { joins(:prometheus_service).merge(PrometheusService.active) }
......
# frozen_string_literal: true
module Vulnerabilities
class Occurrence < ApplicationRecord
class Finding < ApplicationRecord
include ShaAttribute
include ::Gitlab::Utils::StrongMemoize
include Presentable
......@@ -15,14 +15,15 @@ module Vulnerabilities
sha_attribute :project_fingerprint
sha_attribute :location_fingerprint
belongs_to :project
belongs_to :project, inverse_of: :vulnerability_findings
belongs_to :scanner, class_name: 'Vulnerabilities::Scanner'
belongs_to :primary_identifier, class_name: 'Vulnerabilities::Identifier', inverse_of: :primary_occurrences
belongs_to :vulnerability, inverse_of: :findings
belongs_to :primary_identifier, class_name: 'Vulnerabilities::Identifier', inverse_of: :primary_findings, foreign_key: 'primary_identifier_id'
belongs_to :vulnerability, class_name: 'Vulnerability', inverse_of: :findings, foreign_key: 'vulnerability_id'
has_many :finding_identifiers, class_name: 'Vulnerabilities::FindingIdentifier'
has_many :finding_identifiers, class_name: 'Vulnerabilities::FindingIdentifier', inverse_of: :finding, foreign_key: 'occurrence_id'
has_many :identifiers, through: :finding_identifiers, class_name: 'Vulnerabilities::Identifier'
has_many :finding_pipelines, class_name: 'Vulnerabilities::FindingPipeline'
has_many :finding_pipelines, class_name: 'Vulnerabilities::FindingPipeline', inverse_of: :finding, foreign_key: 'occurrence_id'
has_many :pipelines, through: :finding_pipelines, class_name: 'Ci::Pipeline'
attr_writer :sha
......@@ -122,7 +123,7 @@ module Vulnerabilities
end
def self.with_vulnerabilities_for_state(project:, report_type:, project_fingerprints:)
Vulnerabilities::Occurrence
Vulnerabilities::Finding
.joins(:vulnerability)
.where(
project: project,
......
......@@ -4,10 +4,12 @@ module Vulnerabilities
class FindingIdentifier < ApplicationRecord
self.table_name = "vulnerability_occurrence_identifiers"
belongs_to :occurrence, class_name: 'Vulnerabilities::Occurrence'
belongs_to :identifier, class_name: 'Vulnerabilities::Identifier'
alias_attribute :finding_id, :occurrence_id
validates :occurrence, presence: true
belongs_to :finding, class_name: 'Vulnerabilities::Finding', inverse_of: :finding_identifiers, foreign_key: 'occurrence_id'
belongs_to :identifier, class_name: 'Vulnerabilities::Identifier', inverse_of: :finding_identifiers
validates :finding, presence: true
validates :identifier, presence: true
validates :identifier_id, uniqueness: { scope: [:occurrence_id] }
end
......
......@@ -4,10 +4,12 @@ module Vulnerabilities
class FindingPipeline < ApplicationRecord
self.table_name = "vulnerability_occurrence_pipelines"
belongs_to :occurrence, class_name: 'Vulnerabilities::Occurrence'
alias_attribute :finding_id, :occurrence_id
belongs_to :finding, class_name: 'Vulnerabilities::Finding', inverse_of: :finding_pipelines, foreign_key: 'occurrence_id'
belongs_to :pipeline, class_name: '::Ci::Pipeline'
validates :occurrence, presence: true
validates :finding, presence: true
validates :pipeline, presence: true
validates :pipeline_id, uniqueness: { scope: [:occurrence_id] }
end
......
......@@ -8,12 +8,12 @@ module Vulnerabilities
sha_attribute :fingerprint
has_many :finding_identifiers, class_name: 'Vulnerabilities::FindingIdentifier'
has_many :occurrences, through: :finding_identifiers, class_name: 'Vulnerabilities::Occurrence'
has_many :finding_identifiers, class_name: 'Vulnerabilities::FindingIdentifier', inverse_of: :identifier, foreign_key: 'identifier_id'
has_many :findings, through: :finding_identifiers, class_name: 'Vulnerabilities::Finding'
has_many :primary_occurrences, class_name: 'Vulnerabilities::Occurrence', inverse_of: :primary_identifier
has_many :primary_findings, class_name: 'Vulnerabilities::Finding', inverse_of: :primary_identifier, foreign_key: 'primary_identifier_id'
belongs_to :project
belongs_to :project, foreign_key: 'project_id'
validates :project, presence: true
validates :external_type, presence: true
......
......@@ -4,7 +4,7 @@ module Vulnerabilities
class Scanner < ApplicationRecord
self.table_name = "vulnerability_scanners"
has_many :occurrences, class_name: 'Vulnerabilities::Occurrence'
has_many :findings, class_name: 'Vulnerabilities::Finding', inverse_of: :scanner
belongs_to :project
......@@ -17,7 +17,7 @@ module Vulnerabilities
scope :for_projects, -> (project_ids) { where(project_id: project_ids) }
scope :with_report_type, -> do
joins(:occurrences)
joins(:findings)
.select('DISTINCT ON ("vulnerability_scanners"."external_id", "vulnerability_occurrences"."report_type") "vulnerability_scanners".*, "vulnerability_occurrences"."report_type" AS "report_type"')
.order('"vulnerability_scanners"."external_id" ASC, "vulnerability_occurrences"."report_type" ASC')
end
......
......@@ -33,7 +33,7 @@ class Vulnerability < ApplicationRecord
has_one :group, through: :project
has_many :findings, class_name: 'Vulnerabilities::Occurrence', inverse_of: :vulnerability
has_many :findings, class_name: 'Vulnerabilities::Finding', inverse_of: :vulnerability
has_many :issue_links, class_name: 'Vulnerabilities::IssueLink', inverse_of: :vulnerability
has_many :related_issues, through: :issue_links, source: :issue do
def with_vulnerability_links
......@@ -46,9 +46,9 @@ class Vulnerability < ApplicationRecord
has_many :user_mentions, class_name: 'VulnerabilityUserMention'
enum state: { detected: 1, dismissed: 2, resolved: 3, confirmed: 4 }
enum severity: Vulnerabilities::Occurrence::SEVERITY_LEVELS, _prefix: :severity
enum confidence: Vulnerabilities::Occurrence::CONFIDENCE_LEVELS, _prefix: :confidence
enum report_type: Vulnerabilities::Occurrence::REPORT_TYPES
enum severity: Vulnerabilities::Finding::SEVERITY_LEVELS, _prefix: :severity
enum confidence: Vulnerabilities::Finding::CONFIDENCE_LEVELS, _prefix: :confidence
enum report_type: Vulnerabilities::Finding::REPORT_TYPES
validates :project, :author, :title, :severity, :confidence, :report_type, presence: true
......
......@@ -2,7 +2,7 @@
module Security
class VulnerableProjectPresenter < ::Gitlab::View::Presenter::Delegated
SEVERITY_LEVELS = ::Vulnerabilities::Occurrence::SEVERITY_LEVELS.keys
SEVERITY_LEVELS = ::Vulnerabilities::Finding::SEVERITY_LEVELS.keys
presents :project
......@@ -14,7 +14,7 @@ module Security
def counts_for_project(project)
SEVERITY_LEVELS.each_with_object({}) do |severity, counts|
counts["#{severity}_vulnerability_count".to_sym] = ::Vulnerabilities::Occurrence.batch_count_by_project_and_severity(project.id, severity)
counts["#{severity}_vulnerability_count".to_sym] = ::Vulnerabilities::Finding.batch_count_by_project_and_severity(project.id, severity)
end
end
end
......
......@@ -3,7 +3,7 @@
class Vulnerabilities::HistoryEntity < Grape::Entity
present_collection true
Vulnerabilities::Occurrence::SEVERITY_LEVELS.keys.each do |level|
Vulnerabilities::Finding::SEVERITY_LEVELS.keys.each do |level|
expose level do |object|
counts(by_severity[level]&.group_by(&:day) || {})
end
......
# frozen_string_literal: true
class VulnerabilitySummaryEntity < Grape::Entity
Vulnerabilities::Occurrence::SEVERITY_LEVELS.each do |severity_name, severity|
Vulnerabilities::Finding::SEVERITY_LEVELS.each do |severity_name, severity|
expose severity_name do |param|
object[severity] || 0
end
......
# frozen_string_literal: true
class VulnerableProjectEntity < ProjectEntity
::Vulnerabilities::Occurrence::SEVERITY_LEVELS.each_key do |severity_level|
::Vulnerabilities::Finding::SEVERITY_LEVELS.each_key do |severity_level|
expose "#{severity_level}_vulnerability_count"
end
end
......@@ -75,7 +75,7 @@ module Security
level_i = dep_i.dig(:vulnerabilities, 0, :severity) || :info
level_j = dep_j.dig(:vulnerabilities, 0, :severity) || :info
::Vulnerabilities::Occurrence::SEVERITY_LEVELS[level_j] <=> ::Vulnerabilities::Occurrence::SEVERITY_LEVELS[level_i]
::Vulnerabilities::Finding::SEVERITY_LEVELS[level_j] <=> ::Vulnerabilities::Finding::SEVERITY_LEVELS[level_i]
end
end
end
......
......@@ -34,18 +34,18 @@ module Security
@source_reports.first.commit_sha,
@source_reports.first.created_at
)
@occurrences = []
@findings = []
end
def execute
@source_reports.each do |source|
copy_scanners_to_target(source)
copy_identifiers_to_target(source)
copy_occurrences_to_buffer(source)
copy_findings_to_buffer(source)
copy_scanned_resources_to_target(source)
end
copy_occurrences_to_target
copy_findings_to_target
@target_report
end
......@@ -62,8 +62,8 @@ module Security
source_report.identifiers.values.each { |identifier| @target_report.add_identifier(identifier) }
end
def copy_occurrences_to_buffer(source)
@occurrences.concat(source.occurrences)
def copy_findings_to_buffer(source)
@findings.concat(source.findings)
end
def copy_scanned_resources_to_target(source_report)
......@@ -82,49 +82,49 @@ module Security
end
end
def deduplicate_occurrences!
def deduplicate_findings!
seen_identifiers = Set.new
deduplicated = []
@occurrences.each do |occurrence|
@findings.each do |finding|
seen = false
# We are looping through all identifiers in order to find the same vulnerabilities reported for the same location
# but from different source reports and keeping only first of them
occurrence.identifiers.each do |identifier|
finding.identifiers.each do |identifier|
# TODO: remove .downcase here after the DAST parser is harmonized to the common library identifiers' keys format
# See https://gitlab.com/gitlab-org/gitlab/issues/11976#note_191257912
next if %w[cwe wasc].include?(identifier.external_type.downcase) # ignored because these describe a class of vulnerabilities
seen = check_or_mark_seen_identifier!(identifier, occurrence.location.fingerprint, seen_identifiers)
seen = check_or_mark_seen_identifier!(identifier, finding.location.fingerprint, seen_identifiers)
break if seen
end
deduplicated << occurrence unless seen
deduplicated << finding unless seen
end
@occurrences = deduplicated
@findings = deduplicated
end
def sort_occurrences!
@occurrences.sort! do |a, b|
def sort_findings!
@findings.sort! do |a, b|
a_severity, b_severity = a.severity, b.severity
if a_severity == b_severity
a.compare_key <=> b.compare_key
else
Vulnerabilities::Occurrence::SEVERITY_LEVELS[b_severity] <=>
Vulnerabilities::Occurrence::SEVERITY_LEVELS[a_severity]
Vulnerabilities::Finding::SEVERITY_LEVELS[b_severity] <=>
Vulnerabilities::Finding::SEVERITY_LEVELS[a_severity]
end
end
end
def copy_occurrences_to_target
deduplicate_occurrences!
sort_occurrences!
def copy_findings_to_target
deduplicate_findings!
sort_findings!
@occurrences.each { |occurrence| @target_report.add_occurrence(occurrence) }
@findings.each { |finding| @target_report.finding(finding) }
end
def sort_by_ds_analyzers!
......
......@@ -5,7 +5,7 @@ module Security
include Gitlab::Utils::StrongMemoize
# @param [Ci::Pipeline] pipeline
# @param [Hash[Symbol, Array[Symbol]] selection_information keys must be in the set of Vulnerabilities::Occurrence::REPORT_TYPES for example: {dast: [:scanned_resources_count, :vulnerabilities_count], container_scanning:[:vulnerabilities_count]}
# @param [Hash[Symbol, Array[Symbol]] selection_information keys must be in the set of Vulnerabilities::Finding::REPORT_TYPES for example: {dast: [:scanned_resources_count, :vulnerabilities_count], container_scanning:[:vulnerabilities_count]}
def initialize(pipeline, selection_information)
@pipeline = pipeline
@selection_information = selection_information
......
......@@ -6,7 +6,7 @@ module Security
#
class ScannedResourcesCountingService
# @param [Ci::Pipeline] pipeline
# @param Array[Symbol] report_types Summary report types. Valid values are members of Vulnerabilities::Occurrence::REPORT_TYPES
# @param Array[Symbol] report_types Summary report types. Valid values are members of Vulnerabilities::Finding::REPORT_TYPES
def initialize(pipeline, report_types)
@pipeline = pipeline
@report_types = report_types
......
......@@ -6,7 +6,7 @@ module Security
#
class ScannedResourcesService
# @param [Ci::Pipeline] pipeline
# @param Array[Symbol] report_types Summary report types. Valid values are members of Vulnerabilities::Occurrence::REPORT_TYPES
# @param Array[Symbol] report_types Summary report types. Valid values are members of Vulnerabilities::Finding::REPORT_TYPES
# @param [Int] The maximum number of scanned resources to return
def initialize(pipeline, report_types, limit = nil)
@pipeline = pipeline
......
......@@ -30,20 +30,20 @@ module Security
end
def create_all_vulnerabilities!
@report.occurrences.each do |occurrence|
create_vulnerability_finding(occurrence)
@report.findings.each do |finding|
create_vulnerability_finding(finding)
end
end
def create_vulnerability_finding(occurrence)
vulnerability_params = occurrence.to_hash.except(:compare_key, :identifiers, :location, :scanner)
vulnerability_finding = create_or_find_vulnerability_finding(occurrence, vulnerability_params)
def create_vulnerability_finding(finding)
vulnerability_params = finding.to_hash.except(:compare_key, :identifiers, :location, :scanner)
vulnerability_finding = create_or_find_vulnerability_finding(finding, vulnerability_params)
update_vulnerability_scanner(occurrence)
update_vulnerability_scanner(finding)
update_vulnerability_finding(vulnerability_finding, vulnerability_params)
occurrence.identifiers.map do |identifier|
finding.identifiers.map do |identifier|
create_or_update_vulnerability_identifier_object(vulnerability_finding, identifier)
end
......@@ -53,13 +53,13 @@ module Security
end
# rubocop: disable CodeReuse/ActiveRecord
def create_or_find_vulnerability_finding(occurrence, create_params)
return if occurrence.scanner.blank?
def create_or_find_vulnerability_finding(finding, create_params)
return if finding.scanner.blank?
find_params = {
scanner: scanners_objects[occurrence.scanner.key],
primary_identifier: identifiers_objects[occurrence.primary_identifier.key],
location_fingerprint: occurrence.location.fingerprint
scanner: scanners_objects[finding.scanner.key],
primary_identifier: identifiers_objects[finding.primary_identifier.key],
location_fingerprint: finding.location.fingerprint
}
begin
......@@ -74,11 +74,11 @@ module Security
end
end
def update_vulnerability_scanner(occurrence)
return if occurrence.scanner.blank?
def update_vulnerability_scanner(finding)
return if finding.scanner.blank?
scanner = scanners_objects[occurrence.scanner.key]
scanner.update!(occurrence.scanner.to_hash)
scanner = scanners_objects[finding.scanner.key]
scanner.update!(finding.scanner.to_hash)
end
def update_vulnerability_finding(vulnerability_finding, update_params)
......
......@@ -6,7 +6,7 @@ module Security
#
class VulnerabilityCountingService
# @param [Ci::Pipeline] pipeline
# @param Array[String] report_types Summary report types. Valid values are members of Vulnerabilities::Occurrence::REPORT_TYPES
# @param Array[String] report_types Summary report types. Valid values are members of Vulnerabilities::Finding::REPORT_TYPES
def initialize(pipeline, report_types)
@pipeline = pipeline
@report_types = report_types
......@@ -14,7 +14,7 @@ module Security
def execute
findings = ::Security::PipelineVulnerabilitiesFinder.new(pipeline: @pipeline, params: { report_type: @report_types }).execute
findings.occurrences.group_by(&:report_type).compact.transform_values(&:size).reverse_merge(no_counts)
findings.findings.group_by(&:report_type).compact.transform_values(&:size).reverse_merge(no_counts)
end
private
......
......@@ -15,7 +15,7 @@ module Vulnerabilities
vulnerability = Vulnerability.new
Vulnerabilities::Occurrence.transaction(requires_new: true) do
Vulnerabilities::Finding.transaction(requires_new: true) do
# we're using `lock` instead of `with_lock` to avoid extra call to `find` under the hood
finding = @project.vulnerability_findings.lock_for_confirmation!(@finding_id)
......
......@@ -128,15 +128,15 @@ class Gitlab::Seeder::Vulnerabilities
end
def random_confidence_level
::Vulnerabilities::Occurrence::CONFIDENCE_LEVELS.keys.sample
::Vulnerabilities::Finding::CONFIDENCE_LEVELS.keys.sample
end
def random_severity_level
::Vulnerabilities::Occurrence::SEVERITY_LEVELS.keys.sample
::Vulnerabilities::Finding::SEVERITY_LEVELS.keys.sample
end
def random_report_type
::Vulnerabilities::Occurrence::REPORT_TYPES.keys.sample
::Vulnerabilities::Finding::REPORT_TYPES.keys.sample
end
def metadata(line)
......
......@@ -16,11 +16,11 @@ module API
end
end
def vulnerability_occurrences_by(params)
def vulnerability_findings_by(params)
return [] unless pipeline
aggregated_report = Security::PipelineVulnerabilitiesFinder.new(pipeline: pipeline, params: params).execute
aggregated_report.occurrences
aggregated_report.findings
end
end
......@@ -36,8 +36,8 @@ module API
optional :report_type, type: Array[String],
coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce,
desc: 'The type of report vulnerability belongs to',
values: ::Vulnerabilities::Occurrence.report_types.keys,
default: ::Vulnerabilities::Occurrence.report_types.keys
values: ::Vulnerabilities::Finding.report_types.keys,
default: ::Vulnerabilities::Finding.report_types.keys
optional :scope, type: String, desc: 'Return vulnerabilities for the given scope: `dismissed` or `all`',
default: 'dismissed', values: %w[all dismissed]
optional :severity,
......@@ -45,16 +45,16 @@ module API
coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce,
desc: 'Returns vulnerabilities belonging to specified severity level: '\
'`info`, `unknown`, `low`, `medium`, `high`, or `critical`. Defaults to all',
values: ::Vulnerabilities::Occurrence.severities.keys,
default: ::Vulnerabilities::Occurrence.severities.keys
values: ::Vulnerabilities::Finding.severities.keys,
default: ::Vulnerabilities::Finding.severities.keys
optional :confidence,
type: Array[String],
coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce,
desc: 'Returns vulnerabilities belonging to specified confidence level: '\
'`ignore`, `unknown`, `experimental`, `low`, `medium`, `high`, or `confirmed`. '\
'Defaults to all',
values: ::Vulnerabilities::Occurrence.confidences.keys,
default: ::Vulnerabilities::Occurrence.confidences.keys
values: ::Vulnerabilities::Finding.confidences.keys,
default: ::Vulnerabilities::Finding.confidences.keys
optional :scanner,
type: Array[String],
coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce,
......@@ -74,15 +74,15 @@ module API
# Kaminari.paginate_array here is correct
# See https://gitlab.com/gitlab-org/gitlab/issues/33588#note_291849433
# for discussion
vulnerability_occurrences = paginate(
vulnerability_findings = paginate(
Kaminari.paginate_array(
vulnerability_occurrences_by(declared_params)
vulnerability_findings_by(declared_params)
)
)
Gitlab::Vulnerabilities::FindingsPreloader.preload_feedback!(vulnerability_occurrences)
Gitlab::Vulnerabilities::FindingsPreloader.preload_feedback!(vulnerability_findings)
present vulnerability_occurrences,
present vulnerability_findings,
with: ::Vulnerabilities::FindingEntity,
request: GrapeRequestProxy.new(request, current_user)
end
......
......@@ -54,7 +54,7 @@ module Gitlab
def create_vulnerability(report, data, version)
scanner = create_scanner(report, data['scanner'] || mutate_scanner_tool(data['tool']))
identifiers = create_identifiers(report, data['identifiers'])
report.add_occurrence(
report.finding(
::Gitlab::Ci::Reports::Security::Occurrence.new(
uuid: SecureRandom.uuid,
report_type: report.type,
......@@ -104,13 +104,13 @@ module Gitlab
end
def parse_severity_level(input)
return input if ::Vulnerabilities::Occurrence::SEVERITY_LEVELS.key?(input)
return input if ::Vulnerabilities::Finding::SEVERITY_LEVELS.key?(input)
'unknown'
end
def parse_confidence_level(input)
return input if ::Vulnerabilities::Occurrence::CONFIDENCE_LEVELS.key?(input)
return input if ::Vulnerabilities::Finding::CONFIDENCE_LEVELS.key?(input)
'unknown'
end
......
......@@ -7,11 +7,11 @@ module Gitlab
module Reports
module Security
class AggregatedReport
attr_reader :occurrences
attr_reader :findings
def initialize(reports, occurrences)
def initialize(reports, findings)
@reports = reports
@occurrences = occurrences
@findings = findings
end
def created_at
......
......@@ -10,7 +10,7 @@ module Gitlab
attr_reader :created_at
attr_reader :type
attr_reader :commit_sha
attr_reader :occurrences
attr_reader :findings
attr_reader :scanners
attr_reader :identifiers
......@@ -21,7 +21,7 @@ module Gitlab
@type = type
@commit_sha = commit_sha
@created_at = created_at
@occurrences = []
@findings = []
@scanners = {}
@identifiers = {}
@scanned_resources = []
......@@ -39,8 +39,8 @@ module Gitlab
identifiers[identifier.key] ||= identifier
end
def add_occurrence(occurrence)
occurrences << occurrence
def finding(finding)
findings << finding
end
def clone_as_blank
......@@ -62,7 +62,7 @@ module Gitlab
end
def safe?
severities = occurrences.map(&:severity).compact.map(&:downcase)
severities = findings.map(&:severity).compact.map(&:downcase)
(severities & UNSAFE_SEVERITIES).empty?
end
end
......
......@@ -33,20 +33,20 @@ module Gitlab
def added
strong_memoize(:added) do
head_report.occurrences - base_report.occurrences
head_report.findings - base_report.findings
end
end
def fixed
strong_memoize(:fixed) do
base_report.occurrences - head_report.occurrences
base_report.findings - head_report.findings
end
end
def existing
strong_memoize(:existing) do
# Existing vulnerabilities should point to source report for most recent information
head_report.occurrences & base_report.occurrences
head_report.findings & base_report.findings
end
end
......
......@@ -29,7 +29,7 @@ module Gitlab
critical: 0
}
summary_keys = ::Vulnerabilities::Occurrence::SEVERITY_LEVELS.keys.map(&:to_sym)
summary_keys = ::Vulnerabilities::Finding::SEVERITY_LEVELS.keys.map(&:to_sym)
project_ids_to_fetch.each do |project_id|
project_summary = Gitlab::Vulnerabilities::SummaryCache
......
......@@ -3,10 +3,10 @@
FactoryBot.define do
factory :ci_reports_security_aggregated_reports, class: '::Gitlab::Ci::Reports::Security::AggregatedReport' do
reports { FactoryBot.build_list(:ci_reports_security_report, 1) }
occurrences { FactoryBot.build_list(:ci_reports_security_occurrence, 1) }
findings { FactoryBot.build_list(:ci_reports_security_finding, 1) }
initialize_with do
::Gitlab::Ci::Reports::Security::AggregatedReport.new(reports, occurrences)
::Gitlab::Ci::Reports::Security::AggregatedReport.new(reports, findings)
end
end
end
# frozen_string_literal: true
FactoryBot.define do
factory :ci_reports_security_occurrence, class: '::Gitlab::Ci::Reports::Security::Occurrence' do
factory :ci_reports_security_finding, class: '::Gitlab::Ci::Reports::Security::Occurrence' do
compare_key { "#{identifiers.first.external_type}:#{identifiers.first.external_id}:#{location.fingerprint}" }
confidence { :medium }
identifiers { Array.new(1) { FactoryBot.build(:ci_reports_security_identifier) } }
......@@ -30,7 +30,7 @@ FactoryBot.define do
end
scanner factory: :ci_reports_security_scanner
severity { :high }
sequence(:uuid) { generate(:vulnerability_occurrence_uuid) }
sequence(:uuid) { generate(:vulnerability_finding_uuid) }
skip_create
......
......@@ -8,7 +8,7 @@ FactoryBot.define do
scanned_resources { [] }
transient do
occurrences { [] }
findings { [] }
scanners { [] }
identifiers { [] }
end
......@@ -16,7 +16,7 @@ FactoryBot.define do
after :build do |report, evaluator|
evaluator.scanners.each { |s| report.add_scanner(s) }
evaluator.identifiers.each { |id| report.add_identifier(id) }
evaluator.occurrences.each { |o| report.add_occurrence(o) }
evaluator.findings.each { |o| report.finding(o) }
end
skip_create
......
......@@ -45,13 +45,13 @@ FactoryBot.define do
severity { :low }
end
::Vulnerabilities::Occurrence::SEVERITY_LEVELS.keys.each do |severity_level|
::Vulnerabilities::Finding::SEVERITY_LEVELS.keys.each do |severity_level|
trait severity_level do
severity { severity_level }
end
end
::Vulnerabilities::Occurrence::REPORT_TYPES.keys.each do |report_type|
::Vulnerabilities::Finding::REPORT_TYPES.keys.each do |report_type|
trait report_type do
report_type { report_type }
end
......@@ -59,20 +59,20 @@ FactoryBot.define do
trait :with_findings do
after(:build) do |vulnerability|
occurrences_with_solution = build_list(
:vulnerabilities_occurrence,
findings_with_solution = build_list(
:vulnerabilities_finding,
2,
vulnerability: vulnerability,
report_type: vulnerability.report_type,
project: vulnerability.project)
occurrences_with_remediation = build_list(
:vulnerabilities_occurrence,
findings_with_remediation = build_list(
:vulnerabilities_finding,
2,
:with_remediation,
vulnerability: vulnerability,
report_type: vulnerability.report_type,
project: vulnerability.project)
vulnerability.findings = occurrences_with_solution + occurrences_with_remediation
vulnerability.findings = findings_with_solution + findings_with_remediation
end
end
......
......@@ -2,7 +2,7 @@
FactoryBot.define do
factory :vulnerabilities_finding_identifier, class: 'Vulnerabilities::FindingIdentifier' do
occurrence factory: :vulnerabilities_occurrence
finding factory: :vulnerabilities_finding
identifier factory: :vulnerabilities_identifier
end
end
......@@ -2,7 +2,7 @@
FactoryBot.define do
factory :vulnerabilities_finding_pipeline, class: 'Vulnerabilities::FindingPipeline' do
occurrence factory: :vulnerabilities_occurrence
finding factory: :vulnerabilities_finding
pipeline factory: :ci_pipeline
end
end
# frozen_string_literal: true
FactoryBot.define do
sequence :vulnerability_occurrence_uuid do |n|
sequence :vulnerability_finding_uuid do |n|
SecureRandom.uuid
end
factory :vulnerabilities_occurrence_with_remediation, parent: :vulnerabilities_occurrence do
factory :vulnerabilities_finding_with_remediation, parent: :vulnerabilities_finding do
transient do
summary { nil }
end
......@@ -24,10 +24,10 @@ FactoryBot.define do
end
end
factory :vulnerabilities_occurrence, class: 'Vulnerabilities::Occurrence', aliases: [:vulnerabilities_finding] do
factory :vulnerabilities_finding, class: 'Vulnerabilities::Finding', aliases: [:vulnerabilities_occurrence] do
name { 'Cipher with no integrity' }
project
sequence(:uuid) { generate(:vulnerability_occurrence_uuid) }
sequence(:uuid) { generate(:vulnerability_finding_uuid) }
project_fingerprint { generate(:project_fingerprint) }
primary_identifier factory: :vulnerabilities_identifier
location_fingerprint { '4e5b6966dd100170b4b1ad599c7058cce91b57b4' }
......@@ -114,7 +114,7 @@ FactoryBot.define do
end
end
::Vulnerabilities::Occurrence::REPORT_TYPES.keys.each do |security_report_type|
::Vulnerabilities::Finding::REPORT_TYPES.keys.each do |security_report_type|
trait security_report_type do
report_type { security_report_type }
end
......
......@@ -44,19 +44,19 @@ RSpec.describe Security::PipelineVulnerabilitiesFinder do
subject { described_class.new(pipeline: pipeline, params: params).execute }
context 'occurrences' do
context 'findings' do
it 'assigns commit sha to findings' do
expect(subject.occurrences.map(&:sha).uniq).to eq([pipeline.sha])
expect(subject.findings.map(&:sha).uniq).to eq([pipeline.sha])
end
context 'by order' do
let(:params) { { report_type: %w[sast] } }
let!(:high_high) { build(:vulnerabilities_occurrence, confidence: :high, severity: :high) }
let!(:critical_medium) { build(:vulnerabilities_occurrence, confidence: :medium, severity: :critical) }
let!(:critical_high) { build(:vulnerabilities_occurrence, confidence: :high, severity: :critical) }
let!(:unknown_high) { build(:vulnerabilities_occurrence, confidence: :high, severity: :unknown) }
let!(:unknown_medium) { build(:vulnerabilities_occurrence, confidence: :medium, severity: :unknown) }
let!(:unknown_low) { build(:vulnerabilities_occurrence, confidence: :low, severity: :unknown) }
let!(:high_high) { build(:vulnerabilities_finding, confidence: :high, severity: :high) }
let!(:critical_medium) { build(:vulnerabilities_finding, confidence: :medium, severity: :critical) }
let!(:critical_high) { build(:vulnerabilities_finding, confidence: :high, severity: :critical) }
let!(:unknown_high) { build(:vulnerabilities_finding, confidence: :high, severity: :unknown) }
let!(:unknown_medium) { build(:vulnerabilities_finding, confidence: :medium, severity: :unknown) }
let!(:unknown_low) { build(:vulnerabilities_finding, confidence: :low, severity: :unknown) }
it 'orders by severity and confidence' do
allow_next_instance_of(described_class) do |pipeline_vulnerabilities_finder|
......@@ -69,7 +69,7 @@ RSpec.describe Security::PipelineVulnerabilitiesFinder do
high_high
])
expect(subject.occurrences).to eq([critical_high, critical_medium, high_high, unknown_high, unknown_medium, unknown_low])
expect(subject.findings).to eq([critical_high, critical_medium, high_high, unknown_high, unknown_medium, unknown_low])
end
end
end
......@@ -78,31 +78,31 @@ RSpec.describe Security::PipelineVulnerabilitiesFinder do
context 'by report type' do
context 'when sast' do
let(:params) { { report_type: %w[sast] } }
let(:sast_report_fingerprints) {pipeline.security_reports.reports['sast'].occurrences.map(&:location).map(&:fingerprint) }
let(:sast_report_fingerprints) {pipeline.security_reports.reports['sast'].findings.map(&:location).map(&:fingerprint) }
it 'includes only sast' do
expect(subject.occurrences.map(&:location_fingerprint)).to match_array(sast_report_fingerprints)
expect(subject.occurrences.count).to eq(sast_count)
expect(subject.findings.map(&:location_fingerprint)).to match_array(sast_report_fingerprints)
expect(subject.findings.count).to eq(sast_count)
end
end
context 'when dependency_scanning' do
let(:params) { { report_type: %w[dependency_scanning] } }
let(:ds_report_fingerprints) {pipeline.security_reports.reports['dependency_scanning'].occurrences.map(&:location).map(&:fingerprint) }
let(:ds_report_fingerprints) {pipeline.security_reports.reports['dependency_scanning'].findings.map(&:location).map(&:fingerprint) }
it 'includes only dependency_scanning' do
expect(subject.occurrences.map(&:location_fingerprint)).to match_array(ds_report_fingerprints)
expect(subject.occurrences.count).to eq(ds_count)
expect(subject.findings.map(&:location_fingerprint)).to match_array(ds_report_fingerprints)
expect(subject.findings.count).to eq(ds_count)
end
end
context 'when dast' do
let(:params) { { report_type: %w[dast] } }
let(:dast_report_fingerprints) {pipeline.security_reports.reports['dast'].occurrences.map(&:location).map(&:fingerprint) }
let(:dast_report_fingerprints) {pipeline.security_reports.reports['dast'].findings.map(&:location).map(&:fingerprint) }
it 'includes only dast' do
expect(subject.occurrences.map(&:location_fingerprint)).to match_array(dast_report_fingerprints)
expect(subject.occurrences.count).to eq(dast_count)
expect(subject.findings.map(&:location_fingerprint)).to match_array(dast_report_fingerprints)
expect(subject.findings.count).to eq(dast_count)
end
end
......@@ -110,16 +110,16 @@ RSpec.describe Security::PipelineVulnerabilitiesFinder do
let(:params) { { report_type: %w[container_scanning] } }
it 'includes only container_scanning' do
fingerprints = pipeline.security_reports.reports['container_scanning'].occurrences.map(&:location).map(&:fingerprint)
expect(subject.occurrences.map(&:location_fingerprint)).to match_array(fingerprints)
expect(subject.occurrences.count).to eq(cs_count)
fingerprints = pipeline.security_reports.reports['container_scanning'].findings.map(&:location).map(&:fingerprint)
expect(subject.findings.map(&:location_fingerprint)).to match_array(fingerprints)
expect(subject.findings.count).to eq(cs_count)
end
end
end
context 'by scope' do
let(:ds_occurrence) { pipeline.security_reports.reports["dependency_scanning"].occurrences.first }
let(:sast_occurrence) { pipeline.security_reports.reports["sast"].occurrences.first }
let(:ds_finding) { pipeline.security_reports.reports["dependency_scanning"].findings.first }
let(:sast_finding) { pipeline.security_reports.reports["sast"].findings.first }
let!(:feedback) do
[
......@@ -129,8 +129,8 @@ RSpec.describe Security::PipelineVulnerabilitiesFinder do
:dependency_scanning,
project: project,
pipeline: pipeline,
project_fingerprint: ds_occurrence.project_fingerprint,
vulnerability_data: ds_occurrence.raw_metadata
project_fingerprint: ds_finding.project_fingerprint,
vulnerability_data: ds_finding.raw_metadata
),
create(
:vulnerability_feedback,
......@@ -138,8 +138,8 @@ RSpec.describe Security::PipelineVulnerabilitiesFinder do
:sast,
project: project,
pipeline: pipeline,
project_fingerprint: sast_occurrence.project_fingerprint,
vulnerability_data: sast_occurrence.raw_metadata
project_fingerprint: sast_finding.project_fingerprint,
vulnerability_data: sast_finding.raw_metadata
)
]
end
......@@ -148,8 +148,8 @@ RSpec.describe Security::PipelineVulnerabilitiesFinder do
subject { described_class.new(pipeline: pipeline).execute }
it 'returns non-dismissed vulnerabilities' do
expect(subject.occurrences.count).to eq(cs_count + dast_count + ds_count + sast_count - feedback.count)
expect(subject.occurrences.map(&:project_fingerprint)).not_to include(*feedback.map(&:project_fingerprint))
expect(subject.findings.count).to eq(cs_count + dast_count + ds_count + sast_count - feedback.count)
expect(subject.findings.map(&:project_fingerprint)).not_to include(*feedback.map(&:project_fingerprint))
end
end
......@@ -157,8 +157,8 @@ RSpec.describe Security::PipelineVulnerabilitiesFinder do
subject { described_class.new(pipeline: pipeline, params: { report_type: %w[dependency_scanning], scope: 'dismissed' } ).execute }
it 'returns non-dismissed vulnerabilities' do
expect(subject.occurrences.count).to eq(ds_count - 1)
expect(subject.occurrences.map(&:project_fingerprint)).not_to include(ds_occurrence.project_fingerprint)
expect(subject.findings.count).to eq(ds_count - 1)
expect(subject.findings.map(&:project_fingerprint)).not_to include(ds_finding.project_fingerprint)
end
end
......@@ -166,7 +166,7 @@ RSpec.describe Security::PipelineVulnerabilitiesFinder do
let(:params) { { report_type: %w[sast dast container_scanning dependency_scanning], scope: 'all' } }
it 'returns all vulnerabilities' do
expect(subject.occurrences.count).to eq(cs_count + dast_count + ds_count + sast_count)
expect(subject.findings.count).to eq(cs_count + dast_count + ds_count + sast_count)
end
end
end
......@@ -176,7 +176,7 @@ RSpec.describe Security::PipelineVulnerabilitiesFinder do
subject { described_class.new(pipeline: pipeline).execute }
it 'returns all vulnerability severity levels' do
expect(subject.occurrences.map(&:severity).uniq).to match_array(%w[unknown low medium high critical info])
expect(subject.findings.map(&:severity).uniq).to match_array(%w[unknown low medium high critical info])
end
end
......@@ -184,7 +184,7 @@ RSpec.describe Security::PipelineVulnerabilitiesFinder do
subject { described_class.new(pipeline: pipeline, params: { severity: 'low' } ).execute }
it 'returns only low-severity vulnerabilities' do
expect(subject.occurrences.map(&:severity).uniq).to match_array(%w[low])
expect(subject.findings.map(&:severity).uniq).to match_array(%w[low])
end
end
end
......@@ -194,7 +194,7 @@ RSpec.describe Security::PipelineVulnerabilitiesFinder do
subject { described_class.new(pipeline: pipeline).execute }
it 'returns all vulnerability confidence levels' do
expect(subject.occurrences.map(&:confidence).uniq).to match_array %w[unknown low medium high]
expect(subject.findings.map(&:confidence).uniq).to match_array %w[unknown low medium high]
end
end
......@@ -202,7 +202,7 @@ RSpec.describe Security::PipelineVulnerabilitiesFinder do
subject { described_class.new(pipeline: pipeline, params: { confidence: 'medium' } ).execute }
it 'returns only medium-confidence vulnerabilities' do
expect(subject.occurrences.map(&:confidence).uniq).to match_array(%w[medium])
expect(subject.findings.map(&:confidence).uniq).to match_array(%w[medium])
end
end
end
......@@ -212,7 +212,7 @@ RSpec.describe Security::PipelineVulnerabilitiesFinder do
subject { described_class.new(pipeline: pipeline).execute }
it 'returns all vulnerabilities with all scanners available' do
expect(subject.occurrences.map(&:scanner).map(&:external_id).uniq).to match_array %w[bandit bundler_audit find_sec_bugs flawfinder gemnasium klar zaproxy]
expect(subject.findings.map(&:scanner).map(&:external_id).uniq).to match_array %w[bandit bundler_audit find_sec_bugs flawfinder gemnasium klar zaproxy]
end
end
......@@ -220,7 +220,7 @@ RSpec.describe Security::PipelineVulnerabilitiesFinder do
subject { described_class.new(pipeline: pipeline, params: { scanner: 'zaproxy' } ).execute }
it 'returns only vulnerabilities with selected scanner external id' do
expect(subject.occurrences.map(&:scanner).map(&:external_id).uniq).to match_array(%w[zaproxy])
expect(subject.findings.map(&:scanner).map(&:external_id).uniq).to match_array(%w[zaproxy])
end
end
end
......@@ -230,10 +230,10 @@ RSpec.describe Security::PipelineVulnerabilitiesFinder do
let(:params) { { report_type: %w[sast dast container_scanning dependency_scanning], scanner: %w[bandit bundler_audit find_sec_bugs flawfinder gemnasium klar zaproxy], scope: 'all' } }
it 'filters by all params' do
expect(subject.occurrences.count).to eq(cs_count + dast_count + ds_count + sast_count)
expect(subject.occurrences.map(&:scanner).map(&:external_id).uniq).to match_array %w[bandit bundler_audit find_sec_bugs flawfinder gemnasium klar zaproxy]
expect(subject.occurrences.map(&:confidence).uniq).to match_array(%w[unknown low medium high])
expect(subject.occurrences.map(&:severity).uniq).to match_array(%w[unknown low medium high critical info])
expect(subject.findings.count).to eq(cs_count + dast_count + ds_count + sast_count)
expect(subject.findings.map(&:scanner).map(&:external_id).uniq).to match_array %w[bandit bundler_audit find_sec_bugs flawfinder gemnasium klar zaproxy]
expect(subject.findings.map(&:confidence).uniq).to match_array(%w[unknown low medium high])
expect(subject.findings.map(&:severity).uniq).to match_array(%w[unknown low medium high critical info])
end
end
......@@ -242,7 +242,7 @@ RSpec.describe Security::PipelineVulnerabilitiesFinder do
it 'did not find anything' do
expect(subject.created_at).to be_nil
expect(subject.occurrences).to be_empty
expect(subject.findings).to be_empty
end
end
end
......@@ -251,7 +251,7 @@ RSpec.describe Security::PipelineVulnerabilitiesFinder do
subject { described_class.new(pipeline: pipeline).execute }
it 'returns all report_types' do
expect(subject.occurrences.count).to eq(cs_count + dast_count + ds_count + sast_count)
expect(subject.findings.count).to eq(cs_count + dast_count + ds_count + sast_count)
end
end
......@@ -292,37 +292,37 @@ RSpec.describe Security::PipelineVulnerabilitiesFinder do
subject { described_class.new(pipeline: pipeline, params: { report_type: %w[sast], scope: 'all' }).execute }
it 'assigns vulnerability records to findings providing them with computed state' do
confirmed = subject.occurrences.find { |f| f.project_fingerprint == confirmed_fingerprint }
resolved = subject.occurrences.find { |f| f.project_fingerprint == resolved_fingerprint }
dismissed = subject.occurrences.find { |f| f.project_fingerprint == dismissed_fingerprint }
confirmed = subject.findings.find { |f| f.project_fingerprint == confirmed_fingerprint }
resolved = subject.findings.find { |f| f.project_fingerprint == resolved_fingerprint }
dismissed = subject.findings.find { |f| f.project_fingerprint == dismissed_fingerprint }
expect(confirmed.state).to eq 'confirmed'
expect(resolved.state).to eq 'resolved'
expect(dismissed.state).to eq 'dismissed'
expect(subject.occurrences - [confirmed, resolved, dismissed]).to all(have_attributes(state: 'detected'))
expect(subject.findings - [confirmed, resolved, dismissed]).to all(have_attributes(state: 'detected'))
end
end
context 'when being tested for sort stability' do
let(:params) { { report_type: %w[sast] } }
it 'maintains the order of the occurrences having the same severity and confidence' do
it 'maintains the order of the findings having the same severity and confidence' do
select_proc = proc { |o| o.severity == 'medium' && o.confidence == 'high' }
report_occurrences = pipeline.security_reports.reports['sast'].occurrences.select(&select_proc)
report_findings = pipeline.security_reports.reports['sast'].findings.select(&select_proc)
found_occurrences = subject.occurrences.select(&select_proc)
found_findings = subject.findings.select(&select_proc)
found_occurrences.each_with_index do |found, i|
expect(found.metadata['cve']).to eq(report_occurrences[i].compare_key)
found_findings.each_with_index do |found, i|
expect(found.metadata['cve']).to eq(report_findings[i].compare_key)
end
end
end
context 'when scanner is not provided in the report occurrences' do
context 'when scanner is not provided in the report findings' do
let!(:artifact_sast) { create(:ee_ci_job_artifact, :sast_with_missing_scanner, job: build_sast, project: project) }
it 'sets empty scanner' do
sast_scanners = subject.occurrences.select(&:sast?).map(&:scanner)
sast_scanners = subject.findings.select(&:sast?).map(&:scanner)
expect(sast_scanners).to all(have_attributes(project_id: nil, external_id: nil, name: nil))
end
......
......@@ -4,7 +4,7 @@ require 'spec_helper'
RSpec.describe GitlabSchema.types['VulnerabilitySeveritiesCount'] do
let_it_be(:fields) do
::Vulnerabilities::Occurrence::SEVERITY_LEVELS.keys
::Vulnerabilities::Finding::SEVERITY_LEVELS.keys
end
it { expect(described_class).to have_graphql_fields(fields) }
......
......@@ -13,9 +13,9 @@ RSpec.describe Gitlab::BackgroundMigration::UpdateVulnerabilitiesFromDismissalFe
let(:feedback) { table(:vulnerability_feedback) }
let(:namespaces) { table(:namespaces)}
let(:severity) { Vulnerabilities::Occurrence::SEVERITY_LEVELS[:unknown] }
let(:confidence) { Vulnerabilities::Occurrence::CONFIDENCE_LEVELS[:medium] }
let(:report_type) { Vulnerabilities::Occurrence::REPORT_TYPES[:sast] }
let(:severity) { Vulnerabilities::Finding::SEVERITY_LEVELS[:unknown] }
let(:confidence) { Vulnerabilities::Finding::CONFIDENCE_LEVELS[:medium] }
let(:report_type) { Vulnerabilities::Finding::REPORT_TYPES[:sast] }
let!(:user) { users.create!(email: 'author@example.com', username: 'author', projects_limit: 10) }
let!(:project) { projects.create!(namespace_id: namespace.id, name: 'gitlab', path: 'gitlab') }
......
......@@ -12,9 +12,9 @@ RSpec.describe Gitlab::BackgroundMigration::UpdateVulnerabilitiesToDismissed, :m
let(:identifiers) { table(:vulnerability_identifiers) }
let(:feedback) { table(:vulnerability_feedback) }
let(:severity) { Vulnerabilities::Occurrence::SEVERITY_LEVELS[:unknown] }
let(:confidence) { Vulnerabilities::Occurrence::CONFIDENCE_LEVELS[:medium] }
let(:report_type) { Vulnerabilities::Occurrence::REPORT_TYPES[:sast] }
let(:severity) { Vulnerabilities::Finding::SEVERITY_LEVELS[:unknown] }
let(:confidence) { Vulnerabilities::Finding::CONFIDENCE_LEVELS[:medium] }
let(:report_type) { Vulnerabilities::Finding::REPORT_TYPES[:sast] }
let!(:user) { users.create!(id: 13, email: 'author@example.com', username: 'author', projects_limit: 10) }
let!(:project) { projects.create!(id: 123, namespace_id: 12, name: 'gitlab', path: 'gitlab') }
......
......@@ -18,10 +18,10 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Common do
parser.parse!(blob, report)
end
expect(report.occurrences.map(&:severity)).to include("unknown")
expect(report.occurrences.map(&:confidence)).to include("unknown")
expect(report.occurrences.map(&:severity)).not_to include("undefined")
expect(report.occurrences.map(&:confidence)).not_to include("undefined")
expect(report.findings.map(&:severity)).to include("unknown")
expect(report.findings.map(&:confidence)).to include("unknown")
expect(report.findings.map(&:severity)).not_to include("undefined")
expect(report.findings.map(&:confidence)).not_to include("undefined")
end
context 'parsing remediations' do
......@@ -101,7 +101,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Common do
raw_json[:remediations] << fix_with_cve
parser.parse!(raw_json.to_json, report)
vulnerability = report.occurrences.find { |x| x.compare_key == "CVE-1020" }
vulnerability = report.findings.find { |x| x.compare_key == "CVE-1020" }
expect(vulnerability.raw_metadata).to include fix_with_cve.to_json
end
......@@ -120,7 +120,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Common do
raw_json[:remediations] << fix_with_id
parser.parse!(raw_json.to_json, report)
vulnerability = report.occurrences.find { |x| x.compare_key == "CVE-1030" }
vulnerability = report.findings.find { |x| x.compare_key == "CVE-1030" }
expect(vulnerability.raw_metadata).to include fix_with_id.to_json
end
......@@ -148,9 +148,9 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Common do
raw_json[:remediations] << fix_with_id << fix_with_cve
parser.parse!(raw_json.to_json, report)
vulnerability_1030 = report.occurrences.find { |x| x.compare_key == "CVE-1030" }
vulnerability_1030 = report.findings.find { |x| x.compare_key == "CVE-1030" }
expect(vulnerability_1030.raw_metadata).to include fix_with_id.to_json
vulnerability_1020 = report.occurrences.find { |x| x.compare_key == "CVE-1020" }
vulnerability_1020 = report.findings.find { |x| x.compare_key == "CVE-1020" }
expect(vulnerability_1020.raw_metadata).to include fix_with_cve.to_json
end
......@@ -179,7 +179,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Common do
raw_json[:remediations] << fix_with_id << fix_with_id_2
parser.parse!(raw_json.to_json, report)
report.occurrences.map do |vulnerability|
report.findings.map do |vulnerability|
expect(vulnerability.raw_metadata).not_to include(fix_with_id.to_json)
end
end
......@@ -208,7 +208,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Common do
}
end
subject(:scanner) { report.occurrences.first.scanner }
subject(:scanner) { report.findings.first.scanner }
before do
parser.parse!(raw_json.to_json, report)
......
......@@ -18,14 +18,14 @@ RSpec.describe Gitlab::Ci::Parsers::Security::ContainerScanning do
let(:artifact) { create(:ee_ci_job_artifact, :container_scanning) }
let(:image) { 'registry.gitlab.com/gitlab-org/security-products/dast/webgoat-8.0@sha256:bc09fe2e0721dfaeee79364115aeedf2174cce0947b9ae5fe7c33312ee019a4e' }
it "parses all identifiers and occurrences for unapproved vulnerabilities" do
expect(report.occurrences.length).to eq(8)
it "parses all identifiers and findings for unapproved vulnerabilities" do
expect(report.findings.length).to eq(8)
expect(report.identifiers.length).to eq(8)
expect(report.scanners.length).to eq(1)
end
it 'generates expected location' do
location = report.occurrences.first.location
location = report.findings.first.location
expect(location).to be_a(::Gitlab::Ci::Reports::Security::Locations::ContainerScanning)
expect(location).to have_attributes(
......@@ -37,11 +37,11 @@ RSpec.describe Gitlab::Ci::Parsers::Security::ContainerScanning do
end
it "generates expected metadata_version" do
expect(report.occurrences.first.metadata_version).to eq('2.3')
expect(report.findings.first.metadata_version).to eq('2.3')
end
it "adds report image's name to raw_metadata" do
expect(Gitlab::Json.parse(report.occurrences.first.raw_metadata).dig('location', 'image')).to eq(image)
expect(Gitlab::Json.parse(report.findings.first.raw_metadata).dig('location', 'image')).to eq(image)
end
end
end
......@@ -16,13 +16,13 @@ RSpec.describe Gitlab::Ci::Parsers::Security::CoverageFuzzing do
end
end
it 'parses all identifiers and occurrences' do
expect(report.occurrences.length).to eq(1)
it 'parses all identifiers and findings' do
expect(report.findings.length).to eq(1)
expect(report.scanners.length).to eq(1)
end
it 'generates expected location' do
location = report.occurrences.first.location
location = report.findings.first.location
expect(location).to be_a(::Gitlab::Ci::Reports::Security::Locations::CoverageFuzzing)
expect(location).to have_attributes(
......
......@@ -37,15 +37,15 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Dast do
end
end
it 'parses all identifiers, occurrences and scanned resources' do
expect(report.occurrences.length).to eq(occurrence_count)
it 'parses all identifiers, findings and scanned resources' do
expect(report.findings.length).to eq(occurrence_count)
expect(report.identifiers.length).to eq(identifier_count)
expect(report.scanners.length).to eq(scanner_count)
expect(report.scanned_resources.length).to eq(scanned_resources_count)
end
it 'generates expected location' do
location = report.occurrences.last.location
location = report.findings.last.location
expect(location).to be_a(::Gitlab::Ci::Reports::Security::Locations::Dast)
expect(location).to have_attributes(
......@@ -64,7 +64,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Dast do
with_them do
it 'saves properly occurrence' do
occurrence = report.occurrences.last
occurrence = report.findings.last
expect(occurrence.public_send(attribute)).to eq(value)
end
......
......@@ -27,14 +27,14 @@ RSpec.describe Gitlab::Ci::Parsers::Security::DependencyScanning do
end
end
it "parses all identifiers and occurrences" do
expect(report.occurrences.length).to eq(occurrence_count)
it "parses all identifiers and findings" do
expect(report.findings.length).to eq(occurrence_count)
expect(report.identifiers.length).to eq(identifier_count)
expect(report.scanners.length).to eq(scanner_count)
end
it 'generates expected location' do
location = report.occurrences.first.location
location = report.findings.first.location
expect(location).to be_a(::Gitlab::Ci::Reports::Security::Locations::DependencyScanning)
expect(location).to have_attributes(
......@@ -45,7 +45,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::DependencyScanning do
end
it "generates expected metadata_version" do
expect(report.occurrences.first.metadata_version).to eq(version)
expect(report.findings.first.metadata_version).to eq(version)
end
end
......@@ -79,7 +79,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::DependencyScanning do
end
it "generates occurrence with expected remediation" do
occurrence = report.occurrences.last
occurrence = report.findings.last
raw_metadata = Gitlab::Json.parse!(occurrence.raw_metadata)
expect(occurrence.name).to eq("Authentication bypass via incorrect DOM traversal and canonicalization in saml2-js")
......
......@@ -22,14 +22,14 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Sast do
end
end
it "parses all identifiers and occurrences" do
expect(report.occurrences.length).to eq(33)
it "parses all identifiers and findings" do
expect(report.findings.length).to eq(33)
expect(report.identifiers.length).to eq(17)
expect(report.scanners.length).to eq(3)
end
it 'generates expected location' do
location = report.occurrences.first.location
location = report.findings.first.location
expect(location).to be_a(::Gitlab::Ci::Reports::Security::Locations::Sast)
expect(location).to have_attributes(
......@@ -42,7 +42,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Sast do
end
it "generates expected metadata_version" do
expect(report.occurrences.first.metadata_version).to eq('1.2')
expect(report.findings.first.metadata_version).to eq('1.2')
end
end
end
......
......@@ -22,14 +22,14 @@ RSpec.describe Gitlab::Ci::Parsers::Security::SecretDetection do
end
end
it "parses all identifiers and occurrences" do
expect(report.occurrences.length).to eq(1)
it "parses all identifiers and findings" do
expect(report.findings.length).to eq(1)
expect(report.identifiers.length).to eq(1)
expect(report.scanners.length).to eq(1)
end
it 'generates expected location' do
location = report.occurrences.first.location
location = report.findings.first.location
expect(location).to be_a(::Gitlab::Ci::Reports::Security::Locations::SecretDetection)
expect(location).to have_attributes(
......@@ -42,7 +42,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::SecretDetection do
end
it "generates expected metadata_version" do
expect(report.occurrences.first.metadata_version).to eq('3.0')
expect(report.findings.first.metadata_version).to eq('3.0')
end
end
end
......
......@@ -3,10 +3,10 @@
require 'spec_helper'
RSpec.describe Gitlab::Ci::Reports::Security::AggregatedReport do
subject { described_class.new(reports, occurrences) }
subject { described_class.new(reports, findings) }
let(:reports) { build_list(:ci_reports_security_report, 1) }
let(:occurrences) { build_list(:ci_reports_security_occurrence, 1) }
let(:findings) { build_list(:ci_reports_security_finding, 1) }
describe '#created_at' do
context 'no reports' do
......
......@@ -62,7 +62,7 @@ RSpec.describe Gitlab::Ci::Reports::Security::Occurrence do
end
describe "delegation" do
subject { create(:ci_reports_security_occurrence) }
subject { create(:ci_reports_security_finding) }
%i[file_path start_line end_line].each do |attribute|
it "delegates attribute #{attribute} to location" do
......@@ -72,7 +72,7 @@ RSpec.describe Gitlab::Ci::Reports::Security::Occurrence do
end
describe '#to_hash' do
let(:occurrence) { create(:ci_reports_security_occurrence) }
let(:occurrence) { create(:ci_reports_security_finding) }
subject { occurrence.to_hash }
......@@ -98,7 +98,7 @@ RSpec.describe Gitlab::Ci::Reports::Security::Occurrence do
let(:primary_identifier) { create(:ci_reports_security_identifier) }
let(:other_identifier) { create(:ci_reports_security_identifier) }
let(:occurrence) { create(:ci_reports_security_occurrence, identifiers: [primary_identifier, other_identifier]) }
let(:occurrence) { create(:ci_reports_security_finding, identifiers: [primary_identifier, other_identifier]) }
subject { occurrence.primary_identifier }
......@@ -111,7 +111,7 @@ RSpec.describe Gitlab::Ci::Reports::Security::Occurrence do
let(:old_location) { create(:ci_reports_security_locations_sast, file_path: 'old_file.rb') }
let(:new_location) { create(:ci_reports_security_locations_sast, file_path: 'new_file.rb') }
let(:occurrence) { create(:ci_reports_security_occurrence, location: old_location) }
let(:occurrence) { create(:ci_reports_security_finding, location: old_location) }
subject { occurrence.update_location(new_location) }
......
......@@ -41,13 +41,13 @@ RSpec.describe Gitlab::Ci::Reports::Security::Report do
end
end
describe '#add_occurrence' do
let(:occurrence) { create(:ci_reports_security_occurrence) }
describe '#add_finding' do
let(:finding) { create(:ci_reports_security_finding) }
it 'enriches given occurrence and stores it in the collection' do
report.add_occurrence(occurrence)
it 'enriches given finding and stores it in the collection' do
report.finding(finding)
expect(report.occurrences).to eq([occurrence])
expect(report.findings).to eq([finding])
end
end
......@@ -55,7 +55,7 @@ RSpec.describe Gitlab::Ci::Reports::Security::Report do
let(:report) do
create(
:ci_reports_security_report,
occurrences: [create(:ci_reports_security_occurrence)],
findings: [create(:ci_reports_security_finding)],
scanners: [create(:ci_reports_security_scanner)],
identifiers: [create(:ci_reports_security_identifier)]
)
......@@ -67,7 +67,7 @@ RSpec.describe Gitlab::Ci::Reports::Security::Report do
expect(clone.type).to eq(report.type)
expect(clone.commit_sha).to eq(report.commit_sha)
expect(clone.created_at).to eq(report.created_at)
expect(clone.occurrences).to eq([])
expect(clone.findings).to eq([])
expect(clone.scanners).to eq({})
expect(clone.identifiers).to eq({})
end
......@@ -77,7 +77,7 @@ RSpec.describe Gitlab::Ci::Reports::Security::Report do
let(:report) do
create(
:ci_reports_security_report,
occurrences: [create(:ci_reports_security_occurrence)],
findings: [create(:ci_reports_security_finding)],
scanners: [create(:ci_reports_security_scanner)],
identifiers: [create(:ci_reports_security_identifier)]
)
......@@ -85,7 +85,7 @@ RSpec.describe Gitlab::Ci::Reports::Security::Report do
let(:other_report) do
create(
:ci_reports_security_report,
occurrences: [create(:ci_reports_security_occurrence, compare_key: 'other_occurrence')],
findings: [create(:ci_reports_security_finding, compare_key: 'other_finding')],
scanners: [create(:ci_reports_security_scanner, external_id: 'other_scanner', name: 'Other Scanner')],
identifiers: [create(:ci_reports_security_identifier, external_id: 'other_id', name: 'other_scanner')]
)
......@@ -96,7 +96,7 @@ RSpec.describe Gitlab::Ci::Reports::Security::Report do
end
it 'replaces report contents with other reports contents' do
expect(report.occurrences).to eq(other_report.occurrences)
expect(report.findings).to eq(other_report.findings)
expect(report.scanners).to eq(other_report.scanners)
expect(report.identifiers).to eq(other_report.identifiers)
end
......@@ -128,10 +128,10 @@ RSpec.describe Gitlab::Ci::Reports::Security::Report do
context "when the sast report has an unsafe vulnerability" do
where(severity: %w[unknown Unknown high High critical Critical])
with_them do
let(:occurrence) { build(:ci_reports_security_occurrence, severity: severity) }
let(:finding) { build(:ci_reports_security_finding, severity: severity) }
before do
subject.add_occurrence(occurrence)
subject.finding(finding)
end
it { expect(subject.unsafe_severity?).to be(true) }
......@@ -142,10 +142,10 @@ RSpec.describe Gitlab::Ci::Reports::Security::Report do
context "when the sast report has a medium to low severity vulnerability" do
where(severity: %w[medium Medium low Low])
with_them do
let(:occurrence) { build(:ci_reports_security_occurrence, severity: severity) }
let(:finding) { build(:ci_reports_security_finding, severity: severity) }
before do
subject.add_occurrence(occurrence)
subject.finding(finding)
end
it { expect(subject.unsafe_severity?).to be(false) }
......@@ -154,10 +154,10 @@ RSpec.describe Gitlab::Ci::Reports::Security::Report do
end
context "when the sast report has a vulnerability with a `nil` severity" do
let(:occurrence) { build(:ci_reports_security_occurrence, severity: nil) }
let(:finding) { build(:ci_reports_security_finding, severity: nil) }
before do
subject.add_occurrence(occurrence)
subject.finding(finding)
end
it { expect(subject.unsafe_severity?).to be(false) }
......@@ -165,10 +165,10 @@ RSpec.describe Gitlab::Ci::Reports::Security::Report do
end
context "when the sast report has a vulnerability with a blank severity" do
let(:occurrence) { build(:ci_reports_security_occurrence, severity: '') }
let(:finding) { build(:ci_reports_security_finding, severity: '') }
before do
subject.add_occurrence(occurrence)
subject.finding(finding)
end
it { expect(subject.unsafe_severity?).to be(false) }
......
......@@ -41,13 +41,13 @@ RSpec.describe Gitlab::Ci::Reports::Security::Reports do
describe "#violates_default_policy?" do
subject { described_class.new(commit_sha) }
let(:low_severity) { build(:ci_reports_security_occurrence, severity: 'low') }
let(:high_severity) { build(:ci_reports_security_occurrence, severity: 'high') }
let(:low_severity) { build(:ci_reports_security_finding, severity: 'low') }
let(:high_severity) { build(:ci_reports_security_finding, severity: 'high') }
context "when a report has a high severity vulnerability" do
before do
subject.get_report('sast', artifact).add_occurrence(high_severity)
subject.get_report('dependency_scanning', artifact).add_occurrence(low_severity)
subject.get_report('sast', artifact).finding(high_severity)
subject.get_report('dependency_scanning', artifact).finding(low_severity)
end
it { expect(subject.violates_default_policy?).to be(true) }
......@@ -55,8 +55,8 @@ RSpec.describe Gitlab::Ci::Reports::Security::Reports do
context "when none of the reports have a high severity vulnerability" do
before do
subject.get_report('sast', artifact).add_occurrence(low_severity)
subject.get_report('dependency_scanning', artifact).add_occurrence(low_severity)
subject.get_report('sast', artifact).finding(low_severity)
subject.get_report('dependency_scanning', artifact).finding(low_severity)
end
it { expect(subject.violates_default_policy?).to be(false) }
......
......@@ -5,11 +5,11 @@ require 'spec_helper'
RSpec.describe Gitlab::Ci::Reports::Security::VulnerabilityReportsComparer do
let(:identifier) { build(:vulnerabilities_identifier) }
let(:base_vulnerability) { build(:vulnerabilities_occurrence, report_type: :sast, identifiers: [identifier], location_fingerprint: '123', confidence: Vulnerabilities::Occurrence::CONFIDENCE_LEVELS[:high], severity: Vulnerabilities::Occurrence::SEVERITY_LEVELS[:critical]) }
let(:base_report) { build(:ci_reports_security_aggregated_reports, occurrences: [base_vulnerability])}
let(:base_vulnerability) { build(:vulnerabilities_finding, report_type: :sast, identifiers: [identifier], location_fingerprint: '123', confidence: Vulnerabilities::Finding::CONFIDENCE_LEVELS[:high], severity: Vulnerabilities::Finding::SEVERITY_LEVELS[:critical]) }
let(:base_report) { build(:ci_reports_security_aggregated_reports, findings: [base_vulnerability])}
let(:head_vulnerability) { build(:vulnerabilities_occurrence, report_type: :sast, identifiers: [identifier], location_fingerprint: '123', confidence: Vulnerabilities::Occurrence::CONFIDENCE_LEVELS[:high], severity: Vulnerabilities::Occurrence::SEVERITY_LEVELS[:critical]) }
let(:head_report) { build(:ci_reports_security_aggregated_reports, occurrences: [head_vulnerability])}
let(:head_vulnerability) { build(:vulnerabilities_finding, report_type: :sast, identifiers: [identifier], location_fingerprint: '123', confidence: Vulnerabilities::Finding::CONFIDENCE_LEVELS[:high], severity: Vulnerabilities::Finding::SEVERITY_LEVELS[:critical]) }
let(:head_report) { build(:ci_reports_security_aggregated_reports, findings: [head_vulnerability])}
before do
allow(base_vulnerability).to receive(:location).and_return({})
......@@ -20,7 +20,7 @@ RSpec.describe Gitlab::Ci::Reports::Security::VulnerabilityReportsComparer do
describe '#base_report_out_of_date' do
context 'no base report' do
let(:base_report) { build(:ci_reports_security_aggregated_reports, reports: [], occurrences: [])}
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
......@@ -53,8 +53,8 @@ RSpec.describe Gitlab::Ci::Reports::Security::VulnerabilityReportsComparer do
end
context 'when comparing reports with different fingerprints' do
let(:base_vulnerability) { build(:vulnerabilities_occurrence, report_type: :sast, identifiers: [identifier], location_fingerprint: 'A') }
let(:head_vulnerability) { build(:vulnerabilities_occurrence, report_type: :sast, identifiers: [identifier], location_fingerprint: 'B') }
let(:base_vulnerability) { build(:vulnerabilities_finding, report_type: :sast, identifiers: [identifier], location_fingerprint: 'A') }
let(:head_vulnerability) { build(:vulnerabilities_finding, report_type: :sast, identifiers: [identifier], location_fingerprint: 'B') }
it 'does not find any overlap' do
expect(subject.existing).to eq([])
......@@ -62,10 +62,10 @@ RSpec.describe Gitlab::Ci::Reports::Security::VulnerabilityReportsComparer do
end
context 'new vulnerabilities' do
let(:vuln) { build(:vulnerabilities_occurrence, report_type: :sast, identifiers: [identifier], location_fingerprint: '888', confidence: Vulnerabilities::Occurrence::CONFIDENCE_LEVELS[:medium]) }
let(:low_vuln) { build(:vulnerabilities_occurrence, report_type: :sast, identifiers: [identifier], location_fingerprint: '888', confidence: Vulnerabilities::Occurrence::CONFIDENCE_LEVELS[:low]) }
let(:base_report) { build(:ci_reports_security_aggregated_reports, occurrences: [base_vulnerability, vuln])}
let(:head_report) { build(:ci_reports_security_aggregated_reports, occurrences: [head_vulnerability, vuln, low_vuln])}
let(:vuln) { build(:vulnerabilities_finding, report_type: :sast, identifiers: [identifier], location_fingerprint: '888', confidence: Vulnerabilities::Finding::CONFIDENCE_LEVELS[:medium]) }
let(:low_vuln) { build(:vulnerabilities_finding, report_type: :sast, identifiers: [identifier], location_fingerprint: '888', confidence: Vulnerabilities::Finding::CONFIDENCE_LEVELS[:low]) }
let(:base_report) { build(:ci_reports_security_aggregated_reports, findings: [base_vulnerability, vuln])}
let(:head_report) { build(:ci_reports_security_aggregated_reports, findings: [head_vulnerability, vuln, low_vuln])}
it 'does not change order' do
expect(subject.existing).to eq([head_vulnerability, vuln])
......@@ -75,11 +75,11 @@ RSpec.describe Gitlab::Ci::Reports::Security::VulnerabilityReportsComparer do
end
describe '#added' do
let(:vuln) { build(:vulnerabilities_occurrence, report_type: :sast, identifiers: [identifier], location_fingerprint: '888', confidence: Vulnerabilities::Occurrence::CONFIDENCE_LEVELS[:high], severity: Vulnerabilities::Occurrence::SEVERITY_LEVELS[:critical]) }
let(:low_vuln) { build(:vulnerabilities_occurrence, report_type: :sast, identifiers: [identifier], location_fingerprint: '888', confidence: Vulnerabilities::Occurrence::CONFIDENCE_LEVELS[:high], severity: Vulnerabilities::Occurrence::SEVERITY_LEVELS[:low]) }
let(:vuln) { build(:vulnerabilities_finding, report_type: :sast, identifiers: [identifier], location_fingerprint: '888', confidence: Vulnerabilities::Finding::CONFIDENCE_LEVELS[:high], severity: Vulnerabilities::Finding::SEVERITY_LEVELS[:critical]) }
let(:low_vuln) { build(:vulnerabilities_finding, report_type: :sast, identifiers: [identifier], location_fingerprint: '888', confidence: Vulnerabilities::Finding::CONFIDENCE_LEVELS[:high], severity: Vulnerabilities::Finding::SEVERITY_LEVELS[:low]) }
context 'with new vulnerability' do
let(:head_report) { build(:ci_reports_security_aggregated_reports, occurrences: [head_vulnerability, vuln])}
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])
......@@ -87,9 +87,9 @@ RSpec.describe Gitlab::Ci::Reports::Security::VulnerabilityReportsComparer do
end
context 'when comparing reports with different fingerprints' do
let(:base_vulnerability) { build(:vulnerabilities_occurrence, report_type: :sast, identifiers: [identifier], location_fingerprint: 'A') }
let(:head_vulnerability) { build(:vulnerabilities_occurrence, report_type: :sast, identifiers: [identifier], location_fingerprint: 'B') }
let(:head_report) { build(:ci_reports_security_aggregated_reports, occurrences: [head_vulnerability, vuln])}
let(:base_vulnerability) { build(:vulnerabilities_finding, report_type: :sast, identifiers: [identifier], location_fingerprint: 'A') }
let(:head_vulnerability) { build(:vulnerabilities_finding, report_type: :sast, identifiers: [identifier], location_fingerprint: 'B') }
let(:head_report) { build(:ci_reports_security_aggregated_reports, findings: [head_vulnerability, vuln])}
it 'does not find any overlap' do
expect(subject.added).to eq([head_vulnerability, vuln])
......@@ -97,7 +97,7 @@ RSpec.describe Gitlab::Ci::Reports::Security::VulnerabilityReportsComparer do
end
context 'order' do
let(:head_report) { build(:ci_reports_security_aggregated_reports, occurrences: [head_vulnerability, vuln, low_vuln])}
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])
......@@ -106,11 +106,11 @@ RSpec.describe Gitlab::Ci::Reports::Security::VulnerabilityReportsComparer do
end
describe '#fixed' do
let(:vuln) { build(:vulnerabilities_occurrence, report_type: :sast, identifiers: [identifier], location_fingerprint: '888') }
let(:medium_vuln) { build(:vulnerabilities_occurrence, report_type: :sast, identifiers: [identifier], location_fingerprint: '888', confidence: Vulnerabilities::Occurrence::CONFIDENCE_LEVELS[:high], severity: Vulnerabilities::Occurrence::SEVERITY_LEVELS[:medium]) }
let(:vuln) { build(:vulnerabilities_finding, report_type: :sast, identifiers: [identifier], location_fingerprint: '888') }
let(:medium_vuln) { build(:vulnerabilities_finding, report_type: :sast, identifiers: [identifier], location_fingerprint: '888', confidence: Vulnerabilities::Finding::CONFIDENCE_LEVELS[:high], severity: Vulnerabilities::Finding::SEVERITY_LEVELS[:medium]) }
context 'with fixed vulnerability' do
let(:base_report) { build(:ci_reports_security_aggregated_reports, occurrences: [base_vulnerability, vuln])}
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])
......@@ -118,9 +118,9 @@ RSpec.describe Gitlab::Ci::Reports::Security::VulnerabilityReportsComparer do
end
context 'when comparing reports with different fingerprints' do
let(:base_vulnerability) { build(:vulnerabilities_occurrence, report_type: :sast, identifiers: [identifier], location_fingerprint: 'A') }
let(:head_vulnerability) { build(:vulnerabilities_occurrence, report_type: :sast, identifiers: [identifier], location_fingerprint: 'B') }
let(:base_report) { build(:ci_reports_security_aggregated_reports, occurrences: [base_vulnerability, vuln])}
let(:base_vulnerability) { build(:vulnerabilities_finding, report_type: :sast, identifiers: [identifier], location_fingerprint: 'A') }
let(:head_vulnerability) { build(:vulnerabilities_finding, report_type: :sast, identifiers: [identifier], location_fingerprint: 'B') }
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])
......@@ -128,7 +128,7 @@ RSpec.describe Gitlab::Ci::Reports::Security::VulnerabilityReportsComparer do
end
context 'order' do
let(:base_report) { build(:ci_reports_security_aggregated_reports, occurrences: [vuln, medium_vuln, base_vulnerability])}
let(:base_report) { build(:ci_reports_security_aggregated_reports, findings: [vuln, medium_vuln, base_vulnerability])}
it 'does not change' do
expect(subject.fixed).to eq([vuln, medium_vuln])
......@@ -137,7 +137,7 @@ RSpec.describe Gitlab::Ci::Reports::Security::VulnerabilityReportsComparer do
end
describe 'with empty vulnerabilities' do
let(:empty_report) { build(:ci_reports_security_aggregated_reports, reports: [], occurrences: [])}
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(empty_report, empty_report, head_security_scans: [])
......
......@@ -16,9 +16,9 @@ RSpec.describe MigrateVulnerabilityDismissalFeedback, :migration, :sidekiq do
let(:vulnerabilities) { table(:vulnerabilities) }
let(:dismissed_state) { Gitlab::BackgroundMigration::UpdateVulnerabilitiesFromDismissalFeedback::VULNERABILITY_DISMISSED_STATE }
let(:severity) { Vulnerabilities::Occurrence::SEVERITY_LEVELS[:unknown] }
let(:confidence) { Vulnerabilities::Occurrence::CONFIDENCE_LEVELS[:medium] }
let(:report_type) { Vulnerabilities::Occurrence::REPORT_TYPES[:sast] }
let(:severity) { Vulnerabilities::Finding::SEVERITY_LEVELS[:unknown] }
let(:confidence) { Vulnerabilities::Finding::CONFIDENCE_LEVELS[:medium] }
let(:report_type) { Vulnerabilities::Finding::REPORT_TYPES[:sast] }
before do
stub_const("#{described_class.name}::BATCH_SIZE", 1)
......
......@@ -16,9 +16,9 @@ RSpec.describe MigrateVulnerabilityDismissals, :migration, :sidekiq do
let(:vulnerabilities) { table(:vulnerabilities) }
let(:detected_state) { Gitlab::BackgroundMigration::UpdateVulnerabilitiesToDismissed::VULNERABILITY_DETECTED }
let(:severity) { Vulnerabilities::Occurrence::SEVERITY_LEVELS[:unknown] }
let(:confidence) { Vulnerabilities::Occurrence::CONFIDENCE_LEVELS[:medium] }
let(:report_type) { Vulnerabilities::Occurrence::REPORT_TYPES[:sast] }
let(:severity) { Vulnerabilities::Finding::SEVERITY_LEVELS[:unknown] }
let(:confidence) { Vulnerabilities::Finding::CONFIDENCE_LEVELS[:medium] }
let(:report_type) { Vulnerabilities::Finding::REPORT_TYPES[:sast] }
before do
stub_const("#{described_class.name}::BATCH_SIZE", 1)
......
......@@ -172,7 +172,7 @@ RSpec.describe Ci::Build do
it 'parses blobs and add the results to the report' do
subject
expect(security_reports.get_report('sast', artifact).occurrences.size).to eq(33)
expect(security_reports.get_report('sast', artifact).findings.size).to eq(33)
end
it 'adds the created date to the report' do
......@@ -191,10 +191,10 @@ RSpec.describe Ci::Build do
it 'parses blobs and adds the results to the reports' do
subject
expect(security_reports.get_report('sast', sast_artifact).occurrences.size).to eq(33)
expect(security_reports.get_report('dependency_scanning', ds_artifact).occurrences.size).to eq(4)
expect(security_reports.get_report('container_scanning', cs_artifact).occurrences.size).to eq(8)
expect(security_reports.get_report('dast', dast_artifact).occurrences.size).to eq(20)
expect(security_reports.get_report('sast', sast_artifact).findings.size).to eq(33)
expect(security_reports.get_report('dependency_scanning', ds_artifact).findings.size).to eq(4)
expect(security_reports.get_report('container_scanning', cs_artifact).findings.size).to eq(8)
expect(security_reports.get_report('dast', dast_artifact).findings.size).to eq(20)
end
end
......
......@@ -14,7 +14,7 @@ RSpec.describe Ci::Pipeline do
it { is_expected.to have_many(:security_scans).through(:builds).class_name('Security::Scan') }
it { is_expected.to have_many(:downstream_bridges) }
it { is_expected.to have_many(:vulnerability_findings).through(:vulnerabilities_finding_pipelines).class_name('Vulnerabilities::Occurrence') }
it { is_expected.to have_many(:vulnerability_findings).through(:vulnerabilities_finding_pipelines).class_name('Vulnerabilities::Finding') }
it { is_expected.to have_many(:vulnerabilities_finding_pipelines).class_name('Vulnerabilities::FindingPipeline') }
describe '.failure_reasons' do
......@@ -53,8 +53,8 @@ RSpec.describe Ci::Pipeline do
let!(:pipeline_3) { create(:ci_pipeline, project: project) }
before do
create(:vulnerabilities_occurrence, pipelines: [pipeline_1], project: pipeline.project)
create(:vulnerabilities_occurrence, pipelines: [pipeline_2], project: pipeline.project)
create(:vulnerabilities_finding, pipelines: [pipeline_1], project: pipeline.project)
create(:vulnerabilities_finding, pipelines: [pipeline_2], project: pipeline.project)
end
it "returns pipeline with vulnerabilities" do
......@@ -158,18 +158,18 @@ RSpec.describe Ci::Pipeline do
expect(subject.reports.keys).to contain_exactly('sast', 'dependency_scanning', 'container_scanning')
# for each of report categories, we have merged 2 reports with the same data (fixture)
expect(subject.get_report('sast', sast1_artifact).occurrences.size).to eq(33)
expect(subject.get_report('dependency_scanning', ds1_artifact).occurrences.size).to eq(4)
expect(subject.get_report('container_scanning', cs1_artifact).occurrences.size).to eq(8)
expect(subject.get_report('sast', sast1_artifact).findings.size).to eq(33)
expect(subject.get_report('dependency_scanning', ds1_artifact).findings.size).to eq(4)
expect(subject.get_report('container_scanning', cs1_artifact).findings.size).to eq(8)
end
context 'when builds are retried' do
let(:build_sast_1) { create(:ci_build, :retried, name: 'sast_1', pipeline: pipeline, project: project) }
it 'does not take retried builds into account' do
expect(subject.get_report('sast', sast1_artifact).occurrences.size).to eq(33)
expect(subject.get_report('dependency_scanning', ds1_artifact).occurrences.size).to eq(4)
expect(subject.get_report('container_scanning', cs1_artifact).occurrences.size).to eq(8)
expect(subject.get_report('sast', sast1_artifact).findings.size).to eq(33)
expect(subject.get_report('dependency_scanning', ds1_artifact).findings.size).to eq(4)
expect(subject.get_report('container_scanning', cs1_artifact).findings.size).to eq(8)
end
end
end
......
......@@ -4,14 +4,14 @@ require 'spec_helper'
RSpec.describe Vulnerabilities::FindingIdentifier do
describe 'associations' do
it { is_expected.to belong_to(:finding).class_name('Vulnerabilities::Finding').with_foreign_key('occurrence_id') }
it { is_expected.to belong_to(:identifier).class_name('Vulnerabilities::Identifier') }
it { is_expected.to belong_to(:occurrence).class_name('Vulnerabilities::Occurrence') }
end
describe 'validations' do
let!(:finding_identifier) { create(:vulnerabilities_finding_identifier) }
it { is_expected.to validate_presence_of(:occurrence) }
it { is_expected.to validate_presence_of(:finding) }
it { is_expected.to validate_presence_of(:identifier) }
it { is_expected.to validate_uniqueness_of(:identifier_id).scoped_to(:occurrence_id) }
end
......
......@@ -5,13 +5,13 @@ require 'spec_helper'
RSpec.describe Vulnerabilities::FindingPipeline do
describe 'associations' do
it { is_expected.to belong_to(:pipeline).class_name('Ci::Pipeline') }
it { is_expected.to belong_to(:occurrence).class_name('Vulnerabilities::Occurrence') }
it { is_expected.to belong_to(:finding).class_name('Vulnerabilities::Finding') }
end
describe 'validations' do
let!(:finding_pipeline) { create(:vulnerabilities_finding_pipeline) }
it { is_expected.to validate_presence_of(:occurrence) }
it { is_expected.to validate_presence_of(:finding) }
it { is_expected.to validate_presence_of(:pipeline) }
it { is_expected.to validate_uniqueness_of(:pipeline_id).scoped_to(:occurrence_id) }
end
......
......@@ -2,7 +2,7 @@
require 'spec_helper'
RSpec.describe Vulnerabilities::Occurrence do
RSpec.describe Vulnerabilities::Finding do
it { is_expected.to define_enum_for(:confidence) }
it { is_expected.to define_enum_for(:report_type) }
it { is_expected.to define_enum_for(:severity) }
......@@ -13,13 +13,13 @@ RSpec.describe Vulnerabilities::Occurrence do
it { is_expected.to belong_to(:scanner).class_name('Vulnerabilities::Scanner') }
it { is_expected.to belong_to(:vulnerability).inverse_of(:findings) }
it { is_expected.to have_many(:pipelines).class_name('Ci::Pipeline') }
it { is_expected.to have_many(:finding_pipelines).class_name('Vulnerabilities::FindingPipeline') }
it { is_expected.to have_many(:finding_pipelines).class_name('Vulnerabilities::FindingPipeline').with_foreign_key('occurrence_id') }
it { is_expected.to have_many(:identifiers).class_name('Vulnerabilities::Identifier') }
it { is_expected.to have_many(:finding_identifiers).class_name('Vulnerabilities::FindingIdentifier') }
it { is_expected.to have_many(:finding_identifiers).class_name('Vulnerabilities::FindingIdentifier').with_foreign_key('occurrence_id') }
end
describe 'validations' do
let(:occurrence) { build(:vulnerabilities_occurrence) }
let(:finding) { build(:vulnerabilities_finding) }
it { is_expected.to validate_presence_of(:scanner) }
it { is_expected.to validate_presence_of(:project) }
......@@ -36,11 +36,11 @@ RSpec.describe Vulnerabilities::Occurrence do
end
context 'database uniqueness' do
let(:occurrence) { create(:vulnerabilities_occurrence) }
let(:new_occurrence) { occurrence.dup.tap { |o| o.uuid = SecureRandom.uuid } }
let(:finding) { create(:vulnerabilities_finding) }
let(:new_finding) { finding.dup.tap { |o| o.uuid = SecureRandom.uuid } }
it "when all index attributes are identical" do
expect { new_occurrence.save! }.to raise_error(ActiveRecord::RecordNotUnique)
expect { new_finding.save! }.to raise_error(ActiveRecord::RecordNotUnique)
end
describe 'when some parameters are changed' do
......@@ -55,19 +55,19 @@ RSpec.describe Vulnerabilities::Occurrence do
with_them do
it "is valid" do
expect { new_occurrence.update!({ key => create(factory_name) }) }.not_to raise_error
expect { new_finding.update!({ key => create(factory_name) }) }.not_to raise_error
end
end
end
end
context 'order' do
let!(:occurrence1) { create(:vulnerabilities_occurrence, confidence: described_class::CONFIDENCE_LEVELS[:high], severity: described_class::SEVERITY_LEVELS[:high]) }
let!(:occurrence2) { create(:vulnerabilities_occurrence, confidence: described_class::CONFIDENCE_LEVELS[:medium], severity: described_class::SEVERITY_LEVELS[:critical]) }
let!(:occurrence3) { create(:vulnerabilities_occurrence, confidence: described_class::CONFIDENCE_LEVELS[:high], severity: described_class::SEVERITY_LEVELS[:critical]) }
let!(:finding1) { create(:vulnerabilities_finding, confidence: described_class::CONFIDENCE_LEVELS[:high], severity: described_class::SEVERITY_LEVELS[:high]) }
let!(:finding2) { create(:vulnerabilities_finding, confidence: described_class::CONFIDENCE_LEVELS[:medium], severity: described_class::SEVERITY_LEVELS[:critical]) }
let!(:finding3) { create(:vulnerabilities_finding, confidence: described_class::CONFIDENCE_LEVELS[:high], severity: described_class::SEVERITY_LEVELS[:critical]) }
it 'orders by severity and confidence' do
expect(described_class.all.ordered).to eq([occurrence3, occurrence2, occurrence1])
expect(described_class.all.ordered).to eq([finding3, finding2, finding1])
end
end
......@@ -76,18 +76,18 @@ RSpec.describe Vulnerabilities::Occurrence do
subject { described_class.report_type(report_type) }
context 'when occurrence has the corresponding report type' do
let!(:occurrence) { create(:vulnerabilities_occurrence, report_type: report_type) }
context 'when finding has the corresponding report type' do
let!(:finding) { create(:vulnerabilities_finding, report_type: report_type) }
it 'selects the occurrence' do
is_expected.to eq([occurrence])
it 'selects the finding' do
is_expected.to eq([finding])
end
end
context 'when occurrence does not have security reports' do
let!(:occurrence) { create(:vulnerabilities_occurrence, report_type: :dependency_scanning) }
context 'when finding does not have security reports' do
let!(:finding) { create(:vulnerabilities_finding, report_type: :dependency_scanning) }
it 'does not select the occurrence' do
it 'does not select the finding' do
is_expected.to be_empty
end
end
......@@ -98,13 +98,13 @@ RSpec.describe Vulnerabilities::Occurrence do
let(:pipeline) { create(:ci_pipeline, :success, project: project) }
before do
create(:vulnerabilities_occurrence, pipelines: [pipeline], project: project)
create(:vulnerabilities_finding, pipelines: [pipeline], project: project)
end
subject(:occurrences) { described_class.for_pipelines_with_sha([pipeline]) }
subject(:findings) { described_class.for_pipelines_with_sha([pipeline]) }
it 'sets the sha' do
expect(occurrences.first.sha).to eq(pipeline.sha)
expect(findings.first.sha).to eq(pipeline.sha)
end
end
......@@ -117,23 +117,23 @@ RSpec.describe Vulnerabilities::Occurrence do
travel_to(date_1) do
pipeline = create(:ci_pipeline, :success, project: project)
create_list(:vulnerabilities_occurrence, 2,
create_list(:vulnerabilities_finding, 2,
pipelines: [pipeline], project: project, report_type: :sast, severity: :high)
end
travel_to(date_2) do
pipeline = create(:ci_pipeline, :success, project: project)
create_list(:vulnerabilities_occurrence, 2,
create_list(:vulnerabilities_finding, 2,
pipelines: [pipeline], project: project, report_type: :dependency_scanning, severity: :low)
create_list(:vulnerabilities_occurrence, 1,
create_list(:vulnerabilities_finding, 1,
pipelines: [pipeline], project: project, report_type: :dast, severity: :medium)
create_list(:vulnerabilities_occurrence, 1,
create_list(:vulnerabilities_finding, 1,
pipelines: [pipeline], project: project, report_type: :dast, severity: :low)
create_list(:vulnerabilities_occurrence, 2,
create_list(:vulnerabilities_finding, 2,
pipelines: [pipeline], project: project, report_type: :secret_detection, severity: :critical)
end
end
......@@ -147,7 +147,7 @@ RSpec.describe Vulnerabilities::Occurrence do
context 'within 3-day period' do
let(:range) { 3.days }
it 'returns expected counts for occurrences' do
it 'returns expected counts for findings' do
first, second, third = subject
expect(first.day).to eq(date_2)
......@@ -165,7 +165,7 @@ RSpec.describe Vulnerabilities::Occurrence do
context 'within 4-day period' do
let(:range) { 4.days }
it 'returns expected counts for occurrences' do
it 'returns expected counts for findings' do
first, second, third, forth = subject
expect(first.day).to eq(date_1)
......@@ -185,10 +185,10 @@ RSpec.describe Vulnerabilities::Occurrence do
end
describe '.by_report_types' do
let!(:vulnerability_sast) { create(:vulnerabilities_occurrence, report_type: :sast) }
let!(:vulnerability_secret_detection) { create(:vulnerabilities_occurrence, report_type: :secret_detection) }
let!(:vulnerability_dast) { create(:vulnerabilities_occurrence, report_type: :dast) }
let!(:vulnerability_depscan) { create(:vulnerabilities_occurrence, report_type: :dependency_scanning) }
let!(:vulnerability_sast) { create(:vulnerabilities_finding, report_type: :sast) }
let!(:vulnerability_secret_detection) { create(:vulnerabilities_finding, report_type: :secret_detection) }
let!(:vulnerability_dast) { create(:vulnerabilities_finding, report_type: :dast) }
let!(:vulnerability_depscan) { create(:vulnerabilities_finding, report_type: :dependency_scanning) }
subject { described_class.by_report_types(param) }
......@@ -218,8 +218,8 @@ RSpec.describe Vulnerabilities::Occurrence do
end
describe '.by_projects' do
let!(:vulnerability1) { create(:vulnerabilities_occurrence) }
let!(:vulnerability2) { create(:vulnerabilities_occurrence) }
let!(:vulnerability1) { create(:vulnerabilities_finding) }
let!(:vulnerability2) { create(:vulnerabilities_finding) }
subject { described_class.by_projects(param) }
......@@ -233,8 +233,8 @@ RSpec.describe Vulnerabilities::Occurrence do
end
describe '.by_severities' do
let!(:vulnerability_high) { create(:vulnerabilities_occurrence, severity: :high) }
let!(:vulnerability_low) { create(:vulnerabilities_occurrence, severity: :low) }
let!(:vulnerability_high) { create(:vulnerabilities_finding, severity: :high) }
let!(:vulnerability_low) { create(:vulnerabilities_finding, severity: :low) }
subject { described_class.by_severities(param) }
......@@ -256,8 +256,8 @@ RSpec.describe Vulnerabilities::Occurrence do
end
describe '.by_confidences' do
let!(:vulnerability_high) { create(:vulnerabilities_occurrence, confidence: :high) }
let!(:vulnerability_low) { create(:vulnerabilities_occurrence, confidence: :low) }
let!(:vulnerability_high) { create(:vulnerabilities_finding, confidence: :high) }
let!(:vulnerability_low) { create(:vulnerabilities_finding, confidence: :low) }
subject { described_class.by_confidences(param) }
......@@ -279,9 +279,9 @@ RSpec.describe Vulnerabilities::Occurrence do
end
describe '.counted_by_severity' do
let!(:high_vulnerabilities) { create_list(:vulnerabilities_occurrence, 3, severity: :high) }
let!(:medium_vulnerabilities) { create_list(:vulnerabilities_occurrence, 2, severity: :medium) }
let!(:low_vulnerabilities) { create_list(:vulnerabilities_occurrence, 1, severity: :low) }
let!(:high_vulnerabilities) { create_list(:vulnerabilities_finding, 3, severity: :high) }
let!(:medium_vulnerabilities) { create_list(:vulnerabilities_finding, 2, severity: :medium) }
let!(:low_vulnerabilities) { create_list(:vulnerabilities_finding, 1, severity: :low) }
subject { described_class.counted_by_severity }
......@@ -293,9 +293,9 @@ RSpec.describe Vulnerabilities::Occurrence do
describe '.undismissed' do
let_it_be(:project) { create(:project) }
let_it_be(:project2) { create(:project) }
let!(:finding1) { create(:vulnerabilities_occurrence, project: project) }
let!(:finding2) { create(:vulnerabilities_occurrence, project: project, report_type: :dast) }
let!(:finding3) { create(:vulnerabilities_occurrence, project: project2) }
let!(:finding1) { create(:vulnerabilities_finding, project: project) }
let!(:finding2) { create(:vulnerabilities_finding, project: project, report_type: :dast) }
let!(:finding3) { create(:vulnerabilities_finding, project: project2) }
before do
create(
......@@ -319,11 +319,11 @@ RSpec.describe Vulnerabilities::Occurrence do
)
end
it 'returns all non-dismissed occurrences' do
it 'returns all non-dismissed findings' do
expect(described_class.undismissed).to contain_exactly(finding2, finding3)
end
it 'returns non-dismissed occurrences for project' do
it 'returns non-dismissed findings for project' do
expect(project2.vulnerability_findings.undismissed).to contain_exactly(finding3)
end
end
......@@ -333,7 +333,7 @@ RSpec.describe Vulnerabilities::Occurrence do
let(:project) { create(:project) }
it 'fetches a vulnerability count for the given project and severity' do
create(:vulnerabilities_occurrence, pipelines: [pipeline], project: project, severity: :high)
create(:vulnerabilities_finding, pipelines: [pipeline], project: project, severity: :high)
count = described_class.batch_count_by_project_and_severity(project.id, 'high')
......@@ -344,15 +344,15 @@ RSpec.describe Vulnerabilities::Occurrence do
old_pipeline = create(:ci_pipeline, :success, project: project)
latest_pipeline = create(:ci_pipeline, :success, project: project)
latest_failed_pipeline = create(:ci_pipeline, :failed, project: project)
create(:vulnerabilities_occurrence, pipelines: [old_pipeline], project: project, severity: :critical)
create(:vulnerabilities_finding, pipelines: [old_pipeline], project: project, severity: :critical)
create(
:vulnerabilities_occurrence,
:vulnerabilities_finding,
pipelines: [latest_failed_pipeline],
project: project,
severity: :critical
)
create_list(
:vulnerabilities_occurrence, 2,
:vulnerabilities_finding, 2,
pipelines: [latest_pipeline],
project: project,
severity: :critical
......@@ -375,8 +375,8 @@ RSpec.describe Vulnerabilities::Occurrence do
projects.each do |project|
pipeline = create(:ci_pipeline, :success, project: project)
create(:vulnerabilities_occurrence, pipelines: [pipeline], project: project, severity: :high)
create(:vulnerabilities_occurrence, pipelines: [pipeline], project: project, severity: :low)
create(:vulnerabilities_finding, pipelines: [pipeline], project: project, severity: :high)
create(:vulnerabilities_finding, pipelines: [pipeline], project: project, severity: :low)
end
projects_and_severities = [
......@@ -394,8 +394,8 @@ RSpec.describe Vulnerabilities::Occurrence do
end
it 'does not include dismissed vulnerabilities in the counts' do
create(:vulnerabilities_occurrence, pipelines: [pipeline], project: project, severity: :high)
dismissed_vulnerability = create(:vulnerabilities_occurrence, pipelines: [pipeline], project: project, severity: :high)
create(:vulnerabilities_finding, pipelines: [pipeline], project: project, severity: :high)
dismissed_vulnerability = create(:vulnerabilities_finding, pipelines: [pipeline], project: project, severity: :high)
create(
:vulnerability_feedback,
project: project,
......@@ -413,8 +413,8 @@ RSpec.describe Vulnerabilities::Occurrence do
project2 = create(:project)
pipeline1 = create(:ci_pipeline, :success, project: project1)
pipeline2 = create(:ci_pipeline, :success, project: project2)
create(:vulnerabilities_occurrence, pipelines: [pipeline1], project: project1, severity: :critical)
create(:vulnerabilities_occurrence, pipelines: [pipeline2], project: project2, severity: :high)
create(:vulnerabilities_finding, pipelines: [pipeline1], project: project1, severity: :critical)
create(:vulnerabilities_finding, pipelines: [pipeline2], project: project2, severity: :high)
project1_critical_count = described_class.batch_count_by_project_and_severity(project1.id, 'critical')
project1_high_count = described_class.batch_count_by_project_and_severity(project1.id, 'high')
......@@ -430,9 +430,9 @@ RSpec.describe Vulnerabilities::Occurrence do
describe 'feedback' do
let_it_be(:project) { create(:project) }
let(:occurrence) do
let(:finding) do
create(
:vulnerabilities_occurrence,
:vulnerabilities_finding,
report_type: :dependency_scanning,
project: project
)
......@@ -447,12 +447,12 @@ RSpec.describe Vulnerabilities::Occurrence do
:issue,
issue: issue,
project: project,
project_fingerprint: occurrence.project_fingerprint
project_fingerprint: finding.project_fingerprint
)
end
it 'returns associated feedback' do
feedback = occurrence.issue_feedback
feedback = finding.issue_feedback
expect(feedback).to be_present
expect(feedback[:project_id]).to eq project.id
......@@ -468,12 +468,12 @@ RSpec.describe Vulnerabilities::Occurrence do
:dependency_scanning,
:dismissal,
project: project,
project_fingerprint: occurrence.project_fingerprint
project_fingerprint: finding.project_fingerprint
)
end
it 'returns associated feedback' do
feedback = occurrence.dismissal_feedback
feedback = finding.dismissal_feedback
expect(feedback).to be_present
expect(feedback[:project_id]).to eq project.id
......@@ -490,12 +490,12 @@ RSpec.describe Vulnerabilities::Occurrence do
:merge_request,
merge_request: merge_request,
project: project,
project_fingerprint: occurrence.project_fingerprint
project_fingerprint: finding.project_fingerprint
)
end
it 'returns associated feedback' do
feedback = occurrence.merge_request_feedback
feedback = finding.merge_request_feedback
expect(feedback).to be_present
expect(feedback[:project_id]).to eq project.id
......@@ -507,9 +507,9 @@ RSpec.describe Vulnerabilities::Occurrence do
describe '#load_feedback' do
let_it_be(:project) { create(:project) }
let_it_be(:occurrence) do
let_it_be(:finding) do
create(
:vulnerabilities_occurrence,
:vulnerabilities_finding,
report_type: :dependency_scanning,
project: project
)
......@@ -520,20 +520,20 @@ RSpec.describe Vulnerabilities::Occurrence do
:dependency_scanning,
:dismissal,
project: project,
project_fingerprint: occurrence.project_fingerprint
project_fingerprint: finding.project_fingerprint
)
end
let(:expected_feedback) { [feedback] }
subject(:load_feedback) { occurrence.load_feedback.to_a }
subject(:load_feedback) { finding.load_feedback.to_a }
it { is_expected.to eq(expected_feedback) }
context 'when you have multiple occurrences' do
let_it_be(:occurrence_2) do
context 'when you have multiple findings' do
let_it_be(:finding_2) do
create(
:vulnerabilities_occurrence,
:vulnerabilities_finding,
report_type: :dependency_scanning,
project: project
)
......@@ -545,13 +545,13 @@ RSpec.describe Vulnerabilities::Occurrence do
:dependency_scanning,
:dismissal,
project: project,
project_fingerprint: occurrence_2.project_fingerprint
project_fingerprint: finding_2.project_fingerprint
)
end
let(:expected_feedback) { [[feedback], [feedback_2]] }
subject(:load_feedback) { [occurrence, occurrence_2].map(&:load_feedback) }
subject(:load_feedback) { [finding, finding_2].map(&:load_feedback) }
it { is_expected.to eq(expected_feedback) }
end
......@@ -606,37 +606,37 @@ RSpec.describe Vulnerabilities::Occurrence do
end
describe '#scanner_name' do
let(:vulnerabilities_occurrence) { create(:vulnerabilities_occurrence) }
let(:vulnerabilities_finding) { create(:vulnerabilities_finding) }
subject(:scanner_name) { vulnerabilities_occurrence.scanner_name }
subject(:scanner_name) { vulnerabilities_finding.scanner_name }
it { is_expected.to eq(vulnerabilities_occurrence.scanner.name) }
it { is_expected.to eq(vulnerabilities_finding.scanner.name) }
end
describe '#solution' do
subject { vulnerabilities_occurrence.solution }
subject { vulnerabilities_finding.solution }
context 'when solution metadata key is present' do
let(:vulnerabilities_occurrence) { build(:vulnerabilities_occurrence) }
let(:vulnerabilities_finding) { build(:vulnerabilities_finding) }
it { is_expected.to eq(vulnerabilities_occurrence.metadata['solution']) }
it { is_expected.to eq(vulnerabilities_finding.metadata['solution']) }
end
context 'when remediations key is present' do
let(:vulnerabilities_occurrence) do
build(:vulnerabilities_occurrence_with_remediation, summary: "Test remediation")
let(:vulnerabilities_finding) do
build(:vulnerabilities_finding_with_remediation, summary: "Test remediation")
end
it { is_expected.to eq(vulnerabilities_occurrence.remediations.dig(0, 'summary')) }
it { is_expected.to eq(vulnerabilities_finding.remediations.dig(0, 'summary')) }
end
end
describe '#evidence' do
subject { occurrence.evidence }
subject { finding.evidence }
context 'has an evidence fields' do
let(:occurrence) { create(:vulnerabilities_occurrence) }
let(:evidence) { occurrence.metadata['evidence'] }
let(:finding) { create(:vulnerabilities_finding) }
let(:evidence) { finding.metadata['evidence'] }
it do
is_expected.to match a_hash_including(
......@@ -655,7 +655,7 @@ RSpec.describe Vulnerabilities::Occurrence do
end
context 'has no evidence summary when evidence is present, summary is not' do
let(:occurrence) { create(:vulnerabilities_occurrence, raw_metadata: { evidence: {} }) }
let(:finding) { create(:vulnerabilities_finding, raw_metadata: { evidence: {} }) }
it do
is_expected.to match a_hash_including(
......@@ -675,42 +675,42 @@ RSpec.describe Vulnerabilities::Occurrence do
end
describe '#message' do
let(:occurrence) { build(:vulnerabilities_occurrence) }
let(:expected_message) { occurrence.metadata['message'] }
let(:finding) { build(:vulnerabilities_finding) }
let(:expected_message) { finding.metadata['message'] }
subject { occurrence.message }
subject { finding.message }
it { is_expected.to eql(expected_message) }
end
describe '#cve' do
let(:occurrence) { build(:vulnerabilities_occurrence) }
let(:expected_cve) { occurrence.metadata['cve'] }
let(:finding) { build(:vulnerabilities_finding) }
let(:expected_cve) { finding.metadata['cve'] }
subject { occurrence.cve }
subject { finding.cve }
it { is_expected.to eql(expected_cve) }
end
describe "#metadata" do
let(:occurrence) { build(:vulnerabilities_occurrence) }
let(:finding) { build(:vulnerabilities_finding) }
subject { occurrence.metadata }
subject { finding.metadata }
it "handles bool JSON data" do
allow(occurrence).to receive(:raw_metadata) { "true" }
allow(finding).to receive(:raw_metadata) { "true" }
expect(subject).to eq({})
end
it "handles string JSON data" do
allow(occurrence).to receive(:raw_metadata) { '"test"' }
allow(finding).to receive(:raw_metadata) { '"test"' }
expect(subject).to eq({})
end
it "parses JSON data" do
allow(occurrence).to receive(:raw_metadata) { '{ "test": true }' }
allow(finding).to receive(:raw_metadata) { '{ "test": true }' }
expect(subject).to eq({ "test" => true })
end
......
......@@ -5,8 +5,8 @@ require 'spec_helper'
RSpec.describe Vulnerabilities::Identifier do
describe 'associations' do
it { is_expected.to have_many(:finding_identifiers).class_name('Vulnerabilities::FindingIdentifier') }
it { is_expected.to have_many(:occurrences).class_name('Vulnerabilities::Occurrence') }
it { is_expected.to have_many(:primary_occurrences).class_name('Vulnerabilities::Occurrence') }
it { is_expected.to have_many(:findings).class_name('Vulnerabilities::Finding') }
it { is_expected.to have_many(:primary_findings).class_name('Vulnerabilities::Finding') }
it { is_expected.to belong_to(:project) }
end
......
......@@ -4,7 +4,7 @@ require 'spec_helper'
RSpec.describe Vulnerabilities::Scanner do
describe 'associations' do
it { is_expected.to have_many(:occurrences).class_name('Vulnerabilities::Occurrence') }
it { is_expected.to have_many(:findings).class_name('Vulnerabilities::Finding').with_foreign_key('occurrence_id') }
it { is_expected.to belong_to(:project) }
end
......
......@@ -31,7 +31,7 @@ RSpec.describe Vulnerability do
it { is_expected.to belong_to(:project) }
it { is_expected.to belong_to(:milestone) }
it { is_expected.to belong_to(:epic) }
it { is_expected.to have_many(:findings).class_name('Vulnerabilities::Occurrence').inverse_of(:vulnerability) }
it { is_expected.to have_many(:findings).class_name('Vulnerabilities::Finding').inverse_of(:vulnerability) }
it { is_expected.to have_many(:issue_links).class_name('Vulnerabilities::IssueLink').inverse_of(:vulnerability) }
it { is_expected.to have_many(:related_issues).through(:issue_links).source(:issue) }
it { is_expected.to belong_to(:author).class_name('User') }
......@@ -43,7 +43,7 @@ RSpec.describe Vulnerability do
it { is_expected.to have_one(:group).through(:project) }
it { is_expected.to have_many(:findings).class_name('Vulnerabilities::Occurrence').dependent(false) }
it { is_expected.to have_many(:findings).class_name('Vulnerabilities::Finding').dependent(false) }
it { is_expected.to have_many(:notes).dependent(:delete_all) }
it { is_expected.to have_many(:user_mentions).class_name('VulnerabilityUserMention') }
end
......@@ -274,11 +274,11 @@ RSpec.describe Vulnerability do
subject { vulnerability.resolved_on_default_branch }
context 'Vulnerability::Occurrence is present on the pipeline for default branch' do
context 'Vulnerability::Finding is present on the pipeline for default branch' do
it { is_expected.to eq(false) }
end
context 'Vulnerability::Occurrence is not present on the pipeline for default branch' do
context 'Vulnerability::Finding is not present on the pipeline for default branch' do
before do
project.instance_variable_set(:@latest_successful_pipeline_for_default_branch, pipeline_without_vulnerability)
end
......
......@@ -6,7 +6,7 @@ RSpec.describe Security::VulnerableProjectPresenter do
let(:project) { create(:project) }
before do
allow(::Vulnerabilities::Occurrence).to receive(:batch_count_by_project_and_severity).and_return(1)
allow(::Vulnerabilities::Finding).to receive(:batch_count_by_project_and_severity).and_return(1)
end
subject { described_class.new(project) }
......@@ -15,7 +15,7 @@ RSpec.describe Security::VulnerableProjectPresenter do
expect(subject.id).to be(project.id)
end
::Vulnerabilities::Occurrence::SEVERITY_LEVELS.keys.each do |severity_level|
::Vulnerabilities::Finding::SEVERITY_LEVELS.keys.each do |severity_level|
it "exposes a vulnerability count attribute for #{severity_level} vulnerabilities" do
expect(subject.public_send("#{severity_level}_vulnerability_count")).to be(1)
end
......
......@@ -37,13 +37,13 @@ RSpec.describe 'Query.vulnerabilities.identifiers' do
let_it_be(:finding) do
create(
:vulnerabilities_occurrence,
:vulnerabilities_finding,
vulnerability: vulnerability
)
end
let_it_be(:vulnerabilities_finding_identifier) do
create(:vulnerabilities_finding_identifier, identifier: finding_identifier, occurrence: finding)
create(:vulnerabilities_finding_identifier, identifier: finding_identifier, finding: finding)
end
subject { graphql_data.dig('vulnerabilities', 'nodes') }
......
......@@ -32,8 +32,8 @@ RSpec.describe API::VulnerabilityFindings do
create(:vulnerability_feedback, :dismissal, :sast,
project: project,
pipeline: pipeline,
project_fingerprint: sast_report.occurrences.first.project_fingerprint,
vulnerability_data: sast_report.occurrences.first.raw_metadata
project_fingerprint: sast_report.findings.first.project_fingerprint,
vulnerability_data: sast_report.findings.first.raw_metadata
)
end
......@@ -43,21 +43,21 @@ RSpec.describe API::VulnerabilityFindings do
end
# Because fixture reports that power :ee_ci_job_artifact factory contain long report lists,
# we need to make sure that all occurrences for both SAST and Dependency Scanning are included in the response.
# we need to make sure that all findings for both SAST and Dependency Scanning are included in the response.
# That's why the page size is 40.
let(:pagination) { { per_page: 40 } }
it 'returns all non-dismissed vulnerabilities' do
# all occurrences except one that was dismissed
occurrence_count = (sast_report.occurrences.count + ds_report.occurrences.count - 1).to_s
# all findings except one that was dismissed
finding_count = (sast_report.findings.count + ds_report.findings.count - 1).to_s
get api(project_vulnerability_findings_path, user), params: pagination
expect(response).to have_gitlab_http_status(:ok)
expect(response).to include_pagination_headers
expect(response).to match_response_schema('vulnerabilities/occurrence_list', dir: 'ee')
expect(response).to match_response_schema('vulnerabilities/finding_list', dir: 'ee')
expect(response.headers['X-Total']).to eq occurrence_count
expect(response.headers['X-Total']).to eq finding_count
expect(json_response.map { |v| v['report_type'] }.uniq).to match_array %w[dependency_scanning sast]
end
......@@ -68,39 +68,39 @@ RSpec.describe API::VulnerabilityFindings do
end.count
# Threshold is required for the extra query performed in Security::PipelineVulnerabilitiesFinder to load
# the Vulnerabilities providing computed states for the associated Vulnerability::Occurrences
# the Vulnerabilities providing computed states for the associated Vulnerability::Findings
expect { get api(project_vulnerability_findings_path, user) }.not_to exceed_query_limit(control_count).with_threshold(1)
end
describe 'filtering' do
it 'returns vulnerabilities with sast report_type' do
occurrence_count = (sast_report.occurrences.count - 1).to_s # all SAST occurrences except one that was dismissed
finding_count = (sast_report.findings.count - 1).to_s # all SAST findings except one that was dismissed
get api(project_vulnerability_findings_path, user), params: { report_type: 'sast' }
expect(response).to have_gitlab_http_status(:ok)
expect(response.headers['X-Total']).to eq occurrence_count
expect(response.headers['X-Total']).to eq finding_count
expect(json_response.map { |v| v['report_type'] }.uniq).to match_array %w[sast]
# occurrences are implicitly sorted by Security::PipelineVulnerabilitiesFinder and
# findings are implicitly sorted by Security::PipelineVulnerabilitiesFinder and
# Security::MergeReportsService so their order differs from what is present in fixture file
expect(json_response.first['name']).to eq 'ECB mode is insecure'
end
it 'returns vulnerabilities with dependency_scanning report_type' do
occurrence_count = ds_report.occurrences.count.to_s
finding_count = ds_report.findings.count.to_s
get api(project_vulnerability_findings_path, user), params: { report_type: 'dependency_scanning' }
expect(response).to have_gitlab_http_status(:ok)
expect(response.headers['X-Total']).to eq occurrence_count
expect(response.headers['X-Total']).to eq finding_count
expect(json_response.map { |v| v['report_type'] }.uniq).to match_array %w[dependency_scanning]
# occurrences are implicitly sorted by Security::PipelineVulnerabilitiesFinder and
# findings are implicitly sorted by Security::PipelineVulnerabilitiesFinder and
# Security::MergeReportsService so their order differs from what is present in fixture file
expect(json_response.first['name']).to eq 'ruby-ffi DDL loading issue on Windows OS'
end
......@@ -112,13 +112,13 @@ RSpec.describe API::VulnerabilityFindings do
end
it 'returns dismissed vulnerabilities with `all` scope' do
occurrence_count = (sast_report.occurrences.count + ds_report.occurrences.count).to_s
finding_count = (sast_report.findings.count + ds_report.findings.count).to_s
get api(project_vulnerability_findings_path, user), params: { scope: 'all' }.merge(pagination)
expect(response).to have_gitlab_http_status(:ok)
expect(response.headers['X-Total']).to eq occurrence_count
expect(response.headers['X-Total']).to eq finding_count
end
it 'returns vulnerabilities with low severity' do
......@@ -159,13 +159,13 @@ RSpec.describe API::VulnerabilityFindings do
context 'when pipeline_id is supplied' do
it 'returns vulnerabilities from supplied pipeline' do
occurrence_count = (sast_report.occurrences.count + ds_report.occurrences.count - 1).to_s
finding_count = (sast_report.findings.count + ds_report.findings.count - 1).to_s
get api(project_vulnerability_findings_path, user), params: { pipeline_id: pipeline.id }.merge(pagination)
expect(response).to have_gitlab_http_status(:ok)
expect(response.headers['X-Total']).to eq occurrence_count
expect(response.headers['X-Total']).to eq finding_count
end
context 'pipeline has no reports' do
......
......@@ -24,7 +24,7 @@ RSpec.describe 'GET /groups/*group_id/-/security/projects' do
projects = create_list(:project, 2, namespace: group)
projects.each do |project|
::Vulnerabilities::Occurrence::SEVERITY_LEVELS.keys.each do |severity|
::Vulnerabilities::Finding::SEVERITY_LEVELS.keys.each do |severity|
create(:vulnerabilities_occurrence, severity: severity, project: project)
end
end
......
......@@ -47,7 +47,7 @@ RSpec.describe 'GET /-/security/vulnerability_findings' do
let(:findings_request_params) { { page: 2 } }
before do
Vulnerabilities::Occurrence.paginates_per 2
Vulnerabilities::Finding.paginates_per 2
create_list(:vulnerabilities_occurrence, 3, pipelines: [pipeline], project: project)
......@@ -55,7 +55,7 @@ RSpec.describe 'GET /-/security/vulnerability_findings' do
end
after do
Vulnerabilities::Occurrence.paginates_per Vulnerabilities::Occurrence::OCCURRENCES_PER_PAGE
Vulnerabilities::Finding.paginates_per Vulnerabilities::Finding::OCCURRENCES_PER_PAGE
end
it 'returns the list of vulnerability findings that are on the requested page' do
......
......@@ -6,13 +6,13 @@ RSpec.describe Vulnerabilities::FindingReportsComparerEntity do
describe 'container scanning report comparison' do
let_it_be(:user) { create(:user) }
let(:base_occurrences) { create_list(:vulnerabilities_occurrence, 2) }
let(:base_findings) { create_list(:vulnerabilities_finding, 2) }
let(:base_combined_reports) { build_list(:ci_reports_security_report, 1, created_at: nil) }
let(:base_report) { build(:ci_reports_security_aggregated_reports, reports: base_combined_reports, occurrences: base_occurrences)}
let(:base_report) { build(:ci_reports_security_aggregated_reports, reports: base_combined_reports, findings: base_findings)}
let(:head_occurrences) { create_list(:vulnerabilities_occurrence, 1) }
let(:head_findings) { create_list(:vulnerabilities_finding, 1) }
let(:head_combined_reports) { build_list(:ci_reports_security_report, 1, created_at: 2.days.ago) }
let(:head_report) { build(:ci_reports_security_aggregated_reports, reports: head_combined_reports, occurrences: head_occurrences)}
let(:head_report) { build(:ci_reports_security_aggregated_reports, reports: head_combined_reports, findings: head_findings)}
let(:scan) { create(:security_scan, scanned_resources_count: 10) }
let(:security_scans) { [scan] }
......
......@@ -7,12 +7,12 @@ RSpec.describe VulnerableProjectEntity do
let(:vulnerable_project) { ::Security::VulnerableProjectPresenter.new(project) }
before do
allow(::Vulnerabilities::Occurrence).to receive(:batch_count_by_project_and_severity).and_return(2)
allow(::Vulnerabilities::Finding).to receive(:batch_count_by_project_and_severity).and_return(2)
end
subject { described_class.new(vulnerable_project) }
::Vulnerabilities::Occurrence::SEVERITY_LEVELS.keys.each do |severity_level|
::Vulnerabilities::Finding::SEVERITY_LEVELS.keys.each do |severity_level|
it "exposes a vulnerability count attribute for #{severity_level} vulnerabilities" do
expect(subject.as_json["#{severity_level}_vulnerability_count".to_sym]).to be(2)
end
......
......@@ -11,14 +11,14 @@ RSpec.describe VulnerableProjectSerializer do
before do
project.add_developer(user)
allow(::Vulnerabilities::Occurrence).to receive(:batch_count_by_project_and_severity)
allow(::Vulnerabilities::Finding).to receive(:batch_count_by_project_and_severity)
end
describe '#represent' do
subject { serializer.represent(vulnerable_project) }
it 'includes counts for each severity of vulnerability' do
::Vulnerabilities::Occurrence::SEVERITY_LEVELS.keys.each do |severity_level|
::Vulnerabilities::Finding::SEVERITY_LEVELS.keys.each do |severity_level|
expect(subject).to include("#{severity_level}_vulnerability_count".to_sym)
end
end
......
......@@ -14,24 +14,24 @@ RSpec.describe Security::MergeReportsService, '#execute' do
let(:identifier_cwe) { build(:ci_reports_security_identifier, external_id: '789', external_type: 'cwe') }
let(:identifier_wasc) { build(:ci_reports_security_identifier, external_id: '13', external_type: 'wasc') }
let(:occurrence_id_1) do
build(:ci_reports_security_occurrence,
let(:finding_id_1) do
build(:ci_reports_security_finding,
identifiers: [identifier_1_primary, identifier_1_cve],
scanner: scanner_1,
severity: :low
)
end
let(:occurrence_id_1_extra) do
build(:ci_reports_security_occurrence,
let(:finding_id_1_extra) do
build(:ci_reports_security_finding,
identifiers: [identifier_1_primary, identifier_1_cve],
scanner: scanner_1,
severity: :low
)
end
let(:occurrence_id_2_loc_1) do
build(:ci_reports_security_occurrence,
let(:finding_id_2_loc_1) do
build(:ci_reports_security_finding,
identifiers: [identifier_2_primary, identifier_2_cve],
location: build(:ci_reports_security_locations_sast, start_line: 32, end_line: 34),
scanner: scanner_2,
......@@ -39,8 +39,8 @@ RSpec.describe Security::MergeReportsService, '#execute' do
)
end
let(:occurrence_id_2_loc_2) do
build(:ci_reports_security_occurrence,
let(:finding_id_2_loc_2) do
build(:ci_reports_security_finding,
identifiers: [identifier_2_primary, identifier_2_cve],
location: build(:ci_reports_security_locations_sast, start_line: 42, end_line: 44),
scanner: scanner_2,
......@@ -48,70 +48,70 @@ RSpec.describe Security::MergeReportsService, '#execute' do
)
end
let(:occurrence_cwe_1) do
build(:ci_reports_security_occurrence,
let(:finding_cwe_1) do
build(:ci_reports_security_finding,
identifiers: [identifier_cwe],
scanner: scanner_3,
severity: :high
)
end
let(:occurrence_cwe_2) do
build(:ci_reports_security_occurrence,
let(:finding_cwe_2) do
build(:ci_reports_security_finding,
identifiers: [identifier_cwe],
scanner: scanner_1,
severity: :critical
)
end
let(:occurrence_wasc_1) do
build(:ci_reports_security_occurrence,
let(:finding_wasc_1) do
build(:ci_reports_security_finding,
identifiers: [identifier_wasc],
scanner: scanner_1,
severity: :medium
)
end
let(:occurrence_wasc_2) do
build(:ci_reports_security_occurrence,
let(:finding_wasc_2) do
build(:ci_reports_security_finding,
identifiers: [identifier_wasc],
scanner: scanner_2,
severity: :critical
)
end
let(:report_1_occurrences) { [occurrence_id_1, occurrence_id_2_loc_1, occurrence_cwe_2, occurrence_wasc_1] }
let(:report_1_findings) { [finding_id_1, finding_id_2_loc_1, finding_cwe_2, finding_wasc_1] }
let(:report_1) do
build(
:ci_reports_security_report,
scanners: [scanner_1, scanner_2],
occurrences: report_1_occurrences,
identifiers: report_1_occurrences.flat_map(&:identifiers),
findings: report_1_findings,
identifiers: report_1_findings.flat_map(&:identifiers),
scanned_resources: ['example.com', 'example.com/1', 'example.com/2']
)
end
let(:report_2_occurrences) { [occurrence_id_2_loc_2, occurrence_wasc_2] }
let(:report_2_findings) { [finding_id_2_loc_2, finding_wasc_2] }
let(:report_2) do
build(
:ci_reports_security_report,
scanners: [scanner_2],
occurrences: report_2_occurrences,
identifiers: occurrence_id_2_loc_2.identifiers,
findings: report_2_findings,
identifiers: finding_id_2_loc_2.identifiers,
scanned_resources: ['example.com', 'example.com/3']
)
end
let(:report_3_occurrences) { [occurrence_id_1_extra, occurrence_cwe_1] }
let(:report_3_findings) { [finding_id_1_extra, finding_cwe_1] }
let(:report_3) do
build(
:ci_reports_security_report,
scanners: [scanner_1, scanner_3],
occurrences: report_3_occurrences,
identifiers: report_3_occurrences.flat_map(&:identifiers)
findings: report_3_findings,
identifiers: report_3_findings.flat_map(&:identifiers)
)
end
......@@ -137,15 +137,15 @@ RSpec.describe Security::MergeReportsService, '#execute' do
end
it 'deduplicates (except cwe and wasc) and sorts the vulnerabilities by severity (desc) then by compare key' do
expect(subject.occurrences).to(
expect(subject.findings).to(
eq([
occurrence_cwe_2,
occurrence_wasc_2,
occurrence_cwe_1,
occurrence_id_2_loc_2,
occurrence_id_2_loc_1,
occurrence_wasc_1,
occurrence_id_1
finding_cwe_2,
finding_wasc_2,
finding_cwe_1,
finding_id_2_loc_2,
finding_id_2_loc_1,
finding_wasc_1,
finding_id_1
])
)
end
......@@ -170,16 +170,16 @@ RSpec.describe Security::MergeReportsService, '#execute' do
let(:identifier_cve) { build(:ci_reports_security_identifier, external_id: 'CVE-2019-123', external_type: 'cve') }
let(:identifier_npm) { build(:ci_reports_security_identifier, external_id: 'NPM-13', external_type: 'npm') }
let(:occurrence_id_1) { build(:ci_reports_security_occurrence, identifiers: [identifier_gemnasium, identifier_cve, identifier_npm], scanner: gemnasium_scanner, report_type: :dependency_scanning) }
let(:occurrence_id_2) { build(:ci_reports_security_occurrence, identifiers: [identifier_cve], scanner: bundler_audit_scanner, report_type: :dependency_scanning) }
let(:occurrence_id_3) { build(:ci_reports_security_occurrence, identifiers: [identifier_npm], scanner: retire_js_scaner, report_type: :dependency_scanning ) }
let(:finding_id_1) { build(:ci_reports_security_finding, identifiers: [identifier_gemnasium, identifier_cve, identifier_npm], scanner: gemnasium_scanner, report_type: :dependency_scanning) }
let(:finding_id_2) { build(:ci_reports_security_finding, identifiers: [identifier_cve], scanner: bundler_audit_scanner, report_type: :dependency_scanning) }
let(:finding_id_3) { build(:ci_reports_security_finding, identifiers: [identifier_npm], scanner: retire_js_scaner, report_type: :dependency_scanning ) }
let(:gemnasium_report) do
build( :ci_reports_security_report,
type: :dependency_scanning,
scanners: [gemnasium_scanner],
occurrences: [occurrence_id_1],
identifiers: occurrence_id_1.identifiers
findings: [finding_id_1],
identifiers: finding_id_1.identifiers
)
end
......@@ -188,8 +188,8 @@ RSpec.describe Security::MergeReportsService, '#execute' do
:ci_reports_security_report,
type: :dependency_scanning,
scanners: [bundler_audit_scanner],
occurrences: [occurrence_id_2],
identifiers: occurrence_id_2.identifiers
findings: [finding_id_2],
identifiers: finding_id_2.identifiers
)
end
......@@ -198,8 +198,8 @@ RSpec.describe Security::MergeReportsService, '#execute' do
:ci_reports_security_report,
type: :dependency_scanning,
scanners: [retire_js_scaner],
occurrences: [occurrence_id_3],
identifiers: occurrence_id_3.identifiers
findings: [finding_id_3],
identifiers: finding_id_3.identifiers
)
end
......@@ -208,8 +208,8 @@ RSpec.describe Security::MergeReportsService, '#execute' do
:ci_reports_security_report,
type: :dependency_scanning,
scanners: [scanner_2],
occurrences: [occurrence_id_2_loc_1],
identifiers: occurrence_id_2_loc_1.identifiers
findings: [finding_id_2_loc_1],
identifiers: finding_id_2_loc_1.identifiers
)
end
......@@ -217,17 +217,17 @@ RSpec.describe Security::MergeReportsService, '#execute' do
subject { described_class.new(gemnasium_report, retirejs_report, bundler_audit_report).execute }
specify { expect(subject.scanners.values).to eql([bundler_audit_scanner, retire_js_scaner, gemnasium_scanner]) }
specify { expect(subject.occurrences.count).to eq(2) }
specify { expect(subject.occurrences.first.identifiers).to contain_exactly(identifier_cve) }
specify { expect(subject.occurrences.last.identifiers).to contain_exactly(identifier_npm) }
specify { expect(subject.findings.count).to eq(2) }
specify { expect(subject.findings.first.identifiers).to contain_exactly(identifier_cve) }
specify { expect(subject.findings.last.identifiers).to contain_exactly(identifier_npm) }
end
context 'when a custom analyzer is completed before the known analyzers' do
subject { described_class.new(custom_analyzer_report, retirejs_report, bundler_audit_report).execute }
specify { expect(subject.scanners.values).to eql([bundler_audit_scanner, retire_js_scaner, scanner_2]) }
specify { expect(subject.occurrences.count).to eq(3) }
specify { expect(subject.occurrences.last.identifiers).to match_array(occurrence_id_2_loc_1.identifiers) }
specify { expect(subject.findings.count).to eq(3) }
specify { expect(subject.findings.last.identifiers).to match_array(finding_id_2_loc_1.identifiers) }
end
end
end
......@@ -23,7 +23,7 @@ RSpec.describe Security::StoreReportService, '#execute' do
using RSpec::Parameterized::TableSyntax
where(:case_name, :report_type, :scanners, :identifiers, :occurrences, :finding_identifiers, :finding_pipelines) do
where(:case_name, :report_type, :scanners, :identifiers, :findings, :finding_identifiers, :finding_pipelines) do
'with SAST report' | :sast | 3 | 17 | 33 | 39 | 33
'with Dependency Scanning report' | :dependency_scanning | 2 | 7 | 4 | 7 | 4
'with Container Scanning report' | :container_scanning | 1 | 8 | 8 | 8 | 8
......@@ -38,11 +38,11 @@ RSpec.describe Security::StoreReportService, '#execute' do
expect { subject }.to change { Vulnerabilities::Identifier.count }.by(identifiers)
end
it 'inserts all occurrences' do
expect { subject }.to change { Vulnerabilities::Occurrence.count }.by(occurrences)
it 'inserts all findings' do
expect { subject }.to change { Vulnerabilities::Finding.count }.by(findings)
end
it 'inserts all occurrence identifiers (join model)' do
it 'inserts all finding identifiers (join model)' do
expect { subject }.to change { Vulnerabilities::FindingIdentifier.count }.by(finding_identifiers)
end
......@@ -51,18 +51,18 @@ RSpec.describe Security::StoreReportService, '#execute' do
end
it 'inserts all vulnerabilties' do
expect { subject }.to change { Vulnerability.count }.by(occurrences)
expect { subject }.to change { Vulnerability.count }.by(findings)
end
end
context 'invalid data' do
let(:artifact) { create(:ee_ci_job_artifact, :sast) }
let(:occurrence_without_name) { build(:ci_reports_security_occurrence, name: nil) }
let(:finding_without_name) { build(:ci_reports_security_finding, name: nil) }
let(:report) { Gitlab::Ci::Reports::Security::Report.new('container_scanning', nil, nil) }
before do
allow(Gitlab::ErrorTracking).to receive(:track_and_raise_exception).and_call_original
report.add_occurrence(occurrence_without_name)
report.finding(finding_without_name)
end
it 'raises invalid record error' do
......@@ -70,7 +70,7 @@ RSpec.describe Security::StoreReportService, '#execute' do
end
it 'reports the error correctly' do
expected_params = occurrence_without_name.to_hash.dig(:raw_metadata)
expected_params = finding_without_name.to_hash.dig(:raw_metadata)
expect { subject.execute }.to raise_error { |error|
expect(Gitlab::ErrorTracking).to have_received(:track_and_raise_exception).with(error, create_params: expected_params)
}
......@@ -87,8 +87,8 @@ RSpec.describe Security::StoreReportService, '#execute' do
let(:new_report) { new_pipeline.security_reports.get_report(report_type.to_s, artifact) }
let(:report_type) { :sast }
let!(:occurrence) do
create(:vulnerabilities_occurrence,
let!(:finding) do
create(:vulnerabilities_finding,
pipelines: [pipeline],
identifiers: [identifier],
primary_identifier: identifier,
......@@ -97,7 +97,7 @@ RSpec.describe Security::StoreReportService, '#execute' do
location_fingerprint: 'd869ba3f0b3347eb2749135a437dc07c8ae0f420')
end
let!(:vulnerability) { create(:vulnerability, findings: [occurrence], project: project) }
let!(:vulnerability) { create(:vulnerability, findings: [finding], project: project) }
before do
project.add_developer(user)
......@@ -114,11 +114,11 @@ RSpec.describe Security::StoreReportService, '#execute' do
expect { subject }.to change { Vulnerabilities::Identifier.count }.by(16)
end
it 'inserts only new occurrences and reuse existing ones' do
expect { subject }.to change { Vulnerabilities::Occurrence.count }.by(32)
it 'inserts only new findings and reuse existing ones' do
expect { subject }.to change { Vulnerabilities::Finding.count }.by(32)
end
it 'inserts all occurrence pipelines (join model) for this new pipeline' do
it 'inserts all finding pipelines (join model) for this new pipeline' do
expect { subject }.to change { Vulnerabilities::FindingPipeline.where(pipeline: new_pipeline).count }.by(33)
end
......@@ -126,9 +126,9 @@ RSpec.describe Security::StoreReportService, '#execute' do
expect { subject }.to change { Vulnerability.count }.by(32)
end
it 'updates existing occurrences with new data' do
it 'updates existing findings with new data' do
subject
expect(occurrence.reload).to have_attributes(severity: 'medium', name: 'Probable insecure usage of temp file/directory.')
expect(finding.reload).to have_attributes(severity: 'medium', name: 'Probable insecure usage of temp file/directory.')
end
it 'updates existing vulnerability with new data' do
......@@ -138,7 +138,7 @@ RSpec.describe Security::StoreReportService, '#execute' do
end
context 'with existing data from same pipeline' do
let!(:occurrence) { create(:vulnerabilities_occurrence, project: project, pipelines: [pipeline]) }
let!(:finding) { create(:vulnerabilities_finding, project: project, pipelines: [pipeline]) }
let(:report_type) { :sast }
it 'skips report' do
......
......@@ -48,10 +48,10 @@ RSpec.describe Vulnerabilities::CreateService do
end
it 'starts a new transaction for the create sequence' do
allow(Vulnerabilities::Occurrence).to receive(:transaction).and_call_original
allow(Vulnerabilities::Finding).to receive(:transaction).and_call_original
subject
expect(Vulnerabilities::Occurrence).to have_received(:transaction).with(requires_new: true).once
expect(Vulnerabilities::Finding).to have_received(:transaction).with(requires_new: true).once
end
context 'when finding id is unknown' do
......
......@@ -47,7 +47,7 @@ RSpec.shared_examples ProjectVulnerabilityFindingsActions do
let(:action_params) { vulnerable_params.merge(page: 2) }
before do
Vulnerabilities::Occurrence.paginates_per 2
Vulnerabilities::Finding.paginates_per 2
create_list(:vulnerabilities_occurrence, 3, pipelines: [pipeline], project: vulnerable_project)
......@@ -55,7 +55,7 @@ RSpec.shared_examples ProjectVulnerabilityFindingsActions do
end
after do
Vulnerabilities::Occurrence.paginates_per Vulnerabilities::Occurrence::OCCURRENCES_PER_PAGE
Vulnerabilities::Finding.paginates_per Vulnerabilities::Finding::OCCURRENCES_PER_PAGE
end
it 'returns the list of vulnerability findings that are on the requested page' do
......
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