From faeec6eff1e80be893916d16fb6bd3cd8f0246c9 Mon Sep 17 00:00:00 2001
From: wang-guangge <wangguangge@huawei.com>
Date: Wed, 31 May 2023 18:06:22 +0800
Subject: [PATCH] fix hotpatch updateinfo for search hotpatch information
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 hotpatch/baseclass.py           |  14 ++--
 hotpatch/hot-updateinfo.py      | 118 +++++++++-----------------------
 hotpatch/hotpatch.py            |  13 ++--
 hotpatch/hotpatch_updateinfo.py |  58 ++++++++++------
 4 files changed, 83 insertions(+), 120 deletions(-)

diff --git a/hotpatch/baseclass.py b/hotpatch/baseclass.py
index d88ef40..2542a03 100644
--- a/hotpatch/baseclass.py
+++ b/hotpatch/baseclass.py
@@ -115,7 +115,7 @@ class Hotpatch(object):
 
 
 class Cve(object):
-    __slots__ = ['_cve_id', '_hotpatch']
+    __slots__ = ['_cve_id', '_hotpatches']
 
     def __init__(self,
                  id,
@@ -124,15 +124,14 @@ class Cve(object):
         id: str
         """
         self._cve_id = id
-        self._hotpatch = None
+        self._hotpatches = []
 
     @property
-    def hotpatch(self):
-        return self._hotpatch
+    def hotpatches(self):
+        return self._hotpatches
 
-    @hotpatch.setter
-    def hotpatch(self, hotpatch: Hotpatch):
-        self._hotpatch = hotpatch
+    def add_hotpatch(self, hotpatch: Hotpatch):
+        self._hotpatches.append(hotpatch)
 
     @property
     def cve_id(self):
@@ -206,3 +205,4 @@ class Advisory(object):
 
     def add_hotpatch(self, hotpatch: Hotpatch):
         self._hotpatches.append(hotpatch)
+
diff --git a/hotpatch/hot-updateinfo.py b/hotpatch/hot-updateinfo.py
index 779337b..d1e6d58 100644
--- a/hotpatch/hot-updateinfo.py
+++ b/hotpatch/hot-updateinfo.py
@@ -4,46 +4,6 @@ from dnf.cli.commands.updateinfo import UpdateInfoCommand
 import hawkey
 from .hotpatch_updateinfo import HotpatchUpdateInfo
 
-
-class Versions:
-    """
-    Version number processing
-    """
-
-    separator = (".", "-")
-    _connector = "&"
-
-    def _order(self, version, separator=None):
-        """
-        Version of the cutting
-        Args:
-            version: version
-            separator: separator
-
-        Returns:
-
-        """
-        if not separator:
-            separator = self._connector
-        return tuple([int(v) for v in version.split(separator) if v.isdigit()])
-
-    def lgt(self, version, compare_version):
-        """
-        Returns true if the size of the compared version is greater
-        than that of the compared version, or false otherwise
-
-        """
-        for separator in self.separator:
-            version = self._connector.join(
-                [v for v in version.split(separator)])
-            compare_version = self._connector.join(
-                [v for v in compare_version.split(separator)]
-            )
-        version = self._order(version)
-        compare_version = self._order(compare_version)
-        return version >= compare_version
-
-
 @dnf.plugin.register_command
 class HotUpdateinfoCommand(dnf.cli.Command):
     aliases = ['hot-updateinfo']
@@ -118,26 +78,13 @@ class HotUpdateinfoCommand(dnf.cli.Command):
 
         return mapping_nevra_cve
 
-    def _filter_and_format_list_output(self, echo_lines: list, fixed_cve_id: set, fixed_coldpatches: set):
+    def _filter_and_format_list_output(self, echo_lines: list, fixed_cve_id: set):
         """
         Only show specified cve information that have not been fixed, and format output
         """
-        def is_patch_fixed(coldpatch, fixed_coldpatches):
-            """
-            Check whether the coldpatch is fixed
-            """
-            for fixed_coldpatch in fixed_coldpatches:
-                pkg_name, pkg_evr, _ = coldpatch
-                fixed_pkg_name, fixed_pkg_evr, _ = fixed_coldpatch
-                if pkg_name != fixed_pkg_name:
-                    continue
-                if version.lgt(fixed_pkg_evr, pkg_evr):
-                    return True
-            return False
 
         idw = tiw = ciw = 0
         format_lines = set()
-        version = Versions()
         for echo_line in echo_lines:
             cve_id, adv_type, coldpatch, hotpatch = echo_line[0], echo_line[1], echo_line[2], echo_line[3]
             if self.filter_cves is not None and cve_id not in self.filter_cves:
@@ -145,11 +92,8 @@ class HotUpdateinfoCommand(dnf.cli.Command):
             if cve_id in fixed_cve_id:
                 continue
             if not isinstance(coldpatch, str):
-                if is_patch_fixed(coldpatch, fixed_coldpatches):
-                    continue
-                else:
-                    pkg_name, pkg_evr, pkg_arch = coldpatch
-                    coldpatch = '%s-%s.%s' % (pkg_name, pkg_evr, pkg_arch)
+                pkg_name, pkg_evr, pkg_arch = coldpatch
+                coldpatch = '%s-%s.%s' % (pkg_name, pkg_evr, pkg_arch)
 
             idw = max(idw, len(cve_id))
             tiw = max(tiw, len(adv_type))
@@ -178,41 +122,43 @@ class HotUpdateinfoCommand(dnf.cli.Command):
         mapping_nevra_cve = self.get_mapping_nevra_cve()
         echo_lines = []
         fixed_cve_id = set()
-        fixed_coldpatches = set()
         iterated_cve_id = set()
         for ((nevra), aupdated), id2type in sorted(mapping_nevra_cve.items(), key=lambda x: x[0]):
             pkg_name, pkg_evr, pkg_arch = nevra
             for cve_id, atypesev in id2type.items():
                 iterated_cve_id.add(cve_id)
-                label = type2label(self.updateinfo, *atypesev)
-                echo_line = [cve_id, label, nevra, '-']
-                echo_lines.append(echo_line)
-                if cve_id not in self.hp_hawkey.hotpatch_cves:
+                label = type2label(self.updateinfo, *atypesev) 
+                if cve_id not in self.hp_hawkey.hotpatch_cves or not self.hp_hawkey.hotpatch_cves[cve_id].hotpatches:
+                    echo_line = [cve_id, label, nevra, '-']
+                    echo_lines.append(echo_line)
                     continue
-                hotpatch = self.hp_hawkey.hotpatch_cves[cve_id].hotpatch
-                if hotpatch is None or hotpatch.src_pkg_nevre[0] != pkg_name:
-                    continue
-                if hotpatch.state == self.hp_hawkey.INSTALLED:
-                    # record the fixed cves
-                    for cve_id in hotpatch.cves:
-                        fixed_cve_id.add(cve_id)
-                    # record the fixed coldpatch to filter the cves of the corresponding coldpatch with the lower version
-                    fixed_coldpatches.add((nevra))
-                    echo_lines.pop()
-                elif hotpatch.state == self.hp_hawkey.INSTALLABLE:
-                    echo_lines[-1][3] = hotpatch.nevra
+                
+                for hotpatch in self.hp_hawkey.hotpatch_cves[cve_id].hotpatches:
+                    echo_line = [cve_id, label, nevra, '-']
+                    echo_lines.append(echo_line)
+                    if hotpatch.src_pkg_nevre[0] != pkg_name:
+                        continue
+                    if hotpatch.state == self.hp_hawkey.INSTALLED:
+                        # record the fixed cves
+                        for cve_id in hotpatch.cves:
+                            fixed_cve_id.add(cve_id)
+                        echo_lines.pop()
+                    elif hotpatch.state == self.hp_hawkey.INSTALLABLE:
+                        echo_lines[-1][3] = hotpatch.nevra
+
         
         hp_cve_list = list(set(self.hp_hawkey.hotpatch_cves.keys()).difference(iterated_cve_id))
         for cve_id in hp_cve_list:
-            hotpatch = self.hp_hawkey.hotpatch_cves[cve_id].hotpatch
-            if hotpatch is None:
-                continue
-            echo_line = [cve_id, hotpatch.advisory.severity + '/Sec.', '-', '-']
-            if hotpatch.state == self.hp_hawkey.INSTALLED:
-                continue
-            elif hotpatch.state == self.hp_hawkey.INSTALLABLE:
-                echo_line = [cve_id, hotpatch.advisory.severity + '/Sec.', '-', hotpatch.nevra]
-            echo_lines.append(echo_line)
+            for hotpatch in self.hp_hawkey.hotpatch_cves[cve_id].hotpatches:
+                echo_line = [cve_id, hotpatch.advisory.severity + '/Sec.', '-', '-']
+                if hotpatch.state == self.hp_hawkey.INSTALLED:
+                    # record the fixed cves
+                    fixed_cve_id.add(cve_id)
+                    continue
+                elif hotpatch.state == self.hp_hawkey.INSTALLABLE:
+                    echo_line = [cve_id, hotpatch.advisory.severity + '/Sec.', '-', hotpatch.nevra]
+                echo_lines.append(echo_line)
 
         self._filter_and_format_list_output(
-            echo_lines, fixed_cve_id, fixed_coldpatches)
+            echo_lines, fixed_cve_id)
+
diff --git a/hotpatch/hotpatch.py b/hotpatch/hotpatch.py
index 0704a08..ccef636 100644
--- a/hotpatch/hotpatch.py
+++ b/hotpatch/hotpatch.py
@@ -102,12 +102,12 @@ class HotpatchCommand(dnf.cli.Command):
         hotpatch_cves = self.hp_hawkey.hotpatch_cves
         echo_lines = []
         for cve_id in hotpatch_cves.keys():
-            hotpatch = hotpatch_cves[cve_id].hotpatch
-            status = self.hp_hawkey._get_hotpatch_status_in_syscare(hotpatch)
-            if status == '':
-                continue
-            echo_line = [cve_id, hotpatch.syscare_name, status]
-            echo_lines.append(echo_line)
+            for hotpatch in hotpatch_cves[cve_id].hotpatches:
+                status = self.hp_hawkey._get_hotpatch_status_in_syscare(hotpatch)
+                if status == '':
+                    continue
+                echo_line = [cve_id, hotpatch.syscare_name, status]
+                echo_lines.append(echo_line)
 
         self._filter_and_format_list_output(echo_lines)
     
@@ -133,3 +133,4 @@ class HotpatchCommand(dnf.cli.Command):
         else:
             logger.info(_("%s hot patch '%s' succeed"), operate, self.base.output.term.bold(target_patch))
 
+
diff --git a/hotpatch/hotpatch_updateinfo.py b/hotpatch/hotpatch_updateinfo.py
index 6689553..399e05c 100644
--- a/hotpatch/hotpatch_updateinfo.py
+++ b/hotpatch/hotpatch_updateinfo.py
@@ -67,7 +67,7 @@ class HotpatchUpdateInfo(object):
         """
         Initialize hotpatch information from repos
         """
-        # get xxx-hotpatch.xml.gz file paths by traversing the system_cachedir(/var/cache/dnf)
+        # get xxx-updateinfo.xml.gz file paths by traversing the system_cachedir(/var/cache/dnf)
         system_cachedir = self.cli.base.conf.system_cachedir
         all_repos = self.cli.base.repos
         map_repo_updateinfoxml = {}
@@ -80,9 +80,9 @@ class HotpatchUpdateInfo(object):
                     continue
 
                 for xml_file in os.listdir(repodata_path):
-                    # the hotpatch relevant updateinfo is recorded in xxx-hotpatch.xml.gz
-                    if "hotpatch" in xml_file:
-                        repo_name = file.split("-")[0]
+                    # the hotpatch relevant updateinfo is recorded in xxx-updateinfo.xml.gz
+                    if "updateinfo" in xml_file:
+                        repo_name = file.rsplit("-")[0]
                         cache_updateinfo_xml_path = os.path.join(
                             repodata_path, xml_file)
                         map_repo_updateinfoxml[repo_name] = cache_updateinfo_xml_path
@@ -99,7 +99,7 @@ class HotpatchUpdateInfo(object):
         Parse the pkglist information, filter the hotpatches with different arches
         """
         hotpatches = []
-        hot_patch_collection = pkglist.find('collection')
+        hot_patch_collection = pkglist.find('hot_patch_collection')
         arches = self.base.sack.list_arches()
         if not hot_patch_collection:
             return hotpatches
@@ -171,14 +171,19 @@ class HotpatchUpdateInfo(object):
         advisory.cves = advisory_cves
 
         for hotpatch_kwargs in advisory_hotpatches:
+            # parse the id string of the package to list
+            # e.g. parse the id of "CVE-2021-2023,CVE-2021-2024" to ["CVE-2021-2023", "CVE-2021-2024"]
+            hotpatch_ref_id = hotpatch_kwargs.pop('id')
+            hotpatch_ref_id = hotpatch_ref_id.split(',')
+
             hotpatch = Hotpatch(**hotpatch_kwargs)
             hotpatch.advisory = advisory
-            hotpatch.cves = advisory_cves.keys()
+            hotpatch.cves = hotpatch_ref_id
 
             advisory.add_hotpatch(hotpatch)
-
-            for cve in advisory_cves.values():
-                cve.hotpatch = hotpatch
+            
+            for ref_id in hotpatch_ref_id:
+                advisory_cves[ref_id].add_hotpatch(hotpatch)
 
         self._hotpatch_advisories[advisory_kwargs['id']] = advisory
 
@@ -213,9 +218,9 @@ class HotpatchUpdateInfo(object):
     
     def _parse_and_store_from_xml(self, updateinfoxml):
         """
-        Parse and store hotpatch update information from xxx-hotpatch.xml.gz
+        Parse and store hotpatch update information from xxx-updateinfo.xml.gz
 
-        xxx-hotpatch.xml.gz e.g.
+        xxx-updateinfo.xml.gz e.g.
 
         <?xml version="1.0" encoding="UTF-8"?>
         <updates>
@@ -226,19 +231,26 @@ class HotpatchUpdateInfo(object):
                 <release>openEuler</release>
                 <issued date="2022-04-16"></issued>
                 <references>
-                    <reference href="https://nvd.nist.gov/vuln/detail/CVE-2021-46658" id="CVE-2021-1" title="CVE-2021-1" type="cve"></reference>
+                    <reference href="https://nvd.nist.gov/vuln/detail/CVE-2021-1111" id="CVE-2021-1111" title="CVE-2021-1111" type="cve"></reference>
+                    <reference href="https://nvd.nist.gov/vuln/detail/CVE-2021-1112" id="CVE-2021-1112" title="CVE-2021-1112" type="cve"></reference>
                 </references>
                 <description>patch-redis-6.2.5-1-HP001.(CVE-2022-24048)</description>
                 <pkglist>
-                <collection>
+                <hot_patch_collection>
                     <name>openEuler</name>
-                    <package arch="aarch64" name="patch-redis-6.2.5-1-HP001" release="0" version="1">
-                        <filename>patch-redis-6.2.5-1-HP001-0-1.aarch64.rpm</filename>
+                    <package arch="aarch64" name="patch-redis-6.2.5-1-HP001" release="1" version="1" id="CVE-2021-1111" >
+                        <filename>patch-redis-6.2.5-1-HP001-1-1.aarch64.rpm</filename>
+                    </package>
+                    <package arch="x86_64" name="patch-redis-6.2.5-1-HP001" release="1" version="1" id="CVE-2021-1111">
+                        <filename>patch-redis-6.2.5-1-HP001-1-1.x86_64.rpm</filename>
+                    </package>
+                    <package arch="aarch64" name="patch-redis-6.2.5-1-HP002" release="1" version="1" id="CVE-2021-1111,CVE-2021-1112">
+                        <filename>patch-redis-6.2.5-1-HP002-1-1.aarch64.rpm</filename>
                     </package>
-                    <package arch="x86_64" name="patch-redis-6.2.5-1-HP001" release="0" version="1">
-                        <filename>patch-redis-6.2.5-1-HP001-0-1.x86_64.rpm</filename>
+                    <package arch="x86_64" name="patch-redis-6.2.5-1-HP002" release="1" version="1" id="CVE-2021-1111,CVE-2021-1112">
+                        <filename>patch-redis-6.2.5-1-HP002-1-1.x86_64.rpm</filename>
                     </package>
-                <collection>
+                </hot_patch_collection>
                 </pkglist>
             </update>
             ...
@@ -248,6 +260,9 @@ class HotpatchUpdateInfo(object):
         tree = ET.parse(content)
         root = tree.getroot()
         for update in root.iter('update'):
+            # check whether the hotpatch relevant package information is in each advisory
+            if not update.find('pkglist/hot_patch_collection'):
+                continue
             advisory = self._parse_advisory(update)
             self._store_advisory_info(advisory)
 
@@ -288,9 +303,9 @@ class HotpatchUpdateInfo(object):
             mapping_cve_hotpatches[cve_id] = []
             if cve_id not in self.hotpatch_cves:
                 continue
-            hotpatch = self.hotpatch_cves[cve_id].hotpatch
-            if hotpatch is not None and hotpatch.state == self.INSTALLABLE:
-                mapping_cve_hotpatches[cve_id].append(hotpatch.nevra)
+            for hotpatch in self.hotpatch_cves[cve_id].hotpatches:
+                if hotpatch.state == self.INSTALLABLE:
+                    mapping_cve_hotpatches[cve_id].append(hotpatch.nevra)
         return mapping_cve_hotpatches
 
     def get_hotpatches_from_advisories(self, advisories: list[str]) -> dict():
@@ -317,3 +332,4 @@ class HotpatchUpdateInfo(object):
                     mapping_advisory_hotpatches[advisory_id].append(
                             hotpatch.nevra)
         return mapping_advisory_hotpatches
+
-- 
Gitee
