← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] ~andrey-fedoseev/launchpad:more-vulnerability-dates into launchpad:master

 

Andrey Fedoseev has proposed merging ~andrey-fedoseev/launchpad:more-vulnerability-dates into launchpad:master with ~andrey-fedoseev/launchpad:uct-upstream as a prerequisite.

Commit message:
Add `Vulnerability.date_notice_issued` and `date_coordinated_release` fields

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~andrey-fedoseev/launchpad/+git/launchpad/+merge/428892

Update the UCT import/export scripts accordingly

Note that this MP includes changes which are also present in other merge proposals:

https://code.launchpad.net/~andrey-fedoseev/launchpad/+git/launchpad/+merge/428152
https://code.launchpad.net/~andrey-fedoseev/launchpad/+git/launchpad/+merge/428533
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of ~andrey-fedoseev/launchpad:more-vulnerability-dates into launchpad:master.
diff --git a/lib/lp/bugs/interfaces/vulnerability.py b/lib/lp/bugs/interfaces/vulnerability.py
index 9d8292a..474309f 100644
--- a/lib/lp/bugs/interfaces/vulnerability.py
+++ b/lib/lp/bugs/interfaces/vulnerability.py
@@ -282,6 +282,27 @@ class IVulnerabilityEditableAttributes(Interface):
         as_of="devel",
     )
 
+    date_notice_issued = exported(
+        Datetime(
+            title=_(
+                "Date when a security notice was issued for this "
+                "vulnerability."
+            ),
+            required=False,
+            readonly=False,
+        ),
+        as_of="devel",
+    )
+
+    date_coordinated_release = exported(
+        Datetime(
+            title=_("Coordinated Release Date."),
+            required=False,
+            readonly=False,
+        ),
+        as_of="devel",
+    )
+
 
 class IVulnerabilityEdit(Interface):
     """`IVulnerability` attributes that require launchpad.Edit."""
@@ -315,6 +336,8 @@ class IVulnerabilitySet(Interface):
         mitigation=None,
         importance_explanation=None,
         date_made_public=None,
+        date_notice_issued=None,
+        date_coordinated_release=None,
     ):
         """Return a new vulnerability.
 
@@ -330,6 +353,9 @@ class IVulnerabilitySet(Interface):
         :param importance_explanation: Used to explain why our importance
          differs from somebody else's CVSS score.
         :param date_made_public: The date this vulnerability was made public.
+        :param date_coordinated_release: Date when a security notice was issued
+         for this vulnerability.
+        :param date_notice_issued: Coordinated Release Date.
         """
 
     def findByIds(vulnerability_ids, visible_by_user=None):
diff --git a/lib/lp/bugs/model/vulnerability.py b/lib/lp/bugs/model/vulnerability.py
index d8d00a7..075fba8 100644
--- a/lib/lp/bugs/model/vulnerability.py
+++ b/lib/lp/bugs/model/vulnerability.py
@@ -98,6 +98,12 @@ class Vulnerability(StormBase, BugLinkTargetMixin, InformationTypeMixin):
     date_made_public = DateTime(
         name="date_made_public", tzinfo=pytz.UTC, allow_none=True
     )
+    date_notice_issued = DateTime(
+        name="date_notice_issued", tzinfo=pytz.UTC, allow_none=True
+    )
+    date_coordinated_release = DateTime(
+        name="date_coordinated_release", tzinfo=pytz.UTC, allow_none=True
+    )
 
     creator_id = Int(name="creator", allow_none=False)
     creator = Reference(creator_id, "Person.id")
@@ -115,6 +121,8 @@ class Vulnerability(StormBase, BugLinkTargetMixin, InformationTypeMixin):
         mitigation=None,
         importance_explanation=None,
         date_made_public=None,
+        date_notice_issued=None,
+        date_coordinated_release=None,
     ):
         super().__init__()
         self.distribution = distribution
@@ -132,6 +140,8 @@ class Vulnerability(StormBase, BugLinkTargetMixin, InformationTypeMixin):
         self.mitigation = mitigation
         self.importance_explanation = importance_explanation
         self.date_made_public = date_made_public
