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

Proposed by Andrey Fedoseev
Status: Merged
Approved by: Andrey Fedoseev
Approved revision: 4c357c5f6df0e6177d38c3b195a61514aba720bf
Merge reported by: Otto Co-Pilot
Merged at revision: not available
Proposed branch: ~andrey-fedoseev/launchpad:more-vulnerability-dates-new
Merge into: launchpad:master
Diff against target: 396 lines (+121/-67)
7 files modified
lib/contrib/cve_lib.py (+0/-17)
lib/lp/bugs/interfaces/vulnerability.py (+26/-0)
lib/lp/bugs/model/vulnerability.py (+14/-0)
lib/lp/bugs/scripts/tests/test_uct.py (+46/-21)
lib/lp/bugs/scripts/uct/models.py (+12/-16)
lib/lp/bugs/scripts/uct/uctexport.py (+3/-3)
lib/lp/bugs/scripts/uct/uctimport.py (+20/-10)
Reviewer Review Type Date Requested Status
Colin Watson (community) Approve
Review via email: mp+429006@code.launchpad.net

Commit message

Add `Vulnerability.date_notice_issued` and `date_coordinated_release` fields

Description of the change

Update the UCT import/export scripts accordingly

To post a comment you must log in.
Revision history for this message
Colin Watson (cjwatson) :
review: Approve
Revision history for this message
Colin Watson (cjwatson) :
Revision history for this message
Andrey Fedoseev (andrey-fedoseev) :
Revision history for this message
Colin Watson (cjwatson) :
Revision history for this message
Andrey Fedoseev (andrey-fedoseev) wrote :

Colin,

Using different date values in tests helped to find an issue with `cve_lib`: it used to override PublicDate with PublicDateAtUSN or CRD when reading data from files.

