From 897d2d85cb739a7db31e1079fbe09d7bcdfb169b Mon Sep 17 00:00:00 2001 From: Jean van der Walt Date: Mon, 1 Sep 2025 22:00:35 +1200 Subject: [PATCH] Add vulnernabilties_duo_workflows table, model Changelog: changed EE: true --- db/docs/vulnerabilities_duo_workflows.yml | 14 ++++++++ ...17_create_vulnerabilities_duo_workflows.rb | 28 +++++++++++++++ db/schema_migrations/20250828043117 | 1 + db/structure.sql | 34 +++++++++++++++++++ .../entities/vulnerabilities_duo_workflow.rb | 16 +++++++++ ee/app/models/ai/duo_workflows/workflow.rb | 6 ++++ .../entities/vulnerabilities_duo_workflows.rb | 8 +++++ .../vulnerabilities_duo_workflow_spec.rb | 22 ++++++++++++ .../models/ai/duo_workflows/workflow_spec.rb | 9 +++++ 9 files changed, 138 insertions(+) create mode 100644 db/docs/vulnerabilities_duo_workflows.yml create mode 100644 db/migrate/20250828043117_create_vulnerabilities_duo_workflows.rb create mode 100644 db/schema_migrations/20250828043117 create mode 100644 ee/app/models/ai/duo_workflows/entities/vulnerabilities_duo_workflow.rb create mode 100644 ee/spec/factories/ai/duo_workflows/entities/vulnerabilities_duo_workflows.rb create mode 100644 ee/spec/models/ai/duo_workflows/entities/vulnerabilities_duo_workflow_spec.rb diff --git a/db/docs/vulnerabilities_duo_workflows.yml b/db/docs/vulnerabilities_duo_workflows.yml new file mode 100644 index 00000000000000..24adf30de14298 --- /dev/null +++ b/db/docs/vulnerabilities_duo_workflows.yml @@ -0,0 +1,14 @@ +--- +table_name: vulnerabilities_duo_workflows +classes: +- Ai::DuoWorkflows::Entities::VulnerabilitiesDuoWorkflow +feature_categories: +- agent_foundations +description: Links Duo workflows with vulnerabilities so users can track ongoing processes and monitor progress +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/203376 +milestone: '18.4' +gitlab_schema: gitlab_main_org +desired_sharding_key: + project_id: projects + namespace_id: namespaces +table_size: small diff --git a/db/migrate/20250828043117_create_vulnerabilities_duo_workflows.rb b/db/migrate/20250828043117_create_vulnerabilities_duo_workflows.rb new file mode 100644 index 00000000000000..f3f4cdff1bb1a5 --- /dev/null +++ b/db/migrate/20250828043117_create_vulnerabilities_duo_workflows.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +class CreateVulnerabilitiesDuoWorkflows < Gitlab::Database::Migration[2.3] + milestone '18.4' + + def up + create_table :vulnerabilities_duo_workflows do |t| + t.references :duo_workflow, null: false, + foreign_key: { to_table: :duo_workflows_workflows, on_delete: :cascade }, + type: :bigint, + index: false + + t.references :vulnerability, null: false, + foreign_key: { on_delete: :cascade }, + type: :bigint + + t.timestamps_with_timezone null: false + + t.index [:duo_workflow_id, :vulnerability_id], unique: true, + name: 'idx_vdw_unique_association' + t.index :vulnerability_id, name: 'idx_vdw_vulnerability_lookup' + end + end + + def down + drop_table :vulnerabilities_duo_workflows + end +end diff --git a/db/schema_migrations/20250828043117 b/db/schema_migrations/20250828043117 new file mode 100644 index 00000000000000..01c897fe89eb40 --- /dev/null +++ b/db/schema_migrations/20250828043117 @@ -0,0 +1 @@ +b18fd3f53d215f18b230ad0f923ed74621b9b38e2182dfca0d9a252268f412e9 \ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index 03ee4935e5d98f..93067016fb32df 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -26694,6 +26694,23 @@ CREATE TABLE vulnerabilities ( CONSTRAINT check_4d8a873f1f CHECK ((finding_id IS NOT NULL)) ); +CREATE TABLE vulnerabilities_duo_workflows ( + id bigint NOT NULL, + duo_workflow_id bigint NOT NULL, + vulnerability_id bigint NOT NULL, + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone NOT NULL +); + +CREATE SEQUENCE vulnerabilities_duo_workflows_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER SEQUENCE vulnerabilities_duo_workflows_id_seq OWNED BY vulnerabilities_duo_workflows.id; + CREATE SEQUENCE vulnerabilities_id_seq START WITH 1 INCREMENT BY 1 @@ -30308,6 +30325,8 @@ ALTER TABLE ONLY vs_code_settings ALTER COLUMN id SET DEFAULT nextval('vs_code_s ALTER TABLE ONLY vulnerabilities ALTER COLUMN id SET DEFAULT nextval('vulnerabilities_id_seq'::regclass); +ALTER TABLE ONLY vulnerabilities_duo_workflows ALTER COLUMN id SET DEFAULT nextval('vulnerabilities_duo_workflows_id_seq'::regclass); + ALTER TABLE ONLY vulnerability_archive_exports ALTER COLUMN id SET DEFAULT nextval('vulnerability_archive_exports_id_seq'::regclass); ALTER TABLE ONLY vulnerability_archived_records ALTER COLUMN id SET DEFAULT nextval('vulnerability_archived_records_id_seq'::regclass); @@ -33806,6 +33825,9 @@ ALTER TABLE ONLY virtual_registries_settings ALTER TABLE ONLY vs_code_settings ADD CONSTRAINT vs_code_settings_pkey PRIMARY KEY (id); +ALTER TABLE ONLY vulnerabilities_duo_workflows + ADD CONSTRAINT vulnerabilities_duo_workflows_pkey PRIMARY KEY (id); + ALTER TABLE ONLY vulnerabilities ADD CONSTRAINT vulnerabilities_pkey PRIMARY KEY (id); @@ -36700,6 +36722,10 @@ CREATE INDEX idx_user_member_roles_on_member_role_id ON user_member_roles USING CREATE UNIQUE INDEX idx_user_member_roles_on_user_id_unique ON user_member_roles USING btree (user_id); +CREATE UNIQUE INDEX idx_vdw_unique_association ON vulnerabilities_duo_workflows USING btree (duo_workflow_id, vulnerability_id); + +CREATE INDEX idx_vdw_vulnerability_lookup ON vulnerabilities_duo_workflows USING btree (vulnerability_id); + CREATE INDEX idx_vreg_container_reg_upst_on_group ON virtual_registries_container_registry_upstreams USING btree (group_id); CREATE INDEX idx_vuln_reads_for_filtering ON vulnerability_reads USING btree (project_id, state, dismissal_reason, severity DESC, vulnerability_id DESC NULLS LAST); @@ -41122,6 +41148,8 @@ CREATE INDEX index_vuln_rep_info_on_project_id ON vulnerability_representation_i CREATE INDEX index_vulnerabilities_common_finder_query_on_default_branch ON vulnerabilities USING btree (project_id, state, report_type, present_on_default_branch, severity, id); +CREATE INDEX index_vulnerabilities_duo_workflows_on_vulnerability_id ON vulnerabilities_duo_workflows USING btree (vulnerability_id); + 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); @@ -48281,6 +48309,9 @@ ALTER TABLE ONLY users_security_dashboard_projects ALTER TABLE ONLY import_source_user_placeholder_references ADD CONSTRAINT fk_rails_158995b934 FOREIGN KEY (namespace_id) REFERENCES namespaces(id) ON DELETE CASCADE; +ALTER TABLE ONLY vulnerabilities_duo_workflows + ADD CONSTRAINT fk_rails_16159b1567 FOREIGN KEY (duo_workflow_id) REFERENCES duo_workflows_workflows(id) ON DELETE CASCADE; + ALTER TABLE ONLY import_source_users ADD CONSTRAINT fk_rails_167f82fd95 FOREIGN KEY (reassign_to_user_id) REFERENCES users(id) ON DELETE SET NULL; @@ -48338,6 +48369,9 @@ ALTER TABLE ai_usage_events ALTER TABLE ONLY packages_debian_file_metadata ADD CONSTRAINT fk_rails_1ae85be112 FOREIGN KEY (package_file_id) REFERENCES packages_package_files(id) ON DELETE CASCADE; +ALTER TABLE ONLY vulnerabilities_duo_workflows + ADD CONSTRAINT fk_rails_1b0fd2918f FOREIGN KEY (vulnerability_id) REFERENCES vulnerabilities(id) ON DELETE CASCADE; + ALTER TABLE ONLY catalog_verified_namespaces ADD CONSTRAINT fk_rails_1b6bb852c0 FOREIGN KEY (namespace_id) REFERENCES namespaces(id) ON DELETE CASCADE; diff --git a/ee/app/models/ai/duo_workflows/entities/vulnerabilities_duo_workflow.rb b/ee/app/models/ai/duo_workflows/entities/vulnerabilities_duo_workflow.rb new file mode 100644 index 00000000000000..4f347276cc90d7 --- /dev/null +++ b/ee/app/models/ai/duo_workflows/entities/vulnerabilities_duo_workflow.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +module Ai + module DuoWorkflows + module Entities + class VulnerabilitiesDuoWorkflow < ::ApplicationRecord + self.table_name = :vulnerabilities_duo_workflows + + belongs_to :duo_workflow, class_name: '::Ai::DuoWorkflows::Workflow' + belongs_to :vulnerability + + validates :duo_workflow_id, uniqueness: { scope: :vulnerability_id } + end + end + end +end diff --git a/ee/app/models/ai/duo_workflows/workflow.rb b/ee/app/models/ai/duo_workflows/workflow.rb index 6f18b5a961d128..747f1dc3bab68a 100644 --- a/ee/app/models/ai/duo_workflows/workflow.rb +++ b/ee/app/models/ai/duo_workflows/workflow.rb @@ -18,6 +18,12 @@ class Workflow < ::ApplicationRecord has_many :workflows_workloads, class_name: 'Ai::DuoWorkflows::WorkflowsWorkload' has_many :workloads, through: :workflows_workloads, disable_joins: true + # Entities + has_many :vulnerabilities_duo_workflows, foreign_key: :duo_workflow_id, + class_name: 'Ai::DuoWorkflows::Entities::VulnerabilitiesDuoWorkflow', + inverse_of: :duo_workflow + has_many :vulnerabilities, through: :vulnerabilities_duo_workflows + validates :status, presence: true validates :goal, length: { maximum: 16_384 } validates :image, length: { maximum: 2048 }, allow_blank: true diff --git a/ee/spec/factories/ai/duo_workflows/entities/vulnerabilities_duo_workflows.rb b/ee/spec/factories/ai/duo_workflows/entities/vulnerabilities_duo_workflows.rb new file mode 100644 index 00000000000000..86598f3cf2abf7 --- /dev/null +++ b/ee/spec/factories/ai/duo_workflows/entities/vulnerabilities_duo_workflows.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +FactoryBot.define do + factory :vulnerabilities_duo_workflow, class: '::Ai::DuoWorkflows::Entities::VulnerabilitiesDuoWorkflow' do + duo_workflow { association(:duo_workflows_workflow) } + vulnerability { association(:vulnerability) } + end +end diff --git a/ee/spec/models/ai/duo_workflows/entities/vulnerabilities_duo_workflow_spec.rb b/ee/spec/models/ai/duo_workflows/entities/vulnerabilities_duo_workflow_spec.rb new file mode 100644 index 00000000000000..cd7797f59c2087 --- /dev/null +++ b/ee/spec/models/ai/duo_workflows/entities/vulnerabilities_duo_workflow_spec.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Ai::DuoWorkflows::Entities::VulnerabilitiesDuoWorkflow, feature_category: :agent_foundations do + describe 'associations' do + it { is_expected.to belong_to(:duo_workflow).class_name('Ai::DuoWorkflows::Workflow') } + it { is_expected.to belong_to(:vulnerability) } + end + + describe 'validations' do + let(:vulnerability) { create(:vulnerability) } + let(:duo_workflow) { create(:duo_workflows_workflow) } + let!(:existing_record) do + create(:vulnerabilities_duo_workflow, + vulnerability: vulnerability, + duo_workflow: duo_workflow) + end + + it { is_expected.to validate_uniqueness_of(:duo_workflow_id).scoped_to(:vulnerability_id) } + end +end diff --git a/ee/spec/models/ai/duo_workflows/workflow_spec.rb b/ee/spec/models/ai/duo_workflows/workflow_spec.rb index cf70b5d15356a5..44bda180b9c14d 100644 --- a/ee/spec/models/ai/duo_workflows/workflow_spec.rb +++ b/ee/spec/models/ai/duo_workflows/workflow_spec.rb @@ -16,6 +16,15 @@ it { is_expected.to belong_to(:namespace).optional } end + describe 'entities' do + it 'has many vulnerabilities_duo_workflows' do + is_expected.to have_many(:vulnerabilities_duo_workflows) + .class_name('Ai::DuoWorkflows::Entities::VulnerabilitiesDuoWorkflow') + end + + it { is_expected.to have_many(:vulnerabilities).class_name('Vulnerability') } + end + describe '.for_user_with_id!' do it 'finds the workflow for the given user and id' do expect(described_class.for_user_with_id!(user.id, owned_workflow.id)).to eq(owned_workflow) -- GitLab