diff --git a/0001-fix-bug-repeated-display-of-vulnerabilities.patch b/0001-fix-bug-repeated-display-of-vulnerabilities.patch deleted file mode 100644 index 6cf2916..0000000 --- a/0001-fix-bug-repeated-display-of-vulnerabilities.patch +++ /dev/null @@ -1,49 +0,0 @@ -From 83752eec95b4aff92786d09b6291700ed0c405a1 Mon Sep 17 00:00:00 2001 -From: rabbitali -Date: Tue, 29 Aug 2023 21:35:08 +0800 -Subject: [PATCH] the problem of repeated display of vulnerabilities fixed by hot patches -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - ---- - ceres/manages/vulnerability_manage.py | 9 ++++++++- - 1 file changed, 8 insertions(+), 1 deletion(-) - -diff --git a/ceres/manages/vulnerability_manage.py b/ceres/manages/vulnerability_manage.py -index 3f85d3d..747df61 100644 ---- a/ceres/manages/vulnerability_manage.py -+++ b/ceres/manages/vulnerability_manage.py -@@ -435,6 +435,7 @@ class VulnerabilityManage: - if not applied_hotpatch_info_list: - return result - -+ record_key_set = {} - for cve_id, patch_name, hotpatch_status in applied_hotpatch_info_list: - rpm = patch_name.split("-", 1)[0] - # Refer to this example, the CVE can be marked as fixed only if all hotpatch are applied. -@@ -442,7 +443,12 @@ class VulnerabilityManage: - # CVE-2023-1111 redis-6.2.5-1/ACC-1-1/redis-benchmark ACTIVED - # CVE-2023-1111 redis-6.2.5-1/ACC-1-1/redis-cli ACTIVED - # CVE-2023-1111 redis-6.2.5-1/ACC-1-1/redis-server NOT-APPLIED -- if f"{cve_id}-{rpm}" not in self.available_hotpatch_key_set and hotpatch_status in ("ACTIVED", "ACCEPTED"): -+ record_key = f"{cve_id}-{rpm}" -+ if ( -+ (record_key not in self.available_hotpatch_key_set) -+ and (hotpatch_status in ("ACTIVED", "ACCEPTED")) -+ and record_key not in record_key_set -+ ): - result.append( - { - "cve_id": cve_id, -@@ -451,6 +457,7 @@ class VulnerabilityManage: - "hp_status": hotpatch_status, - } - ) -+ record_key_set.add(record_key) - return result - - def cve_fix(self, unfixed_cve_info: dict) -> Tuple[str, dict]: --- -2.33.0 - diff --git a/0001-update-func-named-set-hotpatch-status-by-dnf-plugin.patch b/0001-update-func-named-set-hotpatch-status-by-dnf-plugin.patch new file mode 100644 index 0000000..861f4dc --- /dev/null +++ b/0001-update-func-named-set-hotpatch-status-by-dnf-plugin.patch @@ -0,0 +1,64 @@ +From d6be0a82ace5d07d31a91a628369f71534834441 Mon Sep 17 00:00:00 2001 +From: rabbitali +Date: Wed, 13 Sep 2023 10:58:16 +0800 +Subject: [PATCH 1/1] update func named set_hotpatch_status_by_dnf_plugin + +--- + ceres/manages/vulnerability_manage.py | 30 ++++++++++++++++++++------- + 1 file changed, 22 insertions(+), 8 deletions(-) + +diff --git a/ceres/manages/vulnerability_manage.py b/ceres/manages/vulnerability_manage.py +index f45c1f2..ab4b41c 100644 +--- a/ceres/manages/vulnerability_manage.py ++++ b/ceres/manages/vulnerability_manage.py +@@ -615,12 +615,11 @@ class VulnerabilityManage: + if not self.takeover and self.accepted: + try: + hotpatch_name = hotpatch_pkg.rsplit(".", 1)[0].split("-", 1)[1] +- status_set_result, log = self._set_hotpatch_status_by_dnf_plugin(hotpatch_name, "accept") +- if not status_set_result: +- stdout += "\n" + log ++ _, log = self._set_hotpatch_status_by_dnf_plugin(hotpatch_name, "accept") ++ stdout += f"\n\n{log}" + except IndexError as error: + LOGGER.error(error) +- stdout += "\n" + "hotpatch status set failed due to can't get correct hotpatch name!" ++ stdout += f"\n\nhotpatch status set failed due to can't get correct hotpatch name!" + + return TaskExecuteRes.SUCCEED, stdout + +@@ -637,12 +636,27 @@ class VulnerabilityManage: + Tuple[bool, str] + a tuple containing two elements (operation result, operation log). + """ +- code, stdout, stderr = execute_shell_command(f"dnf hotpatch --{operation} {hotpatch}") +- if code != CommandExitCode.SUCCEED: ++ ++ # replace -ACC to /ACC or -SGL to /SGL ++ # Example: kernel-5.10.0-153.12.0.92.oe2203sp2-ACC-1-1 >> kernel-5.10.0-153.12.0.92.oe2203sp2/ACC-1-1 ++ wait_to_remove_patch = re.sub(r'-(ACC|SGL)', r'/\1', hotpatch) ++ # Example of command execution result: ++ # Succeed: ++ # [root@openEuler ~]# dnf hotpatch --remove kernel-5.10.0-153.12.0.92.oe2203sp2/ACC-1-1 ++ # Last metadata expiration check: 3:24:16 ago on Wed 13 Sep 2023 08:16:17 AM CST. ++ # Gonna remove this hot patch: kernel-5.10.0-153.12.0.92.oe2203sp2/ACC-1-1 ++ # remove hot patch 'kernel-5.10.0-153.12.0.92.oe2203sp2/ACC-1-1' succeed ++ # Fail: ++ # [root@openEuler ~]# dnf hotpatch --accept kernel-5.10.0-153.12.0.92.oe2203sp2/ACC-1-1 ++ # Last metadata expiration check: 3:25:24 ago on Wed 13 Sep 2023 08:16:17 AM CST. ++ # Gonna accept this hot patch: kernel-5.10.0-153.12.0.92.oe2203sp2/ACC-1-1 ++ # accept hot patch 'kernel-5.10.0-153.12.0.92.oe2203sp2/ACC-1-1' failed, remain original status ++ code, stdout, stderr = execute_shell_command(f"dnf hotpatch --{operation} {wait_to_remove_patch}") ++ if code != CommandExitCode.SUCCEED or 'failed' in stdout: + LOGGER.error(f"hotpatch {hotpatch} set status failed!") +- return False, stderr ++ return False, stdout + stderr + +- return True, stdout ++ return True, stdout + stderr + + def cve_rollback(self, cves: List[dict]) -> Tuple[str, list]: + """ +-- +2.33.0 + diff --git a/0002-add-file-sync-func.patch b/0002-add-file-sync-func.patch new file mode 100644 index 0000000..f667757 --- /dev/null +++ b/0002-add-file-sync-func.patch @@ -0,0 +1,155 @@ +From b0f71927a3bdb3096757ca8cdedb233d2b886a4d Mon Sep 17 00:00:00 2001 +From: smjiao +Date: Thu, 7 Sep 2023 16:28:49 +0800 +Subject: [PATCH] add file sync func +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +--- + ceres/__main__.py | 6 ++++ + ceres/function/command.py | 11 ++++++++ + ceres/function/schema.py | 12 ++++++++ + ceres/manages/sync_manage.py | 55 ++++++++++++++++++++++++++++++++++++ + 4 files changed, 84 insertions(+) + create mode 100644 ceres/manages/sync_manage.py + +diff --git a/ceres/__main__.py b/ceres/__main__.py +index d1bbee3..a93ec49 100644 +--- a/ceres/__main__.py ++++ b/ceres/__main__.py +@@ -17,6 +17,7 @@ from ceres.function.command import ( + cve_command_manage, + plugin_command_manage, + register_on_manager, ++ sync_conf_manage, + ) + from ceres.function.log import LOGGER + +@@ -55,6 +56,11 @@ def main(): + cve_group.add_argument("--rollback", type=str) + subparsers_cve.set_defaults(function=cve_command_manage) + ++ subparsers_sync = subparsers.add_parser("sync", help='sync conf file') ++ sync_group = subparsers_sync.add_mutually_exclusive_group(required=True) ++ sync_group.add_argument("--conf", type=str) ++ subparsers_sync.set_defaults(function=sync_conf_manage) ++ + args = parser.parse_args() + try: + args.function(args) +diff --git a/ceres/function/command.py b/ceres/function/command.py +index e4d367a..7324f23 100644 +--- a/ceres/function/command.py ++++ b/ceres/function/command.py +@@ -25,11 +25,13 @@ from ceres.function.schema import ( + HOST_INFO_SCHEMA, + REPO_SET_SCHEMA, + STRING_ARRAY, ++ CONF_SYNC_SCHEMA, + ) + from ceres.function.status import SUCCESS, StatusCode + from ceres.function.util import convert_string_to_json, get_dict_from_file, plugin_status_judge, validate_data + from ceres.manages import plugin_manage + from ceres.manages.collect_manage import Collect ++from ceres.manages.sync_manage import SyncManage + from ceres.manages.vulnerability_manage import VulnerabilityManage + + +@@ -191,3 +193,12 @@ def cve_command_manage(args): + else: + print("Please check the input parameters!") + exit(1) ++ ++ ++def sync_conf_manage(args): ++ if args.conf: ++ config = convert_string_to_json(args.conf) ++ if not validate_data(config, CONF_SYNC_SCHEMA): ++ exit(1) ++ res = StatusCode.make_response_body(SyncManage.sync_contents_to_conf(config)) ++ print(json.dumps(res)) +diff --git a/ceres/function/schema.py b/ceres/function/schema.py +index ada35c3..794152d 100644 +--- a/ceres/function/schema.py ++++ b/ceres/function/schema.py +@@ -113,3 +113,15 @@ CVE_ROLLBACK_SCHEMA = { + } + }, + } ++ ++CONF_SYNC_SCHEMA = { ++ "type": "object", ++ "required": [ ++ "file_path", ++ "content" ++ ], ++ "properties": { ++ "file_path": {"type": "string", "minLength": 1}, ++ "content": {"type": "string", "minLength": 1} ++ } ++} +diff --git a/ceres/manages/sync_manage.py b/ceres/manages/sync_manage.py +new file mode 100644 +index 0000000..9be2a47 +--- /dev/null ++++ b/ceres/manages/sync_manage.py +@@ -0,0 +1,55 @@ ++#!/usr/bin/python3 ++# ****************************************************************************** ++# Copyright (c) Huawei Technologies Co., Ltd. 2022-2022. All rights reserved. ++# licensed under the Mulan PSL v2. ++# You can use this software according to the terms and conditions of the Mulan PSL v2. ++# You may obtain a copy of Mulan PSL v2 at: ++# http://license.coscl.org.cn/MulanPSL2 ++# THIS SOFTWARE IS PROVIDED ON AN 'AS IS' BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR ++# IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR ++# PURPOSE. ++# See the Mulan PSL v2 for more details. ++# ******************************************************************************/ ++# Author: Lay ++# Description: default ++# Date: 2023/6/14 16:31 ++import os ++from ceres.function.log import LOGGER ++from ceres.function.status import ( ++ FILE_NOT_FOUND, ++ UNKNOWN_ERROR, ++ SUCCESS ++) ++ ++ ++class SyncManage: ++ """ ++ Sync managed conf to the host ++ """ ++ ++ @staticmethod ++ def sync_contents_to_conf(config: dict) -> str: ++ """ ++ Write conf into file ++ Args: ++ config(dict): filepath and content for file sync, only. eg: ++ { ++ "file_path" = "/tmp/test" ++ "content" = "contents for this file" ++ } ++ Returns: ++ str: status code ++ """ ++ file_path = config.get('file_path') ++ ++ contents = config.get('content') ++ lines = contents.split('\n') ++ try: ++ with open(file_path, "w", encoding="utf-8") as file: ++ for line in lines: ++ file.write(line + "\n") ++ except Exception as e: ++ LOGGER.error("write sync content to conf failed, with msg{}".format(e)) ++ return UNKNOWN_ERROR ++ ++ return SUCCESS +-- +Gitee + diff --git a/0002-update-query-disk-info-func.patch b/0002-update-query-disk-info-func.patch deleted file mode 100644 index b246880..0000000 --- a/0002-update-query-disk-info-func.patch +++ /dev/null @@ -1,279 +0,0 @@ -From 01c845220663a2572b6559bc25b52da1b2863256 Mon Sep 17 00:00:00 2001 -From: rabbitali -Date: Wed, 30 Aug 2023 10:59:52 +0800 -Subject: [PATCH 1/1] update query disk info func - ---- - ceres/manages/collect_manage.py | 38 ++--- - ceres/tests/manages/test_collect_manage.py | 163 +++++++++++++++++---- - 2 files changed, 152 insertions(+), 49 deletions(-) - -diff --git a/ceres/manages/collect_manage.py b/ceres/manages/collect_manage.py -index 3472903..145d6dc 100644 ---- a/ceres/manages/collect_manage.py -+++ b/ceres/manages/collect_manage.py -@@ -17,6 +17,7 @@ import pwd - import re - from socket import AF_INET, SOCK_DGRAM, socket - from typing import Any, Dict, List, Union -+import xml.etree.ElementTree as ET - - from ceres.conf.constant import ( - HOST_COLLECT_INFO_SUPPORT, -@@ -305,30 +306,33 @@ class Collect: - } - ] - """ -- code, stdout, _ = execute_shell_command("lshw -json -c disk") -+ code, stdout, _ = execute_shell_command("lshw -xml -c disk") - if code != CommandExitCode.SUCCEED: - LOGGER.error(stdout) - return [] - -- # Convert the command result to a json string -- # lshw_data e.g "{...},{...},{...}" -- lshw_data = f"[{stdout}]" -- - try: -- disk_info_list = json.loads(lshw_data) -- except json.decoder.JSONDecodeError: -- LOGGER.warning("Json conversion error, " "please check command 'lshw -json -c disk'") -- disk_info_list = [] -+ tree = ET.ElementTree(ET.fromstring(stdout)) -+ except ET.ParseError as error: -+ LOGGER.error(error) -+ LOGGER.warning("disk info parse error, please check command 'lshw -xml -c disk'") -+ return [] -+ -+ disk_list = tree.findall("node") -+ -+ if not disk_list: -+ return [] - - res = [] -- if disk_info_list: -- for disk_info in disk_info_list: -- res.append( -- { -- "model": disk_info.get('description') or disk_info.get('product'), -- "capacity": f"{disk_info.get('size', 0) // 10 ** 9}GB", -- } -- ) -+ for node in disk_list: -+ model = node.find("description") if node.find("product") is None else node.find("product") -+ size = node.find("size") -+ res.append( -+ { -+ "model": model.text if model is not None else "unknown", -+ "capacity": f"{int(size.text) / (1024**3)} GB" if size is not None else "unknown", -+ } -+ ) - - return res - -diff --git a/ceres/tests/manages/test_collect_manage.py b/ceres/tests/manages/test_collect_manage.py -index b27af55..243aa4c 100644 ---- a/ceres/tests/manages/test_collect_manage.py -+++ b/ceres/tests/manages/test_collect_manage.py -@@ -17,6 +17,7 @@ import pwd - import unittest - import warnings - from unittest import mock -+import xml.etree.ElementTree as ET - - from ceres.conf.constant import CommandExitCode - from ceres.manages.collect_manage import Collect -@@ -454,60 +455,158 @@ class TestCollectManage(unittest.TestCase): - def test_get_disk_info_should_return_disk_info_when_shell_command_execute_succeed_and_only_contain_description( - self, mock_execute_shell_command - ): -- mock_execute_shell_command.return_value = ( -- CommandExitCode.SUCCEED, -- '{"description": "ATA Disk", "size": 42949672960}', -- "", -- ) -- self.assertEqual([{"model": "ATA Disk", "capacity": "42GB"}], Collect()._get_disk_info()) -+ cmd_output = """ -+ -+ -+ -+ -+ -+ -+ Virtual I/O device -+ 0 -+ virtio@3 -+ /dev/vda -+ 42949672960 -+ -+ -+ -+ -+ -+ -+ -+ Partitioned disk -+ MS-DOS partition table -+ -+ -+ -+ -+ -+ -+""" -+ mock_execute_shell_command.return_value = CommandExitCode.SUCCEED, cmd_output, "" -+ self.assertEqual([{"model": "Virtual I/O device", "capacity": "40.0 GB"}], Collect()._get_disk_info()) - - @mock.patch('ceres.manages.collect_manage.execute_shell_command') - def test_get_disk_info_should_return_disk_info_when_shell_command_execute_succeed_and_has_no_description_or_product( - self, mock_execute_shell_command - ): -- mock_execute_shell_command.return_value = ( -- CommandExitCode.SUCCEED, -- '{"size": 42949672960}', -- "", -- ) -- self.assertEqual([{"model": None, "capacity": "42GB"}], Collect()._get_disk_info()) -+ cmd_output = """ -+ -+ -+ -+ -+ -+ -+ 0 -+ virtio@3 -+ /dev/vda -+ 42949672960 -+ -+ -+ -+ -+ -+ -+ -+ Partitioned disk -+ MS-DOS partition table -+ -+ -+ -+ -+ -+ -+""" -+ mock_execute_shell_command.return_value = CommandExitCode.SUCCEED, cmd_output, "" -+ self.assertEqual([{"model": "unknown", "capacity": "40.0 GB"}], Collect()._get_disk_info()) - - @mock.patch('ceres.manages.collect_manage.execute_shell_command') - def test_get_disk_info_should_return_disk_info_when_shell_command_execute_succeed_and_contain_description_and_product( - self, mock_execute_shell_command - ): -- mock_execute_shell_command.return_value = ( -- CommandExitCode.SUCCEED, -- '{"description": "ATA Disk", "size": 42949672960,"product": "MOCK PRODUCT"}', -- "", -- ) -- self.assertEqual([{"model": "ATA Disk", "capacity": "42GB"}], Collect()._get_disk_info()) -+ cmd_output = """ -+ -+ -+ -+ -+ -+ -+ Virtual I/O device -+ ATA Disk -+ 0 -+ virtio@3 -+ /dev/vda -+ 42949672960 -+ -+ -+ -+ -+ -+ -+ -+ Partitioned disk -+ MS-DOS partition table -+ -+ -+ -+ -+ -+ -+""" -+ mock_execute_shell_command.return_value = CommandExitCode.SUCCEED, cmd_output, "" -+ self.assertEqual([{"model": "ATA Disk", "capacity": "40.0 GB"}], Collect()._get_disk_info()) - - @mock.patch('ceres.manages.collect_manage.execute_shell_command') - def test_get_disk_info_should_return_disk_info_when_shell_command_execute_succeed_and_only_contain_product( - self, mock_execute_shell_command - ): -- mock_execute_shell_command.return_value = ( -- CommandExitCode.SUCCEED, -- '{"product": "MOCK PRODUCT", "size": 42949672960}', -- "", -- ) -- self.assertEqual([{"model": "MOCK PRODUCT", "capacity": "42GB"}], Collect()._get_disk_info()) -+ cmd_output = """ -+ -+ -+ -+ -+ -+ -+ MOCK PRODUCT -+ 0 -+ virtio@3 -+ /dev/vda -+ 42949672960 -+ -+ -+ -+ -+ -+ -+ -+ Partitioned disk -+ MS-DOS partition table -+ -+ -+ -+ -+ -+ -+""" -+ mock_execute_shell_command.return_value = CommandExitCode.SUCCEED, cmd_output, "" -+ self.assertEqual([{"model": "MOCK PRODUCT", "capacity": "40.0 GB"}], Collect()._get_disk_info()) - - @mock.patch('ceres.manages.collect_manage.execute_shell_command') - def test_get_disk_info_should_return_disk_info_when_shell_command_execute_fail(self, mock_execute_shell_command): - mock_execute_shell_command.return_value = CommandExitCode.FAIL, "", "" - self.assertEqual([], Collect()._get_disk_info()) - -- @mock.patch.object(json, "loads") -+ @mock.patch.object(ET, "ElementTree") - @mock.patch('ceres.manages.collect_manage.execute_shell_command') - def test_get_disk_info_should_return_disk_info_when_shell_command_execute_succeed_but_decode_error( -- self, mock_execute_shell_command, mock_json_loads -+ self, mock_execute_shell_command, mock_parse_xml - ): -- mock_execute_shell_command.return_value = ( -- CommandExitCode.SUCCEED, -- '{"product": "MOCK PRODUCT", "size": 42949672960}', -- "", -- ) -- mock_json_loads.side_effect = json.decoder.JSONDecodeError('', '', int()) -+ mock_cmd_output = """ -+ -+ -+ -+ -+""" -+ mock_execute_shell_command.return_value = CommandExitCode.SUCCEED, mock_cmd_output, "" -+ mock_parse_xml.side_effect = ET.ParseError - self.assertEqual([], Collect()._get_disk_info()) --- -2.33.0 - diff --git a/0003-adapted-to-cve-rollback-and-update-return-value.patch b/0003-adapted-to-cve-rollback-and-update-return-value.patch deleted file mode 100644 index d1ded65..0000000 --- a/0003-adapted-to-cve-rollback-and-update-return-value.patch +++ /dev/null @@ -1,291 +0,0 @@ -From d7d97d24c0b2b7e0f5686fc9f9bd1674c94c53eb Mon Sep 17 00:00:00 2001 -From: rabbitali -Date: Mon, 4 Sep 2023 17:06:06 +0800 -Subject: [PATCH] adapted to cve rollback and update return value -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - ---- - ceres/conf/constant.py | 5 ++ - ceres/function/status.py | 2 + - ceres/manages/vulnerability_manage.py | 114 ++++++++++++++------------ - 3 files changed, 68 insertions(+), 53 deletions(-) - -diff --git a/ceres/conf/constant.py b/ceres/conf/constant.py -index 8c7723b..79594ae 100644 ---- a/ceres/conf/constant.py -+++ b/ceres/conf/constant.py -@@ -70,3 +70,8 @@ REGISTER_HELP_INFO = """ - class CommandExitCode: - SUCCEED = 0 - FAIL = 255 -+ -+ -+class TaskExecuteRes: -+ SUCCEED = "succeed" -+ FAIL = "fail" -diff --git a/ceres/function/status.py b/ceres/function/status.py -index 70fc212..d1fe088 100644 ---- a/ceres/function/status.py -+++ b/ceres/function/status.py -@@ -25,6 +25,7 @@ REPO_CONTENT_INCORRECT = "Repo.Content.Incorrect" - REPO_NOT_SET = "Repo.Not.Set" - NO_COMMAND = "No.Command" - NOT_PATCH = "Not.Patch" -+PRE_CHECK_ERROR = "Pre.Check.Error" - - COMMAND_EXEC_ERROR = "Command.Error" - -@@ -49,6 +50,7 @@ class StatusCode: - NO_COMMAND: {"msg": "command not found"}, - NOT_PATCH: {"msg": "no valid hot patch is matched"}, - COMMAND_EXEC_ERROR: {"msg": "the input command is incorrect"}, -+ PRE_CHECK_ERROR: {"msg": "Preset check item detection failed"}, - } - - @classmethod -diff --git a/ceres/manages/vulnerability_manage.py b/ceres/manages/vulnerability_manage.py -index 747df61..20bfe1e 100644 ---- a/ceres/manages/vulnerability_manage.py -+++ b/ceres/manages/vulnerability_manage.py -@@ -16,19 +16,10 @@ import json - from collections import defaultdict - from typing import Dict, List, Tuple - --from ceres.conf.constant import REPO_ID_FOR_CVE_MANAGE, CommandExitCode -+from ceres.conf.constant import REPO_ID_FOR_CVE_MANAGE, CommandExitCode, TaskExecuteRes - from ceres.function.check import PreCheck - from ceres.function.log import LOGGER --from ceres.function.status import ( -- FAIL, -- NOT_PATCH, -- PARAM_ERROR, -- REPO_CONTENT_INCORRECT, -- REPO_NOT_SET, -- SUCCESS, -- StatusCode, -- COMMAND_EXEC_ERROR, --) -+from ceres.function.status import * - from ceres.function.util import execute_shell_command - - -@@ -131,7 +122,7 @@ class VulnerabilityManage: - cve_scan_result["check_items"] = items_check_log - if not check_result: - LOGGER.info("The pre-check is failed before execute command!") -- return FAIL, cve_scan_result -+ return PRE_CHECK_ERROR, cve_scan_result - - self.installed_rpm_info = self._query_installed_rpm() - self.available_rpm_info = self._query_available_rpm() -@@ -200,7 +191,7 @@ class VulnerabilityManage: - # kernel-debuginfo.x86_64 5.10.0-60.105.0.132.oe2203 update - # kernel-debugsource.x86_64 5.10.0-60.105.0.132.oe2203 update - # kernel-devel.x86_64 5.10.0-60.105.0.132.oe2203 update -- code, stdout, stderr = execute_shell_command("dnf list available") -+ code, stdout, stderr = execute_shell_command("dnf list available|grep -v '.src'") - if code != CommandExitCode.SUCCEED: - LOGGER.error(stderr) - return result -@@ -435,7 +426,7 @@ class VulnerabilityManage: - if not applied_hotpatch_info_list: - return result - -- record_key_set = {} -+ record_key_set = set() - for cve_id, patch_name, hotpatch_status in applied_hotpatch_info_list: - rpm = patch_name.split("-", 1)[0] - # Refer to this example, the CVE can be marked as fixed only if all hotpatch are applied. -@@ -513,10 +504,10 @@ class VulnerabilityManage: - def gen_failed_result(cves: dict, log: str): - result = [] - for cve in cves: -- cve_fix_result = {"cve_id": cve.get("cve_id"), "result": "failed", "rpms": []} -+ cve_fix_result = {"cve_id": cve.get("cve_id"), "result": TaskExecuteRes.FAIL, "rpms": []} - for rpm in cve.get("rpms"): - cve_fix_result["rpms"].append( -- {"installed_rpm": rpm.get("installed_rpm"), "result": "fail", "log": log} -+ {"installed_rpm": rpm.get("installed_rpm"), "result": TaskExecuteRes.FAIL, "log": log} - ) - result.append(cve_fix_result) - return result -@@ -528,14 +519,14 @@ class VulnerabilityManage: - if not check_result: - LOGGER.info("The pre-check is failed before execute command!") - result["cves"] = gen_failed_result(unfixed_cve_info.get("cves"), "pre-check items check failed") -- return FAIL, result -+ return PRE_CHECK_ERROR, result - - all_cve_fix_result, all_cve_fix_info = [], [] - for cve_info in unfixed_cve_info.get("cves"): - rpms_fix_result, rpm_fix_result_list = [], [] - for rpm_info in cve_info.get("rpms"): - update_result, log = self._update_rpm_by_dnf(rpm_info) -- rpms_fix_result.append(update_result == SUCCESS) -+ rpms_fix_result.append(update_result == TaskExecuteRes.SUCCEED) - rpm_fix_result_list.append( - {"installed_rpm": rpm_info.get("installed_rpm"), "result": update_result, "log": log} - ) -@@ -544,13 +535,13 @@ class VulnerabilityManage: - all_cve_fix_info.append( - { - "cve_id": cve_info.get("cve_id"), -- "result": SUCCESS if all(rpms_fix_result) else FAIL, -+ "result": TaskExecuteRes.SUCCEED if all(rpms_fix_result) else TaskExecuteRes.FAIL, - "rpms": rpm_fix_result_list, - } - ) - - result.update({"cves": all_cve_fix_info}) -- return SUCCESS if all(all_cve_fix_result) else FAIL, result -+ return TaskExecuteRes.SUCCEED if all(all_cve_fix_result) else TaskExecuteRes.FAIL, result - - def _update_rpm_by_dnf(self, cve: dict) -> Tuple[str, str]: - """ -@@ -587,10 +578,10 @@ class VulnerabilityManage: - """ - code, stdout, stderr = execute_shell_command(f"dnf update {rpm_name} -y") - if code != CommandExitCode.SUCCEED: -- return FAIL, stderr -+ return TaskExecuteRes.FAIL, stderr - if "Complete" not in stdout: -- return FAIL, stdout -- return SUCCESS, stdout -+ return TaskExecuteRes.FAIL, stdout -+ return TaskExecuteRes.SUCCEED, stdout - - def _update_hotpatch_by_dnf_plugin(self, hotpatch_pkg: str) -> Tuple[str, str]: - """ -@@ -611,10 +602,10 @@ class VulnerabilityManage: - - code, stdout, stderr = execute_shell_command(update_command) - if code != CommandExitCode.SUCCEED: -- return FAIL, stderr -+ return TaskExecuteRes.FAIL, stderr - - if "Apply hot patch succeed" not in stdout and "No hot patches marked for install" not in stdout: -- return FAIL, stdout -+ return TaskExecuteRes.FAIL, stdout - - if not self.takeover and self.accepted: - try: -@@ -626,7 +617,7 @@ class VulnerabilityManage: - LOGGER.error(error) - stdout += "\n" + "hotpatch status set failed due to can't get correct hotpatch name!" - -- return SUCCESS, stdout -+ return TaskExecuteRes.SUCCEED, stdout - - @staticmethod - def _set_hotpatch_status_by_dnf_plugin(hotpatch: str, operation: str) -> Tuple[bool, str]: -@@ -703,25 +694,47 @@ class VulnerabilityManage: - log = "No valid hot patch is matched." - return NOT_PATCH, [dict(cve_id=cve["cve_id"], log=log, result="fail") for cve in cves] - -- cmd_execute_result = [] -- for base_pkg, hotpatch_cves in hotpatch_list.items(): -- rollback_result, log = self._hotpatch_rollback(base_pkg) -- result = 'succeed' if rollback_result else 'fail' -- cmd_execute_result.extend([dict(cve_id=cve, log=log, result=result) for cve in hotpatch_cves]) -+ wait_to_rollback_patch = [] -+ for cve_info in cves: -+ wait_to_rollback_patch.extend(hotpatch_list.get(cve_info["cve_id"], [])) - -- not_rollback_cve = set([cve["cve_id"] for cve in cves]).difference( -- set([cve_info["cve_id"] for cve_info in cmd_execute_result]) -- ) -+ hotpatch_rollback_res = {} -+ for patch in set(wait_to_rollback_patch): -+ rollback_result, log = self._hotpatch_rollback(patch) -+ hotpatch_rollback_res[patch] = { -+ "result": TaskExecuteRes.SUCCEED if rollback_result else TaskExecuteRes.FAIL, -+ "log": log, -+ } -+ -+ cve_rollback_result = [] -+ -+ for cve_info in cves: -+ if cve_info["cve_id"] not in hotpatch_list: -+ fail_result = { -+ "cve_id": cve_info["cve_id"], -+ "log": "No valid hot patch is matched." -+ if cve_info["hotpatch"] -+ else "Cold patch rollback is not supported.", -+ "result": "fail", -+ } -+ cve_rollback_result.append(fail_result) -+ else: -+ tmp_result_list = [] -+ tmp_log = [] - -- if not_rollback_cve: -- for cve in [cve for cve in cves if cve["cve_id"] in not_rollback_cve]: -- if cve["hotpatch"]: -- log = "No valid hot patch is matched." -- else: -- log = "Cold patch rollback is not supported." -- cmd_execute_result.append(dict(cve_id=cve["cve_id"], log=log, result="fail")) -+ for patch in hotpatch_list.get(cve_info["cve_id"]): -+ tmp_result_list.append(hotpatch_rollback_res[patch]["result"] == TaskExecuteRes.SUCCEED) -+ tmp_log.append(hotpatch_rollback_res[patch]["log"]) - -- return SUCCESS, cmd_execute_result -+ cve_rollback_result.append( -+ { -+ "cve_id": cve_info["cve_id"], -+ "log": "\n".join(tmp_log), -+ "result": TaskExecuteRes.SUCCEED if all(tmp_result_list) else TaskExecuteRes.FAIL, -+ } -+ ) -+ -+ return SUCCESS, cve_rollback_result - - @staticmethod - def _hotpatch_list_cve() -> dict: -@@ -731,15 +744,14 @@ class VulnerabilityManage: - Returns: - dict: e.g - { -- "base-pkg/hotpatch-1": [cve_id1,cveid2], -- "base-pkg/hotpatch-2": [cve_id1,cveid2] -+ "CVE-XXXX-XXX": ["patch 1", "patch 2"] - } - """ - # Run the dnf command to query the hotpatch list,e.g - # Last metadata expiration check: -- # CVE id base-pkg/hotpatch status -- # CVE-1 A-1.1-1/HP1 ACTIVED -- # CVE-2 A-1.1-1/HP1 ACTIVED -+ # CVE id base-pkg/hotpatch status -+ # CVE-1 A-1.1-1/ACC-1-1/binary_file1 ACTIVED -+ # CVE-2 A-1.1-1/ACC-1-1/binary_file2 ACTIVED - code, hotpatch_list_output, _ = execute_shell_command(f"dnf hotpatch --list cve") - if code != CommandExitCode.SUCCEED: - LOGGER.error(f"Failed to hotpatch list cve.") -@@ -754,7 +766,7 @@ class VulnerabilityManage: - cve_id, base_pkg, status = [info.strip() for info in hotpatch_info.split()] - if status != "ACTIVED" and status != "ACCEPTED": - continue -- hotpatch_list[base_pkg].append(cve_id) -+ hotpatch_list[cve_id].append(base_pkg) - - return hotpatch_list - -@@ -765,11 +777,7 @@ class VulnerabilityManage: - Args: - cve_id: cve is rolled back - """ -- execute_result, hotpatch_release_info = self._hotpatch_info(base_pkg_hotpatch) -- if not execute_result: -- return False, "Failed to query patch information." -- -- hotpatch_name = "patch-%s-%s-%s-%s" % tuple(base_pkg_hotpatch.split("/") + list(hotpatch_release_info.values())) -+ hotpatch_name = "patch-%s-%s" % tuple(base_pkg_hotpatch.rsplit("/", 2)[:2]) - - _, stdout, stderr = execute_shell_command(f"dnf remove {hotpatch_name} -y") - return True, stdout + stderr --- -Gitee - diff --git a/0004-update-field-about-cve-fix-return-value.patch b/0004-update-field-about-cve-fix-return-value.patch deleted file mode 100644 index 4eeb6f3..0000000 --- a/0004-update-field-about-cve-fix-return-value.patch +++ /dev/null @@ -1,25 +0,0 @@ -From 5a5304775690a2e05b138f5ba30dfbb3b00e9ca4 Mon Sep 17 00:00:00 2001 -From: rabbitali -Date: Tue, 5 Sep 2023 17:38:10 +0800 -Subject: [PATCH 1/1] update field about cve fix return value - ---- - ceres/manages/vulnerability_manage.py | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/ceres/manages/vulnerability_manage.py b/ceres/manages/vulnerability_manage.py -index 20bfe1e..540f051 100644 ---- a/ceres/manages/vulnerability_manage.py -+++ b/ceres/manages/vulnerability_manage.py -@@ -541,7 +541,7 @@ class VulnerabilityManage: - ) - - result.update({"cves": all_cve_fix_info}) -- return TaskExecuteRes.SUCCEED if all(all_cve_fix_result) else TaskExecuteRes.FAIL, result -+ return SUCCESS if all(all_cve_fix_result) else FAIL, result - - def _update_rpm_by_dnf(self, cve: dict) -> Tuple[str, str]: - """ --- -2.33.0 - diff --git a/aops-ceres-v1.3.0.tar.gz b/aops-ceres-v1.3.0.tar.gz deleted file mode 100644 index 8aa4781..0000000 Binary files a/aops-ceres-v1.3.0.tar.gz and /dev/null differ diff --git a/aops-ceres-v1.3.1.tar.gz b/aops-ceres-v1.3.1.tar.gz new file mode 100644 index 0000000..5c66c47 Binary files /dev/null and b/aops-ceres-v1.3.1.tar.gz differ diff --git a/aops-ceres.spec b/aops-ceres.spec index 679c15e..9a5e344 100644 --- a/aops-ceres.spec +++ b/aops-ceres.spec @@ -1,14 +1,12 @@ Name: aops-ceres -Version: v1.3.0 -Release: 5 +Version: v1.3.1 +Release: 3 Summary: An agent which needs to be adopted in client, it managers some plugins, such as gala-gopher(kpi collection), fluentd(log collection) and so on. License: MulanPSL2 URL: https://gitee.com/openeuler/%{name} Source0: %{name}-%{version}.tar.gz -Patch0001: 0001-fix-bug-repeated-display-of-vulnerabilities.patch -Patch0002: 0002-update-query-disk-info-func.patch -Patch0003: 0003-adapted-to-cve-rollback-and-update-return-value.patch -Patch0004: 0004-update-field-about-cve-fix-return-value.patch +Patch0001: 0001-update-func-named-set-hotpatch-status-by-dnf-plugin.patch +Patch0002: 0002-add-file-sync-func.patch BuildRequires: python3-setuptools Requires: python3-requests python3-jsonschema python3-libconf @@ -43,11 +41,15 @@ An agent which needs to be adopted in client, it managers some plugins, such as %changelog -* Tue Sep 05 2023 wenxin - v1.3.0-5 -- adapted to cve rollback and update return value +* Wed Sep 13 2023 wenxin - v1.3.1-3 +- add file sync func -* Tue Sep 05 2023 wenxin - v1.3.0-4 -- adapted to cve rollback and update return value +* Wed Sep 13 2023 wenxin - v1.3.1-2 +- update func named set_hotpatch_status_by_dnf_plugin + +* Mon Sep 11 2023 zhuyuncheng - v1.3.1-1 +- update rollback task logic, better returned log +- update status code and return None when installed_rpm or available_rpm is empty * Wed Aug 30 2023 wenxin - v1.3.0-3 - update query disk info func