I disabled this behaviour in our copy of `cve_lib` to preserve all original date values during import.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/lib/contrib/cve_lib.py b/lib/contrib/cve_lib.py
2index da211fd..c7e6210 100644
3--- a/lib/contrib/cve_lib.py
4+++ b/lib/contrib/cve_lib.py
5@@ -1472,23 +1472,6 @@ def load_cve(cve, strict=False, srcmap=None):
6 if "Priority" not in data:
7 data.setdefault("Priority", "untriaged")
8 srcmap.setdefault("Priority", (cve, 1))
9- # Perform override fields
10- if "PublicDateAtUSN" in data:
11- data["PublicDate"] = data["PublicDateAtUSN"]
12- srcmap["PublicDate"] = srcmap["PublicDateAtUSN"]
13- if (
14- "CRD" in data
15- and data["CRD"].strip() != ""
16- and data["PublicDate"] != data["CRD"]
17- ):
18- if cve.startswith("embargoed"):
19- print(
20- "%s: %d: adjusting PublicDate to use CRD: %s"
21- % (cve, linenum, data["CRD"]),
22- file=sys.stderr,
23- )
24- data["PublicDate"] = data["CRD"]
25- srcmap["PublicDate"] = srcmap["CRD"]
26
27 # entries need an upstream entry if any entries are from the internal
28 # list of subprojects
29diff --git a/lib/lp/bugs/interfaces/vulnerability.py b/lib/lp/bugs/interfaces/vulnerability.py
30index 9d8292a..474309f 100644
31--- a/lib/lp/bugs/interfaces/vulnerability.py
32+++ b/lib/lp/bugs/interfaces/vulnerability.py
33@@ -282,6 +282,27 @@ class IVulnerabilityEditableAttributes(Interface):
34 as_of="devel",
35 )
36
37+ date_notice_issued = exported(
38+ Datetime(
39+ title=_(
40+ "Date when a security notice was issued for this "
41+ "vulnerability."
42+ ),
43+ required=False,
44+ readonly=False,
45+ ),
46+ as_of="devel",
47+ )
48+
49+ date_coordinated_release = exported(
50+ Datetime(
51+ title=_("Coordinated Release Date."),
52+ required=False,
53+ readonly=False,
54+ ),
55+ as_of="devel",
56+ )
57+
58
59 class IVulnerabilityEdit(Interface):
60 """`IVulnerability` attributes that require launchpad.Edit."""
61@@ -315,6 +336,8 @@ class IVulnerabilitySet(Interface):
62 mitigation=None,
63 importance_explanation=None,
64 date_made_public=None,
65+ date_notice_issued=None,
66+ date_coordinated_release=None,
67 ):
68 """Return a new vulnerability.
69
70@@ -330,6 +353,9 @@ class IVulnerabilitySet(Interface):
71 :param importance_explanation: Used to explain why our importance
72 differs from somebody else's CVSS score.
73 :param date_made_public: The date this vulnerability was made public.
74+ :param date_coordinated_release: Date when a security notice was issued
75+ for this vulnerability.
76+ :param date_notice_issued: Coordinated Release Date.
77 """
78
79 def findByIds(vulnerability_ids, visible_by_user=None):
80diff --git a/lib/lp/bugs/model/vulnerability.py b/lib/lp/bugs/model/vulnerability.py
81index d8d00a7..075fba8 100644
82--- a/lib/lp/bugs/model/vulnerability.py
83+++ b/lib/lp/bugs/model/vulnerability.py
84@@ -98,6 +98,12 @@ class Vulnerability(StormBase, BugLinkTargetMixin, InformationTypeMixin):
85 date_made_public = DateTime(
86 name="date_made_public", tzinfo=pytz.UTC, allow_none=True
87 )
88+ date_notice_issued = DateTime(
89+ name="date_notice_issued", tzinfo=pytz.UTC, allow_none=True
90+ )
91+ date_coordinated_release = DateTime(
92+ name="date_coordinated_release", tzinfo=pytz.UTC, allow_none=True
93+ )
94
95 creator_id = Int(name="creator", allow_none=False)
96 creator = Reference(creator_id, "Person.id")
97@@ -115,6 +121,8 @@ class Vulnerability(StormBase, BugLinkTargetMixin, InformationTypeMixin):
98 mitigation=None,
99 importance_explanation=None,
100 date_made_public=None,
101+ date_notice_issued=None,
102+ date_coordinated_release=None,
103 ):
104 super().__init__()
105 self.distribution = distribution
106@@ -132,6 +140,8 @@ class Vulnerability(StormBase, BugLinkTargetMixin, InformationTypeMixin):
107 self.mitigation = mitigation
108 self.importance_explanation = importance_explanation
109 self.date_made_public = date_made_public
110+ self.date_notice_issued = date_notice_issued
111+ self.date_coordinated_release = date_coordinated_release
112 self.date_created = UTC_NOW
113
114 @property
115@@ -323,6 +333,8 @@ class VulnerabilitySet:
116 mitigation=None,
117 importance_explanation=None,
118 date_made_public=None,
119+ date_notice_issued=None,
120+ date_coordinated_release=None,
121 ):
122 """See `IVulnerabilitySet`."""
123 store = IStore(Vulnerability)
124@@ -338,6 +350,8 @@ class VulnerabilitySet:
125 information_type=information_type,
126 importance_explanation=importance_explanation,
127 date_made_public=date_made_public,
128+ date_notice_issued=date_notice_issued,
129+ date_coordinated_release=date_coordinated_release,
130 )
131 store.add(vulnerability)
132 vulnerability._reconcileAccess()
133diff --git a/lib/lp/bugs/scripts/tests/test_uct.py b/lib/lp/bugs/scripts/tests/test_uct.py
134index 00f995d..71b1abb 100644
135--- a/lib/lp/bugs/scripts/tests/test_uct.py
136+++ b/lib/lp/bugs/scripts/tests/test_uct.py
137@@ -162,7 +162,7 @@ class TestUCTRecord(TestCase):
138 self.assertEqual(load_from.read_text(), saved_to_path.read_text())
139
140
141-class TextCVE(TestCaseWithFactory):
142+class TestCVE(TestCaseWithFactory):
143
144 layer = ZopelessDatabaseLayer
145 maxDiff = None
146@@ -296,14 +296,14 @@ class TextCVE(TestCaseWithFactory):
147
148 self.cve = CVE(
149 sequence="CVE-2022-23222",
150- crd=datetime.datetime(
151- 2020, 1, 14, 8, 15, tzinfo=datetime.timezone.utc
152+ date_made_public=datetime.datetime(
153+ 2022, 1, 14, 8, 15, tzinfo=datetime.timezone.utc
154 ),
155- public_date_at_USN=datetime.datetime(
156+ date_notice_issued=datetime.datetime(
157 2021, 1, 14, 8, 15, tzinfo=datetime.timezone.utc
158 ),
159- public_date=datetime.datetime(
160- 2022, 1, 14, 8, 15, tzinfo=datetime.timezone.utc
161+ date_coordinated_release=datetime.datetime(
162+ 2020, 1, 14, 8, 15, tzinfo=datetime.timezone.utc
163 ),
164 distro_packages=[
165 CVE.DistroPackage(
166@@ -480,14 +480,17 @@ class TestUCTImporterExporter(TestCaseWithFactory):
167 )
168
169 self.lp_cve = self.factory.makeCVE("2022-23222")
170- self.now = datetime.datetime.now(datetime.timezone.utc).replace(
171- microsecond=0
172- )
173 self.cve = CVE(
174 sequence="CVE-2022-23222",
175- crd=None,
176- public_date=self.now,
177- public_date_at_USN=None,
178+ date_made_public=datetime.datetime(
179+ 2022, 1, 14, 8, 15, tzinfo=datetime.timezone.utc
180+ ),
181+ date_notice_issued=datetime.datetime(
182+ 2021, 1, 14, 8, 15, tzinfo=datetime.timezone.utc
183+ ),
184+ date_coordinated_release=datetime.datetime(
185+ 2020, 1, 14, 8, 15, tzinfo=datetime.timezone.utc
186+ ),
187 distro_packages=[
188 CVE.DistroPackage(
189 package=self.ubuntu_package,
190@@ -683,6 +686,13 @@ class TestUCTImporterExporter(TestCaseWithFactory):
191 self.assertEqual(
192 cve.date_made_public, vulnerability.date_made_public
193 )
194+ self.assertEqual(
195+ cve.date_notice_issued, vulnerability.date_notice_issued
196+ )
197+ self.assertEqual(
198+ cve.date_coordinated_release,
199+ vulnerability.date_coordinated_release,
200+ )
201 self.assertEqual([bug], vulnerability.bugs)
202
203 def checkLaunchpadCve(self, lp_cve: CveModel, cve: CVE):
204@@ -693,10 +703,12 @@ class TestUCTImporterExporter(TestCaseWithFactory):
205
206 def checkCVE(self, expected: CVE, actual: CVE):
207 self.assertEqual(expected.sequence, actual.sequence)
208- self.assertEqual(expected.crd, actual.crd)
209- self.assertEqual(expected.public_date, actual.public_date)
210+ self.assertEqual(expected.date_made_public, actual.date_made_public)
211+ self.assertEqual(
212+ expected.date_notice_issued, actual.date_notice_issued
213+ )
214 self.assertEqual(
215- expected.public_date_at_USN, actual.public_date_at_USN
216+ expected.date_coordinated_release, actual.date_coordinated_release
217 )
218 self.assertListEqual(expected.distro_packages, actual.distro_packages)
219 self.assertListEqual(expected.series_packages, actual.series_packages)
220@@ -970,14 +982,27 @@ class TestUCTImporterExporter(TestCaseWithFactory):
221 importer.import_cve(self.cve)
222 self.assertIsNone(importer._find_existing_bug(self.cve, self.lp_cve))
223
224- def test_naive_date_made_public(self):
225+ def test_naive_dates(self):
226 cve = self.cve
227- cve.public_date = cve.public_date.replace(tzinfo=None)
228- bug = self.importer.create_bug(cve, self.lp_cve)
229- self.assertEqual(
230- UTC,
231- bug.vulnerabilities[0].date_made_public.tzinfo,
232+ cve.date_made_public = cve.date_made_public.replace(tzinfo=None)
233+ cve.date_notice_issued = cve.date_notice_issued.replace(tzinfo=None)
234+ cve.date_coordinated_release = cve.date_coordinated_release.replace(
235+ tzinfo=None
236 )
237+ bug = self.importer.create_bug(cve, self.lp_cve)
238+ for date in (
239+ bug.vulnerabilities[0].date_made_public,
240+ bug.vulnerabilities[0].date_notice_issued,
241+ bug.vulnerabilities[0].date_coordinated_release,
242+ ):
243+ self.assertEqual(UTC, date.tzinfo)
244+ self.importer.update_bug(bug, cve, self.lp_cve)
245+ for date in (
246+ bug.vulnerabilities[0].date_made_public,
247+ bug.vulnerabilities[0].date_notice_issued,
248+ bug.vulnerabilities[0].date_coordinated_release,
249+ ):
250+ self.assertEqual(UTC, date.tzinfo)
251
252 def test_make_cve_from_bug(self):
253 self.importer.import_cve(self.cve)
254diff --git a/lib/lp/bugs/scripts/uct/models.py b/lib/lp/bugs/scripts/uct/models.py
255index 440f79d..4234fdd 100644
256--- a/lib/lp/bugs/scripts/uct/models.py
257+++ b/lib/lp/bugs/scripts/uct/models.py
258@@ -483,9 +483,9 @@ class CVE:
259 def __init__(
260 self,
261 sequence: str,
262- crd: Optional[datetime],
263- public_date: Optional[datetime],
264- public_date_at_USN: Optional[datetime],
265+ date_made_public: Optional[datetime],
266+ date_notice_issued: Optional[datetime],
267+ date_coordinated_release: Optional[datetime],
268 distro_packages: List[DistroPackage],
269 series_packages: List[SeriesPackage],
270 upstream_packages: List[UpstreamPackage],
271@@ -502,9 +502,9 @@ class CVE:
272 cvss: List[CVSS],
273 ):
274 self.sequence = sequence
275- self.crd = crd
276- self.public_date = public_date
277- self.public_date_at_USN = public_date_at_USN
278+ self.date_made_public = date_made_public
279+ self.date_notice_issued = date_notice_issued
280+ self.date_coordinated_release = date_coordinated_release
281 self.distro_packages = distro_packages
282 self.series_packages = series_packages
283 self.upstream_packages = upstream_packages
284@@ -614,9 +614,9 @@ class CVE:
285
286 return cls(
287 sequence=uct_record.candidate,
288- crd=uct_record.crd,
289- public_date=uct_record.public_date,
290- public_date_at_USN=uct_record.public_date_at_USN,
291+ date_made_public=uct_record.public_date,
292+ date_notice_issued=uct_record.public_date_at_USN,
293+ date_coordinated_release=uct_record.crd,
294 distro_packages=distro_packages,
295 series_packages=series_packages,
296 upstream_packages=upstream_packages,
297@@ -728,9 +728,9 @@ class CVE:
298 bugs=self.bug_urls,
299 cvss=self.cvss,
300 candidate=self.sequence,
301- crd=self.crd,
302- public_date=self.public_date,
303- public_date_at_USN=self.public_date_at_USN,
304+ crd=self.date_coordinated_release,
305+ public_date=self.date_made_public,
306+ public_date_at_USN=self.date_notice_issued,
307 description=self.description,
308 discovered_by=self.discovered_by,
309 mitigation=self.mitigation,
310@@ -741,10 +741,6 @@ class CVE:
311 packages=list(packages_by_name.values()),
312 )
313
314- @property
315- def date_made_public(self):
316- return self.crd or self.public_date_at_USN or self.public_date
317-
318 @cachedproperty
319 def affected_distributions(self) -> Set[Distribution]:
320 return {p.package.distribution for p in self.distro_packages}
321diff --git a/lib/lp/bugs/scripts/uct/uctexport.py b/lib/lp/bugs/scripts/uct/uctexport.py
322index 65e274c..f14d994 100644
323--- a/lib/lp/bugs/scripts/uct/uctexport.py
324+++ b/lib/lp/bugs/scripts/uct/uctexport.py
325@@ -174,9 +174,9 @@ class UCTExporter:
326
327 return CVE(
328 sequence="CVE-{}".format(lp_cve.sequence),
329- crd=None, # TODO: fix this
330- public_date=vulnerability.date_made_public,
331- public_date_at_USN=None, # TODO: fix this
332+ date_made_public=vulnerability.date_made_public,
333+ date_notice_issued=vulnerability.date_notice_issued,
334+ date_coordinated_release=vulnerability.date_coordinated_release,
335 distro_packages=distro_packages,
336 series_packages=series_packages,
337 upstream_packages=upstream_packages,
338diff --git a/lib/lp/bugs/scripts/uct/uctimport.py b/lib/lp/bugs/scripts/uct/uctimport.py
339index 5f94703..87d6b0d 100644
340--- a/lib/lp/bugs/scripts/uct/uctimport.py
341+++ b/lib/lp/bugs/scripts/uct/uctimport.py
342@@ -296,21 +296,15 @@ class UCTImporter:
343 :param distribution: a `Distribution` affected by the vulnerability
344 :return: a Vulnerability
345 """
346- date_made_public = cve.date_made_public
347- if date_made_public.tzinfo is None:
348- date_made_public = date_made_public.replace(tzinfo=timezone.utc)
349 vulnerability = getUtility(IVulnerabilitySet).new(
350 distribution=distribution,
351- creator=bug.owner,
352- cve=lp_cve,
353 status=cve.status,
354- description=cve.ubuntu_description,
355- notes=cve.notes,
356- mitigation=cve.mitigation,
357 importance=cve.importance,
358+ creator=bug.owner,
359 information_type=InformationType.PUBLICSECURITY,
360- date_made_public=date_made_public,
361+ cve=lp_cve,
362 ) # type: Vulnerability
363+ self._update_vulnerability(vulnerability, cve)
364
365 vulnerability.linkBug(bug, bug.owner)
366
367@@ -333,12 +327,28 @@ class UCTImporter:
368 :param vulnerability: `Vulnerability` model to be updated
369 :param cve: `CVE` with information from UCT
370 """
371+ date_made_public = cve.date_made_public
372+ if date_made_public.tzinfo is None:
373+ date_made_public = date_made_public.replace(tzinfo=timezone.utc)
374+ date_notice_issued = cve.date_notice_issued
375+ if date_notice_issued.tzinfo is None:
376+ date_notice_issued = date_notice_issued.replace(
377+ tzinfo=timezone.utc
378+ )
379+ date_coordinated_release = cve.date_coordinated_release
380+ if date_coordinated_release.tzinfo is None:
381+ date_coordinated_release = date_coordinated_release.replace(
382+ tzinfo=timezone.utc
383+ )
384+
385 vulnerability.status = cve.status
386 vulnerability.description = cve.ubuntu_description
387 vulnerability.notes = cve.notes
388 vulnerability.mitigation = cve.mitigation
389 vulnerability.importance = cve.importance
390- vulnerability.date_made_public = cve.date_made_public
391+ vulnerability.date_made_public = date_made_public
392+ vulnerability.date_notice_issued = date_notice_issued
393+ vulnerability.date_coordinated_release = date_coordinated_release
394
395 def _assign_bug_tasks(
396 self, bug: BugModel, assignee: Optional[Person]

Subscribers

People subscribed via source and target branches

to status/vote changes: