Commit 4d9eb9d7 authored by Lucas Charles's avatar Lucas Charles Committed by Lin Jen-Shin

Split Severity and Confidence values for vulnerability occurrences

Fixes https://gitlab.com/gitlab-org/gitlab-ee/issues/9397
parent 0c80a079
...@@ -39,8 +39,8 @@ class Gitlab::Seeder::Vulnerabilities ...@@ -39,8 +39,8 @@ class Gitlab::Seeder::Vulnerabilities
uuid: random_uuid, uuid: random_uuid,
name: 'Cipher with no integrity', name: 'Cipher with no integrity',
report_type: :sast, report_type: :sast,
severity: random_level, severity: random_severity_level,
confidence: random_level, confidence: random_confidence_level,
project_fingerprint: random_fingerprint, project_fingerprint: random_fingerprint,
location_fingerprint: random_fingerprint, location_fingerprint: random_fingerprint,
primary_identifier: primary_identifier, primary_identifier: primary_identifier,
...@@ -82,8 +82,12 @@ class Gitlab::Seeder::Vulnerabilities ...@@ -82,8 +82,12 @@ class Gitlab::Seeder::Vulnerabilities
project.issues.create!(author: author, title: title) project.issues.create!(author: author, title: title)
end end
def random_level def random_confidence_level
::Vulnerabilities::Occurrence::LEVELS.keys.sample ::Vulnerabilities::Occurrence::CONFIDENCE_LEVELS.keys.sample
end
def random_severity_level
::Vulnerabilities::Occurrence::SEVERITY_LEVELS.keys.sample
end end
def metadata(line) def metadata(line)
......
...@@ -49,7 +49,7 @@ module Security ...@@ -49,7 +49,7 @@ module Security
return items unless params[:severity].present? return items unless params[:severity].present?
items.by_severities( items.by_severities(
Vulnerabilities::Occurrence::LEVELS.values_at( Vulnerabilities::Occurrence::SEVERITY_LEVELS.values_at(
*params[:severity]).compact) *params[:severity]).compact)
end end
......
...@@ -9,18 +9,6 @@ module Vulnerabilities ...@@ -9,18 +9,6 @@ module Vulnerabilities
paginates_per 20 paginates_per 20
# Used for both severity and confidence
LEVELS = {
undefined: 0,
ignore: 1,
unknown: 2,
experimental: 3,
low: 4,
medium: 5,
high: 6,
critical: 7
}.with_indifferent_access.freeze
sha_attribute :project_fingerprint sha_attribute :project_fingerprint
sha_attribute :location_fingerprint sha_attribute :location_fingerprint
...@@ -33,6 +21,28 @@ module Vulnerabilities ...@@ -33,6 +21,28 @@ module Vulnerabilities
has_many :occurrence_pipelines, class_name: 'Vulnerabilities::OccurrencePipeline' has_many :occurrence_pipelines, class_name: 'Vulnerabilities::OccurrencePipeline'
has_many :pipelines, through: :occurrence_pipelines, class_name: 'Ci::Pipeline' has_many :pipelines, through: :occurrence_pipelines, class_name: 'Ci::Pipeline'
CONFIDENCE_LEVELS = {
undefined: 0,
ignore: 1,
unknown: 2,
experimental: 3,
low: 4,
medium: 5,
high: 6,
confirmed: 7
}.with_indifferent_access.freeze
SEVERITY_LEVELS = {
undefined: 0,
info: 1,
unknown: 2,
# experimental: 3, formerly used by confidence, no longer applicable
low: 4,
medium: 5,
high: 6,
critical: 7
}.with_indifferent_access.freeze
REPORT_TYPES = { REPORT_TYPES = {
sast: 0, sast: 0,
dependency_scanning: 1, dependency_scanning: 1,
...@@ -40,7 +50,9 @@ module Vulnerabilities ...@@ -40,7 +50,9 @@ module Vulnerabilities
dast: 3 dast: 3
}.with_indifferent_access.freeze }.with_indifferent_access.freeze
enum confidence: CONFIDENCE_LEVELS, _prefix: :confidence
enum report_type: REPORT_TYPES enum report_type: REPORT_TYPES
enum severity: SEVERITY_LEVELS, _prefix: :severity
validates :scanner, presence: true validates :scanner, presence: true
validates :project, presence: true validates :project, presence: true
...@@ -54,8 +66,8 @@ module Vulnerabilities ...@@ -54,8 +66,8 @@ module Vulnerabilities
# validates :location_fingerprint, presence: true, uniqueness: { scope: [:primary_identifier_id, :scanner_id, :ref, :pipeline_id, :project_id] } # validates :location_fingerprint, presence: true, uniqueness: { scope: [:primary_identifier_id, :scanner_id, :ref, :pipeline_id, :project_id] }
validates :name, presence: true validates :name, presence: true
validates :report_type, presence: true validates :report_type, presence: true
validates :severity, presence: true, inclusion: { in: LEVELS.keys } validates :severity, presence: true
validates :confidence, presence: true, inclusion: { in: LEVELS.keys } validates :confidence, presence: true
validates :metadata_version, presence: true validates :metadata_version, presence: true
validates :raw_metadata, presence: true validates :raw_metadata, presence: true
...@@ -85,7 +97,9 @@ module Vulnerabilities ...@@ -85,7 +97,9 @@ module Vulnerabilities
end end
def self.counted_by_severity def self.counted_by_severity
group(:severity).count group(:severity).count.each_with_object({}) do |(severity, count), accum|
accum[SEVERITY_LEVELS[severity]] = count
end
end end
def feedback(feedback_type:) def feedback(feedback_type:)
...@@ -124,26 +138,6 @@ module Vulnerabilities ...@@ -124,26 +138,6 @@ module Vulnerabilities
feedback(feedback_type: 'issue') feedback(feedback_type: 'issue')
end end
# Override getter and setter for :severity as we can't use enum (it conflicts with :confidence)
# To be replaced with enum using _prefix when migrating to rails 5
def severity
LEVELS.key(read_attribute(:severity))
end
def severity=(severity)
write_attribute(:severity, LEVELS[severity])
end
# Override getter and setter for :confidence as we can't use enum (it conflicts with :severity)
# To be replaced with enum using _prefix when migrating to rails 5
def confidence
LEVELS.key(read_attribute(:confidence))
end
def confidence=(confidence)
write_attribute(:confidence, LEVELS[confidence])
end
def metadata def metadata
strong_memoize(:metadata) do strong_memoize(:metadata) do
begin begin
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
class Vulnerabilities::HistoryEntity < Grape::Entity class Vulnerabilities::HistoryEntity < Grape::Entity
present_collection true present_collection true
Vulnerabilities::Occurrence::LEVELS.keys.each do |level| Vulnerabilities::Occurrence::SEVERITY_LEVELS.keys.each do |level|
expose level do |object| expose level do |object|
counts(by_severity[level]&.group_by(&:day) || {}) counts(by_severity[level]&.group_by(&:day) || {})
end end
......
# frozen_string_literal: true # frozen_string_literal: true
class VulnerabilitySummaryEntity < Grape::Entity class VulnerabilitySummaryEntity < Grape::Entity
Vulnerabilities::Occurrence::LEVELS.each do |severity_name, severity| Vulnerabilities::Occurrence::SEVERITY_LEVELS.each do |severity_name, severity|
expose severity_name do |param| expose severity_name do |param|
object[severity] || 0 object[severity] || 0
end end
......
---
title: Split severity and confidence values for vulnerabilities
merge_request: 9495
author:
type: changed
...@@ -90,7 +90,7 @@ module Gitlab ...@@ -90,7 +90,7 @@ module Gitlab
def severity(value) def severity(value)
case Integer(value) case Integer(value)
when 0 when 0
'ignore' 'info'
when 1 when 1
'low' 'low'
when 2 when 2
...@@ -116,7 +116,7 @@ module Gitlab ...@@ -116,7 +116,7 @@ module Gitlab
when 3 when 3
'high' 'high'
when 4 when 4
'critical' 'confirmed'
else else
'unknown' 'unknown'
end end
......
...@@ -336,9 +336,8 @@ describe Groups::Security::VulnerabilitiesController do ...@@ -336,9 +336,8 @@ describe Groups::Security::VulnerabilitiesController do
expect(json_response).to be_an(Hash) expect(json_response).to be_an(Hash)
expect(json_response).to eq({ expect(json_response).to eq({
"undefined" => {}, "undefined" => {},
"ignore" => {}, "info" => {},
"unknown" => {}, "unknown" => {},
"experimental" => {},
"low" => {}, "low" => {},
"medium" => {}, "medium" => {},
"high" => {}, "high" => {},
......
...@@ -16,11 +16,11 @@ ...@@ -16,11 +16,11 @@
"vulnerability_feedback_dismissal_path": { "type": "string" }, "vulnerability_feedback_dismissal_path": { "type": "string" },
"confidence" : { "confidence" : {
"type": "string", "type": "string",
"enum": ["undefined", "ignore", "unknown", "experimental", "low", "medium", "high", "critical"] "enum": ["undefined", "ignore", "unknown", "experimental", "low", "medium", "high", "confirmed"]
}, },
"severity" : { "severity" : {
"type": "string", "type": "string",
"enum": ["undefined", "ignore", "unknown", "experimental", "low", "medium", "high", "critical"] "enum": ["undefined", "info", "unknown", "low", "medium", "high", "critical"]
}, },
"report_type": { "report_type": {
"type": "string", "type": "string",
......
...@@ -124,7 +124,7 @@ describe Gitlab::Ci::Parsers::Security::Dast do ...@@ -124,7 +124,7 @@ describe Gitlab::Ci::Parsers::Security::Dast do
using RSpec::Parameterized::TableSyntax using RSpec::Parameterized::TableSyntax
where(:severity, :expected) do where(:severity, :expected) do
'0' | 'ignore' '0' | 'info'
'1' | 'low' '1' | 'low'
'2' | 'medium' '2' | 'medium'
'3' | 'high' '3' | 'high'
...@@ -147,7 +147,7 @@ describe Gitlab::Ci::Parsers::Security::Dast do ...@@ -147,7 +147,7 @@ describe Gitlab::Ci::Parsers::Security::Dast do
'1' | 'low' '1' | 'low'
'2' | 'medium' '2' | 'medium'
'3' | 'high' '3' | 'high'
'4' | 'critical' '4' | 'confirmed'
'42' | 'unknown' '42' | 'unknown'
'' | 'unknown' '' | 'unknown'
end end
......
...@@ -3,7 +3,9 @@ ...@@ -3,7 +3,9 @@
require 'spec_helper' require 'spec_helper'
describe Vulnerabilities::Occurrence do describe Vulnerabilities::Occurrence 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(:report_type) }
it { is_expected.to define_enum_for(:severity) }
describe 'associations' do describe 'associations' do
it { is_expected.to belong_to(:project) } it { is_expected.to belong_to(:project) }
...@@ -29,9 +31,7 @@ describe Vulnerabilities::Occurrence do ...@@ -29,9 +31,7 @@ describe Vulnerabilities::Occurrence do
it { is_expected.to validate_presence_of(:metadata_version) } it { is_expected.to validate_presence_of(:metadata_version) }
it { is_expected.to validate_presence_of(:raw_metadata) } it { is_expected.to validate_presence_of(:raw_metadata) }
it { is_expected.to validate_presence_of(:severity) } it { is_expected.to validate_presence_of(:severity) }
it { is_expected.to validate_inclusion_of(:severity).in_array(described_class::LEVELS.keys) }
it { is_expected.to validate_presence_of(:confidence) } it { is_expected.to validate_presence_of(:confidence) }
it { is_expected.to validate_inclusion_of(:confidence).in_array(described_class::LEVELS.keys) }
end end
context 'database uniqueness' do context 'database uniqueness' do
...@@ -219,7 +219,7 @@ describe Vulnerabilities::Occurrence do ...@@ -219,7 +219,7 @@ describe Vulnerabilities::Occurrence do
end end
end end
describe '.count_by_severity' do describe '.counted_by_severity' do
let!(:high_vulnerabilities) { create_list(:vulnerabilities_occurrence, 3, severity: :high) } let!(:high_vulnerabilities) { create_list(:vulnerabilities_occurrence, 3, severity: :high) }
let!(:medium_vulnerabilities) { create_list(:vulnerabilities_occurrence, 2, severity: :medium) } let!(:medium_vulnerabilities) { create_list(:vulnerabilities_occurrence, 2, severity: :medium) }
let!(:low_vulnerabilities) { create_list(:vulnerabilities_occurrence, 1, severity: :low) } let!(:low_vulnerabilities) { create_list(:vulnerabilities_occurrence, 1, severity: :low) }
......
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