diff --git a/db/migrate/20230724185321_pm_affected_packages_add_versions_attribute.rb b/db/migrate/20230724185321_pm_affected_packages_add_versions_attribute.rb new file mode 100644 index 0000000000000000000000000000000000000000..f1adb65324bc4dc4488435167f5329b4bced7cbc --- /dev/null +++ b/db/migrate/20230724185321_pm_affected_packages_add_versions_attribute.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +class PmAffectedPackagesAddVersionsAttribute < Gitlab::Database::Migration[2.1] + enable_lock_retries! + + def change + add_column :pm_affected_packages, :versions, :jsonb, default: [], null: false + end +end diff --git a/db/schema_migrations/20230724185321 b/db/schema_migrations/20230724185321 new file mode 100644 index 0000000000000000000000000000000000000000..b5ffbeb5734258e66376d10efb75831dccc8a20f --- /dev/null +++ b/db/schema_migrations/20230724185321 @@ -0,0 +1 @@ +73dc6e44071a82ff0e6237eaf8619af20944ecb959de803779372138d63ee194 \ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index 93e3ad40756bbdf99b4152f094488658f5aebca1..97e260e30e191ccc7f58c7c866ce1438932724ef 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -20427,6 +20427,7 @@ CREATE TABLE pm_affected_packages ( affected_range text NOT NULL, fixed_versions text[] DEFAULT '{}'::text[], overridden_advisory_fields jsonb DEFAULT '{}'::jsonb NOT NULL, + versions jsonb DEFAULT '[]'::jsonb NOT NULL, CONSTRAINT check_5dd528a2be CHECK ((char_length(package_name) <= 256)), CONSTRAINT check_80dea16c7b CHECK ((char_length(affected_range) <= 512)), CONSTRAINT check_d1d4646298 CHECK ((char_length(solution) <= 2048)), diff --git a/ee/app/models/package_metadata/affected_package.rb b/ee/app/models/package_metadata/affected_package.rb index 4963e31f93f6d45e3cb9109f57884d2173563572..0095112a8468bb000c5ce3bc11027c027e826e87 100644 --- a/ee/app/models/package_metadata/affected_package.rb +++ b/ee/app/models/package_metadata/affected_package.rb @@ -17,5 +17,6 @@ class AffectedPackage < ApplicationRecord validates :affected_range, presence: true, length: { maximum: 512 } validates :overridden_advisory_fields, json_schema: { filename: 'pm_affected_package_overridden_advisory_fields' } validates :fixed_versions, length: { maximum: 10 } + validates :versions, json_schema: { filename: 'pm_affected_package_versions' } end end diff --git a/ee/app/validators/json_schemas/pm_affected_package_versions.json b/ee/app/validators/json_schemas/pm_affected_package_versions.json new file mode 100644 index 0000000000000000000000000000000000000000..d51d870151d42dcbb68be61d83650455ed65a387 --- /dev/null +++ b/ee/app/validators/json_schemas/pm_affected_package_versions.json @@ -0,0 +1,71 @@ +{ + "$id": "#/properties/versions", + "type": "array", + "title": "Version Meta Information", + "minItems": 0, + "maxItems": 32, + "items": { + "$id": "#/properties/versions/items", + "type": "object", + "required": [ + "number", + "commit" + ], + "properties": { + "number": { + "$id": "#/properties/versions/items/properties/number", + "type": "string", + "title": "Version Information.", + "pattern": "^([\\d\\.a-zA-Z_\\-]{1,32})$", + "examples": [ + "1.2.3" + ] + }, + "commit": { + "$id": "#/properties/versions/items/properties/commit", + "type": "object", + "title": "Git commit meta information.", + "required": [ + "tags", + "sha", + "timestamp" + ], + "properties": { + "tags": { + "$id": "#/properties/versions/items/properties/commit/tags", + "type": "array", + "title": "Array of Git Tags associated with this particular version.", + "minItems": 0, + "maxItems": 16, + "items": { + "$id": "#/properties/versions/items/properties/commit/tags/items", + "type": "string", + "examples": [ + "v1.2.3-tag" + ], + "pattern": "^[a-zA-Z0-9_\\-\\./]{0,32}$" + } + }, + "sha": { + "$id": "#/properties/versions/items/properties/commit/sha", + "type": "string", + "title": "Git commit sha.", + "pattern": "^[0-9a-f]{5,40}$", + "examples": [ + "295cf0778821bf08681e2bd0ef0e6cad04fc3001" + ] + }, + "timestamp": { + "$id": "#/properties/versions/items/properties/commit/timestamp", + "type": "string", + "title": "Timestamp of the format YYYYMMDDHHMMSS.", + "pattern": "^\\d{14,14}$", + "examples": [ + "20190626162700" + ] + } + } + } + } + } +} diff --git a/ee/spec/models/package_metadata/affected_package_spec.rb b/ee/spec/models/package_metadata/affected_package_spec.rb index 3d6bc431e06c2cfc0c51a75e29fea774c7cb2d18..d14490333c1abc18868139c5809800fb59051210 100644 --- a/ee/spec/models/package_metadata/affected_package_spec.rb +++ b/ee/spec/models/package_metadata/affected_package_spec.rb @@ -78,5 +78,41 @@ end end end + + describe 'versions' do + subject { build(:pm_affected_package, versions: versions).valid? } + + let(:valid_sha) { '295cf0778821bf08681e2bd0ef0e6cad04fc3001' } + let(:valid_timestamp) { '20190626162700' } + let(:valid_number) { '1.2.3' } + let(:valid_tags) { ['v1.2.3-tag'] } + let(:valid_num_versions) { 1 } + + # rubocop:disable Layout/LineLength + where(:number, :tags, :sha, :timestamp, :num_versions, :is_valid) do + ref(:valid_number) | ref(:valid_tags) | ref(:valid_sha) | ref(:valid_timestamp) | 0 | true + ref(:valid_number) | ref(:valid_tags) | ref(:valid_sha) | ref(:valid_timestamp) | 32 | true + ref(:valid_number) | ref(:valid_tags) | ref(:valid_sha) | ref(:valid_timestamp) | 33 | false + ref(:valid_number) | [''] | ref(:valid_sha) | ref(:valid_timestamp) | ref(:valid_num_versions) | true + ref(:valid_number) | [('a' * 32)] | ref(:valid_sha) | ref(:valid_timestamp) | ref(:valid_num_versions) | true + ref(:valid_number) | [] | ref(:valid_sha) | ref(:valid_timestamp) | ref(:valid_num_versions) | true + ref(:valid_number) | (['a'] * 16) | ref(:valid_sha) | ref(:valid_timestamp) | ref(:valid_num_versions) | true + ref(:valid_number) | (['a'] * 17) | ref(:valid_sha) | ref(:valid_timestamp) | ref(:valid_num_versions) | false + ref(:valid_number) | [('a' * 33)] | ref(:valid_sha) | ref(:valid_timestamp) | ref(:valid_num_versions) | false + '' | ref(:valid_tags) | ref(:valid_sha) | ref(:valid_timestamp) | ref(:valid_num_versions) | false + ref(:valid_number) | ref(:valid_tags) | ('a' * 4) | ref(:valid_timestamp) | ref(:valid_num_versions) | false + ref(:valid_number) | ref(:valid_tags) | ('a' * 41) | ref(:valid_timestamp) | ref(:valid_num_versions) | false + ref(:valid_number) | ref(:valid_tags) | ref(:valid_sha) | '2019062616270' | ref(:valid_num_versions) | false + ref(:valid_number) | ref(:valid_tags) | ref(:valid_sha) | '2019062616270a' | ref(:valid_num_versions) | false + ref(:valid_number) | ref(:valid_tags) | ref(:valid_sha) | '201906261627001' | ref(:valid_num_versions) | false + end + # rubocop:enable Layout/LineLength + + with_them do + let(:versions) { [{ number: number, commit: { tags: tags, sha: sha, timestamp: timestamp } }] * num_versions } + + it { is_expected.to eq(is_valid) } + end + end end end