Merge ~litios/ubuntu-cve-tracker:oval/usns-only-fixed-cves into ubuntu-cve-tracker:master

Proposed by David Fernandez Gonzalez
Status: Merged
Merge reported by: David Fernandez Gonzalez
Merged at revision: 32dcaba0c7ada41f0e265d0f037c864bf83aab0e
Proposed branch: ~litios/ubuntu-cve-tracker:oval/usns-only-fixed-cves
Merge into: ubuntu-cve-tracker:master
Diff against target: 673 lines (+177/-164)
2 files modified
scripts/oval_lib.py (+111/-45)
test/test_json_generation.py (+66/-119)
Reviewer Review Type Date Requested Status
Eduardo Barretto Approve
Review via email: mp+466248@code.launchpad.net

Description of the change

[OVAL/JSON GEN] Only load CVEs into USN if affecting the release.

USN CVEs are now based on release. This ensures that only the affected
CVEs will be listed when generating for a specific release.

Since we have regressions that do not list a CVE and wrong
USN fixed version entries, there is no way we can retrieve the right
Package from the CVE so we are looking based on release and fixed version.

---------

USN loading is performed in the following way:

1. Get the listed releases in the USN. For each:
    - Identify active ESM releases
    - Get the packages mentioned in the USN released and fixed versions
    - Based on the release, package and fixed version,
      retrieve the appropriate Package and save it with the USN fixed version.

2. Get the CVE/LP bugs entries. For each CVE:
    - Get each PkgRelEntry and check if is marked as fixed (or if USN is a regression)
      and that the package and release are mentioned in the USN, if so, get CVE.

If there are no packages for the requested releases, skip USN.

---------

These changes introduced a diff in JSON generation:

* Only fixed/related CVEs will be listed on the USN entries.
* More USNs listed since now we do better handling of parent cases.

JSON generation code updated and tests updated.

To post a comment you must log in.
Revision history for this message
Eduardo Barretto (ebarretto) wrote :