+        self.date_notice_issued = date_notice_issued
+        self.date_coordinated_release = date_coordinated_release
         self.date_created = UTC_NOW
 
     @property
@@ -323,6 +333,8 @@ class VulnerabilitySet:
         mitigation=None,
         importance_explanation=None,
         date_made_public=None,
+        date_notice_issued=None,
+        date_coordinated_release=None,
     ):
         """See `IVulnerabilitySet`."""
         store = IStore(Vulnerability)
@@ -338,6 +350,8 @@ class VulnerabilitySet:
             information_type=information_type,
             importance_explanation=importance_explanation,
             date_made_public=date_made_public,
+            date_notice_issued=date_notice_issued,
+            date_coordinated_release=date_coordinated_release,
         )
         store.add(vulnerability)
         vulnerability._reconcileAccess()
diff --git a/lib/lp/bugs/scripts/tests/test_uct.py b/lib/lp/bugs/scripts/tests/test_uct.py
index 00f995d..b0348f5 100644
--- a/lib/lp/bugs/scripts/tests/test_uct.py
+++ b/lib/lp/bugs/scripts/tests/test_uct.py
@@ -162,7 +162,7 @@ class TestUCTRecord(TestCase):
         self.assertEqual(load_from.read_text(), saved_to_path.read_text())
 
 
-class TextCVE(TestCaseWithFactory):
+class TestCVE(TestCaseWithFactory):
 
     layer = ZopelessDatabaseLayer
     maxDiff = None
