From d11c7dc39edb2bbb8404f0bba92f1c3ee24523af Mon Sep 17 00:00:00 2001 From: mc_rocha Date: Fri, 22 Aug 2025 14:06:23 -0400 Subject: [PATCH 1/3] Add security_policy_dismissals table This MR adds a new database table to stores the relation between security policies and dismissed findings. Changelog: added --- db/docs/security_policy_dismissals.yml | 13 ++++++ ...05145_create_security_policy_dismissals.rb | 28 ++++++++++++ ...reign_key_to_security_policy_dismissals.rb | 16 +++++++ ...reign_key_to_security_policy_dismissals.rb | 16 +++++++ ...reign_key_to_security_policy_dismissals.rb | 16 +++++++ ...reign_key_to_security_policy_dismissals.rb | 16 +++++++ db/schema_migrations/20250821205145 | 1 + db/schema_migrations/20250821210738 | 1 + db/schema_migrations/20250821211206 | 1 + db/schema_migrations/20250821211452 | 1 + db/schema_migrations/20250822200745 | 1 + db/structure.sql | 45 +++++++++++++++++++ ee/app/models/security/policy_dismissal.rb | 18 ++++++++ .../factories/security/policy_dismissal.rb | 11 +++++ .../models/security/policy_dismissal_spec.rb | 22 +++++++++ 15 files changed, 206 insertions(+) create mode 100644 db/docs/security_policy_dismissals.yml create mode 100644 db/migrate/20250821205145_create_security_policy_dismissals.rb create mode 100644 db/migrate/20250821210738_add_merge_request_foreign_key_to_security_policy_dismissals.rb create mode 100644 db/migrate/20250821211206_add_security_policy_foreign_key_to_security_policy_dismissals.rb create mode 100644 db/migrate/20250821211452_add_user_foreign_key_to_security_policy_dismissals.rb create mode 100644 db/post_migrate/20250822200745_add_project_foreign_key_to_security_policy_dismissals.rb create mode 100644 db/schema_migrations/20250821205145 create mode 100644 db/schema_migrations/20250821210738 create mode 100644 db/schema_migrations/20250821211206 create mode 100644 db/schema_migrations/20250821211452 create mode 100644 db/schema_migrations/20250822200745 create mode 100644 ee/app/models/security/policy_dismissal.rb create mode 100644 ee/spec/factories/security/policy_dismissal.rb create mode 100644 ee/spec/models/security/policy_dismissal_spec.rb diff --git a/db/docs/security_policy_dismissals.yml b/db/docs/security_policy_dismissals.yml new file mode 100644 index 00000000000000..4e0372745f9907 --- /dev/null +++ b/db/docs/security_policy_dismissals.yml @@ -0,0 +1,13 @@ +--- +table_name: security_policy_dismissals +classes: + - Security::PolicyDismissal +feature_categories: + - security_policy_management +description: Stores the relation between security policies and dismissed findings +introduced_by_url: +milestone: '18.4' +gitlab_schema: gitlab_main_cell +sharding_key: + project_id: projects +table_size: small diff --git a/db/migrate/20250821205145_create_security_policy_dismissals.rb b/db/migrate/20250821205145_create_security_policy_dismissals.rb new file mode 100644 index 00000000000000..b383a85d10b64a --- /dev/null +++ b/db/migrate/20250821205145_create_security_policy_dismissals.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +class CreateSecurityPolicyDismissals < Gitlab::Database::Migration[2.3] + milestone '18.4' + + INDEX_NAME = 'i_policy_dismissals_on_merge_request_id_and_security_policy_id' + + def up + # Factory: /ee/spec/factories/security/policy_dismissal.rb + create_table :security_policy_dismissals do |t| # rubocop:disable Migration/EnsureFactoryForTable -- reason above + t.timestamps_with_timezone null: false + t.bigint :project_id, null: false + t.bigint :merge_request_id, null: false + t.bigint :security_policy_id, null: false + t.bigint :dismissed_by_id, null: true + t.text :security_findings_uuids, array: true, default: [], null: false + + t.index [:merge_request_id, :security_policy_id], unique: true, name: INDEX_NAME + t.index :project_id + t.index :security_policy_id + t.index :dismissed_by_id + end + end + + def down + drop_table :security_policy_dismissals + end +end diff --git a/db/migrate/20250821210738_add_merge_request_foreign_key_to_security_policy_dismissals.rb b/db/migrate/20250821210738_add_merge_request_foreign_key_to_security_policy_dismissals.rb new file mode 100644 index 00000000000000..a490ffb821a3ad --- /dev/null +++ b/db/migrate/20250821210738_add_merge_request_foreign_key_to_security_policy_dismissals.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +class AddMergeRequestForeignKeyToSecurityPolicyDismissals < Gitlab::Database::Migration[2.3] + disable_ddl_transaction! + milestone '18.4' + + def up + add_concurrent_foreign_key :security_policy_dismissals, :merge_requests, + column: :merge_request_id, + on_delete: :cascade + end + + def down + remove_foreign_key_if_exists :security_policy_dismissals, column: :merge_request_id + end +end diff --git a/db/migrate/20250821211206_add_security_policy_foreign_key_to_security_policy_dismissals.rb b/db/migrate/20250821211206_add_security_policy_foreign_key_to_security_policy_dismissals.rb new file mode 100644 index 00000000000000..f5760a101c7413 --- /dev/null +++ b/db/migrate/20250821211206_add_security_policy_foreign_key_to_security_policy_dismissals.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +class AddSecurityPolicyForeignKeyToSecurityPolicyDismissals < Gitlab::Database::Migration[2.3] + disable_ddl_transaction! + milestone '18.4' + + def up + add_concurrent_foreign_key :security_policy_dismissals, :security_policies, + column: :security_policy_id, + on_delete: :cascade + end + + def down + remove_foreign_key_if_exists :security_policy_dismissals, column: :security_policy_id + end +end diff --git a/db/migrate/20250821211452_add_user_foreign_key_to_security_policy_dismissals.rb b/db/migrate/20250821211452_add_user_foreign_key_to_security_policy_dismissals.rb new file mode 100644 index 00000000000000..e9c79f9ea0d651 --- /dev/null +++ b/db/migrate/20250821211452_add_user_foreign_key_to_security_policy_dismissals.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +class AddUserForeignKeyToSecurityPolicyDismissals < Gitlab::Database::Migration[2.3] + disable_ddl_transaction! + milestone '18.4' + + def up + add_concurrent_foreign_key :security_policy_dismissals, :users, + column: :dismissed_by_id, + on_delete: :nullify + end + + def down + remove_foreign_key_if_exists :security_policy_dismissals, column: :dismissed_by_id + end +end diff --git a/db/post_migrate/20250822200745_add_project_foreign_key_to_security_policy_dismissals.rb b/db/post_migrate/20250822200745_add_project_foreign_key_to_security_policy_dismissals.rb new file mode 100644 index 00000000000000..f30a84249d6c23 --- /dev/null +++ b/db/post_migrate/20250822200745_add_project_foreign_key_to_security_policy_dismissals.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +class AddProjectForeignKeyToSecurityPolicyDismissals < Gitlab::Database::Migration[2.3] + disable_ddl_transaction! + milestone '18.4' + + def up + add_concurrent_foreign_key :security_policy_dismissals, :projects, + column: :project_id, + on_delete: :cascade + end + + def down + remove_foreign_key_if_exists :security_policy_dismissals, column: :project_id + end +end diff --git a/db/schema_migrations/20250821205145 b/db/schema_migrations/20250821205145 new file mode 100644 index 00000000000000..2240966ca291ad --- /dev/null +++ b/db/schema_migrations/20250821205145 @@ -0,0 +1 @@ +83d53f9ee72944febea52690f3962f61ba73d97aa471b3fe0152875dce2737ec \ No newline at end of file diff --git a/db/schema_migrations/20250821210738 b/db/schema_migrations/20250821210738 new file mode 100644 index 00000000000000..e7215bc9aff24a --- /dev/null +++ b/db/schema_migrations/20250821210738 @@ -0,0 +1 @@ +1f097772d190fb90c64be219572ad1b4a7427ac4fd14ef773f20814328d6406d \ No newline at end of file diff --git a/db/schema_migrations/20250821211206 b/db/schema_migrations/20250821211206 new file mode 100644 index 00000000000000..c566b889c85ab3 --- /dev/null +++ b/db/schema_migrations/20250821211206 @@ -0,0 +1 @@ +0a036d3c5dfddf7e178cf070c011e2554c9bd25e833ff4d9bce402aabc2e858b \ No newline at end of file diff --git a/db/schema_migrations/20250821211452 b/db/schema_migrations/20250821211452 new file mode 100644 index 00000000000000..709453f294241b --- /dev/null +++ b/db/schema_migrations/20250821211452 @@ -0,0 +1 @@ +e7d1baccc513297e86e377a49b787430ce0288e332096fcdc0379d88478d7b09 \ No newline at end of file diff --git a/db/schema_migrations/20250822200745 b/db/schema_migrations/20250822200745 new file mode 100644 index 00000000000000..6394c7f7c52627 --- /dev/null +++ b/db/schema_migrations/20250822200745 @@ -0,0 +1 @@ +b3c81f066066792ced7c6abf85cd4e235643641aa811ff99533365993ed2d43c \ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index 7c0fdf534f3f69..bd72c5dd8d738c 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -24869,6 +24869,26 @@ CREATE SEQUENCE security_policies_id_seq ALTER SEQUENCE security_policies_id_seq OWNED BY security_policies.id; +CREATE TABLE security_policy_dismissals ( + id bigint NOT NULL, + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone NOT NULL, + project_id bigint NOT NULL, + merge_request_id bigint NOT NULL, + security_policy_id bigint NOT NULL, + dismissed_by_id bigint, + security_findings_uuids text[] DEFAULT '{}'::text[] NOT NULL +); + +CREATE SEQUENCE security_policy_dismissals_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER SEQUENCE security_policy_dismissals_id_seq OWNED BY security_policy_dismissals.id; + CREATE TABLE security_policy_project_links ( id bigint NOT NULL, project_id bigint NOT NULL, @@ -30415,6 +30435,8 @@ ALTER TABLE ONLY security_pipeline_execution_project_schedules ALTER COLUMN id S ALTER TABLE ONLY security_policies ALTER COLUMN id SET DEFAULT nextval('security_policies_id_seq'::regclass); +ALTER TABLE ONLY security_policy_dismissals ALTER COLUMN id SET DEFAULT nextval('security_policy_dismissals_id_seq'::regclass); + ALTER TABLE ONLY security_policy_project_links ALTER COLUMN id SET DEFAULT nextval('security_policy_project_links_id_seq'::regclass); ALTER TABLE ONLY security_policy_requirements ALTER COLUMN id SET DEFAULT nextval('security_policy_requirements_id_seq'::regclass); @@ -33817,6 +33839,9 @@ ALTER TABLE ONLY security_pipeline_execution_project_schedules ALTER TABLE ONLY security_policies ADD CONSTRAINT security_policies_pkey PRIMARY KEY (id); +ALTER TABLE ONLY security_policy_dismissals + ADD CONSTRAINT security_policy_dismissals_pkey PRIMARY KEY (id); + ALTER TABLE ONLY security_policy_project_links ADD CONSTRAINT security_policy_project_links_pkey PRIMARY KEY (id); @@ -36449,6 +36474,8 @@ CREATE UNIQUE INDEX i_pm_package_versions_on_package_id_and_version ON pm_packag CREATE UNIQUE INDEX i_pm_packages_purl_type_and_name ON pm_packages USING btree (purl_type, name); +CREATE UNIQUE INDEX i_policy_dismissals_on_merge_request_id_and_security_policy_id ON security_policy_dismissals USING btree (merge_request_id, security_policy_id); + CREATE INDEX i_project_compliance_violations_on_namespace_id_created_at_id ON project_compliance_violations USING btree (namespace_id, created_at DESC, id DESC); CREATE INDEX i_project_requirement_statuses_on_namespace_id_framework_id ON project_requirement_compliance_statuses USING btree (namespace_id, compliance_framework_id, id); @@ -40921,6 +40948,12 @@ CREATE INDEX index_security_policies_on_policy_management_project_id ON security CREATE UNIQUE INDEX index_security_policies_on_unique_config_type_policy_index ON security_policies USING btree (security_orchestration_policy_configuration_id, type, policy_index); +CREATE INDEX index_security_policy_dismissals_on_dismissed_by_id ON security_policy_dismissals USING btree (dismissed_by_id); + +CREATE INDEX index_security_policy_dismissals_on_project_id ON security_policy_dismissals USING btree (project_id); + +CREATE INDEX index_security_policy_dismissals_on_security_policy_id ON security_policy_dismissals USING btree (security_policy_id); + CREATE UNIQUE INDEX index_security_policy_project_links_on_project_and_policy ON security_policy_project_links USING btree (security_policy_id, project_id); CREATE INDEX index_security_policy_requirements_on_compliance_requirement_id ON security_policy_requirements USING btree (compliance_requirement_id); @@ -46562,6 +46595,9 @@ ALTER TABLE ONLY jira_tracker_data ALTER TABLE ONLY packages_composer_packages ADD CONSTRAINT fk_2f085bfc2a FOREIGN KEY (creator_id) REFERENCES users(id) ON DELETE SET NULL; +ALTER TABLE ONLY security_policy_dismissals + ADD CONSTRAINT fk_2f3a252c44 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE; + ALTER TABLE ONLY required_code_owners_sections ADD CONSTRAINT fk_2f43f5cbbb FOREIGN KEY (protected_branch_project_id) REFERENCES projects(id) ON DELETE CASCADE; @@ -46946,6 +46982,9 @@ ALTER TABLE ONLY project_export_jobs ALTER TABLE ONLY security_policy_requirements ADD CONSTRAINT fk_5b4fae9635 FOREIGN KEY (compliance_requirement_id) REFERENCES compliance_requirements(id) ON DELETE CASCADE; +ALTER TABLE ONLY security_policy_dismissals + ADD CONSTRAINT fk_5b565b576c FOREIGN KEY (dismissed_by_id) REFERENCES users(id) ON DELETE SET NULL; + ALTER TABLE ONLY packages_conan_file_metadata ADD CONSTRAINT fk_5bb7e23d6d FOREIGN KEY (package_revision_id) REFERENCES packages_conan_package_revisions(id) ON DELETE CASCADE; @@ -47843,6 +47882,9 @@ ALTER TABLE p_ci_runner_machine_builds ALTER TABLE ONLY ai_catalog_item_consumers ADD CONSTRAINT fk_bba1649fa5 FOREIGN KEY (ai_catalog_item_id) REFERENCES ai_catalog_items(id) ON DELETE RESTRICT; +ALTER TABLE ONLY security_policy_dismissals + ADD CONSTRAINT fk_bc10da1827 FOREIGN KEY (merge_request_id) REFERENCES merge_requests(id) ON DELETE CASCADE; + ALTER TABLE ONLY wiki_page_meta_user_mentions ADD CONSTRAINT fk_bc155eba89 FOREIGN KEY (wiki_page_meta_id) REFERENCES wiki_page_meta(id) ON DELETE CASCADE; @@ -47894,6 +47936,9 @@ ALTER TABLE ONLY design_management_versions ALTER TABLE ONLY packages_packages ADD CONSTRAINT fk_c188f0dba4 FOREIGN KEY (creator_id) REFERENCES users(id) ON DELETE SET NULL; +ALTER TABLE ONLY security_policy_dismissals + ADD CONSTRAINT fk_c2379f1e97 FOREIGN KEY (security_policy_id) REFERENCES security_policies(id) ON DELETE CASCADE; + ALTER TABLE ONLY sbom_occurrences ADD CONSTRAINT fk_c2a5562923 FOREIGN KEY (source_id) REFERENCES sbom_sources(id) ON DELETE CASCADE; diff --git a/ee/app/models/security/policy_dismissal.rb b/ee/app/models/security/policy_dismissal.rb new file mode 100644 index 00000000000000..90730b3cc17fe8 --- /dev/null +++ b/ee/app/models/security/policy_dismissal.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +module Security + class PolicyDismissal < ApplicationRecord + self.table_name = 'security_policy_dismissals' + + belongs_to :project, class_name: 'Project' + belongs_to :merge_request, class_name: 'MergeRequest' + belongs_to :security_policy, class_name: 'Security::Policy' + belongs_to :dismissed_by, class_name: 'User', optional: true + + validates :project_id, presence: true + validates :merge_request_id, presence: true + validates :merge_request_id, uniqueness: { scope: :security_policy_id } + validates :security_policy_id, presence: true + validates :security_findings_uuids, presence: true + end +end diff --git a/ee/spec/factories/security/policy_dismissal.rb b/ee/spec/factories/security/policy_dismissal.rb new file mode 100644 index 00000000000000..25798308ced446 --- /dev/null +++ b/ee/spec/factories/security/policy_dismissal.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +FactoryBot.define do + factory :policy_dismissal, class: 'Security::PolicyDismissal' do + project + merge_request + security_policy + dismissed_by factory: :user + security_findings_uuids { [SecureRandom.uuid] } + end +end diff --git a/ee/spec/models/security/policy_dismissal_spec.rb b/ee/spec/models/security/policy_dismissal_spec.rb new file mode 100644 index 00000000000000..fe2f2c9f1e46b3 --- /dev/null +++ b/ee/spec/models/security/policy_dismissal_spec.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Security::PolicyDismissal, feature_category: :security_policy_management do + describe 'associations' do + it { is_expected.to belong_to(:project) } + it { is_expected.to belong_to(:merge_request) } + it { is_expected.to belong_to(:security_policy) } + it { is_expected.to belong_to(:dismissed_by).optional.class_name('User') } + end + + describe 'validations' do + subject(:policy_dismissal) { create(:policy_dismissal) } + + it { is_expected.to validate_presence_of(:project_id) } + it { is_expected.to validate_presence_of(:merge_request_id) } + it { is_expected.to validate_presence_of(:security_policy_id) } + it { is_expected.to validate_presence_of(:security_findings_uuids) } + it { is_expected.to(validate_uniqueness_of(:merge_request_id).scoped_to(%i[security_policy_id])) } + end +end -- GitLab From 212d79794cc2ff0d8be73ae5f1af3c8759d93b1b Mon Sep 17 00:00:00 2001 From: mc_rocha Date: Tue, 26 Aug 2025 13:48:36 -0400 Subject: [PATCH 2/3] Address reviewer comments --- db/docs/security_policy_dismissals.yml | 4 ++-- ee/app/models/ee/merge_request.rb | 2 ++ ee/app/models/ee/project.rb | 2 ++ ee/app/models/security/policy.rb | 3 +++ ee/app/models/security/policy_dismissal.rb | 9 +++------ ee/spec/models/ee/project_spec.rb | 1 + ee/spec/models/merge_request_spec.rb | 1 + ee/spec/models/security/policy_dismissal_spec.rb | 9 +++------ ee/spec/models/security/policy_spec.rb | 1 + spec/lib/gitlab/import_export/all_models.yml | 2 ++ 10 files changed, 20 insertions(+), 14 deletions(-) diff --git a/db/docs/security_policy_dismissals.yml b/db/docs/security_policy_dismissals.yml index 4e0372745f9907..07eb27e06c3d30 100644 --- a/db/docs/security_policy_dismissals.yml +++ b/db/docs/security_policy_dismissals.yml @@ -5,9 +5,9 @@ classes: feature_categories: - security_policy_management description: Stores the relation between security policies and dismissed findings -introduced_by_url: +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/202431 milestone: '18.4' -gitlab_schema: gitlab_main_cell +gitlab_schema: gitlab_main_org sharding_key: project_id: projects table_size: small diff --git a/ee/app/models/ee/merge_request.rb b/ee/app/models/ee/merge_request.rb index a4d2c2d43b4a9b..c4a2027f4e9ce8 100644 --- a/ee/app/models/ee/merge_request.rb +++ b/ee/app/models/ee/merge_request.rb @@ -99,6 +99,8 @@ def set_applicable_when_copying_rules(applicable_ids) has_many :v2_approval_rules, through: :v2_approval_rules_merge_requests, class_name: 'MergeRequests::ApprovalRule', source: :approval_rule + has_many :policy_dismissals, class_name: 'Security::PolicyDismissal', inverse_of: :merge_request + delegate :sha, to: :head_pipeline, prefix: :head_pipeline, allow_nil: true delegate :sha, to: :base_pipeline, prefix: :base_pipeline, allow_nil: true delegate :wrapped_approval_rules, :invalid_approvers_rules, to: :approval_state diff --git a/ee/app/models/ee/project.rb b/ee/app/models/ee/project.rb index 882fc4fcc38bc9..dc10b39869fc37 100644 --- a/ee/app/models/ee/project.rb +++ b/ee/app/models/ee/project.rb @@ -247,6 +247,8 @@ def lock_for_confirmation!(id) -> { ready_with_active_connection }, class_name: 'Ai::ActiveContext::Code::Repository' + has_many :policy_dismissals, class_name: '::Security::PolicyDismissal', inverse_of: :project + elastic_index_dependant_association :issues, on_change: :visibility_level elastic_index_dependant_association :issues, on_change: :archived elastic_index_dependant_association :work_items, on_change: :visibility_level diff --git a/ee/app/models/security/policy.rb b/ee/app/models/security/policy.rb index 54677dcf0331e5..67406a33b63370 100644 --- a/ee/app/models/security/policy.rb +++ b/ee/app/models/security/policy.rb @@ -42,6 +42,9 @@ class Policy < ApplicationRecord has_many :approval_policy_merge_request_bypass_events, class_name: 'Security::ApprovalPolicyMergeRequestBypassEvent', foreign_key: :security_policy_id, inverse_of: :security_policy + has_many :policy_dismissals, + class_name: 'Security::PolicyDismissal', + foreign_key: :security_policy_id, inverse_of: :security_policy enum :type, { approval_policy: 0, diff --git a/ee/app/models/security/policy_dismissal.rb b/ee/app/models/security/policy_dismissal.rb index 90730b3cc17fe8..7b7bc67dcec849 100644 --- a/ee/app/models/security/policy_dismissal.rb +++ b/ee/app/models/security/policy_dismissal.rb @@ -4,15 +4,12 @@ module Security class PolicyDismissal < ApplicationRecord self.table_name = 'security_policy_dismissals' - belongs_to :project, class_name: 'Project' - belongs_to :merge_request, class_name: 'MergeRequest' - belongs_to :security_policy, class_name: 'Security::Policy' + belongs_to :project, class_name: 'Project', optional: false + belongs_to :merge_request, class_name: 'MergeRequest', optional: false + belongs_to :security_policy, class_name: 'Security::Policy', optional: false belongs_to :dismissed_by, class_name: 'User', optional: true - validates :project_id, presence: true - validates :merge_request_id, presence: true validates :merge_request_id, uniqueness: { scope: :security_policy_id } - validates :security_policy_id, presence: true validates :security_findings_uuids, presence: true end end diff --git a/ee/spec/models/ee/project_spec.rb b/ee/spec/models/ee/project_spec.rb index c25d20e8618aab..29f4598c12280a 100644 --- a/ee/spec/models/ee/project_spec.rb +++ b/ee/spec/models/ee/project_spec.rb @@ -118,6 +118,7 @@ it { is_expected.to have_many(:configured_ai_catalog_items).class_name('Ai::Catalog::ItemConsumer') } it { is_expected.to have_many(:ai_flow_triggers).class_name('Ai::FlowTrigger') } + it { is_expected.to have_many(:policy_dismissals).class_name('Security::PolicyDismissal') } include_examples 'ci_cd_settings delegation' do let(:attributes_with_prefix) do diff --git a/ee/spec/models/merge_request_spec.rb b/ee/spec/models/merge_request_spec.rb index 6a06feb18cbdf5..d3d9ee7cc5972b 100644 --- a/ee/spec/models/merge_request_spec.rb +++ b/ee/spec/models/merge_request_spec.rb @@ -33,6 +33,7 @@ it { is_expected.to have_many(:scan_result_policy_reads_through_approval_rules).through(:approval_rules).class_name('Security::ScanResultPolicyRead') } it { is_expected.to have_many(:security_policies_through_violations).through(:scan_result_policy_violations).class_name('Security::Policy') } it { is_expected.to have_many(:change_requesters).through(:requested_changes) } + it { is_expected.to have_many(:policy_dismissals) } describe 'policy violations' do let(:policy_1) { create(:scan_result_policy_read, project: project) } diff --git a/ee/spec/models/security/policy_dismissal_spec.rb b/ee/spec/models/security/policy_dismissal_spec.rb index fe2f2c9f1e46b3..830c62f4786091 100644 --- a/ee/spec/models/security/policy_dismissal_spec.rb +++ b/ee/spec/models/security/policy_dismissal_spec.rb @@ -4,18 +4,15 @@ RSpec.describe Security::PolicyDismissal, feature_category: :security_policy_management do describe 'associations' do - it { is_expected.to belong_to(:project) } - it { is_expected.to belong_to(:merge_request) } - it { is_expected.to belong_to(:security_policy) } + it { is_expected.to belong_to(:project).required } + it { is_expected.to belong_to(:merge_request).required } + it { is_expected.to belong_to(:security_policy).required } it { is_expected.to belong_to(:dismissed_by).optional.class_name('User') } end describe 'validations' do subject(:policy_dismissal) { create(:policy_dismissal) } - it { is_expected.to validate_presence_of(:project_id) } - it { is_expected.to validate_presence_of(:merge_request_id) } - it { is_expected.to validate_presence_of(:security_policy_id) } it { is_expected.to validate_presence_of(:security_findings_uuids) } it { is_expected.to(validate_uniqueness_of(:merge_request_id).scoped_to(%i[security_policy_id])) } end diff --git a/ee/spec/models/security/policy_spec.rb b/ee/spec/models/security/policy_spec.rb index 12c8f64961e7a8..d592b727c45fb3 100644 --- a/ee/spec/models/security/policy_spec.rb +++ b/ee/spec/models/security/policy_spec.rb @@ -13,6 +13,7 @@ it { is_expected.to have_one(:security_pipeline_execution_policy_config_link) } it { is_expected.to have_many(:security_pipeline_execution_project_schedules) } it { is_expected.to have_many(:approval_policy_merge_request_bypass_events) } + it { is_expected.to have_many(:policy_dismissals) } it do is_expected.to validate_uniqueness_of(:security_orchestration_policy_configuration_id).scoped_to(%i[type diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml index 7b89079d0c4035..6b59b48441e477 100644 --- a/spec/lib/gitlab/import_export/all_models.yml +++ b/spec/lib/gitlab/import_export/all_models.yml @@ -292,6 +292,7 @@ merge_requests: - approval_metrics - approval_policy_merge_request_bypass_events - generated_ref_commits +- policy_dismissals external_pull_requests: - project merge_request_diff: @@ -958,6 +959,7 @@ project: - analyzer_statuses - configured_ai_catalog_items - security_project_tracked_contexts +- policy_dismissals award_emoji: - awardable - user -- GitLab From 53faadf10349ad7757175060520998a57e112d08 Mon Sep 17 00:00:00 2001 From: mc_rocha Date: Tue, 2 Sep 2025 16:30:06 -0400 Subject: [PATCH 3/3] Update column name --- ...250821205145_create_security_policy_dismissals.rb | 4 ++-- ...user_foreign_key_to_security_policy_dismissals.rb | 4 ++-- ...ject_foreign_key_to_security_policy_dismissals.rb | 0 db/structure.sql | 12 ++++++------ ee/app/models/security/policy_dismissal.rb | 2 +- ee/spec/factories/security/policy_dismissal.rb | 2 +- ee/spec/models/security/policy_dismissal_spec.rb | 2 +- 7 files changed, 13 insertions(+), 13 deletions(-) rename db/{post_migrate => migrate}/20250822200745_add_project_foreign_key_to_security_policy_dismissals.rb (100%) diff --git a/db/migrate/20250821205145_create_security_policy_dismissals.rb b/db/migrate/20250821205145_create_security_policy_dismissals.rb index b383a85d10b64a..d53bafdcceb2e3 100644 --- a/db/migrate/20250821205145_create_security_policy_dismissals.rb +++ b/db/migrate/20250821205145_create_security_policy_dismissals.rb @@ -12,13 +12,13 @@ def up t.bigint :project_id, null: false t.bigint :merge_request_id, null: false t.bigint :security_policy_id, null: false - t.bigint :dismissed_by_id, null: true + t.bigint :user_id, null: true t.text :security_findings_uuids, array: true, default: [], null: false t.index [:merge_request_id, :security_policy_id], unique: true, name: INDEX_NAME t.index :project_id t.index :security_policy_id - t.index :dismissed_by_id + t.index :user_id end end diff --git a/db/migrate/20250821211452_add_user_foreign_key_to_security_policy_dismissals.rb b/db/migrate/20250821211452_add_user_foreign_key_to_security_policy_dismissals.rb index e9c79f9ea0d651..c14d69c126d662 100644 --- a/db/migrate/20250821211452_add_user_foreign_key_to_security_policy_dismissals.rb +++ b/db/migrate/20250821211452_add_user_foreign_key_to_security_policy_dismissals.rb @@ -6,11 +6,11 @@ class AddUserForeignKeyToSecurityPolicyDismissals < Gitlab::Database::Migration[ def up add_concurrent_foreign_key :security_policy_dismissals, :users, - column: :dismissed_by_id, + column: :user_id, on_delete: :nullify end def down - remove_foreign_key_if_exists :security_policy_dismissals, column: :dismissed_by_id + remove_foreign_key_if_exists :security_policy_dismissals, column: :user_id end end diff --git a/db/post_migrate/20250822200745_add_project_foreign_key_to_security_policy_dismissals.rb b/db/migrate/20250822200745_add_project_foreign_key_to_security_policy_dismissals.rb similarity index 100% rename from db/post_migrate/20250822200745_add_project_foreign_key_to_security_policy_dismissals.rb rename to db/migrate/20250822200745_add_project_foreign_key_to_security_policy_dismissals.rb diff --git a/db/structure.sql b/db/structure.sql index bd72c5dd8d738c..b735d71437feea 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -24876,7 +24876,7 @@ CREATE TABLE security_policy_dismissals ( project_id bigint NOT NULL, merge_request_id bigint NOT NULL, security_policy_id bigint NOT NULL, - dismissed_by_id bigint, + user_id bigint, security_findings_uuids text[] DEFAULT '{}'::text[] NOT NULL ); @@ -40948,12 +40948,12 @@ CREATE INDEX index_security_policies_on_policy_management_project_id ON security CREATE UNIQUE INDEX index_security_policies_on_unique_config_type_policy_index ON security_policies USING btree (security_orchestration_policy_configuration_id, type, policy_index); -CREATE INDEX index_security_policy_dismissals_on_dismissed_by_id ON security_policy_dismissals USING btree (dismissed_by_id); - CREATE INDEX index_security_policy_dismissals_on_project_id ON security_policy_dismissals USING btree (project_id); CREATE INDEX index_security_policy_dismissals_on_security_policy_id ON security_policy_dismissals USING btree (security_policy_id); +CREATE INDEX index_security_policy_dismissals_on_user_id ON security_policy_dismissals USING btree (user_id); + CREATE UNIQUE INDEX index_security_policy_project_links_on_project_and_policy ON security_policy_project_links USING btree (security_policy_id, project_id); CREATE INDEX index_security_policy_requirements_on_compliance_requirement_id ON security_policy_requirements USING btree (compliance_requirement_id); @@ -46982,9 +46982,6 @@ ALTER TABLE ONLY project_export_jobs ALTER TABLE ONLY security_policy_requirements ADD CONSTRAINT fk_5b4fae9635 FOREIGN KEY (compliance_requirement_id) REFERENCES compliance_requirements(id) ON DELETE CASCADE; -ALTER TABLE ONLY security_policy_dismissals - ADD CONSTRAINT fk_5b565b576c FOREIGN KEY (dismissed_by_id) REFERENCES users(id) ON DELETE SET NULL; - ALTER TABLE ONLY packages_conan_file_metadata ADD CONSTRAINT fk_5bb7e23d6d FOREIGN KEY (package_revision_id) REFERENCES packages_conan_package_revisions(id) ON DELETE CASCADE; @@ -47975,6 +47972,9 @@ ALTER TABLE ONLY boards_epic_list_user_preferences ALTER TABLE ONLY user_broadcast_message_dismissals ADD CONSTRAINT fk_c7cbf5566d FOREIGN KEY (broadcast_message_id) REFERENCES broadcast_messages(id) ON DELETE CASCADE; +ALTER TABLE ONLY security_policy_dismissals + ADD CONSTRAINT fk_c7cfc32196 FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL; + ALTER TABLE ONLY packages_debian_group_distribution_keys ADD CONSTRAINT fk_c802025a67 FOREIGN KEY (group_id) REFERENCES namespaces(id) ON DELETE CASCADE; diff --git a/ee/app/models/security/policy_dismissal.rb b/ee/app/models/security/policy_dismissal.rb index 7b7bc67dcec849..46b8bf23a1d4a9 100644 --- a/ee/app/models/security/policy_dismissal.rb +++ b/ee/app/models/security/policy_dismissal.rb @@ -7,7 +7,7 @@ class PolicyDismissal < ApplicationRecord belongs_to :project, class_name: 'Project', optional: false belongs_to :merge_request, class_name: 'MergeRequest', optional: false belongs_to :security_policy, class_name: 'Security::Policy', optional: false - belongs_to :dismissed_by, class_name: 'User', optional: true + belongs_to :user, class_name: 'User', optional: true validates :merge_request_id, uniqueness: { scope: :security_policy_id } validates :security_findings_uuids, presence: true diff --git a/ee/spec/factories/security/policy_dismissal.rb b/ee/spec/factories/security/policy_dismissal.rb index 25798308ced446..04b3e90ddf0af7 100644 --- a/ee/spec/factories/security/policy_dismissal.rb +++ b/ee/spec/factories/security/policy_dismissal.rb @@ -5,7 +5,7 @@ project merge_request security_policy - dismissed_by factory: :user + user security_findings_uuids { [SecureRandom.uuid] } end end diff --git a/ee/spec/models/security/policy_dismissal_spec.rb b/ee/spec/models/security/policy_dismissal_spec.rb index 830c62f4786091..51c6d90aa50778 100644 --- a/ee/spec/models/security/policy_dismissal_spec.rb +++ b/ee/spec/models/security/policy_dismissal_spec.rb @@ -7,7 +7,7 @@ it { is_expected.to belong_to(:project).required } it { is_expected.to belong_to(:merge_request).required } it { is_expected.to belong_to(:security_policy).required } - it { is_expected.to belong_to(:dismissed_by).optional.class_name('User') } + it { is_expected.to belong_to(:user).optional } end describe 'validations' do -- GitLab