Merge ~litios/ubuntu-cve-tracker:oval/fix_broken_criteria into ubuntu-cve-tracker:master

Proposed by David Fernandez Gonzalez
Status: Merged
Merge reported by: David Fernandez Gonzalez
Merged at revision: 2ce9e012bd027389b5bf836a2c7e4fc000218f0f
Proposed branch: ~litios/ubuntu-cve-tracker:oval/fix_broken_criteria
Merge into: ubuntu-cve-tracker:master
Diff against target: 173 lines (+42/-22)
1 file modified
scripts/oval_lib.py (+42/-22)
Reviewer Review Type Date Requested Status
Paulo Flabiano Smorigo Approve
Eduardo Barretto Approve
Review via email: mp+457529@code.launchpad.net

Description of the change

This PR fixes several issues:

1. Prevent empty CVEs from being created when the affected packages have no valid binaries.

2. Properly set parent release priorities. A bug occurred where the status wasn't set correctly, making the results unpredictable.

3. Set the right version and status when there are parent releases involved.
   1. If the status is vulnerable, use the top release with the latest available binaries.
   2. If the status is fixed, find the parent in which it was fixed and use that.

To post a comment you must log in.
Revision history for this message
David Fernandez Gonzalez (litios) wrote :

Diffs:

* Jammy: https://pastebin.canonical.com/p/NTjzD4Fkjj/ (None)
* Jammy OCI: https://pastebin.canonical.com/p/h5nvD5RWnP/ (None)
* Bionic: https://pastebin.canonical.com/p/NVXXRq7Hd3/ (Empty CVE gone)
* Bionic OCI: https://pastebin.canonical.com/p/r4jfy6nhnc/ (Emtpy CVE gone)
* ESM-apps Xenial: Too long for pastebin (Many esm-apps/xenial -> xenial, affects kernel too)
  - Results diff: (None)

Revision history for this message
Eduardo Barretto (ebarretto) wrote :

lgtm, thanks for handling/investigating this!

review: Approve
Revision history for this message
Paulo Flabiano Smorigo (pfsmorigo) wrote :

LGTM

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
diff --git a/scripts/oval_lib.py b/scripts/oval_lib.py
index f0c89fc..489200e 100644
--- a/scripts/oval_lib.py
+++ b/scripts/oval_lib.py
@@ -311,15 +311,24 @@ class CVE:
311 continue311 continue
312 312
313 if pkg.name not in pkg_rel:313 if pkg.name not in pkg_rel:
314 pkg_rel[pkg.name] = pkg.rel314 pkg_rel[pkg.name] = pkg
315 elif releases.index(pkg.rel) < releases.index(pkg_rel[pkg.name]):315 else:
316 pkg_rel[pkg.name] = pkg.rel316 pkg_rel_entry = self.pkg_rel_entries[str(pkg)]
317 317 curr_pkg_rel_entry = self.pkg_rel_entries[str(pkg_rel[pkg.name])]
318 if curr_pkg_rel_entry.status == 'fixed':
319 priority = releases.index(pkg.rel) > releases.index(pkg_rel[pkg.name].rel) and \
320 pkg_rel_entry.fixed_version in pkg.versions_binaries
321 else:
322 priority = releases.index(pkg.rel) < releases.index(pkg_rel[pkg.name].rel)
323
324 if priority:
325 pkg_rel[pkg.name] = pkg
326
318 for pkg in self.pkgs:327 for pkg in self.pkgs:
319 if self.pkg_rel_entries[str(pkg)].is_not_applicable():328 if self.pkg_rel_entries[str(pkg)].is_not_applicable():
320 continue329 continue
321330
322 if pkg.name in pkg_rel and pkg_rel[pkg.name] == pkg.rel:331 if pkg.name in pkg_rel and pkg_rel[pkg.name].rel == pkg.rel:
323 pkgs.append(pkg)332 pkgs.append(pkg)
324 333
325 return pkgs334 return pkgs
@@ -433,11 +442,13 @@ class Package:
433 def get_binaries(self, source_version, binary_version):442 def get_binaries(self, source_version, binary_version):
434 if not self.versions_binaries: return {}443 if not self.versions_binaries: return {}
435 if source_version not in self.versions_binaries:444 if source_version not in self.versions_binaries:
436 if len(self.versions_binaries[self.earliest_version]) != 1:
437 print(f"WARN: Version {source_version} doesn't exist yet the package {self.name} has different versions for the binaries")
438
439 version_binaries = self.versions_binaries[self.earliest_version]445 version_binaries = self.versions_binaries[self.earliest_version]
440 return version_binaries[self.get_binary_versions(self.earliest_version)[0]]446 if len(version_binaries) > 1:
447 print(f"WARN: Version {source_version} doesn't exist yet the package {self.name} has different versions for the binaries")
448 elif len(version_binaries) == 0:
449 return []
450 binary_versions = self.get_binary_versions(self.earliest_version)
451 return version_binaries[binary_versions[0]]
441 return self.versions_binaries[source_version][binary_version]452 return self.versions_binaries[source_version][binary_version]
442453
443 def __str__(self) -> str:454 def __str__(self) -> str:
@@ -483,12 +494,13 @@ class OvalGenerator:
483 self.release_codename = cve_lib.release_progenitor(self.release) if cve_lib.release_progenitor(self.release) else self.release.replace('/', '_')494 self.release_codename = cve_lib.release_progenitor(self.release) if cve_lib.release_progenitor(self.release) else self.release.replace('/', '_')
484 self.release_name = cve_lib.release_name(self.release)495 self.release_name = cve_lib.release_name(self.release)
485 496
486 self.parent_releases = set()497 self.parent_releases = list()
487 current_release = self.release498 current_release = self.release
488 while(cve_lib.release_parent(current_release)):499 while(cve_lib.release_parent(current_release)):
489 current_release = cve_lib.release_parent(current_release)500 current_release = cve_lib.release_parent(current_release)
490 if current_release != self.release:501 if current_release != self.release and \
491 self.parent_releases.add(current_release)502 current_release not in self.parent_releases:
503 self.parent_releases.append(current_release)
492 504
493 self.ns = 'oval:com.ubuntu.{0}'.format(self.release_codename)505 self.ns = 'oval:com.ubuntu.{0}'.format(self.release_codename)
494 self.id = 100506 self.id = 100
@@ -1441,7 +1453,7 @@ class OvalGeneratorPkg(OvalGenerator):
1441 self._increase_id(is_definition=True)1453 self._increase_id(is_definition=True)
14421454
1443 all_pkgs = dict()1455 all_pkgs = dict()
1444 for parent_release in list(self.parent_releases)[::-1]:1456 for parent_release in self.parent_releases[::-1]:
1445 all_pkgs.update(self.packages[parent_release])1457 all_pkgs.update(self.packages[parent_release])
1446 1458
1447 all_pkgs.update(self.packages[self.release])1459 all_pkgs.update(self.packages[self.release])
@@ -1556,16 +1568,18 @@ class OvalGeneratorCVE(OvalGenerator):
15561568
1557 return instruction1569 return instruction
15581570
1559 def _populate_pkg(self, cve: CVE, package: Package, root_element, main_criteria, cache, fixed_versions) -> None:1571 def _populate_pkg(self, cve: CVE, package: Package, root_element, main_criteria, cache, fixed_versions) -> bool:
1560 tests = root_element.find("tests")1572 tests = root_element.find("tests")
1561 objects = root_element.find("objects")1573 objects = root_element.find("objects")
1562 variables = root_element.find("variables")1574 variables = root_element.find("variables")
1563 states = root_element.find("states")1575 states = root_element.find("states")
1564 pkg_rel_entry = cve.pkg_rel_entries[str(package)]1576 pkg_rel_entry = cve.pkg_rel_entries[str(package)]
1577 added = False
15651578
1566 source_version = package.get_version_to_check(pkg_rel_entry.fixed_version)1579 source_version = package.get_version_to_check(pkg_rel_entry.fixed_version)
1567 for binary_version in package.get_binary_versions(source_version):1580 for binary_version in package.get_binary_versions(source_version):
1568 binaries = package.get_binaries(source_version, binary_version)1581 binaries = package.get_binaries(source_version, binary_version)
1582 if not binaries: continue
1569 cache_entry = f'{package.name}-{binary_version}'1583 cache_entry = f'{package.name}-{binary_version}'
15701584
1571 cache.setdefault(cache_entry, dict(bin_id=None, def_id=None))1585 cache.setdefault(cache_entry, dict(bin_id=None, def_id=None))
@@ -1578,6 +1592,7 @@ class OvalGeneratorCVE(OvalGenerator):
1578 cache_entry_bin = cache_entry1592 cache_entry_bin = cache_entry
15791593
1580 if pkg_rel_entry.status == 'vulnerable' and not self.fixed_only:1594 if pkg_rel_entry.status == 'vulnerable' and not self.fixed_only:
1595 added = True
1581 if not cache_entry in cache or not cache[cache_entry]['def_id']:1596 if not cache_entry in cache or not cache[cache_entry]['def_id']:
1582 self._add_criterion(self.definition_id, pkg_rel_entry, cve, main_criteria)1597 self._add_criterion(self.definition_id, pkg_rel_entry, cve, main_criteria)
15831598
@@ -1594,6 +1609,7 @@ class OvalGeneratorCVE(OvalGenerator):
1594 else:1609 else:
1595 self._add_criterion(cache[cache_entry]['def_id'], pkg_rel_entry, cve, main_criteria)1610 self._add_criterion(cache[cache_entry]['def_id'], pkg_rel_entry, cve, main_criteria)
1596 elif pkg_rel_entry.status == 'fixed':1611 elif pkg_rel_entry.status == 'fixed':
1612 added = True
1597 if binary_version in fixed_versions:1613 if binary_version in fixed_versions:
1598 self._add_criterion(fixed_versions[binary_version], pkg_rel_entry, cve, main_criteria)1614 self._add_criterion(fixed_versions[binary_version], pkg_rel_entry, cve, main_criteria)
1599 else:1615 else:
@@ -1611,6 +1627,8 @@ class OvalGeneratorCVE(OvalGenerator):
1611 fixed_versions[binary_version] = self.definition_id1627 fixed_versions[binary_version] = self.definition_id
1612 self._increase_id(is_definition=False)1628 self._increase_id(is_definition=False)
16131629
1630 return added
1631
1614 def _populate_kernel_pkg(self, cve: CVE, package: Package, root_element, main_criteria, running_kernel_id, cache, fixed_versions) -> None:1632 def _populate_kernel_pkg(self, cve: CVE, package: Package, root_element, main_criteria, running_kernel_id, cache, fixed_versions) -> None:
1615 # Kernel binaries have all same version1633 # Kernel binaries have all same version
1616 version = package.get_latest_version()1634 version = package.get_latest_version()
@@ -1636,27 +1654,29 @@ class OvalGeneratorCVE(OvalGenerator):
16361654
1637 def _generate_elements_from_cve(self, cve, supported_releases, root_element, running_kernel_id, pkg_cache, fixed_versions) -> None:1655 def _generate_elements_from_cve(self, cve, supported_releases, root_element, running_kernel_id, pkg_cache, fixed_versions) -> None:
1638 if not cve.pkgs: return1656 if not cve.pkgs: return
1639 added = False1657 cve_added = False
1640 definition_element = self._generate_definition_object(cve)1658 definition_element = self._generate_definition_object(cve)
1641 instructions = ''1659 instructions = ''
1642 pkgs = cve.get_pkgs(supported_releases)1660 pkgs = cve.get_pkgs(supported_releases)
1643 for pkg in pkgs:1661 for pkg in pkgs:
1662 pkg_added = False
1644 if not pkg.versions_binaries: continue1663 if not pkg.versions_binaries: continue
1645 if not pkg.get_binary_versions(next(iter(pkg.versions_binaries))): continue
1646 if self._ignore_source_package(pkg.name): continue1664 if self._ignore_source_package(pkg.name): continue
16471665
1648 added = True
1649 pkg_rel_entry = cve.pkg_rel_entries[str(pkg)]1666 pkg_rel_entry = cve.pkg_rel_entries[str(pkg)]
1650 if pkg.is_kernel_pkg and self.oval_format != 'oci':1667 if pkg.is_kernel_pkg and self.oval_format != 'oci':
1651 self._populate_kernel_pkg(cve, pkg, root_element, definition_element, running_kernel_id, pkg_cache, fixed_versions)1668 self._populate_kernel_pkg(cve, pkg, root_element, definition_element, running_kernel_id, pkg_cache, fixed_versions)
1669 pkg_added = True
1652 else:1670 else:
1653 self._populate_pkg(cve, pkg, root_element, definition_element, pkg_cache, fixed_versions)1671 pkg_added = self._populate_pkg(cve, pkg, root_element, definition_element, pkg_cache, fixed_versions)
1654 1672
1655 if pkg_rel_entry.status == 'fixed' and pkg.versions_binaries:1673 if pkg_rel_entry.status == 'fixed' and pkg_added:
1656 product_description = cve_lib.get_subproject_description(pkg_rel_entry.release)1674 product_description = cve_lib.get_subproject_description(pkg_rel_entry.release)
1657 instructions = self.prepare_instructions(instructions, cve, product_description, pkg, pkg_rel_entry.fixed_version)1675 instructions = self.prepare_instructions(instructions, cve, product_description, pkg, pkg_rel_entry.fixed_version)
1676
1677 cve_added = cve_added | pkg_added
1658 1678
1659 if added:1679 if cve_added:
1660 definitions = root_element.find("definitions")1680 definitions = root_element.find("definitions")
1661 metadata = definition_element.find('metadata')1681 metadata = definition_element.find('metadata')
1662 metadata.find('description').text = metadata.find('description').text + instructions1682 metadata.find('description').text = metadata.find('description').text + instructions
@@ -1681,11 +1701,11 @@ class OvalGeneratorCVE(OvalGenerator):
16811701
1682 pkg_cache = {}1702 pkg_cache = {}
1683 fixed_versions = {}1703 fixed_versions = {}
1684 accepted_releases = list(self.parent_releases)1704 accepted_releases = self.parent_releases.copy()
1685 accepted_releases.insert(0, self.release)1705 accepted_releases.insert(0, self.release)
16861706
1687 all_cves = self.cves[self.release]1707 all_cves = self.cves[self.release]
1688 for parent_release in list(self.parent_releases):1708 for parent_release in self.parent_releases:
1689 for cve in self.cves[parent_release]:1709 for cve in self.cves[parent_release]:
1690 if cve not in all_cves:1710 if cve not in all_cves:
1691 all_cves[cve] = self.cves[parent_release][cve]1711 all_cves[cve] = self.cves[parent_release][cve]

Subscribers

People subscribed via source and target branches