From caf0777563760020281a8bf43175d96937e8cc5a Mon Sep 17 00:00:00 2001 From: Alexandre Laroche Date: Tue, 16 Dec 2025 10:07:41 -0500 Subject: [PATCH 1/2] Add support for CVSS 4.0 --- Gemfile | 2 +- Gemfile.checksum | 2 +- Gemfile.lock | 7 +- Gemfile.next.checksum | 2 +- Gemfile.next.lock | 7 +- .../vulnerability_cvss_vectors.json | 5 +- db/init_structure.sql | 1 + ...0722053202_add_cvss_v4_to_pm_advisories.rb | 21 ++++ db/schema_migrations/20250722053202 | 1 + db/structure.sql | 4 + ee/app/models/package_metadata/advisory.rb | 2 + .../advisory/advisory_ingestion_task.rb | 1 + ...ed_package_overridden_advisory_fields.json | 11 ++ .../vulnerabilities/cvss_vector_validator.rb | 2 +- .../gitlab/vulnerability_scanning/advisory.rb | 15 ++- .../vulnerability_scanning/finding_builder.rb | 10 +- .../package_metadata/advisory_data_objects.rb | 1 + .../package_metadata/pm_advisories.rb | 1 + .../vulnerability_scanning/advisories.rb | 2 + .../sync/advisories/v2/maven.ndjson | 2 +- .../gitlab/database/type/cvss_vector_spec.rb | 18 +-- .../vulnerability_scanning/advisory_spec.rb | 21 ++++ .../finding_builder_spec.rb | 1 + .../finding_builder_spec.rb | 1 + .../finding_builder_spec.rb | 11 ++ .../models/package_metadata/advisory_spec.rb | 3 + .../package_metadata/affected_package_spec.rb | 1 + .../data_object_fabricator_spec.rb | 1 + .../cvss_vector_validator_shared_examples.rb | 111 ++++++++++++++++++ 29 files changed, 243 insertions(+), 24 deletions(-) create mode 100644 db/migrate/20250722053202_add_cvss_v4_to_pm_advisories.rb create mode 100644 db/schema_migrations/20250722053202 diff --git a/Gemfile b/Gemfile index dbac1c0531fb39..71a65862ddcc38 100644 --- a/Gemfile +++ b/Gemfile @@ -708,7 +708,7 @@ gem 'ed25519', '~> 1.4.0', feature_category: :shared # rubocop:todo Gemfile/Miss gem 'error_tracking_open_api', path: 'gems/error_tracking_open_api', feature_category: :shared # rubocop:todo Gemfile/MissingFeatureCategory -- https://gitlab.com/gitlab-org/gitlab/-/issues/581839 # Vulnerability advisories -gem 'cvss-suite', '~> 3.3.0', require: 'cvss_suite', feature_category: :software_composition_analysis +gem 'cvss-suite', '~> 4.1.1', require: 'cvss_suite', feature_category: :software_composition_analysis # Work with RPM packages gem 'arr-pm', '~> 0.0.12', feature_category: :package_registry diff --git a/Gemfile.checksum b/Gemfile.checksum index 2ea5a4bfd2f168..56c4e3fa3e7cc3 100644 --- a/Gemfile.checksum +++ b/Gemfile.checksum @@ -100,7 +100,7 @@ {"name":"css_parser","version":"1.14.0","platform":"ruby","checksum":"f2ce6148cd505297b07bdbe7a5db4cce5cf530071f9b732b9a23538d6cdc0113"}, {"name":"cssbundling-rails","version":"1.4.3","platform":"ruby","checksum":"53aecd5a7d24ac9c8fcd92975acd0e830fead4ee4583d3d3d49bb64651946e41"}, {"name":"csv","version":"3.3.0","platform":"ruby","checksum":"0bbd1defdc31134abefed027a639b3723c2753862150f4c3ee61cab71b20d67d"}, -{"name":"cvss-suite","version":"3.3.0","platform":"ruby","checksum":"54199fc1e0d5d833b1cb161453439143665c644cd3be2d21c2eee57fa06ed843"}, +{"name":"cvss-suite","version":"4.1.2","platform":"ruby","checksum":"b2ced80bb9573d29cd42f728e7653b3df740b480bc18f8ab1c350fde859896f5"}, {"name":"danger","version":"9.4.2","platform":"ruby","checksum":"43e552c6731030235a30fdeafe703d2e2ab9c30917154489cb0ecd9ad3259d80"}, {"name":"danger-gitlab","version":"8.0.0","platform":"ruby","checksum":"497dd7d0f6513913de651019223d8058cf494df10acbd17de92b175dfa04a3a8"}, {"name":"database_cleaner-active_record","version":"2.2.2","platform":"ruby","checksum":"88296b9f3088c31f7c0d4fcec10f68e4b71c96698043916de59b04debec10388"}, diff --git a/Gemfile.lock b/Gemfile.lock index d808c4d2101593..ef27a98b9d59bf 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -471,7 +471,8 @@ GEM cssbundling-rails (1.4.3) railties (>= 6.0.0) csv (3.3.0) - cvss-suite (3.3.0) + cvss-suite (4.1.2) + bigdecimal (~> 3.1) danger (9.4.2) claide (~> 1.0) claide-plugins (>= 0.9.2) @@ -2132,7 +2133,7 @@ DEPENDENCIES creole (~> 0.5.0) cssbundling-rails (= 1.4.3) csv_builder! - cvss-suite (~> 3.3.0) + cvss-suite (~> 4.1.1) database_cleaner-active_record (~> 2.2.0) debug (~> 1.11.0) declarative_policy (~> 2.0.1) @@ -2461,4 +2462,4 @@ DEPENDENCIES zeitwerk (= 2.6.18) BUNDLED WITH - 2.7.1 + 2.7.2 diff --git a/Gemfile.next.checksum b/Gemfile.next.checksum index 2ea5a4bfd2f168..56c4e3fa3e7cc3 100644 --- a/Gemfile.next.checksum +++ b/Gemfile.next.checksum @@ -100,7 +100,7 @@ {"name":"css_parser","version":"1.14.0","platform":"ruby","checksum":"f2ce6148cd505297b07bdbe7a5db4cce5cf530071f9b732b9a23538d6cdc0113"}, {"name":"cssbundling-rails","version":"1.4.3","platform":"ruby","checksum":"53aecd5a7d24ac9c8fcd92975acd0e830fead4ee4583d3d3d49bb64651946e41"}, {"name":"csv","version":"3.3.0","platform":"ruby","checksum":"0bbd1defdc31134abefed027a639b3723c2753862150f4c3ee61cab71b20d67d"}, -{"name":"cvss-suite","version":"3.3.0","platform":"ruby","checksum":"54199fc1e0d5d833b1cb161453439143665c644cd3be2d21c2eee57fa06ed843"}, +{"name":"cvss-suite","version":"4.1.2","platform":"ruby","checksum":"b2ced80bb9573d29cd42f728e7653b3df740b480bc18f8ab1c350fde859896f5"}, {"name":"danger","version":"9.4.2","platform":"ruby","checksum":"43e552c6731030235a30fdeafe703d2e2ab9c30917154489cb0ecd9ad3259d80"}, {"name":"danger-gitlab","version":"8.0.0","platform":"ruby","checksum":"497dd7d0f6513913de651019223d8058cf494df10acbd17de92b175dfa04a3a8"}, {"name":"database_cleaner-active_record","version":"2.2.2","platform":"ruby","checksum":"88296b9f3088c31f7c0d4fcec10f68e4b71c96698043916de59b04debec10388"}, diff --git a/Gemfile.next.lock b/Gemfile.next.lock index d808c4d2101593..ef27a98b9d59bf 100644 --- a/Gemfile.next.lock +++ b/Gemfile.next.lock @@ -471,7 +471,8 @@ GEM cssbundling-rails (1.4.3) railties (>= 6.0.0) csv (3.3.0) - cvss-suite (3.3.0) + cvss-suite (4.1.2) + bigdecimal (~> 3.1) danger (9.4.2) claide (~> 1.0) claide-plugins (>= 0.9.2) @@ -2132,7 +2133,7 @@ DEPENDENCIES creole (~> 0.5.0) cssbundling-rails (= 1.4.3) csv_builder! - cvss-suite (~> 3.3.0) + cvss-suite (~> 4.1.1) database_cleaner-active_record (~> 2.2.0) debug (~> 1.11.0) declarative_policy (~> 2.0.1) @@ -2461,4 +2462,4 @@ DEPENDENCIES zeitwerk (= 2.6.18) BUNDLED WITH - 2.7.1 + 2.7.2 diff --git a/app/validators/json_schemas/vulnerability_cvss_vectors.json b/app/validators/json_schemas/vulnerability_cvss_vectors.json index 0da6de0a69d187..3a59478edf0d7c 100644 --- a/app/validators/json_schemas/vulnerability_cvss_vectors.json +++ b/app/validators/json_schemas/vulnerability_cvss_vectors.json @@ -11,7 +11,10 @@ }, "vector": { "type": "string", - "example": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H" + "examples": [ + "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H", + "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N" + ] } }, "required": [ diff --git a/db/init_structure.sql b/db/init_structure.sql index 1181a5e247d197..898d06ab173eca 100644 --- a/db/init_structure.sql +++ b/db/init_structure.sql @@ -19995,6 +19995,7 @@ CREATE TABLE pm_advisories ( urls text[] DEFAULT '{}'::text[], identifiers jsonb NOT NULL, cve text, + cvss_v4 text, CONSTRAINT check_152def3868 CHECK ((char_length(cvss_v2) <= 128)), CONSTRAINT check_19cbd06439 CHECK ((char_length(advisory_xid) <= 36)), CONSTRAINT check_b1c980b212 CHECK ((char_length(cve) <= 24)), diff --git a/db/migrate/20250722053202_add_cvss_v4_to_pm_advisories.rb b/db/migrate/20250722053202_add_cvss_v4_to_pm_advisories.rb new file mode 100644 index 00000000000000..ace2da5e64af15 --- /dev/null +++ b/db/migrate/20250722053202_add_cvss_v4_to_pm_advisories.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +class AddCvssV4ToPmAdvisories < Gitlab::Database::Migration[2.3] + milestone '18.3' + + disable_ddl_transaction! + + def up + with_lock_retries do + add_column :pm_advisories, :cvss_v4, :text, null: true, if_not_exists: true + end + + add_text_limit :pm_advisories, :cvss_v4, 180, validate: false + end + + def down + with_lock_retries do + remove_column :pm_advisories, :cvss_v4, if_exists: true + end + end +end diff --git a/db/schema_migrations/20250722053202 b/db/schema_migrations/20250722053202 new file mode 100644 index 00000000000000..22de721edd8187 --- /dev/null +++ b/db/schema_migrations/20250722053202 @@ -0,0 +1 @@ +1bf276c59fceab97c32f28fcbe7eaabbe504dfdaca49d1bce83b2e49a9f9123c \ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index 48de31cf4594ee..e97a4ed87e05b1 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -23871,6 +23871,7 @@ CREATE TABLE pm_advisories ( urls text[] DEFAULT '{}'::text[], identifiers jsonb NOT NULL, cve text, + cvss_v4 text, CONSTRAINT check_152def3868 CHECK ((char_length(cvss_v2) <= 128)), CONSTRAINT check_19cbd06439 CHECK ((char_length(advisory_xid) <= 36)), CONSTRAINT check_b1c980b212 CHECK ((char_length(cve) <= 24)), @@ -34806,6 +34807,9 @@ ALTER TABLE ONLY chat_names ALTER TABLE ONLY chat_teams ADD CONSTRAINT chat_teams_pkey PRIMARY KEY (id); +ALTER TABLE pm_advisories + ADD CONSTRAINT check_011cca490f CHECK ((char_length(cvss_v4) <= 180)) NOT VALID; + ALTER TABLE epic_issues ADD CONSTRAINT check_048dce81f3 CHECK ((work_item_parent_link_id IS NOT NULL)) NOT VALID; diff --git a/ee/app/models/package_metadata/advisory.rb b/ee/app/models/package_metadata/advisory.rb index c1829f57f36443..bd7dbcbeb9f56c 100644 --- a/ee/app/models/package_metadata/advisory.rb +++ b/ee/app/models/package_metadata/advisory.rb @@ -16,6 +16,7 @@ class Advisory < ApplicationRecord attribute :cvss_v2, Gitlab::Database::Type::CvssVector.new attribute :cvss_v3, Gitlab::Database::Type::CvssVector.new + attribute :cvss_v4, Gitlab::Database::Type::CvssVector.new validates :advisory_xid, presence: true, length: { maximum: 36 } validates :source_xid, presence: true @@ -24,6 +25,7 @@ class Advisory < ApplicationRecord validates :description, length: { maximum: 8192 } validates :cvss_v2, 'vulnerabilities/cvss_vector': { allowed_versions: [2] }, if: -> { cvss_v2.present? } validates :cvss_v3, 'vulnerabilities/cvss_vector': { allowed_versions: [3.0, 3.1] }, if: -> { cvss_v3.present? } + validates :cvss_v4, 'vulnerabilities/cvss_vector': { allowed_versions: [4.0] }, if: -> { cvss_v4.present? } validates :identifiers, json_schema: { filename: 'pm_advisory_identifiers' } validates :urls, length: { minimum: 0, maximum: 20 } validates_each :urls do |record, _, urls| diff --git a/ee/app/services/package_metadata/ingestion/advisory/advisory_ingestion_task.rb b/ee/app/services/package_metadata/ingestion/advisory/advisory_ingestion_task.rb index 180e2e5fe63053..97bea80f8cedc1 100644 --- a/ee/app/services/package_metadata/ingestion/advisory/advisory_ingestion_task.rb +++ b/ee/app/services/package_metadata/ingestion/advisory/advisory_ingestion_task.rb @@ -64,6 +64,7 @@ def advisories description: data_object.description, cvss_v2: data_object.cvss_v2, cvss_v3: data_object.cvss_v3, + cvss_v4: data_object.cvss_v4, identifiers: data_object.identifiers, urls: data_object.urls, cve: data_object.cve, diff --git a/ee/app/validators/json_schemas/pm_affected_package_overridden_advisory_fields.json b/ee/app/validators/json_schemas/pm_affected_package_overridden_advisory_fields.json index 000b711492161b..e66bf1f67ec23e 100644 --- a/ee/app/validators/json_schemas/pm_affected_package_overridden_advisory_fields.json +++ b/ee/app/validators/json_schemas/pm_affected_package_overridden_advisory_fields.json @@ -111,6 +111,17 @@ ], "pattern": "^CVSS:3\\.[\\d\\.]+\\/AV:[NALP]\\/AC:[LH]\\/PR:[NLH]\\/UI:[NR]\\/S:[UC]\\/C:[NLH]\\/I:[NLH]\\/A:[NLH]$" }, + "cvss_v4": { + "$id": "#/properties/cvss_v4", + "type": "string", + "title": "Common Vulnerability Scoring System Vector (according to CVSS version 4)", + "default": "", + "examples": [ + "CVSS:4.0/AV:L/AC:L/AT:P/PR:L/UI:N/VC:H/VI:N/VA:N/SC:H/SI:N/SA:N" + ], + "pattern": "^CVSS:4\\.0\\/AV:[NALP]\\/AC:[LH]\\/AT:[NP]\\/PR:[NLH]\\/UI:[NPA]\\/VC:[HLN]\\/VI:[HLN]\\/VA:[HLN]\\/SC:[HLN]\\/SI:[HLN]\\/SA:[HLN](\\/E:[XAPU])?(\\/CR:[XHML])?(\\/IR:[XHML])?(\\/AR:[XHML])?(\\/MAV:[XNALP])?(\\/MAC:[XLH])?(\\/MAT:[XNP])?(\\/MPR:[XNLH])?(\\/MUI:[XNPA])?(\\/MVC:[XNLH])?(\\/MVI:[XNLH])?(\\/MVA:[XNLH])?(\\/MSC:[XNLH])?(\\/MSI:[XNLHS])?(\\/MSA:[XNLHS])?(\\/S:[XNP])?(\\/AU:[XNY])?(\\/R:[XAUI])?(\\/V:[XDC])?(\\/RE:[XLMH])?(\\/U:(X|Clear|Green|Amber|Red))?$", + "maxLength": 200 + }, "urls": { "$id": "#/properties/urls", "type": "array", diff --git a/ee/app/validators/vulnerabilities/cvss_vector_validator.rb b/ee/app/validators/vulnerabilities/cvss_vector_validator.rb index 5ef50a3dba1dba..480b25a3105d0e 100644 --- a/ee/app/validators/vulnerabilities/cvss_vector_validator.rb +++ b/ee/app/validators/vulnerabilities/cvss_vector_validator.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true # Validates CVSS vector strings. -# Works for CVSS 3.1, 3.0, and 2.0 +# Works for CVSS 4.0, 3.1, 3.0, and 2.0 module Vulnerabilities class CvssVectorValidator < ActiveModel::EachValidator VALIDATIONS = %i[ diff --git a/ee/lib/gitlab/vulnerability_scanning/advisory.rb b/ee/lib/gitlab/vulnerability_scanning/advisory.rb index e53b562bb6ef19..fb2e75bf9b720e 100644 --- a/ee/lib/gitlab/vulnerability_scanning/advisory.rb +++ b/ee/lib/gitlab/vulnerability_scanning/advisory.rb @@ -3,7 +3,7 @@ module Gitlab module VulnerabilityScanning class Advisory - attr_reader :xid, :title, :description, :solution, :identifiers, :urls, :cvss_v2, :cvss_v3, :source_xid + attr_reader :xid, :title, :description, :solution, :identifiers, :urls, :cvss_v2, :cvss_v3, :cvss_v4, :source_xid def self.from_affected_package(affected_package:, advisory:) new( @@ -14,6 +14,7 @@ def self.from_affected_package(affected_package:, advisory:) urls: advisory.urls, cvss_v2: advisory.cvss_v2, cvss_v3: advisory.cvss_v3, + cvss_v4: advisory.cvss_v4, solution: affected_package.solution_text, source_xid: advisory.source_xid ) @@ -31,11 +32,12 @@ def self.from_affected_package(affected_package:, advisory:) # @param solution [String] The solution of the advisory. # @param identifiers [Array] A list of identifiers for the advisory, e.g. ["CVE-2023-00001"]. # @param urls [Array] A list of URLs with resources related to the advisory, e.g. ["https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2023-00001"]. + # @param cvss_v4 [String, CvssSuite::Cvss40, nil] A cvss vector string or CvssSuite::Cvss40 object. # @param cvss_v3 [String, CvssSuite::Cvss3, CvssSuite::Cvss31, nil] A cvss vector string, CvssSuite::Cvss3 object, # or CvssSuite::Cvss31 object. # @param cvss_v2 [String, CvssSuite::Cvss2, nil] A cvss vector string or CvssSuite::Cvss2 object. def initialize( - xid:, title:, description:, solution:, identifiers:, urls:, source_xid:, cvss_v3: nil, + xid:, title:, description:, solution:, identifiers:, urls:, source_xid:, cvss_v4: nil, cvss_v3: nil, cvss_v2: nil) @xid = xid @title = title @@ -43,6 +45,7 @@ def initialize( @solution = solution @identifiers = identifiers @urls = normalize_urls(urls) + @cvss_v4 = init_cvss_v4(cvss_v4) @cvss_v3 = init_cvss_v3(cvss_v3) @cvss_v2 = init_cvss_v2(cvss_v2) @source_xid = source_xid @@ -51,6 +54,14 @@ def initialize( private + def init_cvss_v4(cvss) + return if cvss.nil? + + return cvss if cvss.instance_of?(CvssSuite::Cvss40) + + CvssSuite.new(cvss) + end + def init_cvss_v3(cvss) return if cvss.nil? diff --git a/ee/lib/gitlab/vulnerability_scanning/finding_builder.rb b/ee/lib/gitlab/vulnerability_scanning/finding_builder.rb index 5e4b1a2feb581d..378cf470aaf03b 100644 --- a/ee/lib/gitlab/vulnerability_scanning/finding_builder.rb +++ b/ee/lib/gitlab/vulnerability_scanning/finding_builder.rb @@ -162,7 +162,7 @@ def links end def cvss_vectors_with_vendor - [advisory.cvss_v3, advisory.cvss_v2].compact.map do |cvss| + cvss_scores.map do |cvss| { 'vendor' => vendor_from_identifiers, 'vector' => cvss.vector @@ -178,9 +178,13 @@ def vendor_from_identifiers strong_memoize_attr :vendor_from_identifiers def severity - advisory.cvss_v3&.severity&.downcase || advisory.cvss_v2&.severity&.downcase + cvss_scores.first&.severity&.downcase end - strong_memoize_attr :severity + + def cvss_scores + [advisory.cvss_v4, advisory.cvss_v3, advisory.cvss_v2].compact.select(&:valid?) + end + strong_memoize_attr :cvss_scores def details {} diff --git a/ee/spec/factories/package_metadata/advisory_data_objects.rb b/ee/spec/factories/package_metadata/advisory_data_objects.rb index 6e3a93f44e1315..d8818c090565c0 100644 --- a/ee/spec/factories/package_metadata/advisory_data_objects.rb +++ b/ee/spec/factories/package_metadata/advisory_data_objects.rb @@ -9,6 +9,7 @@ description { FFaker::Lorem.paragraph } cvss_v2 { "AV:N/AC:M/Au:N/C:N/I:P/A:N" } cvss_v3 { "CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:C/C:N/I:L/A:L" } + cvss_v4 { "CVSS:4.0/AV:L/AC:L/AT:P/PR:L/UI:N/VC:H/VI:N/VA:N/SC:H/SI:N/SA:N" } urls { Array.new(2) { FFaker::Internet.uri("https") } } identifiers do [ diff --git a/ee/spec/factories/package_metadata/pm_advisories.rb b/ee/spec/factories/package_metadata/pm_advisories.rb index 74a4feab5cce4c..9b9c661cfe3598 100644 --- a/ee/spec/factories/package_metadata/pm_advisories.rb +++ b/ee/spec/factories/package_metadata/pm_advisories.rb @@ -9,6 +9,7 @@ description { FFaker::Lorem.paragraph } cvss_v2 { "AV:N/AC:M/Au:N/C:N/I:P/A:N" } cvss_v3 { "CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:C/C:N/I:L/A:L" } + cvss_v4 { "CVSS:4.0/AV:L/AC:L/AT:P/PR:L/UI:N/VC:H/VI:N/VA:N/SC:H/SI:N/SA:N" } identifiers do [ association(:pm_identifier, :cve), diff --git a/ee/spec/factories/vulnerability_scanning/advisories.rb b/ee/spec/factories/vulnerability_scanning/advisories.rb index f0593993852a79..aca8aa88b77425 100644 --- a/ee/spec/factories/vulnerability_scanning/advisories.rb +++ b/ee/spec/factories/vulnerability_scanning/advisories.rb @@ -17,6 +17,7 @@ urls { Array.new(2) { FFaker::Internet.uri("https") } } cvss_v2 { "AV:N/AC:M/Au:N/C:N/I:P/A:N" } cvss_v3 { "CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:C/C:N/I:L/A:L" } + cvss_v4 { "CVSS:4.0/AV:L/AC:L/AT:P/PR:L/UI:N/VC:H/VI:N/VA:N/SC:H/SI:N/SA:N" } source_xid { 'glad' } skip_create @@ -31,6 +32,7 @@ urls: urls, cvss_v2: cvss_v2, cvss_v3: cvss_v3, + cvss_v4: cvss_v4, source_xid: source_xid ) end diff --git a/ee/spec/fixtures/package_metadata/sync/advisories/v2/maven.ndjson b/ee/spec/fixtures/package_metadata/sync/advisories/v2/maven.ndjson index d31181964a98da..9e46a0bce2db64 100644 --- a/ee/spec/fixtures/package_metadata/sync/advisories/v2/maven.ndjson +++ b/ee/spec/fixtures/package_metadata/sync/advisories/v2/maven.ndjson @@ -1,2 +1,2 @@ -{"advisory":{"id":"d4f176d6-0a07-46f4-9da5-22df92e5efa0","source":"glad","title":"Incorrect Permission Assignment for Critical Resource","description":"A missing permission check in Jenkins Google Kubernetes Engine Plugin allows attackers with Overall/Read permission to obtain limited information about the scope of a credential with an attacker-specified credentials ID.","cvss_v2":"AV:N/AC:L/Au:S/C:P/I:N/A:N","cvss_v3":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N","published_date":"2019-10-16","urls":["https://nvd.nist.gov/vuln/detail/CVE-2019-10445","https://jenkins.io/security/advisory/2019-10-16/#SECURITY-1607"],"identifiers":[{"type":"cve","name":"CVE-2019-10445","value":"CVE-2019-10445","url":"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-10445"},{"type":"cwe","name":"CWE-1035","value":"1035","url":"https://cwe.mitre.org/data/definitions/1035.html"},{"type":"cwe","name":"CWE-862","value":"862","url":"https://cwe.mitre.org/data/definitions/862.html"},{"type":"cwe","name":"CWE-937","value":"937","url":"https://cwe.mitre.org/data/definitions/937.html"}]},"packages":[{"name":"org.jenkins-ci.plugins/google-kubernetes-engine","purl_type":"maven","affected_range":"(,0.7.0]","solution":"Upgrade to version 0.8 or above.","fixed_versions":["0.8"]}]} +{"advisory":{"id":"d4f176d6-0a07-46f4-9da5-22df92e5efa0","source":"glad","title":"Incorrect Permission Assignment for Critical Resource","description":"A missing permission check in Jenkins Google Kubernetes Engine Plugin allows attackers with Overall/Read permission to obtain limited information about the scope of a credential with an attacker-specified credentials ID.","cvss_v2":"AV:N/AC:L/Au:S/C:P/I:N/A:N","cvss_v3":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N","cvss_v4":"CVSS:4.0/AV:L/AC:L/AT:P/PR:L/UI:N/VC:H/VI:N/VA:N/SC:H/SI:N/SA:N","published_date":"2019-10-16","urls":["https://nvd.nist.gov/vuln/detail/CVE-2019-10445","https://jenkins.io/security/advisory/2019-10-16/#SECURITY-1607"],"identifiers":[{"type":"cve","name":"CVE-2019-10445","value":"CVE-2019-10445","url":"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-10445"},{"type":"cwe","name":"CWE-1035","value":"1035","url":"https://cwe.mitre.org/data/definitions/1035.html"},{"type":"cwe","name":"CWE-862","value":"862","url":"https://cwe.mitre.org/data/definitions/862.html"},{"type":"cwe","name":"CWE-937","value":"937","url":"https://cwe.mitre.org/data/definitions/937.html"}]},"packages":[{"name":"org.jenkins-ci.plugins/google-kubernetes-engine","purl_type":"maven","affected_range":"(,0.7.0]","solution":"Upgrade to version 0.8 or above.","fixed_versions":["0.8"]}]} {"advisory":{"id":"950539e3-9c79-455d-8778-d0e343e6f0db","source":"glad","title":"Excessive Iteration","description":"Issue summary: Checking excessively long DH keys or parameters may be very slow.\n\nImpact summary: Applications that use the functions DH_check(), DH_check_ex()\nor EVP_PKEY_param_check() to check a DH key or DH parameters may experience long\ndelays. Where the key or parameters that are being checked have been obtained\nfrom an untrusted source this may lead to a Denial of Service.\n\nThe function DH_check() performs various checks on DH parameters. After fixing\nCVE-2023-3446 it was discovered that a large q parameter value can also trigger\nan overly long computation during some of these checks. A correct q value,\nif present, cannot be larger than the modulus p parameter, thus it is\nunnecessary to perform these checks if q is larger than p.\n\nAn application that calls DH_check() and supplies a key or parameters obtained\nfrom an untrusted source could be vulnerable to a Denial of Service attack.\n\nThe function DH_check() is itself called by a number of other OpenSSL functions.\nAn application calling any of those other functions may similarly be affected.\nThe other functions affected by this are DH_check_ex() and\nEVP_PKEY_param_check().\n\nAlso vulnerable are the OpenSSL dhparam and pkeyparam command line applications\nwhen using the \"-check\" option.\n\nThe OpenSSL SSL/TLS implementation is not affected by this issue.\n\nThe OpenSSL 3.0 and 3.1 FIPS providers are not affected by this issue.","cvss_v3":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L","published_date":"2023-07-31","urls":["https://nvd.nist.gov/vuln/detail/CVE-2023-3817","https://www.openssl.org/news/secadv/20230731.txt","https://git.openssl.org/gitweb/?p=openssl.git;a=commitdiff;h=6a1eb62c29db6cb5eec707f9338aee00f44e26f5","https://git.openssl.org/gitweb/?p=openssl.git;a=commitdiff;h=9002fd07327a91f35ba6c1307e71fa6fd4409b7f","https://git.openssl.org/gitweb/?p=openssl.git;a=commitdiff;h=91ddeba0f2269b017dc06c46c993a788974b1aa5","https://git.openssl.org/gitweb/?p=openssl.git;a=commitdiff;h=869ad69aadd985c7b8ca6f4e5dd0eb274c9f3644","http://www.openwall.com/lists/oss-security/2023/07/31/1","http://seclists.org/fulldisclosure/2023/Jul/43"],"identifiers":[{"type":"cve","name":"CVE-2023-3817","value":"CVE-2023-3817","url":"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2023-3817"},{"type":"cwe","name":"CWE-1035","value":"1035","url":"https://cwe.mitre.org/data/definitions/1035.html"},{"type":"cwe","name":"CWE-834","value":"834","url":"https://cwe.mitre.org/data/definitions/834.html"},{"type":"cwe","name":"CWE-937","value":"937","url":"https://cwe.mitre.org/data/definitions/937.html"}]},"packages":null} diff --git a/ee/spec/lib/gitlab/database/type/cvss_vector_spec.rb b/ee/spec/lib/gitlab/database/type/cvss_vector_spec.rb index 515070ddc243fa..2c4eae21396566 100644 --- a/ee/spec/lib/gitlab/database/type/cvss_vector_spec.rb +++ b/ee/spec/lib/gitlab/database/type/cvss_vector_spec.rb @@ -18,6 +18,7 @@ [ 'CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:N', 'CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:N', + 'CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:N/SC:N/SI:N/SA:N', 'AV:L/AC:H/Au:M/C:N/I:N/A:N' ].each do |vector| it "serializes CvssSuite::Cvss for #{vector}" do @@ -30,13 +31,15 @@ using RSpec::Parameterized::TableSyntax where(:input, :expected) do - 'CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:N' | true - CvssSuite.new('CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:N') | true - nil | true - 1 | false - true | false - 'foo' | false - 'CVSS:3.1/AV:INVALID/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:N' | false + 'CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:N' | true + 'CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:N/SC:N/SI:N/SA:N' | true + CvssSuite.new('CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:N') | true + CvssSuite.new('CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:N/SC:N/SI:N/SA:N') | true + nil | true + 1 | false + true | false + 'foo' | false + 'CVSS:3.1/AV:INVALID/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:N' | false end with_them do @@ -54,6 +57,7 @@ it 'casts strings to CvssSuite::Cvss' do expect(type.cast('CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:N')).to be_a(::CvssSuite::Cvss31) expect(type.cast('CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:N')).to be_a(::CvssSuite::Cvss3) + expect(type.cast('CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:N/SC:N/SI:N/SA:N')).to be_a(::CvssSuite::Cvss40) expect(type.cast('AV:L/AC:H/Au:M/C:N/I:N/A:N')).to be_a(::CvssSuite::Cvss2) end end diff --git a/ee/spec/lib/gitlab/vulnerability_scanning/advisory_spec.rb b/ee/spec/lib/gitlab/vulnerability_scanning/advisory_spec.rb index f0ba17ba2f25c7..0ea005332928e0 100644 --- a/ee/spec/lib/gitlab/vulnerability_scanning/advisory_spec.rb +++ b/ee/spec/lib/gitlab/vulnerability_scanning/advisory_spec.rb @@ -4,6 +4,7 @@ require 'cvss_suite' RSpec.describe Gitlab::VulnerabilityScanning::Advisory, feature_category: :software_composition_analysis do + let(:cvss_v4) { nil } let(:cvss_v3) { nil } let(:cvss_v2) { nil } let(:urls) { ["https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2023-00001"] } @@ -17,11 +18,31 @@ solution: 'The solution goes here', identifiers: ['CVE-2023-00001'], urls: urls, + cvss_v4: cvss_v4, cvss_v3: cvss_v3, cvss_v2: cvss_v2, source_xid: 'glad') end + describe '#cvss_v4' do + context 'when cvss_v4 is nil' do + it { expect(advisory.cvss_v4).to be_nil } + end + + context 'when cvss_v4 is a string' do + let(:cvss_v4) { "CVSS:4.0/AV:L/AC:L/AT:P/PR:L/UI:N/VC:H/VI:N/VA:N/SC:H/SI:N/SA:N" } + + it { expect(advisory.cvss_v4).to be_instance_of(CvssSuite::Cvss40) } + it { expect(advisory.cvss_v4.vector).to eq('CVSS:4.0/AV:L/AC:L/AT:P/PR:L/UI:N/VC:H/VI:N/VA:N/SC:H/SI:N/SA:N') } + end + + context 'when cvss_v4 is a CvssSuite::Cvss40 object' do + let(:cvss_v4) { CvssSuite::Cvss40.new("CVSS:4.0/AV:L/AC:L/AT:P/PR:L/UI:N/VC:H/VI:N/VA:N/SC:H/SI:N/SA:N") } + + it { expect(advisory.cvss_v4).to be(cvss_v4) } + end + end + describe '#cvss_v3' do context 'when cvss_v3 is nil' do it { expect(advisory.cvss_v3).to be_nil } diff --git a/ee/spec/lib/gitlab/vulnerability_scanning/container_scanning/finding_builder_spec.rb b/ee/spec/lib/gitlab/vulnerability_scanning/container_scanning/finding_builder_spec.rb index 12ecf96b5a9ab3..09d9fde21b8d63 100644 --- a/ee/spec/lib/gitlab/vulnerability_scanning/container_scanning/finding_builder_spec.rb +++ b/ee/spec/lib/gitlab/vulnerability_scanning/container_scanning/finding_builder_spec.rb @@ -19,6 +19,7 @@ description: "buffer.c in named in ISC BIND 9 before 9.9.9-P3, 9.10.x before 9.10.4-P3, and 9.11.x", cvss_v2: "AV:N/AC:L/Au:N/C:N/I:N/A:C", cvss_v3: "CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H", + cvss_v4: "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N", urls: %w[http://rhn.redhat.com/errata/RHSA-2016-1944.html http://rhn.redhat.com/errata/RHSA-2016-1945.html http://rhn.redhat.com/errata/RHSA-2016-2099.html], identifiers: [ build(:pm_identifier, type: "cve", name: "CVE-2018-1000538", diff --git a/ee/spec/lib/gitlab/vulnerability_scanning/dependency_scanning/finding_builder_spec.rb b/ee/spec/lib/gitlab/vulnerability_scanning/dependency_scanning/finding_builder_spec.rb index 55afd39de66ca4..978dc5defd529f 100644 --- a/ee/spec/lib/gitlab/vulnerability_scanning/dependency_scanning/finding_builder_spec.rb +++ b/ee/spec/lib/gitlab/vulnerability_scanning/dependency_scanning/finding_builder_spec.rb @@ -19,6 +19,7 @@ description: "Minio a Allocation of Memory Without Limits or Throttling vulnerability in write-to-RAM.", cvss_v2: "AV:N/AC:L/Au:N/C:N/I:N/A:P", cvss_v3: "CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H", + cvss_v4: "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N", urls: ["https://nvd.nist.gov/vuln/detail/CVE-2018-1000538", "https://github.com/minio/minio/pull/5957"], identifiers: [ build(:pm_identifier, type: "cve", name: "CVE-2018-1000538", diff --git a/ee/spec/lib/gitlab/vulnerability_scanning/finding_builder_spec.rb b/ee/spec/lib/gitlab/vulnerability_scanning/finding_builder_spec.rb index f1a6ef971198b4..a023e55e34341a 100644 --- a/ee/spec/lib/gitlab/vulnerability_scanning/finding_builder_spec.rb +++ b/ee/spec/lib/gitlab/vulnerability_scanning/finding_builder_spec.rb @@ -6,6 +6,7 @@ let(:sbom_source) { build(:ci_reports_sbom_source) } let(:scanner) { Gitlab::VulnerabilityScanning::SecurityScanner.fabricate } let(:location) { build(:ci_reports_security_locations_sast) } + let(:cvss_v4) { "CVSS:4.0/AV:L/AC:L/AT:P/PR:L/UI:N/VC:H/VI:N/VA:N/SC:H/SI:N/SA:N" } let(:cvss_v3) { "CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H" } let(:cvss_v2) { "AV:N/AC:L/Au:N/C:N/I:N/A:P" } let(:identifiers) do @@ -19,6 +20,7 @@ build(:vs_advisory, title: "Allocation of File Descriptors or Handles Without Limits or Throttling", description: "Minio a Allocation of Memory Without Limits or Throttling vulnerability in write-to-RAM.", + cvss_v4: cvss_v4, cvss_v3: cvss_v3, cvss_v2: cvss_v2, urls: ["https://nvd.nist.gov/vuln/detail/CVE-2018-1000538", "https://github.com/minio/minio/pull/5957"], @@ -143,6 +145,10 @@ it 'populates finding cvss data' do expect(builder.finding.cvss).to eq([ + { + 'vendor' => 'NVD', + 'vector' => cvss_v4 + }, { 'vendor' => 'NVD', 'vector' => cvss_v3 @@ -155,6 +161,7 @@ end context 'when vectors are missing' do + let(:cvss_v4) { nil } let(:cvss_v3) { nil } let(:cvss_v2) { nil } @@ -168,6 +175,10 @@ it 'reports vendor as Unknown' do expect(builder.finding.cvss).to eq([ + { + 'vendor' => 'Unknown', + 'vector' => cvss_v4 + }, { 'vendor' => 'Unknown', 'vector' => cvss_v3 diff --git a/ee/spec/models/package_metadata/advisory_spec.rb b/ee/spec/models/package_metadata/advisory_spec.rb index 09a9ddebc3e842..151db10dd7a63c 100644 --- a/ee/spec/models/package_metadata/advisory_spec.rb +++ b/ee/spec/models/package_metadata/advisory_spec.rb @@ -10,14 +10,17 @@ describe 'validations' do it_behaves_like 'model with cvss v2 vector validation', :cvss_v2 it_behaves_like 'model with cvss v3 vector validation', :cvss_v3 + it_behaves_like 'model with cvss v4 vector validation', :cvss_v4 it { is_expected.to validate_presence_of(:advisory_xid) } it { is_expected.to validate_presence_of(:source_xid) } it { is_expected.to validate_presence_of(:published_date) } it { is_expected.to allow_value(nil).for(:cvss_v2) } it { is_expected.to allow_value(nil).for(:cvss_v3) } + it { is_expected.to allow_value(nil).for(:cvss_v4) } it { is_expected.not_to allow_value('').for(:cvss_v2) } it { is_expected.not_to allow_value('').for(:cvss_v3) } + it { is_expected.not_to allow_value('').for(:cvss_v4) } describe 'length validation' do where(:attribute, :value, :is_valid) do diff --git a/ee/spec/models/package_metadata/affected_package_spec.rb b/ee/spec/models/package_metadata/affected_package_spec.rb index 3df5b0caa3b6a2..30c1e4e9081b7f 100644 --- a/ee/spec/models/package_metadata/affected_package_spec.rb +++ b/ee/spec/models/package_metadata/affected_package_spec.rb @@ -48,6 +48,7 @@ [:description, 'A description with `markdown`'], [:cvss_v2, 'AV:N/AC:M/Au:N/C:N/I:P/A:P'], [:cvss_v3, 'CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:C/C:N/I:L/A:L'], + [:cvss_v4, 'CVSS:4.0/AV:L/AC:L/AT:P/PR:L/UI:N/VC:H/VI:N/VA:N/SC:H/SI:N/SA:N'], [:identifiers, [{ type: 'foo', name: 'bar', value: 'baz' }]], [:urls, ["https://nvd.nist.gov/vuln/detail/CVE-2019-3888", "https://bugzilla.redhat.com/show_bug.cgi?id=CVE-2019-3888"]] diff --git a/ee/spec/services/package_metadata/data_object_fabricator_spec.rb b/ee/spec/services/package_metadata/data_object_fabricator_spec.rb index 160362e55360c3..270f13d74cd75c 100644 --- a/ee/spec/services/package_metadata/data_object_fabricator_spec.rb +++ b/ee/spec/services/package_metadata/data_object_fabricator_spec.rb @@ -95,6 +95,7 @@ "with an attacker-specified credentials ID.", cvss_v2: "AV:N/AC:L/Au:S/C:P/I:N/A:N", cvss_v3: "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N", + cvss_v4: "CVSS:4.0/AV:L/AC:L/AT:P/PR:L/UI:N/VC:H/VI:N/VA:N/SC:H/SI:N/SA:N", published_date: "2019-10-16", urls: ["https://nvd.nist.gov/vuln/detail/CVE-2019-10445", "https://jenkins.io/security/advisory/2019-10-16/#SECURITY-1607"])]) diff --git a/ee/spec/support/shared_examples/models/vulnerabilities/cvss_vector_validator_shared_examples.rb b/ee/spec/support/shared_examples/models/vulnerabilities/cvss_vector_validator_shared_examples.rb index 11f80ac629861c..742f2ef4570445 100644 --- a/ee/spec/support/shared_examples/models/vulnerabilities/cvss_vector_validator_shared_examples.rb +++ b/ee/spec/support/shared_examples/models/vulnerabilities/cvss_vector_validator_shared_examples.rb @@ -281,3 +281,114 @@ end end end + +RSpec.shared_examples 'model with cvss v4 vector validation' do |attribute| + let(:cvss_version) { '4.0' } + let(:attack_vector) { 'N' } + let(:attack_complexity) { 'L' } + let(:attack_requirements) { 'N' } + let(:privileges_required) { 'N' } + let(:user_interaction) { 'N' } + let(:vuln_confidentiality) { 'N' } + let(:vuln_integrity) { 'N' } + let(:vuln_availability) { 'N' } + let(:sub_confidentiality) { 'N' } + let(:sub_integrity) { 'N' } + let(:sub_availability) { 'N' } + + let(:base_params) do + { + CVSS: cvss_version, + AV: attack_vector, + AC: attack_complexity, + AT: attack_requirements, + PR: privileges_required, + UI: user_interaction, + VC: vuln_confidentiality, + VI: vuln_integrity, + VA: vuln_availability, + SC: sub_confidentiality, + SI: sub_integrity, + SA: sub_availability + } + end + + let(:params) { base_params } + + let(:vector) do + params.map { |key, value| "#{key}:#{value}" }.join('/') + end + + before do + subject[attribute] = vector + subject.validate + end + + it_behaves_like 'model with cvss generic cvss validation', attribute + + context 'when validating', :aggregate_failures do + describe 'version' do + context 'when version is valid' do + it { is_expected.to be_valid } + end + + context 'when version param is invalid' do + where(:cvss_version) { ['3.1', '3.0', '2.0', '???', ''] } + + with_them do + it_behaves_like 'model with invalid cvss vector string', attribute + end + end + + context 'when given a valid cvss v3 vector' do + let(:vector) { "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:N" } + + it { is_expected.to be_invalid } + + it 'reports correct error' do + expect(subject.errors[attribute]).to include('must use version 4.0') + end + end + end + + describe 'parameters' do + { + attack_vector: %w[N A L P], + attack_complexity: %w[L H], + attack_requirements: %w[N P], + privileges_required: %w[N L H], + user_interaction: %w[N P A], + vuln_confidentiality: %w[N L H], + vuln_integrity: %w[N L H], + vuln_availability: %w[N L H], + sub_confidentiality: %w[N L H], + sub_integrity: %w[N L H], + sub_availability: %w[N L H] + }.each do |parameter, valid_parameter_values| + context "when #{parameter} is valid" do + where(parameter) { valid_parameter_values } + + with_them do + it { is_expected.to be_valid } + end + end + + context "when #{parameter} is invalid" do + where(parameter) { ['X', '?', '🦊', ''] } + + with_them do + it_behaves_like 'model with invalid cvss vector string', attribute + end + end + end + + %i[CVSS AV AC AT PR UI VC VI VA SC SI SA].each do |parameter| + context "when #{parameter} is missing" do + let(:params) { base_params.except(parameter) } + + it_behaves_like 'model with invalid cvss vector string', attribute + end + end + end + end +end -- GitLab From 7008fc3b1523df1a439bf315899bd00d2adec51f Mon Sep 17 00:00:00 2001 From: Alexandre Laroche Date: Tue, 16 Dec 2025 12:12:36 -0500 Subject: [PATCH 2/2] Fix --- ee/lib/gitlab/vulnerability_scanning/finding_builder.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/ee/lib/gitlab/vulnerability_scanning/finding_builder.rb b/ee/lib/gitlab/vulnerability_scanning/finding_builder.rb index 378cf470aaf03b..3bd609795fa0e8 100644 --- a/ee/lib/gitlab/vulnerability_scanning/finding_builder.rb +++ b/ee/lib/gitlab/vulnerability_scanning/finding_builder.rb @@ -180,6 +180,7 @@ def vendor_from_identifiers def severity cvss_scores.first&.severity&.downcase end + strong_memoize_attr :severity def cvss_scores [advisory.cvss_v4, advisory.cvss_v3, advisory.cvss_v2].compact.select(&:valid?) -- GitLab