Commit 90ca3058 authored by Sashi Kumar Kumaresan's avatar Sashi Kumar Kumaresan Committed by Tetiana Chupryna

Create vulnerability read model

This change adds a migration to create vulnerability_reads
table that will increase the performance of filtering of
vulnerabilities.

Changelog: added
parent 71eb80a6
# frozen_string_literal: true
class CreateVulnerabilityReads < Gitlab::Database::Migration[1.0]
def change
create_table :vulnerability_reads do |t|
t.bigint :vulnerability_id, null: false
t.bigint :project_id, null: false
t.bigint :scanner_id, null: false
t.integer :report_type, limit: 2, null: false
t.integer :severity, limit: 2, null: false
t.integer :state, limit: 2, null: false
t.boolean :has_issues, default: false, null: false
t.boolean :resolved_on_default_branch, default: false, null: false
t.uuid :uuid, null: false
t.text :location_image, limit: 2048
t.index :vulnerability_id, unique: true
t.index :scanner_id
t.index :uuid, unique: true
t.index [:project_id, :state, :severity, :vulnerability_id], name: :index_vuln_reads_on_project_id_state_severity_and_vuln_id, order: { vulnerability_id: :desc }
t.index :location_image, where: "report_type IN (2, 7)", name: :index_vulnerability_reads_on_location_image
end
end
end
# frozen_string_literal: true
class AddForeignKeyToVulnerabilityReadsOnVulnerability < Gitlab::Database::Migration[1.0]
disable_ddl_transaction!
def up
add_concurrent_foreign_key :vulnerability_reads, :vulnerabilities, column: :vulnerability_id, on_delete: :cascade
end
def down
with_lock_retries do
remove_foreign_key :vulnerability_reads, column: :vulnerability_id
end
end
end
# frozen_string_literal: true
class AddForeignKeyToVulnerabilityReadsOnProject < Gitlab::Database::Migration[1.0]
disable_ddl_transaction!
def up
add_concurrent_foreign_key :vulnerability_reads, :projects, column: :project_id, on_delete: :cascade
end
def down
with_lock_retries do
remove_foreign_key :vulnerability_reads, column: :project_id
end
end
end
# frozen_string_literal: true
class AddForeignKeyToVulnerabilityReadsOnScanner < Gitlab::Database::Migration[1.0]
disable_ddl_transaction!
def up
add_concurrent_foreign_key :vulnerability_reads, :vulnerability_scanners, column: :scanner_id, on_delete: :cascade
end
def down
with_lock_retries do
remove_foreign_key :vulnerability_reads, column: :scanner_id
end
end
end
c7c29b136fbe00271807fcd3133baf7a6e9ded40989fc274e941fc99f2c19e4d
\ No newline at end of file
d9a0886d95cd54add9e63475a2f1ca0601304bb64ffe6e6d9e62cb8997d5fe40
\ No newline at end of file
f25ee0df287f1c44740be143831537bf262d09d7068ceca1c516ee964bc3aa24
\ No newline at end of file
e032fd334d175d803b943c6328048705e81bd70af6ac226a032281304840f1cd
\ No newline at end of file
......@@ -20782,6 +20782,30 @@ CREATE SEQUENCE vulnerability_occurrences_id_seq
ALTER SEQUENCE vulnerability_occurrences_id_seq OWNED BY vulnerability_occurrences.id;
CREATE TABLE vulnerability_reads (
id bigint NOT NULL,
vulnerability_id bigint NOT NULL,
project_id bigint NOT NULL,
scanner_id bigint NOT NULL,
report_type smallint NOT NULL,
severity smallint NOT NULL,
state smallint NOT NULL,
has_issues boolean DEFAULT false NOT NULL,
resolved_on_default_branch boolean DEFAULT false NOT NULL,
uuid uuid NOT NULL,
location_image text,
CONSTRAINT check_380451bdbe CHECK ((char_length(location_image) <= 2048))
);
CREATE SEQUENCE vulnerability_reads_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE vulnerability_reads_id_seq OWNED BY vulnerability_reads.id;
CREATE TABLE vulnerability_remediations (
id bigint NOT NULL,
created_at timestamp with time zone NOT NULL,
......@@ -22026,6 +22050,8 @@ ALTER TABLE ONLY vulnerability_occurrence_pipelines ALTER COLUMN id SET DEFAULT
ALTER TABLE ONLY vulnerability_occurrences ALTER COLUMN id SET DEFAULT nextval('vulnerability_occurrences_id_seq'::regclass);
ALTER TABLE ONLY vulnerability_reads ALTER COLUMN id SET DEFAULT nextval('vulnerability_reads_id_seq'::regclass);
ALTER TABLE ONLY vulnerability_remediations ALTER COLUMN id SET DEFAULT nextval('vulnerability_remediations_id_seq'::regclass);
ALTER TABLE ONLY vulnerability_scanners ALTER COLUMN id SET DEFAULT nextval('vulnerability_scanners_id_seq'::regclass);
......@@ -24042,6 +24068,9 @@ ALTER TABLE ONLY vulnerability_occurrence_pipelines
ALTER TABLE ONLY vulnerability_occurrences
ADD CONSTRAINT vulnerability_occurrences_pkey PRIMARY KEY (id);
ALTER TABLE ONLY vulnerability_reads
ADD CONSTRAINT vulnerability_reads_pkey PRIMARY KEY (id);
ALTER TABLE ONLY vulnerability_remediations
ADD CONSTRAINT vulnerability_remediations_pkey PRIMARY KEY (id);
......@@ -27575,6 +27604,8 @@ COMMENT ON INDEX index_verification_codes_on_phone_and_visitor_id_code IS 'JiHu-
CREATE UNIQUE INDEX index_vuln_historical_statistics_on_project_id_and_date ON vulnerability_historical_statistics USING btree (project_id, date);
CREATE INDEX index_vuln_reads_on_project_id_state_severity_and_vuln_id ON vulnerability_reads USING btree (project_id, state, severity, vulnerability_id DESC);
CREATE INDEX index_vulnerabilities_on_author_id ON vulnerabilities USING btree (author_id);
CREATE INDEX index_vulnerabilities_on_confirmed_by_id ON vulnerabilities USING btree (confirmed_by_id);
......@@ -27669,6 +27700,14 @@ CREATE UNIQUE INDEX index_vulnerability_occurrences_on_uuid ON vulnerability_occ
CREATE INDEX index_vulnerability_occurrences_on_vulnerability_id ON vulnerability_occurrences USING btree (vulnerability_id);
CREATE INDEX index_vulnerability_reads_on_location_image ON vulnerability_reads USING btree (location_image) WHERE (report_type = ANY (ARRAY[2, 7]));
CREATE INDEX index_vulnerability_reads_on_scanner_id ON vulnerability_reads USING btree (scanner_id);
CREATE UNIQUE INDEX index_vulnerability_reads_on_uuid ON vulnerability_reads USING btree (uuid);
CREATE UNIQUE INDEX index_vulnerability_reads_on_vulnerability_id ON vulnerability_reads USING btree (vulnerability_id);
CREATE UNIQUE INDEX index_vulnerability_remediations_on_project_id_and_checksum ON vulnerability_remediations USING btree (project_id, checksum);
CREATE UNIQUE INDEX index_vulnerability_scanners_on_project_id_and_external_id ON vulnerability_scanners USING btree (project_id, external_id);
......@@ -28981,6 +29020,9 @@ ALTER TABLE ONLY releases
ALTER TABLE ONLY geo_event_log
ADD CONSTRAINT fk_4a99ebfd60 FOREIGN KEY (repositories_changed_event_id) REFERENCES geo_repositories_changed_events(id) ON DELETE CASCADE;
ALTER TABLE ONLY vulnerability_reads
ADD CONSTRAINT fk_5001652292 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
ALTER TABLE ONLY alert_management_alerts
ADD CONSTRAINT fk_51ab4b6089 FOREIGN KEY (prometheus_alert_id) REFERENCES prometheus_alerts(id) ON DELETE CASCADE;
......@@ -29029,6 +29071,9 @@ ALTER TABLE ONLY dast_profile_schedules
ALTER TABLE ONLY events
ADD CONSTRAINT fk_61fbf6ca48 FOREIGN KEY (group_id) REFERENCES namespaces(id) ON DELETE CASCADE;
ALTER TABLE ONLY vulnerability_reads
ADD CONSTRAINT fk_62736f638f FOREIGN KEY (vulnerability_id) REFERENCES vulnerabilities(id) ON DELETE CASCADE;
ALTER TABLE ONLY merge_requests
ADD CONSTRAINT fk_641731faff FOREIGN KEY (updated_by_id) REFERENCES users(id) ON DELETE SET NULL;
......@@ -29320,6 +29365,9 @@ ALTER TABLE ONLY vulnerabilities
ALTER TABLE ONLY project_access_tokens
ADD CONSTRAINT fk_b27801bfbf FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
ALTER TABLE ONLY vulnerability_reads
ADD CONSTRAINT fk_b28c28abf1 FOREIGN KEY (scanner_id) REFERENCES vulnerability_scanners(id) ON DELETE CASCADE;
ALTER TABLE ONLY issues
ADD CONSTRAINT fk_b37be69be6 FOREIGN KEY (work_item_type_id) REFERENCES work_item_types(id);
# frozen_string_literal: true
module Vulnerabilities
class Read < ApplicationRecord
self.table_name = "vulnerability_reads"
belongs_to :vulnerability
belongs_to :project
belongs_to :scanner, class_name: 'Vulnerabilities::Scanner'
validates :vulnerability_id, uniqueness: true, presence: true
validates :project_id, presence: true
validates :scanner_id, presence: true
validates :report_type, presence: true
validates :severity, presence: true
validates :state, presence: true
validates :uuid, uniqueness: { case_sensitive: false }, presence: true
validates :location_image, length: { maximum: 2048 }
validates :has_issues, inclusion: { in: [true, false], message: _('must be a boolean value') }
validates :resolved_on_default_branch, inclusion: { in: [true, false], message: _('must be a boolean value') }
enum state: ::Enums::Vulnerability.vulnerability_states
enum report_type: ::Enums::Vulnerability.report_types
enum severity: ::Enums::Vulnerability.severity_levels, _prefix: :severity
end
end
# frozen_string_literal: true
FactoryBot.define do
factory :vulnerability_read, class: 'Vulnerabilities::Read' do
vulnerability factory: :vulnerability
project factory: :project
scanner factory: :vulnerabilities_scanner
report_type { :sast }
severity { :high }
state { Vulnerability.states[:detected] }
uuid { SecureRandom.uuid }
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Vulnerabilities::Read, type: :model do
describe 'associations' do
it { is_expected.to belong_to(:vulnerability) }
it { is_expected.to belong_to(:project) }
it { is_expected.to belong_to(:scanner).class_name('Vulnerabilities::Scanner') }
end
describe 'validations' do
let!(:vulnerability_read) { create(:vulnerability_read) }
it { is_expected.to validate_presence_of(:vulnerability_id) }
it { is_expected.to validate_presence_of(:project_id) }
it { is_expected.to validate_presence_of(:scanner_id) }
it { is_expected.to validate_presence_of(:report_type) }
it { is_expected.to validate_presence_of(:severity) }
it { is_expected.to validate_presence_of(:state) }
it { is_expected.to validate_presence_of(:uuid) }
it { is_expected.to validate_length_of(:location_image).is_at_most(2048) }
it { is_expected.to validate_uniqueness_of(:vulnerability_id) }
it { is_expected.to validate_uniqueness_of(:uuid).case_insensitive }
it { is_expected.to allow_value(true).for(:has_issues) }
it { is_expected.to allow_value(false).for(:has_issues) }
it { is_expected.not_to allow_value(nil).for(:has_issues) }
it { is_expected.to allow_value(true).for(:resolved_on_default_branch) }
it { is_expected.to allow_value(false).for(:resolved_on_default_branch) }
it { is_expected.not_to allow_value(nil).for(:resolved_on_default_branch) }
end
end
......@@ -528,6 +528,7 @@ vulnerability_issue_links: :gitlab_main
vulnerability_occurrence_identifiers: :gitlab_main
vulnerability_occurrence_pipelines: :gitlab_main
vulnerability_occurrences: :gitlab_main
vulnerability_reads: :gitlab_main
vulnerability_remediations: :gitlab_main
vulnerability_scanners: :gitlab_main
vulnerability_statistics: :gitlab_main
......
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