tested locally and looks good to me!
thanks!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/scripts/oval_lib.py b/scripts/oval_lib.py
2index 9127dad..a33cca2 100755
3--- a/scripts/oval_lib.py
4+++ b/scripts/oval_lib.py
5@@ -494,23 +494,29 @@ class USN:
6
7 self.lp_bugs = lp_bugs
8 self.cves = cve_objs
9- self.pkgs = self._generate_pkg_fixed_ver_tuple_dict(pkgs_by_rel)
10-
11- def _generate_pkg_fixed_ver_tuple_dict(self, pkgs_by_rel):
12- tup_dict = {}
13- for rel, pkgs in pkgs_by_rel.items():
14- tup_dict[rel] = {}
15- for src_name, pkg_object in pkgs.items():
16- fixed_ver = self.releases[rel]['sources'][src_name]['version']
17- tup_dict[rel][src_name] = (pkg_object, fixed_ver)
18- return tup_dict
19+ self.pkgs = pkgs_by_rel
20
21 def get_pkg_fixed_version(self, package: Package):
22+ """Get the fixed version of a given package in the USN"""
23 if not self.is_package_present(package): return None
24- return self.pkgs[package.rel][package.name][1]
25+ for rel in self.pkgs:
26+ if package.name in self.pkgs[rel]:
27+ return self.pkgs[rel][package.name][1]
28
29 def is_package_present(self, package: Package):
30- return package.rel in self.pkgs and package.name in self.pkgs[package.rel]
31+ """Check if a package is present in the USN, regardless of the release"""
32+ for rel in self.pkgs:
33+ if package.name in self.pkgs[rel]:
34+ return True
35+
36+ return False
37+
38+ def get_package_release_in_usn(self, package: Package):
39+ """Get the release of a given package in the USN"""
40+ if not self.is_package_present(package): return None
41+ for rel in self.pkgs:
42+ if package.name in self.pkgs[rel]:
43+ return self.pkgs[rel][package.name][0].rel
44
45 def __str__(self) -> str:
46 # return f'description: {self.description}\nid: {self.id}\ncves: {self.cves}\npkgs: {self.pkgs}\n' # TODO: remove this ugly debug print
47@@ -593,9 +599,11 @@ class OvalGenerator:
48 def _get_objects_including_parents(self, objects) -> dict:
49 all_objs = dict()
50 for parent_release in self.parent_releases[::-1]:
51- all_objs.update(objects[parent_release])
52+ if parent_release in objects:
53+ all_objs.update(objects[parent_release])
54
55- all_objs.update(objects[self.release])
56+ if self.release in objects:
57+ all_objs.update(objects[self.release])
58 return all_objs
59
60 def _add_structure(self, root) -> None:
61@@ -1799,33 +1807,82 @@ class OvalGeneratorUSNs(OvalGenerator):
62 def _load_usns(self, usn_database):
63 usns = {}
64 # go thru every USN in the JSON
65- for usn_id, usn_data in dict(sorted(usn_database.items())).items():
66+ for usn_id, usn_data in usn_database.items():
67 # take existing CVE and Package objects
68- cve_objs = {}
69- pkg_objs_by_rels = {}
70+ cves = {}
71+ pkgs = {}
72 lp_bugs = []
73+ # Package - fixed version loading
74 for rel, info in usn_data['releases'].items():
75- # CVE stays the same across releases
76- for cve in usn_data['cves']:
77- if 'launchpad' in cve:
78- lp_bugs.append(cve)
79- elif cve_objs.get(cve) is None:
80- try:
81- cve_objs[cve] = self.cves[rel][cve]
82- except KeyError:
83- pass
84- pkg_objs = {}
85- for pkg, _ in info['sources'].items():
86- try:
87- pkg_objs[pkg] = self.packages[rel][pkg]
88- except KeyError:
89- pass
90- if pkg_objs:
91- pkg_objs_by_rels[rel] = pkg_objs
92+ esm_rels = list(filter(lambda esm_rel: rel in esm_rel and rel != esm_rel, cve_lib.get_active_esm_releases()))
93+ all_rels = esm_rels.copy()
94+ all_rels.append(rel)
95+
96+ # Sanity check to skip in case none of the releases was requested
97+ rel_exists = False
98+ for rel in all_rels:
99+ if rel in self.releases: rel_exists = True
100+ if not rel_exists: continue
101+
102+
103+ # Get package
104+ for pkg, pkg_info in info['sources'].items():
105+ pkg_obj = None
106+ if 'esm' in pkg_info['version']:
107+ for current_rel in esm_rels:
108+ if current_rel not in self.packages: continue
109+ if pkg not in self.packages[current_rel]: continue
110+ pkg_obj = self.packages[current_rel][pkg]
111+ break
112+ if pkg_obj:
113+ pkgs.setdefault(current_rel, {})
114+ pkgs[current_rel][pkg] = (pkg_obj, pkg_info['version'])
115+ else:
116+ if rel not in self.packages: continue
117+ if pkg not in self.packages[rel]: continue
118+ pkg_obj = self.packages[rel][pkg]
119+ pkgs.setdefault(rel, {})
120+ pkgs[rel][pkg] = (pkg_obj, pkg_info['version'])
121+
122+ # CVE loading
123+ for cve_text in usn_data['cves']:
124+ if 'launchpad.net' in cve_text:
125+ lp_bugs.append(cve_text)
126+ continue
127+ elif not re.search(r'CVE-\d{4}-\d{4,7}', cve_text):
128+ continue
129+
130+ cve = None
131+ for rel in usn_data['releases']:
132+ if rel not in self.cves or \
133+ cve_text not in self.cves[rel]: continue
134+ cve = self.cves[rel][cve_text]
135+ break
136+
137+ if not cve: continue
138+
139+ # Was the CVE fixed in this USN for any of the packages of the considered releases?
140+ for pkg_rel_entry in cve.pkg_rel_entries.values():
141+ if pkg_rel_entry.orig_status != 'released' and 'regression' not in usn_data['title']: continue
142+ if 'esm' in pkg_rel_entry.release:
143+ rel = cve_lib.get_orig_rel_name(pkg_rel_entry.release)
144+ else:
145+ rel = pkg_rel_entry.release
146+ if rel not in usn_data['releases'] or \
147+ pkg_rel_entry.pkg.name not in usn_data['releases'][rel]['sources']: continue
148+ # We have some cases like -2 USNs or wrong versions
149+ # so we cannot compare against CVE version, TBD once
150+ # we have fixed USN wrong versions.
151+ cves.setdefault(pkg_rel_entry.release, {})
152+ pkg_info = usn_data['releases'][rel]['sources'][pkg_rel_entry.pkg.name]
153+ cves[pkg_rel_entry.release][cve.number] = cve
154+
155 # create a USN object with fields in the USN and
156 # corresponding CVEs and Packages
157- usns[usn_id] = USN(usn_data, cve_objs, pkg_objs_by_rels, lp_bugs)
158- return usns
159+ if len(pkgs) == 0: continue
160+ usns[usn_id] = USN(usn_data, cves, pkgs, lp_bugs)
161+
162+ return dict(sorted(usns.items()))
163
164 def _generate_advisory(self, usn: USN) -> etree.Element:
165 severities = ['low', 'medium', 'high', 'critical']
166@@ -1833,7 +1890,7 @@ class OvalGeneratorUSNs(OvalGenerator):
167 severity = etree.SubElement(advisory, "severity")
168 issued = etree.SubElement(advisory, "issued")
169 severity = None
170- for cve in usn.cves:
171+ for cve in usn.cves[self.release]:
172 cve_obj = self._generate_cve_tag(self.cves[cve])
173 advisory.append(cve_obj)
174
175@@ -2731,10 +2788,12 @@ class JSONPkgGenerator(OvalGeneratorUSNs):
176 self.generator_type = 'json_pkg'
177
178 def _generate_usn_info(self, usn: USN):
179+ cves = list(self._get_objects_including_parents(usn.cves).keys())
180+ if len(cves) == 0 and len(usn.lp_bugs) == 0: return {} # If no CVEs or LP bugs, don't add the USN
181 return {
182 'description': usn.description,
183 'published_at': datetime.fromtimestamp(usn.timestamp).isoformat(timespec="seconds"),
184- 'related_cves': list(usn.cves.keys()),
185+ 'related_cves': cves,
186 'related_launchpad_bugs': usn.lp_bugs
187 }
188
189@@ -2794,15 +2853,21 @@ class JSONPkgGenerator(OvalGeneratorUSNs):
190
191 def _generate_cve_pkg_info(self, package: Package) -> dict:
192 pkg_cves = {}
193+ all_releases = self.parent_releases.copy()
194+ all_releases.append(self.release)
195+
196 for cve in package.cves:
197 cve_pkg_rel_entry = None
198
199 for pkg_rel_entry_id in cve.pkg_rel_entries:
200 pkg_rel_entry_package, pkg_rel_entry_rel = pkg_rel_entry_id.split('/', maxsplit=1)
201 if pkg_rel_entry_package == package.name and \
202- pkg_rel_entry_rel == package.rel:
203- cve_pkg_rel_entry = cve.pkg_rel_entries[pkg_rel_entry_id]
204- break
205+ pkg_rel_entry_rel in all_releases:
206+
207+ if cve_pkg_rel_entry and cve_pkg_rel_entry.status != 'fixed':
208+ cve_pkg_rel_entry = cve.pkg_rel_entries[pkg_rel_entry_id]
209+ elif not cve_pkg_rel_entry:
210+ cve_pkg_rel_entry = cve.pkg_rel_entries[pkg_rel_entry_id]
211
212 if not cve_pkg_rel_entry:
213 print(f'CVE entry for {package.name} - {package.rel} in {cve.number} not found')
214@@ -2820,11 +2885,12 @@ class JSONPkgGenerator(OvalGeneratorUSNs):
215 usns_pkg_info = dict()
216 usns_regression_pkg_info = dict()
217 for usn_id, usn in self.usns.items():
218- if usn.is_package_present(package):
219+ if usn.is_package_present(package) and \
220+ (usn.get_package_release_in_usn(package) == self.release or \
221+ usn.get_package_release_in_usn(package) in self.parent_releases):
222 usn_pkg_info = {
223 'source_fixed_version': usn.get_pkg_fixed_version(package)
224 }
225-
226 if 'regression' in usn.title.lower():
227 usns_regression_pkg_info[f'USN-{usn_id}'] = usn_pkg_info
228 else:
229@@ -2899,9 +2965,9 @@ class JSONPkgGenerator(OvalGeneratorUSNs):
230 self.release_codename = self.release_codename.replace('/', '_')
231 self.parent_releases = self._get_parent_releases(release)
232 if self._expand_release(self.release, self.expand):
233- self.output_file = f'com.ubuntu.{self.release.replace("/", "_")}.pkg.json'
234+ self.output_file = f'com.ubuntu.{self.release.replace("/", "_")}.pkg.json'
235 else:
236- self.output_file = f'com.ubuntu.{self.release_codename}.pkg.json'
237+ self.output_file = f'com.ubuntu.{self.release_codename}.pkg.json'
238
239 def generate_json(self) -> None:
240 for release in self.releases:
241diff --git a/test/test_json_generation.py b/test/test_json_generation.py
242index 9f44642..6638b5d 100644
243--- a/test/test_json_generation.py
244+++ b/test/test_json_generation.py
245@@ -129,7 +129,6 @@ class EmptyJSONPkgGenerator(JSONPkgGenerator):
246 self.pkg_cache = None
247 self.expand = True
248 pass
249-
250 #### TESTS ####
251
252 @pytest.mark.parametrize("mock_cve", [
253@@ -202,55 +201,6 @@ def test_generate_cves_info():
254 assert generated_info['cvss_score'] == (float
255 (mock_cve.cvss[0]['baseScore']) if mock_cve.cvss else None)
256
257-def test_generate_cves_info_parents():
258- cves_jammy = {
259- 'CVE-0001-0001': generate_mock_cve(pkgs=dict(), cve_id='CVE-0001-0001'),
260- 'CVE-0001-0002': generate_mock_cve(pkgs=dict(), cve_id='CVE-0001-0002', public_date='2020-08-04 17:00:00 PDT'),
261- }
262-
263- cves_jammy_esm_apps = {
264- 'CVE-0001-0003': generate_mock_cve(pkgs=dict(), cve_id='CVE-0001-0003', public_date='2020-08-04 17:00:00 PST'),
265- }
266-
267- cves_jammy_esm_infra = {
268- 'CVE-0001-0004': generate_mock_cve(pkgs=dict(), cve_id='CVE-0001-0004', cvss=None)
269- }
270-
271- json_gen = EmptyJSONPkgGenerator()
272- json_gen.cves = {'jammy': {}, 'esm-apps/jammy': {}, 'esm-infra/jammy': {}}
273-
274- for cve_id, cve in cves_jammy.items():
275- json_gen.cves['jammy'][cve_id] = cve
276-
277- for cve_id, cve in cves_jammy_esm_apps.items():
278- json_gen.cves['esm-apps/jammy'][cve_id] = cve
279-
280- for cve_id, cve in cves_jammy_esm_infra.items():
281- json_gen.cves['esm-infra/jammy'][cve_id] = cve
282-
283-
284- json_gen._init_ids('jammy')
285- generated_info_cves = json_gen._generate_cves_info()
286- assert len(generated_info_cves) == 2
287- for cve_id in cves_jammy.keys():
288- assert cve_id in generated_info_cves.keys()
289-
290- json_gen._init_ids('esm-infra/jammy')
291- json_gen.parent_releases = ['jammy']
292- generated_info_cves = json_gen._generate_cves_info()
293- assert len(generated_info_cves) == 3
294- for cve_id in list(cves_jammy.keys()) + list(cves_jammy_esm_infra.keys()):
295- assert cve_id in generated_info_cves.keys()
296-
297-
298- json_gen._init_ids('esm-apps/jammy')
299- json_gen.parent_releases = ['esm-infra/jammy', 'jammy']
300- generated_info_cves = json_gen._generate_cves_info()
301- assert len(generated_info_cves) == 4
302- for cve_id in list(cves_jammy.keys()) + list(cves_jammy_esm_infra.keys()) + list(cves_jammy_esm_apps.keys()):
303- assert cve_id in generated_info_cves.keys()
304-
305-
306 @pytest.mark.parametrize("status,note,json_status,fixed_version", [
307 ("ignored", "not for us", "vulnerable", None),
308 ("needed", "", "vulnerable", None),
309@@ -268,6 +218,7 @@ def test_generate_cve_pkg_info(status, note, json_status, fixed_version):
310 cve = generate_mock_cve(pkgs=dict(), cve_id='CVE-0001-0001', priority='critical')
311 cve.add_pkg(package, 'jammy', status, note)
312 json_gen = EmptyJSONPkgGenerator()
313+ json_gen._init_ids('jammy')
314
315 info = json_gen._generate_cve_pkg_info(package)
316 info = info['CVE-0001-0001']
317@@ -280,29 +231,31 @@ def test_generate_cve_pkg_info(status, note, json_status, fixed_version):
318 generate_mock_usn(
319 description='this is a test description',
320 timestamp=1708590783,
321- cve_objs={'CVE-0001-0001': None, 'CVE-0001-0002': None},
322+ cve_objs={'jammy':{'CVE-0001-0001': None, 'CVE-0001-0002': None}},
323 lp_bugs=['http://bug1.com', 'http://bug2.com']
324 ),
325 generate_mock_usn(
326 description='this is a test description',
327 timestamp=1708590783,
328+ cve_objs={'jammy':{}},
329 lp_bugs=['http://bug1.com', 'http://bug2.com']
330 ),
331 generate_mock_usn(
332 description='this is a test description',
333 timestamp=1708590783,
334- cve_objs={'CVE-0001-0001': None, 'CVE-0001-0002': None}
335+ cve_objs={'jammy':{'CVE-0001-0001': None, 'CVE-0001-0002': None}},
336 ),
337 ])
338 def test_generate_usn_info(usn):
339 # 1708590783 is ~2024-02-22T09:33:03+00:00
340 date = datetime.fromtimestamp(1708590783).isoformat(timespec="seconds")
341 json_gen = EmptyJSONPkgGenerator()
342+ json_gen._init_ids('jammy')
343
344 info = json_gen._generate_usn_info(usn)
345 assert info['description'] == usn.description
346 assert info['published_at'] == date
347- assert info['related_cves'] == list(usn.cves.keys())
348+ assert info['related_cves'] == list(usn.cves['jammy'].keys())
349 assert info['related_launchpad_bugs'] == usn.lp_bugs
350
351 def test_generate_usns_info():
352@@ -312,7 +265,7 @@ def test_generate_usns_info():
353 id='1000-1',
354 description='this is a test description',
355 timestamp=1708590783,
356- cve_objs={'CVE-0001-0001': None, 'CVE-0001-0002': None},
357+ cve_objs={'jammy':{'CVE-0001-0001': None, 'CVE-0001-0002': None}},
358 lp_bugs=['http://bug1.com', 'http://bug2.com']
359 ),
360 generate_mock_usn(
361@@ -325,7 +278,7 @@ def test_generate_usns_info():
362 id='1000-3',
363 description='this is a test description',
364 timestamp=1708590783,
365- cve_objs={'CVE-0001-0001': None, 'CVE-0001-0002': None}
366+ cve_objs={'jammy':{'CVE-0001-0001': None, 'CVE-0001-0002': None}},
367 )]
368
369 wrong_release_usn = generate_mock_usn(
370@@ -333,11 +286,20 @@ def test_generate_usns_info():
371 description='this is a test description',
372 timestamp=1708590783,
373 releases=['bionic'],
374- cve_objs={'CVE-0001-0001': None, 'CVE-0001-0002': None}
375+ cve_objs={'bionic':{'CVE-0001-0001': None, 'CVE-0001-0002': None}},
376+ )
377+
378+ other_release = generate_mock_usn(
379+ id='3000-1',
380+ description='this is a test description',
381+ timestamp=1708590783,
382+ releases=['jammy', 'bionic'],
383+ cve_objs={'jammy': {'CVE-0001-0001': None, 'CVE-0001-0002': None}, 'bionic': {'CVE-0001-0003'}}
384 )
385
386 final_usns = usns.copy()
387 final_usns.append(wrong_release_usn)
388+ final_usns.append(other_release)
389 usns_dict = dict(map(lambda usn: (usn.id, usn), final_usns))
390 json_gen = EmptyJSONPkgGenerator()
391 json_gen.usns = usns_dict
392@@ -345,20 +307,25 @@ def test_generate_usns_info():
393 info = json_gen._generate_usns_info()
394
395 assert 'USN-2000-1' not in info
396+ assert 'USN-3000-1' in info and 'CVE-0001-0003' not in info[f'USN-3000-1']['related_cves']
397
398 for usn in usns:
399 assert f'USN-{usn.id}' in info
400 assert info[f'USN-{usn.id}']['description'] == usn.description
401 assert info[f'USN-{usn.id}']['published_at'] == date
402- assert info[f'USN-{usn.id}']['related_cves'] == list(usn.cves.keys())
403+ if 'jammy' in usn.cves:
404+ assert info[f'USN-{usn.id}']['related_cves'] == list(usn.cves['jammy'].keys())
405+ else:
406+ assert info[f'USN-{usn.id}']['related_cves'] == []
407 assert info[f'USN-{usn.id}']['related_launchpad_bugs'] == usn.lp_bugs
408+
409
410 def test_generate_usns_info_parents():
411 jammy_usns = [generate_mock_usn(
412 id='1000-1',
413 description='this is a test description',
414 timestamp=1708590783,
415- cve_objs={'CVE-0001-0001': None, 'CVE-0001-0002': None},
416+ cve_objs={'jammy': {'CVE-0001-0001': None, 'CVE-0001-0002': None}},
417 lp_bugs=['http://bug1.com', 'http://bug2.com']
418 ),
419 generate_mock_usn(
420@@ -371,7 +338,8 @@ def test_generate_usns_info_parents():
421 id='1000-3',
422 description='this is a test description',
423 timestamp=1708590783,
424- cve_objs={'CVE-0001-0001': None, 'CVE-0001-0002': None}
425+ releases=['jammy'],
426+ cve_objs={'jammy': {'CVE-0001-0001': None, 'CVE-0001-0002': None}, 'bionic': {'CVE-0001-0003'}}
427 )]
428
429 jammy_infra_usns = [
430@@ -379,8 +347,8 @@ def test_generate_usns_info_parents():
431 id='2000-1',
432 description='this is a test description',
433 timestamp=1708590783,
434- releases=['esm-infra/jammy'],
435- cve_objs={'CVE-0001-0001': None, 'CVE-0001-0002': None}
436+ releases=['jammy'],
437+ cve_objs={'jammy': {'CVE-0001-0001': None, 'CVE-0001-0002': None}, 'bionic': {'CVE-0001-0003'}}
438 )
439 ]
440
441@@ -389,8 +357,8 @@ def test_generate_usns_info_parents():
442 id='3000-1',
443 description='this is a test description',
444 timestamp=1708590783,
445- releases=['esm-apps/jammy'],
446- cve_objs={'CVE-0001-0001': None, 'CVE-0001-0002': None}
447+ releases=['jammy'],
448+ cve_objs={'jammy': {'CVE-0001-0001': None, 'CVE-0001-0002': None}, 'bionic': {'CVE-0001-0003'}}
449 )
450 ]
451
452@@ -400,22 +368,8 @@ def test_generate_usns_info_parents():
453 json_gen.usns = usns_dict
454 json_gen._init_ids('jammy')
455 info = json_gen._generate_usns_info()
456- assert len(info.keys()) == 3
457- for usn in jammy_usns:
458- assert f'USN-{usn.id}' in info
459-
460- json_gen._init_ids('esm-infra/jammy')
461- json_gen.parent_releases = ['jammy']
462- info = json_gen._generate_usns_info()
463- assert len(info.keys()) == 4
464- for usn in jammy_usns + jammy_infra_usns:
465- assert f'USN-{usn.id}' in info
466-
467- json_gen._init_ids('esm-apps/jammy')
468- json_gen.parent_releases = ['esm-infra/jammy', 'jammy']
469- info = json_gen._generate_usns_info()
470 assert len(info.keys()) == 5
471- for usn in jammy_usns + jammy_infra_usns + jammy_apps_usns:
472+ for usn in jammy_usns:
473 assert f'USN-{usn.id}' in info
474
475 def test_generate_usn_pkg_info():
476@@ -451,9 +405,9 @@ def test_generate_usn_pkg_info():
477 id='1000-1',
478 description='this is a test description',
479 timestamp=1708590783,
480- cve_objs={'CVE-0001-0001': None, 'CVE-0001-0002': None},
481+ cve_objs={'jammy': {'CVE-0001-0001': None, 'CVE-0001-0002': None}},
482 lp_bugs=['http://bug1.com', 'http://bug2.com'],
483- pkgs_by_rel={'jammy': {'foo': pkg}},
484+ pkgs_by_rel={'jammy': {'foo': (pkg, '1.0.0')}},
485 releases=releases_1
486 )
487
488@@ -462,13 +416,15 @@ def test_generate_usn_pkg_info():
489 title='Regression',
490 description='this is a test description',
491 timestamp=1708590783,
492- cve_objs={'CVE-0001-0001': None, 'CVE-0001-0002': None},
493+ cve_objs={'jammy': {'CVE-0001-0001': None, 'CVE-0001-0002': None}},
494 lp_bugs=['http://bug1.com', 'http://bug2.com'],
495- pkgs_by_rel={'jammy': {'foo': pkg}},
496+ pkgs_by_rel={'jammy': {'foo': (pkg, '2.0.0')}},
497 releases=releases_2
498 )
499
500 json_gen = EmptyJSONPkgGenerator()
501+ json_gen._init_ids('jammy')
502+
503 usns_dict = dict(map(lambda usn: (usn.id, usn), [usn, usn_regression]))
504 json_gen.usns = usns_dict
505
506@@ -482,6 +438,7 @@ def test_generate_usn_pkg_info():
507 assert usn_info['USN-1000-1']['source_fixed_version'] == '1.0.0'
508 assert usn_reg_info['USN-1000-2']['source_fixed_version'] == '2.0.0'
509
510+ json_gen._init_ids('bionic')
511 usn_info, usn_reg_info = json_gen._generate_usn_pkg_info(pkg_not)
512
513 assert 'USN-1000-1' not in usn_info
514@@ -554,20 +511,20 @@ def test_generate_package_info(mocker):
515 id='1000-1',
516 description='this is a test description',
517 timestamp=1708590783,
518- cve_objs={'CVE-0001-0001': None, 'CVE-0001-0002': None},
519+ cve_objs={'jammy': {'CVE-0001-0001': None, 'CVE-0001-0002': None}},
520 lp_bugs=['http://bug1.com', 'http://bug2.com'],
521- pkgs_by_rel={'jammy': {'foo': pkg}},
522+ pkgs_by_rel={'jammy': {'foo': (pkg, '1.0.0')}},
523 releases=releases_1
524 )
525
526 usn_regression = generate_mock_usn(
527 id='1000-2',
528- title='Regression',
529+ title='A regression',
530 description='this is a test description',
531 timestamp=1708590783,
532- cve_objs={'CVE-0001-0001': None, 'CVE-0001-0002': None},
533+ cve_objs={'bionic': {'CVE-0001-0001': None, 'CVE-0001-0002': None}},
534 lp_bugs=['http://bug1.com', 'http://bug2.com'],
535- pkgs_by_rel={'bionic': {'bar': pkg2}},
536+ pkgs_by_rel={'bionic': {'bar': (pkg2, '2.0.0')}},
537 releases=releases_2
538 )
539
540@@ -644,9 +601,9 @@ def test_generate_packages_info(mocker):
541 id='1000-1',
542 description='this is a test description',
543 timestamp=1708590783,
544- cve_objs={'CVE-0001-0001': None, 'CVE-0001-0002': None},
545+ cve_objs={'jammy': {'CVE-0001-0001': None, 'CVE-0001-0002': None}},
546 lp_bugs=['http://bug1.com', 'http://bug2.com'],
547- pkgs_by_rel={'jammy': {'foo': pkg}},
548+ pkgs_by_rel={'jammy': {'foo': (pkg, '1.0.0')}},
549 releases=releases_1
550 )
551
552@@ -655,9 +612,9 @@ def test_generate_packages_info(mocker):
553 title='Regression',
554 description='this is a test description',
555 timestamp=1708590783,
556- cve_objs={'CVE-0001-0001': None, 'CVE-0001-0002': None},
557+ cve_objs={'bionic': {'CVE-0001-0001': None, 'CVE-0001-0002': None}},
558 lp_bugs=['http://bug1.com', 'http://bug2.com'],
559- pkgs_by_rel={'bionic': {'bar': pkg2}},
560+ pkgs_by_rel={'bionic': {'bar': (pkg2, '2.0.0')}},
561 releases=releases_2
562 )
563
564@@ -706,7 +663,7 @@ def test_generate_packages_info_parents(mocker):
565 }
566
567 releases_infra_jammy = {
568- 'esm-infra/jammy': {
569+ 'jammy': {
570 'sources': {
571 'bar': {
572 'version': '2.0.0'
573@@ -716,7 +673,7 @@ def test_generate_packages_info_parents(mocker):
574 }
575
576 releases_apps_jammy = {
577- 'esm-apps/jammy': {
578+ 'jammy': {
579 'sources': {
580 'dodo': {
581 'version': '3.0.0'
582@@ -747,9 +704,9 @@ def test_generate_packages_info_parents(mocker):
583 id='1000-1',
584 description='this is a test description',
585 timestamp=1708590783,
586- cve_objs={'CVE-0001-0001': None, 'CVE-0001-0002': None},
587+ cve_objs={'jammy': {'CVE-0001-0001': None, 'CVE-0001-0002': None}},
588 lp_bugs=['http://bug1.com', 'http://bug2.com'],
589- pkgs_by_rel={'jammy': {'foo': pkg}},
590+ pkgs_by_rel={'jammy': {'foo': (pkg, '1.0.0')}},
591 releases=releases_jammy
592 )
593
594@@ -758,9 +715,9 @@ def test_generate_packages_info_parents(mocker):
595 title='Infra',
596 description='this is a test description',
597 timestamp=1708590783,
598- cve_objs={'CVE-0001-0001': None, 'CVE-0001-0002': None},
599+ cve_objs={'jammy': {'CVE-0001-0001': None, 'CVE-0001-0002': None}},
600 lp_bugs=['http://bug1.com', 'http://bug2.com'],
601- pkgs_by_rel={'esm-infra/jammy': {'bar': pkg2}},
602+ pkgs_by_rel={'jammy': {'bar': (pkg2, '2.0.0')}},
603 releases=releases_infra_jammy
604 )
605
606@@ -769,9 +726,9 @@ def test_generate_packages_info_parents(mocker):
607 title='Apps',
608 description='this is a test description',
609 timestamp=1708590783,
610- cve_objs={'CVE-0001-0001': None, 'CVE-0001-0002': None},
611+ cve_objs={'jammy': {'CVE-0001-0001': None, 'CVE-0001-0002': None}},
612 lp_bugs=['http://bug1.com', 'http://bug2.com'],
613- pkgs_by_rel={'esm-apps/jammy': {'dodo': pkg3}},
614+ pkgs_by_rel={'jammy': {'dodo': (pkg3, '3.0.0')}},
615 releases=releases_apps_jammy
616 )
617
618@@ -787,10 +744,10 @@ def test_generate_packages_info_parents(mocker):
619 json_gen = EmptyJSONPkgGenerator()
620 usns_dict = dict(map(lambda usn: (usn.id, usn), [usn, usn_infra, usn_apps]))
621 json_gen.usns = usns_dict
622- json_gen.cves = {'jammy': {}, 'esm-apps/jammy': {}, 'esm-infra/jammy': {}}
623+ json_gen.cves = {'jammy': {}}
624 json_gen.cves['jammy']['CVE-0001-0001'] = cve
625- json_gen.cves['esm-infra/jammy']['CVE-0001-0002'] = cve2
626- json_gen.cves['esm-apps/jammy']['CVE-0001-0003'] = cve3
627+ json_gen.cves['jammy']['CVE-0001-0002'] = cve2
628+ json_gen.cves['jammy']['CVE-0001-0003'] = cve3
629
630 json_gen.packages = {
631 'jammy': {
632@@ -805,25 +762,10 @@ def test_generate_packages_info_parents(mocker):
633 }
634 }
635
636- json_gen._init_ids('jammy')
637- info = json_gen._generate_packages_info()
638- assert 'foo' in info
639- assert 'bar' not in info
640- assert 'dodo' not in info
641- assert info['foo']['cves']['CVE-0001-0001']['status'] == 'vulnerable'
642-
643 json_gen._init_ids('esm-infra/jammy')
644- json_gen.parent_releases = ['jammy']
645- info = json_gen._generate_packages_info()
646- assert 'foo' in info
647- assert 'bar' in info
648- assert 'dodo' not in info
649- assert info['foo']['cves']['CVE-0001-0001']['status'] == 'vulnerable'
650- assert info['bar']['cves']['CVE-0001-0002']['status'] == 'fixed'
651- assert info['bar']['ubuntu_security_notices']['USN-2000-1']['source_fixed_version'] == '2.0.0'
652+ json_gen.parent_releases = ['esm-apps/jammy', 'jammy']
653+ json_gen.orig_parent = 'jammy'
654
655- json_gen._init_ids('esm-apps/jammy')
656- json_gen.parent_releases = ['esm-infra/jammy', 'jammy']
657 info = json_gen._generate_packages_info()
658 assert 'foo' in info
659 assert 'bar' in info
660@@ -832,8 +774,13 @@ def test_generate_packages_info_parents(mocker):
661 assert info['bar']['cves']['CVE-0001-0002']['status'] == 'fixed'
662 assert info['dodo']['cves']['CVE-0001-0003']['status'] == 'fixed'
663 assert info['foo']['cves']['CVE-0001-0001']['source_fixed_version'] == '4'
664+ assert info['bar']['cves']['CVE-0001-0002']['source_fixed_version'] == '1'
665+ assert info['dodo']['cves']['CVE-0001-0003']['source_fixed_version'] == '2'
666+ assert info['foo']['ubuntu_security_notices']['USN-1000-1']['source_fixed_version'] == '1.0.0'
667+ assert info['bar']['ubuntu_security_notices']['USN-2000-1']['source_fixed_version'] == '2.0.0'
668 assert info['dodo']['ubuntu_security_notices']['USN-3000-1']['source_fixed_version'] == '3.0.0'
669
670+
671 @pytest.mark.parametrize("expand,release,filename", [
672 (False, 'esm-apps/bionic', 'com.ubuntu.bionic.pkg.json'),
673 (True, 'esm-apps/bionic', 'com.ubuntu.esm-apps_bionic.pkg.json'),

Subscribers

People subscribed via source and target branches