@@ -296,14 +296,14 @@ class TextCVE(TestCaseWithFactory):
 
         self.cve = CVE(
             sequence="CVE-2022-23222",
-            crd=datetime.datetime(
-                2020, 1, 14, 8, 15, tzinfo=datetime.timezone.utc
+            date_made_public=datetime.datetime(
+                2022, 1, 14, 8, 15, tzinfo=datetime.timezone.utc
             ),
-            public_date_at_USN=datetime.datetime(
+            date_notice_issued=datetime.datetime(
                 2021, 1, 14, 8, 15, tzinfo=datetime.timezone.utc
             ),
-            public_date=datetime.datetime(
-                2022, 1, 14, 8, 15, tzinfo=datetime.timezone.utc
+            date_coordinated_release=datetime.datetime(
+                2020, 1, 14, 8, 15, tzinfo=datetime.timezone.utc
             ),
             distro_packages=[
                 CVE.DistroPackage(
@@ -485,9 +485,9 @@ class TestUCTImporterExporter(TestCaseWithFactory):
         )
         self.cve = CVE(
             sequence="CVE-2022-23222",
-            crd=None,
-            public_date=self.now,
-            public_date_at_USN=None,
+            date_made_public=self.now,
+            date_notice_issued=self.now,
+            date_coordinated_release=self.now,
             distro_packages=[
                 CVE.DistroPackage(
                     package=self.ubuntu_package,
@@ -683,6 +683,13 @@ class TestUCTImporterExporter(TestCaseWithFactory):
             self.assertEqual(
                 cve.date_made_public, vulnerability.date_made_public
             )
+            self.assertEqual(
+                cve.date_notice_issued, vulnerability.date_notice_issued
+            )
+            self.assertEqual(
+                cve.date_coordinated_release,
+                vulnerability.date_coordinated_release,
+            )
             self.assertEqual([bug], vulnerability.bugs)
 
     def checkLaunchpadCve(self, lp_cve: CveModel, cve: CVE):
@@ -693,10 +700,12 @@ class TestUCTImporterExporter(TestCaseWithFactory):
 
     def checkCVE(self, expected: CVE, actual: CVE):
         self.assertEqual(expected.sequence, actual.sequence)
-        self.assertEqual(expected.crd, actual.crd)
-        self.assertEqual(expected.public_date, actual.public_date)
+        self.assertEqual(expected.date_made_public, actual.date_made_public)
+        self.assertEqual(
+            expected.date_notice_issued, actual.date_notice_issued
+        )
         self.assertEqual(
-            expected.public_date_at_USN, actual.public_date_at_USN
+            expected.date_coordinated_release, actual.date_coordinated_release
         )
         self.assertListEqual(expected.distro_packages, actual.distro_packages)
         self.assertListEqual(expected.series_packages, actual.series_packages)
@@ -970,14 +979,27 @@ class TestUCTImporterExporter(TestCaseWithFactory):
         importer.import_cve(self.cve)
         self.assertIsNone(importer._find_existing_bug(self.cve, self.lp_cve))
 
-    def test_naive_date_made_public(self):
+    def test_naive_dates(self):
         cve = self.cve
-        cve.public_date = cve.public_date.replace(tzinfo=None)
-        bug = self.importer.create_bug(cve, self.lp_cve)
-        self.assertEqual(
-            UTC,
-            bug.vulnerabilities[0].date_made_public.tzinfo,
+        cve.date_made_public = cve.date_made_public.replace(tzinfo=None)
+        cve.date_notice_issued = cve.date_notice_issued.replace(tzinfo=None)
+        cve.date_coordinated_release = cve.date_coordinated_release.replace(
+            tzinfo=None
         )
+        bug = self.importer.create_bug(cve, self.lp_cve)
+        for date in (
+            bug.vulnerabilities[0].date_made_public,
+            bug.vulnerabilities[0].date_notice_issued,
+            bug.vulnerabilities[0].date_coordinated_release,
+        ):
+            self.assertEqual(UTC, date.tzinfo)
+        self.importer.update_bug(bug, cve, self.lp_cve)
+        for date in (
+            bug.vulnerabilities[0].date_made_public,
+            bug.vulnerabilities[0].date_notice_issued,
+            bug.vulnerabilities[0].date_coordinated_release,
+        ):
+            self.assertEqual(UTC, date.tzinfo)
 
     def test_make_cve_from_bug(self):
         self.importer.import_cve(self.cve)
diff --git a/lib/lp/bugs/scripts/uct/models.py b/lib/lp/bugs/scripts/uct/models.py
index 440f79d..4234fdd 100644
--- a/lib/lp/bugs/scripts/uct/models.py
+++ b/lib/lp/bugs/scripts/uct/models.py
@@ -483,9 +483,9 @@ class CVE:
     def __init__(
         self,
         sequence: str,
-        crd: Optional[datetime],
-        public_date: Optional[datetime],
-        public_date_at_USN: Optional[datetime],
+        date_made_public: Optional[datetime],
+        date_notice_issued: Optional[datetime],
+        date_coordinated_release: Optional[datetime],
         distro_packages: List[DistroPackage],
         series_packages: List[SeriesPackage],
         upstream_packages: List[UpstreamPackage],
@@ -502,9 +502,9 @@ class CVE:
         cvss: List[CVSS],
     ):
         self.sequence = sequence
-        self.crd = crd
-        self.public_date = public_date
-        self.public_date_at_USN = public_date_at_USN
+        self.date_made_public = date_made_public
+        self.date_notice_issued = date_notice_issued
+        self.date_coordinated_release = date_coordinated_release
         self.distro_packages = distro_packages
         self.series_packages = series_packages
         self.upstream_packages = upstream_packages
@@ -614,9 +614,9 @@ class CVE:
 
         return cls(
             sequence=uct_record.candidate,
-            crd=uct_record.crd,
-            public_date=uct_record.public_date,
-            public_date_at_USN=uct_record.public_date_at_USN,
+            date_made_public=uct_record.public_date,
+            date_notice_issued=uct_record.public_date_at_USN,
+            date_coordinated_release=uct_record.crd,
             distro_packages=distro_packages,
             series_packages=series_packages,
             upstream_packages=upstream_packages,
@@ -728,9 +728,9 @@ class CVE:
             bugs=self.bug_urls,
             cvss=self.cvss,
             candidate=self.sequence,
-            crd=self.crd,
-            public_date=self.public_date,
-            public_date_at_USN=self.public_date_at_USN,
+            crd=self.date_coordinated_release,
+            public_date=self.date_made_public,
+            public_date_at_USN=self.date_notice_issued,
             description=self.description,
             discovered_by=self.discovered_by,
             mitigation=self.mitigation,
@@ -741,10 +741,6 @@ class CVE:
             packages=list(packages_by_name.values()),
         )
 
-    @property
-    def date_made_public(self):
-        return self.crd or self.public_date_at_USN or self.public_date
-
     @cachedproperty
     def affected_distributions(self) -> Set[Distribution]:
         return {p.package.distribution for p in self.distro_packages}
diff --git a/lib/lp/bugs/scripts/uct/uctexport.py b/lib/lp/bugs/scripts/uct/uctexport.py
index 45366a9..b4c3b87 100644
--- a/lib/lp/bugs/scripts/uct/uctexport.py
+++ b/lib/lp/bugs/scripts/uct/uctexport.py
@@ -165,9 +165,9 @@ class UCTExporter:
 
         return CVE(
             sequence="CVE-{}".format(lp_cve.sequence),
-            crd=None,  # TODO: fix this
-            public_date=vulnerability.date_made_public,
-            public_date_at_USN=None,  # TODO: fix this
+            date_made_public=vulnerability.date_made_public,
+            date_notice_issued=vulnerability.date_notice_issued,
+            date_coordinated_release=vulnerability.date_coordinated_release,
             distro_packages=distro_packages,
             series_packages=series_packages,
             upstream_packages=upstream_packages,
diff --git a/lib/lp/bugs/scripts/uct/uctimport.py b/lib/lp/bugs/scripts/uct/uctimport.py
index 13639b5..3af3ad7 100644
--- a/lib/lp/bugs/scripts/uct/uctimport.py
+++ b/lib/lp/bugs/scripts/uct/uctimport.py
@@ -297,21 +297,15 @@ class UCTImporter:
         :param distribution: a `Distribution` affected by the vulnerability
         :return: a Vulnerability
         """
-        date_made_public = cve.date_made_public
-        if date_made_public.tzinfo is None:
-            date_made_public = date_made_public.replace(tzinfo=timezone.utc)
         vulnerability = getUtility(IVulnerabilitySet).new(
             distribution=distribution,
-            creator=bug.owner,
-            cve=lp_cve,
             status=cve.status,
-            description=cve.ubuntu_description,
-            notes=cve.notes,
-            mitigation=cve.mitigation,
             importance=cve.importance,
+            creator=bug.owner,
             information_type=InformationType.PUBLICSECURITY,
-            date_made_public=date_made_public,
+            cve=lp_cve,
         )  # type: Vulnerability
+        self._update_vulnerability(vulnerability, cve)
 
         vulnerability.linkBug(bug, bug.owner)
 
@@ -334,12 +328,28 @@ class UCTImporter:
         :param vulnerability: `Vulnerability` model to be updated
         :param cve: `CVE` with information from UCT
         """
+        date_made_public = cve.date_made_public
+        if date_made_public.tzinfo is None:
+            date_made_public = date_made_public.replace(tzinfo=timezone.utc)
+        date_notice_issued = cve.date_notice_issued
+        if date_notice_issued.tzinfo is None:
+            date_notice_issued = date_notice_issued.replace(
+                tzinfo=timezone.utc
+            )
+        date_coordinated_release = cve.date_coordinated_release
+        if date_coordinated_release.tzinfo is None:
+            date_coordinated_release = date_coordinated_release.replace(
+                tzinfo=timezone.utc
+            )
+
         vulnerability.status = cve.status
         vulnerability.description = cve.ubuntu_description
         vulnerability.notes = cve.notes
         vulnerability.mitigation = cve.mitigation
         vulnerability.importance = cve.importance
-        vulnerability.date_made_public = cve.date_made_public
+        vulnerability.date_made_public = date_made_public
+        vulnerability.date_notice_issued = date_notice_issued
+        vulnerability.date_coordinated_release = date_coordinated_release
 
     def _assign_bug_tasks(
         self, bug: BugModel, assignee: Optional[Person]