From fbb3f86c70b4df1f8275563b04913ea29ce69585 Mon Sep 17 00:00:00 2001
From: Yasha Rise <yrise@gitlab.com>
Date: Tue, 31 Dec 2024 14:59:18 +0000
Subject: [PATCH] Populate cve column in pm_advisories at ingestion

---
 .../package_metadata/advisory_data_object.rb  | 15 +++++-
 .../advisory/advisory_ingestion_task.rb       |  1 +
 .../package_metadata/advisory_data_objects.rb |  1 +
 .../advisory_data_object_spec.rb              | 46 ++++++++++++++++++-
 4 files changed, 60 insertions(+), 3 deletions(-)

diff --git a/ee/app/services/package_metadata/advisory_data_object.rb b/ee/app/services/package_metadata/advisory_data_object.rb
index 9d8b0bb0668b6..5c270e5a438d4 100644
--- a/ee/app/services/package_metadata/advisory_data_object.rb
+++ b/ee/app/services/package_metadata/advisory_data_object.rb
@@ -12,6 +12,7 @@ def self.create(data, purl_type)
       end
 
       advisory['source_xid'] = source
+      advisory['cve'] = extract_cve(advisory['identifiers'])
 
       packages = data['packages'].clone
       raise ArgumentError, 'Missing packages attribute' unless packages
@@ -26,13 +27,22 @@ def self.create_affected_packages(packages, purl_type)
       end
     end
 
+    def self.extract_cve(identifiers)
+      return unless identifiers
+
+      cve_identifier = identifiers.find { |identifier| identifier['type']&.casecmp?('cve') }
+      cve_identifier['name'] if cve_identifier
+    end
+
+    private_class_method :extract_cve
+
     attr_accessor :advisory_xid, :source_xid, :published_date, :title, :description, :cvss_v2, :cvss_v3, :urls,
-      :identifiers, :affected_packages
+      :identifiers, :affected_packages, :cve
 
     # rubocop:disable Metrics/ParameterLists
     def initialize(
       advisory_xid:, source_xid:, published_date:, title: '', description: '', cvss_v2: nil, cvss_v3: nil, urls: [],
-      identifiers: [], affected_packages: [])
+      identifiers: [], affected_packages: [], cve: nil)
       # rubocop:enable Metrics/ParameterLists
       @advisory_xid = advisory_xid
       @source_xid = source_xid
@@ -44,6 +54,7 @@ def initialize(
       @urls = urls
       @identifiers = identifiers.map { |ident| ident.transform_keys(&:to_sym) }
       @affected_packages = affected_packages
+      @cve = cve
     end
   end
 end
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 33111ba7dfeb7..180e2e5fe6305 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
@@ -66,6 +66,7 @@ def advisories
               cvss_v3: data_object.cvss_v3,
               identifiers: data_object.identifiers,
               urls: data_object.urls,
+              cve: data_object.cve,
               created_at: now,
               updated_at: now
             )
diff --git a/ee/spec/factories/package_metadata/advisory_data_objects.rb b/ee/spec/factories/package_metadata/advisory_data_objects.rb
index 91506f88987c6..6e3a93f44e131 100644
--- a/ee/spec/factories/package_metadata/advisory_data_objects.rb
+++ b/ee/spec/factories/package_metadata/advisory_data_objects.rb
@@ -16,6 +16,7 @@
         association(:pm_identifier, :gemnasium)
       ]
     end
+    cve { identifiers[0]['name'] }
 
     affected_packages { [association(:pm_affected_package_data_object)] }
 
diff --git a/ee/spec/services/package_metadata/advisory_data_object_spec.rb b/ee/spec/services/package_metadata/advisory_data_object_spec.rb
index dd2c56ec5df3c..895549da5676a 100644
--- a/ee/spec/services/package_metadata/advisory_data_object_spec.rb
+++ b/ee/spec/services/package_metadata/advisory_data_object_spec.rb
@@ -71,7 +71,8 @@
           have_attributes(purl_type: purl_type,
             package_name: 'org.jenkins-ci.plugins/google-kubernetes-engine', affected_range: '(,0.7.0]',
             solution: 'Upgrade to version 0.8 or above.', fixed_versions: ["0.8"])
-        ]))
+        ],
+        cve: "CVE-2019-10445"))
     }
 
     context 'when an attribute is missing' do
@@ -90,6 +91,7 @@
           :cvvs_v3      | false
           :urls         | false
           :identifiers  | false
+          :cve          | false
           :id           | true
           :source       | true
         end
@@ -132,5 +134,47 @@
         end
       end
     end
+
+    context 'when there is no CVE identifier' do
+      let(:hash) do
+        {
+          "advisory" =>
+            {
+              "id" => "test-id",
+              "source" => "glad",
+              "published_date" => "2023-01-01",
+              "identifiers" => [
+                { "type" => "cwe", "name" => "CWE-79", "value" => "79" }
+              ]
+            },
+          "packages" => []
+        }
+      end
+
+      it 'sets cve to nil' do
+        expect(described_class.create(hash, purl_type).cve).to be_nil
+      end
+    end
+
+    context 'when the CVE type is in different case' do
+      let(:hash) do
+        {
+          "advisory" =>
+            {
+              "id" => "test-id",
+              "source" => "glad",
+              "published_date" => "2023-01-01",
+              "identifiers" => [
+                { "type" => "CVe", "name" => "CVE-2021-5678", "value" => "CVE-2021-5678" }
+              ]
+            },
+          "packages" => []
+        }
+      end
+
+      it 'extracts the CVE name case-insensitively' do
+        expect(described_class.create(hash, purl_type).cve).to eq('CVE-2021-5678')
+      end
+    end
   end
 end
-- 
GitLab