From e0a11d8450aad557a620bd28603c6b2de286dcbb Mon Sep 17 00:00:00 2001 From: gongzt Date: Fri, 2 Jun 2023 19:38:13 +0800 Subject: [PATCH] update hotpatch status related operation support (cherry picked from commit d241cd0d52330ff74037c17d45d323e6d8ad1859) --- 0004-add-dnf-full-repair.patch | 170 +++++++------- ...tch-status-related-operation-support.patch | 218 ++++++++++++++++++ aops-apollo.spec | 2 + 3 files changed, 304 insertions(+), 86 deletions(-) create mode 100644 0006-update-hotpatch-status-related-operation-support.patch diff --git a/0004-add-dnf-full-repair.patch b/0004-add-dnf-full-repair.patch index 307294d..5b53a10 100644 --- a/0004-add-dnf-full-repair.patch +++ b/0004-add-dnf-full-repair.patch @@ -1,10 +1,7 @@ -From 1ce1c474dcbd3c4f8285f595e6a9071c81f88396 Mon Sep 17 00:00:00 2001 +From 66356dfbe509f2e9d445a2185ff68abdfaa1cd3a Mon Sep 17 00:00:00 2001 From: gongzt -Date: Fri, 2 Jun 2023 15:56:03 +0800 +Date: Fri, 2 Jun 2023 19:24:22 +0800 Subject: [PATCH 1/1] Add dnf full repair -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit --- hotpatch/hotupgrade.py | 57 +++++++++++++++++++++++++++++++++++++++--- @@ -15,88 +12,89 @@ index 4f6a6fb..6adafda 100644 --- a/hotpatch/hotupgrade.py +++ b/hotpatch/hotupgrade.py @@ -20,9 +20,11 @@ from dnf.cli.option_parser import OptionParser - from dnf.cli.output import Output - from dnfpluginscore import _, logger - --from .syscare import Syscare -+from .syscare import Syscare, cmd_output, SUCCEED - from .hotpatch_updateinfo import HotpatchUpdateInfo - -+EMPTY_TAG = "-" -+ - - @dnf.plugin.register_command - class HotupgradeCommand(dnf.cli.Command): + from dnf.cli.output import Output + from dnfpluginscore import _, logger + +-from .syscare import Syscare ++from .syscare import Syscare, cmd_output, SUCCEED + from .hotpatch_updateinfo import HotpatchUpdateInfo + ++EMPTY_TAG = "-" ++ + + @dnf.plugin.register_command + class HotupgradeCommand(dnf.cli.Command): @@ -61,7 +63,8 @@ class HotupgradeCommand(dnf.cli.Command): - advisory_pkgs = self.get_hotpatch_based_on_advisory(self.opts.advisory) - self.hp_list = cve_pkgs + advisory_pkgs - else: -- raise dnf.exceptions.Error(_('No qualified rpm package name or cve/advisory id.')) -+ self.hp_list = self.upgrade_all() -+ logger.info(_("Gonna apply these hot patches:%s"), self.hp_list) - - hp_target_map = self._get_available_hotpatches(self.hp_list) - if not hp_target_map: + advisory_pkgs = self.get_hotpatch_based_on_advisory(self.opts.advisory) + self.hp_list = cve_pkgs + advisory_pkgs + else: +- raise dnf.exceptions.Error(_('No qualified rpm package name or cve/advisory id.')) ++ self.hp_list = self.upgrade_all() ++ logger.info(_("Gonna apply these hot patches:%s"), self.hp_list) + + hp_target_map = self._get_available_hotpatches(self.hp_list) + if not hp_target_map: @@ -177,8 +180,8 @@ class HotupgradeCommand(dnf.cli.Command): - def _remove_hot_patches(self, target_patch_map: dict) -> None: - output = Output(self.base, dnf.conf.Conf()) - logger.info(_("Gonna remove these hot patches: %s"), list(target_patch_map.values())) -- #remove_flag = output.userconfirm() -- #if not remove_flag: -+ # remove_flag = output.userconfirm() -+ # if not remove_flag: - # raise dnf.exceptions.Error(_('Operation aborted.')) - - self.syscare.save() + def _remove_hot_patches(self, target_patch_map: dict) -> None: + output = Output(self.base, dnf.conf.Conf()) + logger.info(_("Gonna remove these hot patches: %s"), list(target_patch_map.values())) +- #remove_flag = output.userconfirm() +- #if not remove_flag: ++ # remove_flag = output.userconfirm() ++ # if not remove_flag: + # raise dnf.exceptions.Error(_('Operation aborted.')) + + self.syscare.save() @@ -266,3 +269,49 @@ class HotupgradeCommand(dnf.cli.Command): - for hp in advisory_hp_dict.values(): - hp_list += hp - return list(set(hp_list)) -+ -+ @staticmethod -+ def get_hot_updateinfo_list(): -+ """ -+ Find all hotpatches and upgrade all -+ use command : dnf hot-updateinfo list cves -+ Last metadata expiration check: 0:48:26 ago on 2023年06月01日 星期四 20时29分55秒. -+ CVE-2023-3332 Low/Sec. - - -+ CVE-2023-3331 Low/Sec. - - -+ CVE-2023-1112 Important/Sec. - patch-redis-6.2.5-1-HP001-1-1.x86_64 -+ CVE-2023-1111 Important/Sec. - patch-redis-6.2.5-1-HP001-1-1.x86_64 -+ -+ return:list -+ [["CVE-2023-3332","Low/Sec.", "-" ,"-"]] -+ -+ """ -+ cmd = ["dnf", "hot-updateinfo", "list", "cves"] -+ -+ output, return_code = cmd_output(cmd) -+ if return_code != SUCCEED: -+ return [] -+ -+ content = output.split('\n') -+ if len(content) <= 2: -+ return [] -+ result = [] -+ for item in content[1:-1]: -+ tmp = item.split() -+ result.append(tmp) -+ return result -+ -+ def upgrade_all(self): -+ """ -+ upgrade all exist cve and hot patches -+ -+ Return: -+ find all patches and return patches list -+ e.g.: -+ ['patch-redis-6.2.5-1-HP2-1-1.x86_64'] -+ """ -+ hotpatchs_info = self.get_hot_updateinfo_list() -+ hp_list = [] -+ for item in hotpatchs_info: -+ if item[-1] != EMPTY_TAG: -+ hp_list.append(item[-1]) -+ return list(set(hp_list)) --- + for hp in advisory_hp_dict.values(): + hp_list += hp + return list(set(hp_list)) ++ ++ @staticmethod ++ def get_hot_updateinfo_list(): ++ """ ++ Find all hotpatches and upgrade all ++ use command : dnf hot-updateinfo list cves ++ Last metadata expiration check: 0:48:26 ago on 2023年06月01日 星期四 20时29分55秒. ++ CVE-2023-3332 Low/Sec. - - ++ CVE-2023-3331 Low/Sec. - - ++ CVE-2023-1112 Important/Sec. - patch-redis-6.2.5-1-HP001-1-1.x86_64 ++ CVE-2023-1111 Important/Sec. - patch-redis-6.2.5-1-HP001-1-1.x86_64 ++ ++ return:list ++ [["CVE-2023-3332","Low/Sec.", "-" ,"-"]] ++ ++ """ ++ cmd = ["dnf", "hot-updateinfo", "list", "cves"] ++ ++ output, return_code = cmd_output(cmd) ++ if return_code != SUCCEED: ++ return [] ++ ++ content = output.split('\n') ++ if len(content) <= 2: ++ return [] ++ result = [] ++ for item in content[1:-1]: ++ tmp = item.split() ++ result.append(tmp) ++ return result ++ ++ def upgrade_all(self): ++ """ ++ upgrade all exist cve and hot patches ++ ++ Return: ++ find all patches and return patches list ++ e.g.: ++ ['patch-redis-6.2.5-1-HP2-1-1.x86_64'] ++ """ ++ hotpatchs_info = self.get_hot_updateinfo_list() ++ hp_list = [] ++ for item in hotpatchs_info: ++ if item[-1] != EMPTY_TAG: ++ hp_list.append(item[-1]) ++ return list(set(hp_list)) +-- +2.33.0 diff --git a/0006-update-hotpatch-status-related-operation-support.patch b/0006-update-hotpatch-status-related-operation-support.patch new file mode 100644 index 0000000..979268b --- /dev/null +++ b/0006-update-hotpatch-status-related-operation-support.patch @@ -0,0 +1,218 @@ +From 5d699207efd9164bef54b10e31c2082980e37f38 Mon Sep 17 00:00:00 2001 +From: rabbitali +Date: Thu, 1 Jun 2023 15:53:34 +0800 +Subject: [PATCH] update hot patch status related operation support +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +--- + apollo/database/proxy/cve.py | 20 ++++++++++++++++---- + apollo/database/proxy/host.py | 8 +++++--- + apollo/database/proxy/task.py | 7 +++++-- + apollo/database/table.py | 3 ++- + apollo/function/schema/cve.py | 2 ++ + apollo/function/schema/host.py | 1 + + apollo/function/schema/task.py | 4 +++- + 7 files changed, 34 insertions(+), 11 deletions(-) + +diff --git a/apollo/database/proxy/cve.py b/apollo/database/proxy/cve.py +index 9dc96ae..13a1ae6 100644 +--- a/apollo/database/proxy/cve.py ++++ b/apollo/database/proxy/cve.py +@@ -224,7 +224,10 @@ class CveMysqlProxy(MysqlProxy): + Returns: + set + """ +- filters = {CveHostAssociation.fixed == filter_dict.get("fixed", False)} ++ # when fixed does not have a value, the query data is not meaningful ++ # the default query is unfixed CVE information ++ fixed = filter_dict.get("fixed", False) ++ filters = {CveHostAssociation.fixed == fixed} + if not filter_dict: + return filters + +@@ -235,6 +238,12 @@ class CveMysqlProxy(MysqlProxy): + filters.add(Host.host_group_name.in_(filter_dict["host_group"])) + if filter_dict.get("repo"): + filters.add(Host.repo_name.in_(filter_dict["repo"])) ++ if filter_dict.get("hp_status"): ++ filters.add(CveHostAssociation.hp_status.in_(filter_dict["hp_status"])) ++ if filter_dict.get("hotpatch") and fixed is True: ++ filters.add(CveHostAssociation.fixed_by_hp.in_(filter_dict["hotpatch"])) ++ elif filter_dict.get("hotpatch") and fixed is False: ++ filters.add(CveHostAssociation.support_hp.in_(filter_dict["hotpatch"])) + return filters + + def _query_cve_hosts(self, username, cve_id, filters): +@@ -250,7 +259,8 @@ class CveMysqlProxy(MysqlProxy): + """ + cve_query = self.session.query(Host.host_id, Host.host_name, Host.host_ip, Host.host_group_name, + Host.repo_name, Host.last_scan, CveHostAssociation.support_hp, +- CveHostAssociation.fixed, CveHostAssociation.fixed_by_hp) \ ++ CveHostAssociation.fixed, CveHostAssociation.fixed_by_hp, ++ CveHostAssociation.hp_status ) \ + .join(CveHostAssociation, Host.host_id == CveHostAssociation.host_id) \ + .filter(Host.user == username, CveHostAssociation.cve_id == cve_id) \ + .filter(*filters) +@@ -268,7 +278,8 @@ class CveMysqlProxy(MysqlProxy): + "host_group": row.host_group_name, + "repo": row.repo_name, + "last_scan": row.last_scan, +- "hotpatch": row.fixed_by_hp if row.fixed is True else row.support_hp ++ "hotpatch": row.fixed_by_hp if row.fixed is True else row.support_hp, ++ "hp_status": row.hp_status + } + result.append(host_info) + return result +@@ -382,7 +393,8 @@ class CveMysqlProxy(MysqlProxy): + sqlalchemy.orm.query.Query + """ + cve_query = self.session.query(CveHostAssociation.cve_id, Host.host_id, Host.host_name, Host.host_ip, +- CveHostAssociation.support_hp, CveHostAssociation.fixed_by_hp, CveHostAssociation.fixed) \ ++ CveHostAssociation.support_hp, CveHostAssociation.fixed_by_hp, ++ CveHostAssociation.fixed) \ + .join(CveHostAssociation, Host.host_id == CveHostAssociation.host_id) \ + .filter(CveHostAssociation.cve_id.in_(cve_list)) \ + .filter(*filters) +diff --git a/apollo/database/proxy/host.py b/apollo/database/proxy/host.py +index a9431a9..3fdf97b 100644 +--- a/apollo/database/proxy/host.py ++++ b/apollo/database/proxy/host.py +@@ -525,7 +525,8 @@ class HostProxy(HostMysqlProxy, CveEsProxy): + "%" + filter_dict["cve_id"] + "%")) + if filter_dict.get("severity"): + filters.add(Cve.severity.in_(filter_dict["severity"])) +- ++ if filter_dict.get("hp_status"): ++ filters.add(CveHostAssociation.hp_status.in_(filter_dict["hp_status"])) + if filter_dict.get("hotpatch") and fixed is True: + filters.add(CveHostAssociation.fixed_by_hp.in_(filter_dict["hotpatch"])) + elif filter_dict.get("hotpatch") and fixed is False: +@@ -548,7 +549,7 @@ class HostProxy(HostMysqlProxy, CveEsProxy): + """ + host_cve_query = self.session.query(CveHostAssociation.cve_id, Cve.publish_time, Cve.severity, Cve.cvss_score, + CveHostAssociation.fixed, CveHostAssociation.support_hp, +- CveHostAssociation.fixed_by_hp) \ ++ CveHostAssociation.fixed_by_hp, CveHostAssociation.hp_status) \ + .select_from(CveHostAssociation) \ + .outerjoin(Cve, CveHostAssociation.cve_id == Cve.cve_id) \ + .outerjoin(Host, Host.host_id == CveHostAssociation.host_id) \ +@@ -577,7 +578,8 @@ class HostProxy(HostMysqlProxy, CveEsProxy): + "severity": row.severity, + "description": description_dict[cve_id] if description_dict.get(cve_id) else "", + "cvss_score": row.cvss_score, +- "hotpatch": row.fixed_by_hp if row.fixed is True else row.support_hp ++ "hotpatch": row.fixed_by_hp if row.fixed is True else row.support_hp, ++ "hp_status": row.hp_status + } + result.append(cve_info) + return result +diff --git a/apollo/database/proxy/task.py b/apollo/database/proxy/task.py +index e660f02..ac15485 100644 +--- a/apollo/database/proxy/task.py ++++ b/apollo/database/proxy/task.py +@@ -265,7 +265,8 @@ class TaskMysqlProxy(MysqlProxy): + "affected": True, + "fixed": True, + "fixed_by_hp": fix_cve.get("fixed_by_hp"), +- "support_hp": None ++ "support_hp": None, ++ "hp_status": fix_cve.get("hp_status") + } + + self.session.query(CveHostAssociation) \ +@@ -1397,6 +1398,7 @@ class TaskMysqlProxy(MysqlProxy): + "task_name": basic_task.task_name, + "task_type": basic_task.task_type, + "check_items": basic_task.check_items.split(',') if basic_task.check_items else [], ++ "accepted": basic_task.accepted, + "total_hosts": [], + "tasks": [] + } +@@ -1423,7 +1425,7 @@ class TaskMysqlProxy(MysqlProxy): + Returns: + sqlalchemy.orm.Query + """ +- task_query = self.session.query(Task.task_id, Task.task_name, Task.task_type, Task.check_items) \ ++ task_query = self.session.query(Task.task_id, Task.task_name, Task.task_type, Task.check_items, Task.accepted) \ + .filter(Task.task_id == task_id) + return task_query + +@@ -2606,6 +2608,7 @@ class TaskProxy(TaskMysqlProxy, TaskEsProxy): + "auto_reboot": True, + "create_time": 1, + "check_items": "", ++ "accepted": True + "info": [ + { + "cve_id": "cve1", +diff --git a/apollo/database/table.py b/apollo/database/table.py +index 251aaeb..33f4380 100644 +--- a/apollo/database/table.py ++++ b/apollo/database/table.py +@@ -37,7 +37,7 @@ class CveHostAssociation(Base, MyBase): + fixed = Column(Boolean) + support_hp = Column(Boolean, default=None) + fixed_by_hp = Column(Boolean, default=None) +- ++ hp_status = Column(String(20)) + + class CveAffectedPkgs(Base, MyBase): + """ +@@ -144,6 +144,7 @@ class Task(Base, MyBase): + create_time = Column(Integer) + host_num = Column(Integer) + check_items = Column(String(32)) ++ accepted = Column(Boolean, default=False) + + username = Column(String(40), ForeignKey('user.username')) + +diff --git a/apollo/function/schema/cve.py b/apollo/function/schema/cve.py +index 635e5eb..6584941 100644 +--- a/apollo/function/schema/cve.py ++++ b/apollo/function/schema/cve.py +@@ -61,6 +61,8 @@ class CveHostFilterSchema(Schema): + repo = fields.List(fields.String( + validate=lambda s: len(s) != 0), required=False) + fixed = fields.Boolean(required=True, validate=validate.OneOf([True, False])) ++ hotpatch = fields.List(fields.Boolean(validate=validate.OneOf([True, False])), required=False) ++ hp_status = fields.List(fields.String(validate=validate.OneOf(["ACCEPTED", "ACTIVED"])), required=False) + + + class GetCveHostsSchema(Schema): +diff --git a/apollo/function/schema/host.py b/apollo/function/schema/host.py +index 84dcfbe..a0cc4b5 100644 +--- a/apollo/function/schema/host.py ++++ b/apollo/function/schema/host.py +@@ -93,6 +93,7 @@ class HostCvesFilterSchema(Schema): + hotpatch = fields.List(fields.Boolean( + validate=validate.OneOf([True, False])), required=False) + fixed = fields.Boolean(validate=validate.OneOf([True, False])) ++ hp_status = fields.List(fields.String(validate=validate.OneOf(["ACCEPTED", "ACTIVED"])), required=False) + + + class GetHostCvesSchema(Schema): +diff --git a/apollo/function/schema/task.py b/apollo/function/schema/task.py +index 472fd53..415c2ca 100644 +--- a/apollo/function/schema/task.py ++++ b/apollo/function/schema/task.py +@@ -89,7 +89,8 @@ class GenerateCveTaskSchema(Schema): + task_name = fields.String(required=True, validate=lambda s: len(s) != 0) + description = fields.String( + required=True, validate=lambda s: 0 < len(s) <= 50) +- auto_reboot = fields.Boolean(required=False, default=True) ++ auto_reboot = fields.Boolean(required=False, default=False) ++ accepted = fields.Boolean(required=True, validate=validate.OneOf([True, False])) + check_items = fields.String(required=False, validate=lambda s: 0 < len(s) <= 32) + info = fields.List(fields.Nested(CveInfoDictSchema), required=True, validate=lambda s: len(s) > 0) + +@@ -226,6 +227,7 @@ class InstallPcakageInfoSchema(Schema): + class FixedCveInfoSchema(Schema): + cve_id = fields.String(required=True, validate=lambda s: len(s) != 0) + fixed_by_hp = fields.Boolean(required=True, validate=validate.OneOf([True, False])) ++ hp_status = fields.String(validate=validate.OneOf(["ACCEPTED", "ACTIVED"]), required=False) + + + class CveScanCallbackSchema(Schema): +-- diff --git a/aops-apollo.spec b/aops-apollo.spec index 057e74a..d9dcbfb 100644 --- a/aops-apollo.spec +++ b/aops-apollo.spec @@ -10,6 +10,7 @@ Patch0002: 0002-fix-bug-and-update-the-code-of-parsing.patch Patch0003: 0003-fix-hotpatch-updateinfo-for-search-hotpatch-info.patch Patch0004: 0004-add-dnf-full-repair.patch Patch0005: 0005-fix-generate-task-is-not-verified-host-and-cve.patch +Patch0005: 0006-update-hotpatch-status-related-operation-support.patch BuildRequires: python3-setuptools Requires: aops-vulcanus >= v1.2.0 @@ -85,6 +86,7 @@ cp -r hotpatch %{buildroot}/%{python3_sitelib}/dnf-plugins/ - fix hotpatch updateinfo for search hotpatch information - add dnf full repair - the host and cve were not verified when the generate task was fixed +- update hotpatch status related operation support * Wed May 31 2023 wenxin - v1.2.1-2 - fix issue that can not be filtered by CVE ID when query cve rollbak task info