Commit 11a99bfe authored by Mehmet Emin INAC's avatar Mehmet Emin INAC

Introduce `Security::Finding` model

This model will represent all the findings discovered on all pipelines.
Unlike `Vulnerabilities::Finding` this model stores only the subset of
finding data which will be used to optimize the performance of pipeline
security tab by identifying which report artifact(s) to download and
parse while generating the paginated list of findings.
parent 1f176abf
# frozen_string_literal: true
class CreateSecurityFindingsTable < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
create_table :security_findings, id: :bigserial do |t|
t.references :scan, null: false, foreign_key: { to_table: :security_scans, on_delete: :cascade }
t.references :scanner, null: false, foreign_key: { to_table: :vulnerability_scanners, on_delete: :cascade }
t.integer :severity, limit: 2, index: true, null: false
t.integer :confidence, limit: 2, index: true, null: false
t.text :project_fingerprint, index: true, null: false
end
add_text_limit :security_findings, :project_fingerprint, 40
end
def down
drop_table :security_findings
end
end
d5e81848257b3391d99b198b177531a4c190ca6f19b27c9aedaa931f6eb3165a
\ No newline at end of file
......@@ -15353,6 +15353,25 @@ CREATE SEQUENCE public.scim_oauth_access_tokens_id_seq
ALTER SEQUENCE public.scim_oauth_access_tokens_id_seq OWNED BY public.scim_oauth_access_tokens.id;
CREATE TABLE public.security_findings (
id bigint NOT NULL,
scan_id bigint NOT NULL,
scanner_id bigint NOT NULL,
severity smallint NOT NULL,
confidence smallint NOT NULL,
project_fingerprint text NOT NULL,
CONSTRAINT check_b9508c6df8 CHECK ((char_length(project_fingerprint) <= 40))
);
CREATE SEQUENCE public.security_findings_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE public.security_findings_id_seq OWNED BY public.security_findings.id;
CREATE TABLE public.security_scans (
id bigint NOT NULL,
created_at timestamp with time zone NOT NULL,
......@@ -17348,6 +17367,8 @@ ALTER TABLE ONLY public.scim_identities ALTER COLUMN id SET DEFAULT nextval('pub
ALTER TABLE ONLY public.scim_oauth_access_tokens ALTER COLUMN id SET DEFAULT nextval('public.scim_oauth_access_tokens_id_seq'::regclass);
ALTER TABLE ONLY public.security_findings ALTER COLUMN id SET DEFAULT nextval('public.security_findings_id_seq'::regclass);
ALTER TABLE ONLY public.security_scans ALTER COLUMN id SET DEFAULT nextval('public.security_scans_id_seq'::regclass);
ALTER TABLE ONLY public.self_managed_prometheus_alert_events ALTER COLUMN id SET DEFAULT nextval('public.self_managed_prometheus_alert_events_id_seq'::regclass);
......@@ -18604,6 +18625,9 @@ ALTER TABLE ONLY public.scim_identities
ALTER TABLE ONLY public.scim_oauth_access_tokens
ADD CONSTRAINT scim_oauth_access_tokens_pkey PRIMARY KEY (id);
ALTER TABLE ONLY public.security_findings
ADD CONSTRAINT security_findings_pkey PRIMARY KEY (id);
ALTER TABLE ONLY public.security_scans
ADD CONSTRAINT security_scans_pkey PRIMARY KEY (id);
......@@ -20760,6 +20784,16 @@ CREATE INDEX index_secure_ci_builds_on_user_id_created_at_parser_features ON pub
CREATE INDEX index_security_ci_builds_on_name_and_id_parser_features ON public.ci_builds USING btree (name, id) WHERE (((name)::text = ANY (ARRAY[('container_scanning'::character varying)::text, ('dast'::character varying)::text, ('dependency_scanning'::character varying)::text, ('license_management'::character varying)::text, ('sast'::character varying)::text, ('secret_detection'::character varying)::text, ('coverage_fuzzing'::character varying)::text, ('license_scanning'::character varying)::text])) AND ((type)::text = 'Ci::Build'::text));
CREATE INDEX index_security_findings_on_confidence ON public.security_findings USING btree (confidence);
CREATE INDEX index_security_findings_on_project_fingerprint ON public.security_findings USING btree (project_fingerprint);
CREATE INDEX index_security_findings_on_scan_id ON public.security_findings USING btree (scan_id);
CREATE INDEX index_security_findings_on_scanner_id ON public.security_findings USING btree (scanner_id);
CREATE INDEX index_security_findings_on_severity ON public.security_findings USING btree (severity);
CREATE INDEX index_self_managed_prometheus_alert_events_on_environment_id ON public.self_managed_prometheus_alert_events USING btree (environment_id);
CREATE INDEX index_sent_notifications_on_noteable_type_noteable_id ON public.sent_notifications USING btree (noteable_id) WHERE ((noteable_type)::text = 'Issue'::text);
......@@ -22628,6 +22662,9 @@ ALTER TABLE ONLY public.list_user_preferences
ALTER TABLE ONLY public.project_custom_attributes
ADD CONSTRAINT fk_rails_719c3dccc5 FOREIGN KEY (project_id) REFERENCES public.projects(id) ON DELETE CASCADE;
ALTER TABLE ONLY public.security_findings
ADD CONSTRAINT fk_rails_729b763a54 FOREIGN KEY (scanner_id) REFERENCES public.vulnerability_scanners(id) ON DELETE CASCADE;
ALTER TABLE ONLY public.dast_scanner_profiles
ADD CONSTRAINT fk_rails_72a8ba7141 FOREIGN KEY (project_id) REFERENCES public.projects(id) ON DELETE CASCADE;
......@@ -22955,6 +22992,9 @@ ALTER TABLE ONLY public.approval_project_rules_users
ALTER TABLE ONLY public.lists
ADD CONSTRAINT fk_rails_baed5f39b7 FOREIGN KEY (milestone_id) REFERENCES public.milestones(id) ON DELETE CASCADE;
ALTER TABLE ONLY public.security_findings
ADD CONSTRAINT fk_rails_bb63863cf1 FOREIGN KEY (scan_id) REFERENCES public.security_scans(id) ON DELETE CASCADE;
ALTER TABLE ONLY public.approval_merge_request_rules_users
ADD CONSTRAINT fk_rails_bc8972fa55 FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE;
......
# frozen_string_literal: true
# This model represents the vulnerability findings
# discovered for all pipelines to use in pipeline
# security tab.
#
# Unlike `Vulnerabilities::Finding` model, this one
# only stores some important meta information to
# calculate which report artifact to download and parse.
module Security
class Finding < ApplicationRecord
self.table_name = 'security_findings'
belongs_to :scan, optional: false
belongs_to :scanner, class_name: 'Vulnerabilities::Scanner', optional: false
# TODO: These are duplicated between this model and Vulnerabilities::Finding,
# we should create a shared module to encapculate this in one place.
enum confidence: Vulnerabilities::Finding::CONFIDENCE_LEVELS, _prefix: :confidence
enum severity: Vulnerabilities::Finding::SEVERITY_LEVELS, _prefix: :severity
validates :project_fingerprint, presence: true, length: { maximum: 40 }
end
end
......@@ -8,8 +8,11 @@ module Security
validates :scan_type, presence: true
belongs_to :build, class_name: 'Ci::Build'
has_one :pipeline, class_name: 'Ci::Pipeline', through: :build
has_many :findings
enum scan_type: {
sast: 1,
dependency_scanning: 2,
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Security::Finding do
describe 'associations' do
it { is_expected.to belong_to(:scan).required }
it { is_expected.to belong_to(:scanner).required }
end
describe 'validations' do
it { is_expected.to validate_presence_of(:project_fingerprint) }
it { is_expected.to validate_length_of(:project_fingerprint).is_at_most(40) }
end
end
......@@ -6,6 +6,7 @@ RSpec.describe Security::Scan do
describe 'associations' do
it { is_expected.to belong_to(:build) }
it { is_expected.to have_one(:pipeline).through(:build).class_name('Ci::Pipeline') }
it { is_expected.to have_many(:findings) }
end
describe 'validations' 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