← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] ~ines-almeida/launchpad:add-uct-handler-get-imports-exports into launchpad:master

 

Ines Almeida has proposed merging ~ines-almeida/launchpad:add-uct-handler-get-imports-exports into launchpad:master.

Commit message:
Add UCT as optional handler for getImports & getExports

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~ines-almeida/launchpad/+git/launchpad/+merge/494673

Besides the modified tests, all tests with 'cve' and 'test_uct' in the name passed
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of ~ines-almeida/launchpad:add-uct-handler-get-imports-exports into launchpad:master.
diff --git a/lib/lp/bugs/model/tests/test_vulnerability.py b/lib/lp/bugs/model/tests/test_vulnerability.py
index 0e00d1c..2e49aa8 100644
--- a/lib/lp/bugs/model/tests/test_vulnerability.py
+++ b/lib/lp/bugs/model/tests/test_vulnerability.py
@@ -12,6 +12,7 @@ from zope.component import getUtility
 from zope.security.interfaces import Unauthorized
 from zope.security.proxy import removeSecurityProxy
 
+from testscenarios.testcase import WithScenarios
 from lp.app.enums import InformationType
 from lp.app.errors import (
     IncompatibleArguments,
@@ -39,6 +40,7 @@ from lp.bugs.model.importvulnerabilityjob import ImportVulnerabilityJob
 from lp.bugs.model.vulnerability import (
     VULNERABILITY_EXPORT_HANDLER_ENABLED_FEATURE_FLAG,
     VULNERABILITY_IMPORT_HANDLER_ENABLED_FEATURE_FLAG,
+    IDistributionSet,
     UnauthorizedVulnerabilityHandler,
 )
 from lp.bugs.model.vulnerabilitysubscription import VulnerabilitySubscription
@@ -1614,45 +1616,85 @@ class TestVulnerabilitySetWebService(TestCaseWithFactory):
         self.assertEqual(200, response.status)
 
 
-class TestVulnerabilityGetJobs(TestCaseWithFactory):
+class TestVulnerabilityGetJobs(WithScenarios, TestCaseWithFactory):
     """Tests for collecting export vulnerability job info."""
 
     layer = LaunchpadZopelessLayer
+    
+    scenarios = [
+        ("soss", {
+            "handler": VulnerabilityHandlerEnum.SOSS,
+            "name": "soss",
+            "distribution_name": "soss",
+            "cve_key": "2025-1979",
+        }),
+        ("uct", {
+            "handler": VulnerabilityHandlerEnum.UCT,
+            "name": "uct",
+            "distribution_name": "ubuntu",
+            "cve_key": "2025-0001",
+        })
+    ]
+
 
     def setUp(self):
         super().setUp()
         self.requester = self.factory.makePerson()
-        self.handler = VulnerabilityHandlerEnum.SOSS
 
-        self.cve_path = (
-            Path(__file__).parent
-            / ".."
-            / ".."
-            / "scripts"
-            / "soss"
-            / "tests"
-            / "sampledata"
-            / "CVE-2025-1979"
-        )
         self.repository = self.factory.makeGitRepository()
         self.refs = self.factory.makeGitRefs(
             repository=self.repository,
             paths=("ref/heads/main", "ref/tags/v1.0"),
         )
+        
+        if self.name == "uct":
+            self.distribution = getUtility(IDistributionSet).getByName(self.distribution_name)
+            self.distribution.security_admin = self.requester
+            
+            # Create data from UCT CVE file
+            distroseries = self.factory.makeDistroSeries(
+                name="xenial",
+                distribution=self.distribution,
+            )
+            sourcepackagename = self.factory.makeSourcePackageName("gbrowse")
+            sp = self.factory.makeSourcePackage(
+                distroseries=distroseries,
+                publish=True,
+                sourcepackagename=sourcepackagename,
+            )
+
+        else:
+            self.distribution = self.factory.makeDistribution(
+                name=self.distribution_name,
+                owner=self.requester,
+            )
 
     def _create_import_job(self):
         """Helper to create a single ImportVulnerabilityJob."""
-        with open(self.cve_path, encoding="utf-8") as file:
+        
+        cve_name = "CVE-" + self.cve_key
+        cve_path = (
+            Path(__file__).parent
+            / ".."
+            / ".."
+            / "scripts"
+            / self.name
+            / "tests"
+            / "sampledata"
+            / cve_name
+        )
+        
+        with open(cve_path, "rb") as file:
             self.useFixture(
                 GitHostingFixture(
                     blob=file.read(),
                     refs=self.refs,
-                    diff_stats={"added": ["cves/CVE-2025-1979"]},
+                    diff_stats={"added": [f"cves/{cve_name}"]},
                 )
             )
 
         job = getUtility(IImportVulnerabilityJobSource).create(
-            handler=VulnerabilityHandlerEnum.SOSS,
+            handler=self.handler,
             git_repository=self.repository.id,
             git_ref="ref/tags/v1.0",
             git_paths=["cves"],
@@ -1673,15 +1715,14 @@ class TestVulnerabilityGetJobs(TestCaseWithFactory):
         import_job.job.complete()
 
         job = getUtility(IExportVulnerabilityJobSource).create(
-            handler=VulnerabilityHandlerEnum.SOSS,
+            handler=self.handler,
         )
 
         return job
 
     def test_getImports(self):
         """Test getImports function runs as expected."""
-        self.factory.makeCVE("2025-1979")
-        self.factory.makeDistribution(name="soss", owner=self.requester)
+        self.factory.makeCVE(self.cve_key)
 
         job = self._create_import_job()
 
@@ -1710,8 +1751,7 @@ class TestVulnerabilityGetJobs(TestCaseWithFactory):
 
     def test_getImports_full(self):
         """Test retrieval of exactly 10 import jobs."""
-        self.factory.makeCVE("2025-1979")
-        self.factory.makeDistribution(name="soss", owner=self.requester)
+        self.factory.makeCVE(self.cve_key)
 
         jobs = []
 
@@ -1748,8 +1788,7 @@ class TestVulnerabilityGetJobs(TestCaseWithFactory):
 
     def test_getImports_more_than_10(self):
         """Test that only the last 10 import jobs are returned."""
-        self.factory.makeCVE("2025-1979")
-        self.factory.makeDistribution(name="soss", owner=self.requester)
+        self.factory.makeCVE(self.cve_key)
 
         jobs = []
 
@@ -1787,8 +1826,13 @@ class TestVulnerabilityGetJobs(TestCaseWithFactory):
 
     def test_getImports_unauthenticated(self):
         """Test imports fail when requester lacks handler rights."""
-        self.factory.makeCVE("2025-1979")
-        self.factory.makeDistribution(name="soss")
+        self.factory.makeCVE(self.cve_key)
+        
+        # Remove permissions
+        if self.name == "uct":
+            self.distribution.security_admin = self.factory.makePerson()
+        else:
+            self.distribution.owner = self.factory.makePerson()
 
         job = self._create_import_job()
 
@@ -1804,8 +1848,7 @@ class TestVulnerabilityGetJobs(TestCaseWithFactory):
 
     def test_getExports(self):
         """Test getExports function runs as expected."""
-        self.factory.makeCVE("2025-1979")
-        self.factory.makeDistribution(name="soss", owner=self.requester)
+        self.factory.makeCVE(self.cve_key)
 
         job = self._create_export_job()
 
@@ -1835,9 +1878,7 @@ class TestVulnerabilityGetJobs(TestCaseWithFactory):
 
     def test_getExports_full(self):
         """Test retrieval of exactly 10 export jobs."""
-        self.factory.makeCVE("2025-1979")
-        self.factory.makeDistribution(name="soss", owner=self.requester)
-
+        self.factory.makeCVE(self.cve_key)
         jobs = []
 
         for _ in range(10):
@@ -1873,8 +1914,7 @@ class TestVulnerabilityGetJobs(TestCaseWithFactory):
 
     def test_getExports_more_than_10(self):
         """Test that only the last 10 export jobs are returned."""
-        self.factory.makeCVE("2025-1979")
-        self.factory.makeDistribution(name="soss", owner=self.requester)
+        self.factory.makeCVE(self.cve_key)
 
         jobs = []
 
@@ -1913,8 +1953,13 @@ class TestVulnerabilityGetJobs(TestCaseWithFactory):
 
     def test_getExports_unauthenticated(self):
         """Test exports fail when requester lacks handler rights."""
-        self.factory.makeCVE("2025-1979")
-        self.factory.makeDistribution(name="soss")
+        self.factory.makeCVE(self.cve_key)
+
+        # Remove permissions
+        if self.name == "uct":
+            self.distribution.security_admin = self.factory.makePerson()
+        else:
+            self.distribution.owner = self.factory.makePerson()
 
         job = self._create_export_job()
 
diff --git a/lib/lp/bugs/model/vulnerability.py b/lib/lp/bugs/model/vulnerability.py
index ffc3402..81807c3 100644
--- a/lib/lp/bugs/model/vulnerability.py
+++ b/lib/lp/bugs/model/vulnerability.py
@@ -576,14 +576,21 @@ class VulnerabilitySet:
     ):
         from lp.bugs.model.importvulnerabilityjob import ImportVulnerabilityJob
         from lp.bugs.model.vulnerabilityjob import VulnerabilityJob
-        from lp.bugs.scripts.soss.sossimport import SOSSImporter
 
         distribution = getUtility(IDistributionSet).getByName(
             HANDLER_DISTRIBUTION_MAP.get(handler)
         )
 
         if handler == VulnerabilityHandlerEnum.SOSS:
+            from lp.bugs.scripts.soss.sossimport import SOSSImporter
+
             importer = SOSSImporter(distribution)
+
+        elif handler == VulnerabilityHandlerEnum.UCT:
+            from lp.bugs.scripts.uct.uctimport import UCTImporter
+
+            importer = UCTImporter(distribution)
+
         else:
             raise NotFoundError(f"{handler} not found")
 
@@ -616,6 +623,11 @@ class VulnerabilitySet:
 
             exporter = SOSSExporter()
 
+        elif handler == VulnerabilityHandlerEnum.UCT:
+            from lp.bugs.scripts.uct.uctexport import UCTExporter
+
+            exporter = UCTExporter()
+
         else:
             raise NotFoundError(f"{handler} not found")
 
diff --git a/lib/lp/bugs/scripts/uct/tests/sampledata/CVE-2025-0001 b/lib/lp/bugs/scripts/uct/tests/sampledata/CVE-2025-0001
new file mode 100644
index 0000000..a65c6f4
--- /dev/null
+++ b/lib/lp/bugs/scripts/uct/tests/sampledata/CVE-2025-0001
@@ -0,0 +1,21 @@
+Candidate: CVE-2025-0001
+PublicDate: 2025-01-01 06:00:00 UTC
+References:
+ https://www.cve.org/CVERecord?id=CVE-2025-0001
+Description:
+ Test description for this test CVE
+Ubuntu-Description:
+Notes:
+Bugs:
+Priority: high
+ This has a high priority because it is a vulnerability that allows a remote
+ attacker to execute code in a machine, and it looks to be easily exploitable
+ given that it involves regular functionalities provided by the application.
+Discovered-by:
+Assigned-to:
+CVSS:
+ nvd: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H [9.8 CRITICAL]
+
+Patches_gbrowse:
+upstream_gbrowse: released (2.56+dfsg-1)
+xenial_gbrowse: ignored (end of standard support)
diff --git a/lib/lp/testing/factory.py b/lib/lp/testing/factory.py
index 8b22e09..95ae12e 100644
--- a/lib/lp/testing/factory.py
+++ b/lib/lp/testing/factory.py
@@ -5958,7 +5958,7 @@ class LaunchpadObjectFactory(ObjectFactory):
 
     def makeCVE(
         self,
-        sequence,
+        sequence=None,
         description=None,
         cvestate=CveStatus.CANDIDATE,
         date_made_public=None,
@@ -5966,6 +5966,9 @@ class LaunchpadObjectFactory(ObjectFactory):
         cvss=None,
     ):
         """Create a new CVE record."""
+        if sequence is None:
+            sequence = "2000-%04i" % self.getUniqueInteger()
+        
         if description is None:
             description = self.getUniqueUnicode()