diff --git a/cve-agency-manager/cve_tracking/.gitignore b/cve-agency-manager/cve_tracking/.gitignore index 11614af2870733183efe883810764d8708bddf8f..5f9908e1f652c7e6c28a3367d75a223a19cff326 100644 --- a/cve-agency-manager/cve_tracking/.gitignore +++ b/cve-agency-manager/cve_tracking/.gitignore @@ -8,6 +8,7 @@ __pycache__/ # Distribution / packaging .Python +.xlsx build/ develop-eggs/ dist/ @@ -95,6 +96,7 @@ venv/ ENV/ env.bak/ venv.bak/ +.vscode/ # Spyder project settings .spyderproject diff --git a/cve-agency-manager/cve_tracking/README.md b/cve-agency-manager/cve_tracking/README.md index 45d962b3a2b9634e06951249eae032abf782c621..adeaaf3a3fb0f8e987ce2dd28a63da729431a499 100644 --- a/cve-agency-manager/cve_tracking/README.md +++ b/cve-agency-manager/cve_tracking/README.md @@ -48,7 +48,7 @@ python 可执行代码 > > -i 需要评论的 issue 编号 > - > 注意:默认仓库为 src-openeuler,如果要更改,请修改 main.py 同目录下 constant.py 中的 DEFAULT_OWNER 的值。 + > 注意:请修改 main.py 同目录下 cve-tracking.yaml 中的 # 访问api的私人令牌 token 的值与 config.ini 中的 DEFAULT_OWNER 的值 2. 补丁查找及下载(验证) @@ -88,7 +88,32 @@ python 可执行代码 > > -nd 是否需要下载源代码,默认为需要下载,若无需下载添加该参数 -4. 意见反馈 +4. 查找下载验证及评论 + + ```shell + python3 main.py findp_adaptive_patch -c cve_num -r rpm_name -f patch_save_path -s source_path -p -b branch [-nd] -i issue_num + ``` + + + > 参数说明: + > + > -c cve 的编号 + > + > -r rpm 包名称 + > + > -f 补丁文件的下载目录,不设置默认为/opt/cve_tracking/patches + > + > -s 源码包下载路径,不设置默认为/opt/cve_tracking/source_code + > + > -b 源码包所在的 gitee 的 src-openeuler 仓库的分支,默认为 master + > + > -p 是否进行补丁应用,默认为不应用,若需要应用,添加该参数。 + > + > -i 需要评论的 issue 编号 + > + > 注意:请修改 main.py 同目录下 cve-tracking.yaml 中的 # 访问api的私人令牌 token 的值与 config.ini 中的 DEFAULT_OWNER FEEDBACK_ISSUE_OWNER FEEDBACK_ISSUE_REPO 的值 + +5. 意见反馈 若工具没有查找到补丁且人工查找到补丁信息,可通过该功能进行反馈,将会在配置的平台中提交 issue。将发现新 patch 的平台信息上报至指定的仓库,便于 CVE 修复人员完善工具的查找。指定的仓库可以在 conf.ini 配置文件的【FEEDBACK】中进行配置 diff --git a/cve-agency-manager/cve_tracking/cli/__init__.py b/cve-agency-manager/cve_tracking/cli/__init__.py index 41ef326fdecdf0c1e0a9812ed47acd15b8fd5d74..9242bb9b27e6b504256e953351b0264de3966bf9 100644 --- a/cve-agency-manager/cve_tracking/cli/__init__.py +++ b/cve-agency-manager/cve_tracking/cli/__init__.py @@ -15,9 +15,11 @@ from .comment import CommentCommand from .feedback import FeedbackCommand from .save import SaveCommand from .verify import VerifyCommand +from .findp_adaptive_patch import FindAdaptivePatch __all__ = ("CveTrackingCommand", "CommentCommand", "SaveCommand", "VerifyCommand", - "FeedbackCommand") + "FeedbackCommand", + "FindAdaptivePatch") diff --git a/cve-agency-manager/cve_tracking/cli/comment.py b/cve-agency-manager/cve_tracking/cli/comment.py index b45186377c580cd21e2b634f98cf5afd0776ca5e..5f9e22b43ca2c32183fd96f789851d3641fd4568 100644 --- a/cve-agency-manager/cve_tracking/cli/comment.py +++ b/cve-agency-manager/cve_tracking/cli/comment.py @@ -50,7 +50,8 @@ class CommentCommand(CveTrackingCommand): """ logger.info("Start to perform search and comment functions") if not all([args.cve_num, args.rpm_name, args.issue]): - raise InputError(msg="Comment command parameters: cve_num/rpm_name/issue") + raise InputError( + msg="Comment command parameters: cve_num/rpm_name/issue") # find patch detail patch_obj = Patch(cve_num=args.cve_num, rpm_name=args.rpm_name) @@ -59,5 +60,5 @@ class CommentCommand(CveTrackingCommand): # comment cve issue gitee = Gitee() gitee.set_attr(owner=CONFIG.DEFAULT_OWNER, repo=args.rpm_name) - await issue_comment(patch_details=patch_details, number=args.issue, gitee=gitee) + await issue_comment(patch_details=patch_details, number=args.issue, gitee=gitee, packing=False) logger.info("End to perform search and comment functions") diff --git a/cve-agency-manager/cve_tracking/cli/findp_adaptive_patch.py b/cve-agency-manager/cve_tracking/cli/findp_adaptive_patch.py new file mode 100644 index 0000000000000000000000000000000000000000..ccfa4dfdae995dfdd4e7807f68440d934921b311 --- /dev/null +++ b/cve-agency-manager/cve_tracking/cli/findp_adaptive_patch.py @@ -0,0 +1,106 @@ +#!/usr/bin/python3 +# ****************************************************************************** +# Copyright (c) Huawei Technologies Co., Ltd. 2021-2021. 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. +# ******************************************************************************/ +from cli.base import CveTrackingCommand +from conf import CONFIG +from core.crawler.patch import Patch +from core.download import save +from core.comment.issue_comment import issue_comment +from exception import InputError +from logger import logger +from util.gitee_api import Gitee + + +class FindAdaptivePatch(CveTrackingCommand): + """ + Execute the command line to find the patch and save the patch file + """ + + def __init__(self): + super(FindAdaptivePatch, self).__init__() + self.parser = CveTrackingCommand.sub_parse.add_parser('findp_adaptive_patch', + help="Search for patches, download patch files, verify and comment on patches") + self._add_common_param() + self._add_params() + + def _add_params(self): + """ + Add the unique parameters of the command + :return: None + """ + self.parser.add_argument('-f', '--patch_path', metavar='patch_patch', type=str, action='store', required=False, + default=CONFIG.PATCH_SAVE_PATH, + help='Path to save the patch file, the default is"/opt/cve_tracking/patches"') + self.parser.add_argument('-s', '--source_path', metavar='source_patch', type=str, action='store', + required=False, default=CONFIG.SOURCE_CODE_PATH, + help='Source download path, the default is: "/opt/cve_tracking/source_code"') + self.parser.add_argument('-p', '--packing', action='store_true', required=False, default=False, + help='Whether to package the verification patch') + self.parser.add_argument('-b', '--branch', metavar='branch', type=str, action='store', required=False, + default='master', help='Branch of rpm, default is "master"') + self.parser.add_argument( + "-i" + , + "--issue", + metavar="issue", + type=str, + required=True, + action="store", + default=None, + help="CVE related issue number", + ) + @staticmethod + async def run_command(args): + """ + Search patch, save file function execution entry + :param args: command line params + :return: None + """ + logger.info(f'Start to execute the download command, cve: {args.cve_num}, ' + f'rpm: {args.rpm_name}, patch save path: {args.patch_path}') + if not all([args.rpm_name, args.cve_num, args.patch_path]): + raise InputError('Download command parameter error, please confirm "rpm_name"/cve_num/patch_path"') + if args.packing and not all([args.source_path, args.branch]): + raise InputError('Download command parameter error, please confirm "source_path/branch"') + + # find patch info + patch = Patch(cve_num=args.cve_num, rpm_name=args.rpm_name) + patch_detail_info = await patch.find_patches_detail() + # download patch file + + #TODO 写函数优化 + patch_url_state = save.save_patch(patch_details=patch_detail_info, cve=args.cve_num, path=args.patch_path,args=args) + for patch in patch_detail_info: + for issue_info in patch.get("details", list()): + patch_url_state = _issue_info(issue_info,patch_url_state) + + + # comment cve issue + gitee = Gitee() + gitee.set_attr(owner=CONFIG.DEFAULT_OWNER, repo=args.rpm_name) + await issue_comment(patch_details=patch_detail_info, number=args.issue, gitee=gitee, packing=True) + logger.info("End to perform search and comment and download and packing functions") + +def _issue_info(issue_info,patch_url_state): + for prs in issue_info["issue"].get("prs", list()): + for url in prs.get("commits", []): + patch_url_state = _patch_url_state(prs,url,patch_url_state) + return patch_url_state + +def _patch_url_state(prs,url,patch_url_state): + for key, value in patch_url_state.items(): + if key == url: + if not prs.get("application"): + prs["application"] = patch_url_state[key] + else: + prs["application"] = prs["application"] + "
" + patch_url_state[key] + return patch_url_state diff --git a/cve-agency-manager/cve_tracking/cli/save.py b/cve-agency-manager/cve_tracking/cli/save.py index 2d9d5b8bc50c307705be1f1dfd661fc7d1ae5ad2..d27ba3cf6bb622b2bb8134aa2a5100f6c7b9fbca 100644 --- a/cve-agency-manager/cve_tracking/cli/save.py +++ b/cve-agency-manager/cve_tracking/cli/save.py @@ -14,7 +14,6 @@ from cli.base import CveTrackingCommand from conf import CONFIG from core.crawler.patch import Patch from core.download import save -from core.verification.apply import PathApply from exception import InputError from logger import logger @@ -45,7 +44,7 @@ class SaveCommand(CveTrackingCommand): self.parser.add_argument('-p', '--packing', action='store_true', required=False, default=False, help='Whether to package the verification patch') self.parser.add_argument('-b', '--branch', metavar='branch', type=str, action='store', required=False, - default='master', help='Branch of rpm, default is "master"') + default='master', help='Branch of rpm, default is "master"') @staticmethod async def run_command(args): @@ -64,12 +63,8 @@ class SaveCommand(CveTrackingCommand): # find patch info patch = Patch(cve_num=args.cve_num, rpm_name=args.rpm_name) patch_detail_info = await patch.find_patches_detail() - # download patch file - save.save_patch(patch_details=patch_detail_info, cve=args.cve_num, path=args.patch_path) - logger.info(f'End to execute the save file, patch save path:{args.patch_path}') - # apply patch - if args.packing: - patch_apply = PathApply(rpm_name=args.rpm_name, patch_path=args.patch_path, - source_path=args.source_path, branch_rpm=args.branch) - patch_apply.packing_source() + # download patch file and apply patch + patch_url_state = save.save_patch(patch_details=patch_detail_info, cve=args.cve_num, path=args.patch_path,args=args) + logger.info(f'End to execute the save file, patch save path:{args.patch_path}') + logger.info(f'Result is {patch_url_state}') diff --git a/cve-agency-manager/cve_tracking/cli/verify.py b/cve-agency-manager/cve_tracking/cli/verify.py index ace288d8adce701b7790a1ca0321c1e8e330995a..2bd20a80c7cdfc5235a080e7d1a1db734fdd070d 100644 --- a/cve-agency-manager/cve_tracking/cli/verify.py +++ b/cve-agency-manager/cve_tracking/cli/verify.py @@ -13,7 +13,7 @@ from cli.base import CveTrackingCommand from conf import CONFIG from core.verification.apply import PathApply - +from logger import logger class VerifyCommand(CveTrackingCommand): """ @@ -24,17 +24,17 @@ class VerifyCommand(CveTrackingCommand): super(VerifyCommand, self).__init__() self.parser = CveTrackingCommand.sub_parse.add_parser('packing', help='Test patch file application results') self._add_params() + self._add_common_param() + def _add_params(self): """ Add the unique parameters of the command :return: None """ - self.parser.add_argument('-r', '--rpm_name', metavar='rpm_name', type=str, required=True, default=None, - help='Source rpm name') self.parser.add_argument('-f', '--patch_path', metavar='patch_path', type=str, action='store', required=True, default=None, help='Path of patch file') - self.parser.add_argument('-s', '--source_path', metavar='source_path', type=str, action='store', required=False, + self.parser.add_argument('-s', '--source_path', metavar='source_path', type=str, action='store',required=False, default=CONFIG.SOURCE_CODE_PATH, help='Source download path, the default is: "/opt/cve_tracking/source_code"') self.parser.add_argument('-b', '--branch', metavar='branch', type=str, action='store', required=False, @@ -52,5 +52,7 @@ class VerifyCommand(CveTrackingCommand): """ branch = "no" if args.no_download else args.branch patch_apply = PathApply(rpm_name=args.rpm_name, branch_rpm=branch, patch_path=args.patch_path, - source_path=args.source_path) - patch_apply.packing_source() + source_path=args.source_path,cve_num=args.cve_num) + status = patch_apply.packing_source() + patch_apply.clear_trash() + logger.info("End of adaptation Process,resule is %s"%status) \ No newline at end of file diff --git a/cve-agency-manager/cve_tracking/conf/setting.py b/cve-agency-manager/cve_tracking/conf/setting.py index d9a5152c628cbccd4d8edc68081d1be7d070c5dc..9e3fd8e157c1f6557b786c262aa7b61820f0cbcd 100644 --- a/cve-agency-manager/cve_tracking/conf/setting.py +++ b/cve-agency-manager/cve_tracking/conf/setting.py @@ -25,7 +25,7 @@ class DefaultConfig: # Log related # ********** # level of LOG - LOGGER_LEVEL = "INFO" + LOGGER_LEVEL = "ERROR" # Log file path LOGGER_PATH = "/var/log/cve_tracking" # Log file name @@ -48,14 +48,14 @@ class DefaultConfig: # Feedback issue related configuration # ********** - FEEDBACK_PLATFORM = "github" + FEEDBACK_PLATFORM = "gitee" # Feedback to the owner of the issue - FEEDBACK_ISSUE_OWNER = "liheavy" + FEEDBACK_ISSUE_OWNER = "song-jintang" # Feedback to the repo of the issue - FEEDBACK_ISSUE_REPO = "cve_tracking" + FEEDBACK_ISSUE_REPO = "song-jintang" # The owner of the comment function submits the comment - DEFAULT_OWNER = "src-openeuler" + DEFAULT_OWNER = "song-jintang" class Config: @@ -163,8 +163,11 @@ class YamlConfiguration: else: regulars = filter(lambda x: x["label"] == label, self.regex) return [regx for reg in regulars for regx in reg["regular"]] - + def token(self, name): + """ + get and match gitee github gitlab token + """ token = filter(lambda x: x["name"] == name, self.authentication) if token: return list(token)[-1]["token"] diff --git a/cve-agency-manager/cve_tracking/config.ini b/cve-agency-manager/cve_tracking/config.ini index ccd7f5809e7a834c6673b7d29e00ee934f3ae169..e4638ea91ef21d20eb9ec9ad0326ae3d8d2628d7 100644 --- a/cve-agency-manager/cve_tracking/config.ini +++ b/cve-agency-manager/cve_tracking/config.ini @@ -13,6 +13,7 @@ ;Log related configuration [LOG] ;level of LOG +; LOGGER_LEVEL = CRITICAL LOGGER_LEVEL = INFO ; Log file path LOGGER_PATH = /var/log/cve_tracking @@ -33,7 +34,7 @@ SOURCE_CODE_PATH = /opt/cve_tracking/source_code ;Comment function related configuration [COMMENT] ;The owner of the comment function submits the comment -DEFAULT_OWNER = src-openeuler +DEFAULT_OWNER = song-jintang ;Feedback issue related configuration [FEEDBACK] @@ -41,6 +42,6 @@ DEFAULT_OWNER = src-openeuler ;Please configure the token of the corresponding platform synchronously in cve-tracking.yaml FEEDBACK_PLATFORM = gitee ;Feedback to the owner of the issue -FEEDBACK_ISSUE_OWNER = liheavy +FEEDBACK_ISSUE_OWNER = song-jintang ;Feedback to the repo of the issue -FEEDBACK_ISSUE_REPO = cve_tracking \ No newline at end of file +FEEDBACK_ISSUE_REPO = kernel \ No newline at end of file diff --git a/cve-agency-manager/cve_tracking/core/comment/issue_comment.py b/cve-agency-manager/cve_tracking/core/comment/issue_comment.py index f5fdfbc467ee0b4b6b6e5302fa4496d305f35430..1137aac91ad163f516c3a59d7566d3a15dc07f80 100644 --- a/cve-agency-manager/cve_tracking/core/comment/issue_comment.py +++ b/cve-agency-manager/cve_tracking/core/comment/issue_comment.py @@ -36,7 +36,7 @@ class Table: def _rowspan(num, platform): return """{}""".format(str(num), platform) - def span_content(self, pr_url, pr_status, commits): + def span_content(self, packing, pr_url, pr_status, commits, application): """ Splicing comment content :param pr_url: pull request url @@ -45,15 +45,21 @@ class Table: :return: content spliced """ hyperlink_commits = [f"{commit}" for commit in commits] - contents = self._td(pr_url) + self._td(pr_status) + self._td("
".join(hyperlink_commits)) + if packing: + contents = self._td(pr_url) + self._td(pr_status) + self._td("
".join(hyperlink_commits))+self._td(application) + else: + contents = self._td(pr_url) + self._td(pr_status) + self._td("
".join(hyperlink_commits)) return contents - def _table_name(self): + def _table_name(self,packing): """ Splicing header :return: header """ - table_names = ["参考网址", "关联pr", "状态", "补丁链接"] + if packing: + table_names = ["参考网址", "关联pr", "状态", "补丁链接","验证结果    "] + else: + table_names = ["参考网址", "关联pr", "状态", "补丁链接"] table_names = " ".join(map(self._th, table_names)) return self._tr(table_names) @@ -64,7 +70,8 @@ class Table: number += len(issues["issue"].get("prs", list())) return number - def table(self, lst): + def table(self, lst,packing): + """ Splicing table :param lst: cve info @@ -82,13 +89,14 @@ class Table: if not details: none_tr = self._tr(self._td(platform) + self._td("") * 3) contents += none_tr - for issue_info in details: for pr in issue_info["issue"].get("prs", list()): content = self.span_content( + packing, pr["url"], pr["status"], pr.get("commits", []), + pr.get("application") ) if platform_index == 0: rowspan_content = rowspan_content + content @@ -97,11 +105,10 @@ class Table: else: span_content = self._tr(content) contents += span_content - - return self._table(self._table_name() + contents) + return self._table(self._table_name(packing) + contents) -async def issue_comment(patch_details, number, gitee: Gitee): +async def issue_comment(patch_details, number, gitee: Gitee,packing): """ Call gitee's api to add issue comments :param patch_details: patch detail @@ -109,7 +116,7 @@ async def issue_comment(patch_details, number, gitee: Gitee): :param gitee: gitee instance :return: None """ - table = Table().table(patch_details) + table = Table().table(patch_details,packing) instruction = "\n" * 2 + "> 说明:抱歉,当前工具暂未找到推荐补丁,请人工查找或者之后评论'/find-patch'尝试再次查找。\n" \ "若人工查找到补丁,烦请在此issue下评论 '/report-patch 参考网址 补丁链接1,补丁链接2' 便于我们不断优化工具,不胜感激。\n" \ "如 /report-patch https://security-tracker.debian.org/tracker/CVE-2021-3997 https://github.com/systemd/systemd/commit/5b1cf7a9be37e20133c0208005274ce4a5b5c6a1" diff --git a/cve-agency-manager/cve_tracking/core/crawler/patch.py b/cve-agency-manager/cve_tracking/core/crawler/patch.py index 97c44ffc27da8c19f73360112a68e3b685eb96c3..ef1c4384d0f4a04183268b77065bc547cf0050d7 100644 --- a/cve-agency-manager/cve_tracking/core/crawler/patch.py +++ b/cve-agency-manager/cve_tracking/core/crawler/patch.py @@ -13,7 +13,6 @@ import re import asyncio from collections import namedtuple - from constant import Constant from core.platform import CvePlatform from logger import logger @@ -36,8 +35,12 @@ class Patch: self.pr_status_dict = dict() self.github_api = Github() self.gitlab_api = Gitlab() - self._SummaryInfo = namedtuple("SummaryInfo", ["pr_list", "issues_list"]) + self._SummaryInfo = namedtuple( + "SummaryInfo", ["pr_list", "issues_list"]) self._PatchDetail = namedtuple("PatchDetail", ["platform", "details"]) + self.zero_num = 0 + self.platform_num = 2 + self.platform_re_num = 2 @property def issue_relation_info(self): @@ -47,44 +50,75 @@ class Patch: def pr_relation_info(self): return {"url": None, "status": None, "commits": []} + def get_detail(self, detail, detail_num): + """ + String splicing + """ + if detail["details"] != [] and detail["details"][self.zero_num]["issue"]["prs"] != [] and detail["details"][self.zero_num]["issue"]["prs"][self.zero_num]["commits"] != []: + commit_num = 0 + for commit in detail["details"][self.zero_num]["issue"]["prs"][self.zero_num]["commits"]: + self.patch_detail_list[detail_num]["details"][self.zero_num]["issue"]["prs"][self.zero_num]["commits"][commit_num] = settings.get_platform()[ + self.platform_num]["redirect"][self.platform_re_num]["prefix"] + commit + commit_num += 1 + return detail, detail_num + async def find_patches_detail(self): """ Further analyze the obtained patch information to get the final patch link :return: self.patch_detail_list """ - await self._find_patches_info() - # commit,pr,issue summary structure,convenient for deduplication and concurrent search - summary_info = self._SummaryInfo(pr_list=list(), issues_list=list()) - for patch_info in self.patch_info_list: - summary_info.pr_list.extend(patch_info.get("pr", [])) - summary_info.issues_list.extend(patch_info.get("issue", [])) - - # Find issue linked pr and add it to the pr list obtained earlier - if summary_info.issues_list: - issue_task_list = await self._create_async_task( - self._get_issue_link_pr, summary_info.issues_list - ) - issue_done_task, _ = await asyncio.wait(issue_task_list) - for task in issue_done_task: - summary_info.pr_list.extend(task.result()) - - if summary_info.pr_list: - # Query pr status - pr_status_task_list = await self._create_async_task( - self._get_pr_status, summary_info.pr_list - ) - await asyncio.wait(pr_status_task_list) - # Find pr contain commits - pr_task_list = await self._create_async_task( - self._get_pr_contain_commits, summary_info.pr_list + specialrpms = settings.specialrpm + sp_name_list = [sp_detail.get("name") for sp_detail in specialrpms] + if self.rpm_name not in sp_name_list: + await self._find_patches_info() + # commit,pr,issue summary structure,convenient for deduplication and concurrent search + summary_info = self._SummaryInfo( + pr_list=list(), issues_list=list()) + for patch_info in self.patch_info_list: + summary_info.pr_list.extend(patch_info.get("pr", [])) + summary_info.issues_list.extend(patch_info.get("issue", [])) + + # Find issue linked pr and add it to the pr list obtained earlier + if summary_info.issues_list: + issue_task_list = await self._create_async_task( + self._get_issue_link_pr, summary_info.issues_list + ) + issue_done_task, _ = await asyncio.wait(issue_task_list) + for task in issue_done_task: + summary_info.pr_list.extend(task.result()) + + if summary_info.pr_list: + # Query pr status + pr_status_task_list = await self._create_async_task( + self._get_pr_status, summary_info.pr_list + ) + await asyncio.wait(pr_status_task_list) + # Find pr contain commits + pr_task_list = await self._create_async_task( + self._get_pr_contain_commits, summary_info.pr_list + ) + await asyncio.wait(pr_task_list) + # Generate the final information that needs to be output + self._convert_path_detail() + logger.info( + f'Find the patch information of cve "{self.cve_num}" as: \n{self.patch_detail_list}' ) - await asyncio.wait(pr_task_list) - - # Generate the final information that needs to be output - self._convert_path_detail() - logger.info( - f'Find the patch information of cve "{self.cve_num}" as: \n{self.patch_detail_list}' - ) + # Adding a Special Platform + else: + for sp_name in specialrpms: + if self.rpm_name == sp_name.get("name") and sp_name.get("request") is False: + # patch_detail_list.append() + sp_name["patch_url"] = sp_name.get( + "patch_url").format(self.cve_num) + self.patch_detail_list.append({'platform': '{}'.format(sp_name["url"]), 'details': [{'issue': { + 'url': None, 'prs': [{'url': None, 'status': None, 'commits': ['{}'.format(sp_name["patch_url"])]}]}}]}) + + detail_num = 0 + # 修改ghostscript,path链接 + if self.rpm_name == "ghostscript": + for detail in self.patch_detail_list: + detail, detail_num = self.get_detail(detail, detail_num) + detail_num += 1 return self.patch_detail_list async def _find_patches_info(self): @@ -136,19 +170,22 @@ class Patch: elif Constant.GITLAB in issue: try: issue_info = issue.split("/") - self.gitlab_api.set_attr(owner=issue_info[-5], repo=issue_info[-4]) + self.gitlab_api.set_attr( + owner=issue_info[-5], repo=issue_info[-4]) self._set_gitlab_host(issue) issue_num = issue_info[-1] response = await self.gitlab_api.issue_relevance_pull( issue_id=issue_num ) - pr_list = self._convert_api_response(response, url_key="web_url") + pr_list = self._convert_api_response( + response, url_key="web_url") except IndexError: logger.warning( f"The issue link: {issue} does not conform to the standard format" ) else: - logger.warning(f"Issue {issue} failed to match the relevant code platform") + logger.warning( + f"Issue {issue} failed to match the relevant code platform") # Record the relationship between issue and pr to facilitate subsequent patch information processing logger.info(f"According to issue {issue} get pr: {str(pr_list)}") @@ -196,7 +233,8 @@ class Patch: ) commits_list = self._convert_api_response(response, "web_url") - logger.info(f"According to pull {pr} get commits: {str(commits_list)}") + logger.info( + f"According to pull {pr} get commits: {str(commits_list)}") self.issue_pr_dict[pr] = commits_list @@ -214,7 +252,8 @@ class Patch: elif Constant.GITLAB in pr: self.gitlab_api.set_attr(owner=pr_info[-5], repo=pr_info[-4]) else: - logger.warning(f"Pr {pr} failed to match the relevant code platform") + logger.warning( + f"Pr {pr} failed to match the relevant code platform") return None except IndexError: logger.warning( @@ -245,7 +284,8 @@ class Patch: :param issue_pr: issue or pull :return: None """ - _host_match = re.search(pattern=Constant.GITLAB_HOST_REGEX, string=issue_pr) + _host_match = re.search( + pattern=Constant.GITLAB_HOST_REGEX, string=issue_pr) _host = _host_match.group() self.gitlab_api.host = _host @@ -294,4 +334,4 @@ class Patch: patch_detail.details.append(issue_relation_info) - self.patch_detail_list.append(dict(patch_detail._asdict())) + self.patch_detail_list.append(dict(patch_detail._asdict())) \ No newline at end of file diff --git a/cve-agency-manager/cve_tracking/core/download/save.py b/cve-agency-manager/cve_tracking/core/download/save.py index 329b4cec08fd874f34895101fe9ab342ec0b7e10..5ef1f813e334807bb24232b52ec37cb20e7ac43a 100644 --- a/cve-agency-manager/cve_tracking/core/download/save.py +++ b/cve-agency-manager/cve_tracking/core/download/save.py @@ -15,7 +15,7 @@ import re from conf import CONFIG, settings from logger import logger from request import http - +from core.verification.apply import PathApply def _file(cve, file): """ @@ -28,7 +28,7 @@ def _file(cve, file): os.makedirs(file, exist_ok=True) file_name = ( "fix-cve-" - + str(sum([os.path.isfile(list_index) for list_index in os.listdir(file)]) + 1) + + str(sum([os.path.isfile(file+"/"+list_index) for list_index in os.listdir(file)]) + 1) + ".patch" ) return os.path.join(file, file_name) @@ -50,7 +50,7 @@ def _patch_url(url): return url + target_regex[-1] -def save_patch(patch_details, cve, path=CONFIG.PATCH_SAVE_PATH): +def save_patch(patch_details, cve, path=CONFIG.PATCH_SAVE_PATH,args=None): """ Download and save the patch file :param patch_details: patch info @@ -63,15 +63,32 @@ def save_patch(patch_details, cve, path=CONFIG.PATCH_SAVE_PATH): for issue_info in patch.get("details", list()): for pr in issue_info["issue"].get("prs", list()): commits.update(pr.get("commits", [])) - - # download patch - for url in commits: - out_file = _file(cve=cve, file=path) - patch_url = _patch_url(url=url) or url - file = http.download(url=patch_url, out_fname=out_file) - if not file: - logger.error("Failed to download the file: %s" % patch_url) - + if not args.packing: + # download patch + for url in commits: + out_file = _file(cve=cve, file=path) + patch_url = _patch_url(url=url) or url + file = http.download(url=patch_url, out_fname=out_file) + if not file: + logger.error("Failed to download the file: %s" % patch_url) + return None + else: + patch_url_state={} + for url in commits: + patch_url_state[url] = "" + for url in commits: + out_file = _file(cve=cve, file=path) + patch_url = _patch_url(url=url) or url + file = http.download(url=patch_url, out_fname=out_file) + if not file: + logger.error("Failed to download the file: %s" % patch_url) + patch_url_state[url] = "不适配" + patch_apply = PathApply(rpm_name=args.rpm_name, patch_path=args.patch_path, + source_path=args.source_path, branch_rpm=args.branch,cve_num=args.cve_num) + stat = patch_apply.packing_source() + patch_apply.clear_trash() + patch_url_state[url] = stat + return patch_url_state __all__ = ("save_patch",) diff --git a/cve-agency-manager/cve_tracking/core/feedback/issue_mode.py b/cve-agency-manager/cve_tracking/core/feedback/issue_mode.py index ec2250d065d5082eb85ba97e0f8e9299f450a669..5932369acd7f60bdf56c2c9f5437057f0f04e520 100644 --- a/cve-agency-manager/cve_tracking/core/feedback/issue_mode.py +++ b/cve-agency-manager/cve_tracking/core/feedback/issue_mode.py @@ -17,6 +17,8 @@ from util.github_api import Github from util.gitlab_api import Gitlab + + class IssueMode: """ Feedback by creating an issue diff --git a/cve-agency-manager/cve_tracking/core/platform/cve_platform.py b/cve-agency-manager/cve_tracking/core/platform/cve_platform.py index a057edf0c56d225d74f38b5097b62c02b0064571..fe516576f68961e2433055855d759dcc9ccb7816 100644 --- a/cve-agency-manager/cve_tracking/core/platform/cve_platform.py +++ b/cve-agency-manager/cve_tracking/core/platform/cve_platform.py @@ -17,8 +17,6 @@ from collections import namedtuple from functools import wraps from bs4 import BeautifulSoup - -from constant import Constant from exception import RequestError from logger import logger from request import http @@ -68,7 +66,7 @@ class CvePlatform: self.cve_num = cve_num self.base_url = base_url self._format = format_text - self._Patch = namedtuple("Patch", ["platform", "commits", "pr", "issue"]) + self._patch = namedtuple("Patch", ["platform", "commits", "pr", "issue"]) @property def crawler_url(self): @@ -84,16 +82,16 @@ class CvePlatform: Structure of cve patch information :return: instance of _Patch """ - return self._Patch(platform=self.crawler_url, commits=[], pr=[], issue=[]) + return self._patch(platform=self.crawler_url, commits=[], pr=[], issue=[]) - async def _rule_redirect(self, response): + async def _rule_redirect(self, _response_text): """ Page multi layer jump data parsing :param response: http response data :return: response data """ for redirect_rule in self._platform.get("redirect", []): - format_text = self.format_text(response.text) + format_text = self.format_text(_response_text) target_val = list( set( re.findall( @@ -108,7 +106,6 @@ class CvePlatform: response = await self._method(redirect_rule)( url, data=redirect_rule.get("body") ) - return response @staticmethod @@ -125,26 +122,29 @@ class CvePlatform: _response = await self._method(self._platform)( self.crawler_url, data=self._platform.get("body") ) + _response_text = _response.text if "redirect" in self._platform: - _response = await self._rule_redirect(response=_response) + redirect_response = await self._rule_redirect(_response_text=_response_text) + if not redirect_response.text: + redirect_response.text = "" + _response_text += redirect_response.text except RequestError: return None - - if _response.error or not _response.text: + if _response.error or not _response_text: logger.error( f"Failed to access URL {self.crawler_url}, detail: {_response.error}" ) return None - + formatted_text = ( - self.format_text(_response.text) + self.format_text(_response_text) if self._format == "text" - else self.json(_response.text) + else self.json(_response_text) ) if formatted_text is None: - logger.error(f"Failed to format string {_response.text}") + logger.error(f"Failed to format string {_response_text}") return None - + patch_info_dict = self.match_patch( formatted_text, dict(self.patch_info._asdict()) ) @@ -162,12 +162,14 @@ class CvePlatform: :return: None """ # Matching url de-duplication + for key, value in patch_info.items(): if isinstance(value, list): patch_info[key] = list(set(value)) - logger.info(f"Find patch: {patch_info}") + return patch_info + # def specialrpms_match_patch(): @staticmethod def format_text(text): @@ -190,9 +192,9 @@ class CvePlatform: try: text_dict = json.loads(text) return json.dumps(text_dict, indent=4) - except JSONDecodeError as e: + except JSONDecodeError as errors: logger.error( f"The format of the content obtained by bugzilla website is incorrect, " - f"content is {text}, message is {e.msg}" + f"content is {text}, message is {errors.msg}" ) return None diff --git a/cve-agency-manager/cve_tracking/core/verification/apply.py b/cve-agency-manager/cve_tracking/core/verification/apply.py index 6cf5b533c147d9a2893c33f686542492f8a09376..58e5e4d31da88f98b1cf86fa60aa676e5fb7ed9e 100644 --- a/cve-agency-manager/cve_tracking/core/verification/apply.py +++ b/cve-agency-manager/cve_tracking/core/verification/apply.py @@ -11,7 +11,6 @@ # See the Mulan PSL v2 for more details. # ******************************************************************************/ import os.path -import stat import subprocess from exception import ConfigNotFoundError, PathNotExistError, PathEmptyError @@ -24,13 +23,12 @@ class PathApply: """ Patch verification class """ - - def __init__(self, rpm_name, patch_path, source_path, branch_rpm): + def __init__(self, rpm_name, patch_path, source_path, branch_rpm,cve_num): self.rpm_name = rpm_name self.patch_path = patch_path self.source_path = source_path self.branch_rpm = branch_rpm - + self.cve_num = cve_num def packing_source(self): """ Package verification implementation class @@ -43,15 +41,23 @@ class PathApply: run_shell = os.path.join(CURRENT_PATH, 'run.sh') try: output = subprocess.check_output( - [run_shell, self.rpm_name, self.branch_rpm, self.patch_path, self.source_path], + [run_shell, self.rpm_name, self.branch_rpm, self.patch_path, self.source_path,self.cve_num], stderr=subprocess.STDOUT, shell=False) apply_result = output.decode('utf-8') logger.info(f'Patch verification process:\n{apply_result}') - logger.info(f'Apply patch successfully, log is {self.source_path}/{self.rpm_name}/result.log') - except subprocess.CalledProcessError as e: - logger.info(f'Patch verification process:\n{e.output.decode("utf-8")}') - logger.error(f'Apply patch failed, log is {self.source_path}/{self.rpm_name}/result.log') + return "完全适配" + except subprocess.CalledProcessError as grepexc: + apply_result = grepexc.output.decode('utf-8') + if "200" in apply_result: + logger.info(f'Patch verification process:\n{apply_result}') + return "完全适配" + elif "202" in apply_result: + logger.info(f'Patch verification process:\n{apply_result}') + return "不完全适配" + else: + logger.info(f'Patch verification process:\n{apply_result}') + return "不适配" def _validate_path(self): """ @@ -84,3 +90,14 @@ class PathApply: for file in os.listdir(CURRENT_PATH): if str(file).endswith('.sh'): os.chmod(os.path.join(CURRENT_PATH, file), 0o0755) + + def clear_trash(self): + """ + clear patch_path,rpmbuild/SPEC and rpmbuild/SOURCE + """ + run_shell = os.path.join(CURRENT_PATH, 'trash.sh') + try: + subprocess.check_output([run_shell,self.patch_path],stderr=subprocess.STDOUT,shell=False) + logger.info(f'Clear successfully !') + except subprocess.CalledProcessError as errors: + logger.error('Clear failed %s!!'%errors) \ No newline at end of file diff --git a/cve-agency-manager/cve_tracking/core/verification/common.sh b/cve-agency-manager/cve_tracking/core/verification/common.sh old mode 100644 new mode 100755 diff --git a/cve-agency-manager/cve_tracking/core/verification/packing.sh b/cve-agency-manager/cve_tracking/core/verification/packing.sh old mode 100644 new mode 100755 index 8ab7db464c3137f4c8dafd12c1bc3ddbdf2ed956..75312a3e80d890e424f9aa9cce2280ec08cc2007 --- a/cve-agency-manager/cve_tracking/core/verification/packing.sh +++ b/cve-agency-manager/cve_tracking/core/verification/packing.sh @@ -78,6 +78,7 @@ function update_spec() { echo "[INFO] Start to update spec file" cd ${source_path} spec_file=$(ls | grep *.spec | grep -v *.bak | grep -v *.update) + spec_f = $(pwd) if [[ -z ${spec_file} ]]; then echo "[ERROR] spec file is not found" exit 1 diff --git a/cve-agency-manager/cve_tracking/core/verification/run.sh b/cve-agency-manager/cve_tracking/core/verification/run.sh old mode 100644 new mode 100755 index 9b9f073edda7b76eed86b0f1e610949001b6ed0f..c7e9e594b2abf179e846dba6b4f809c48ee317ea --- a/cve-agency-manager/cve_tracking/core/verification/run.sh +++ b/cve-agency-manager/cve_tracking/core/verification/run.sh @@ -1,63 +1,236 @@ #!/bin/bash # shellcheck disable=SC2068 # shellcheck disable=SC2164 -# shellcheck disable=SC2002 -# shellcheck disable=SC2045 -# shellcheck disable=SC2115 +# shellcheck disable=SC2010 +# shellcheck disable=SC2063 +# shellcheck disable=SC2062 +# shellcheck disable=SC2035 rpm_name=$1 -remote_branch=$2 -patch_path=$3 +rpm_branch=$2 +patches_path=$3 source_path=$4 +cve_num=$5 +path_file_name="" +install_retry_count=0 +# The rpm package that needs to be installed when compiling +requires_rpms="" +spec_file="" +home_path=$( + cd ~ + pwd -P +) +root_build_path=${home_path}/"rpmbuild" current_path=$( cd "$(dirname "$0")" pwd ) . ${current_path}/common.sh -function check() { - # Check input legitimacy - if [[ -z ${rpm_name} ]] || [[ -z ${remote_branch} ]]; then - echo "[ERROR] rpm name or gitee branch not specified" +function check_patch() { + if [[ -z ${patches_path} ]]; then + echo "400[ERROR] Input patch is null" + exit 1 + fi + for patch in ${patches_path[@]}/$cve_num; do + if [[ ! -d ${patch} ]]; then + echo "[ERROR] ${patch} is not found" + exit 1 + fi + done + if [ "`ls -A ${patches_path}/$cve_num`" = "" ];then + echo "400补丁未下载" + exit 1 + fi + for patch in `ls ${patches_path[@]}/$cve_num`; do + if [[ ${patch} == fix*.tmp ]]; then + echo "400补丁下载错误" + rm -f $patches_path/$cve_num/$patch + exit 1 + fi + done + +} + +function check_local() { + if [[ ! -d ${source_path} ]]; then + echo "400[ERROR] The source package path ${source_path} does not exist" exit 1 fi - # Check if the path exists - if [[ ! -d ${patch_path} ]] || [[ ! -d ${source_path} ]]; then - echo "[ERROR] The patch file directory: ${patch_path} or the source code download directory: ${source_path} does not exist is not exist" +} + +function check_remote() { + if [[ -z ${rpm_name} ]] || [[ -z ${rpm_branch} ]]; then + echo "400[ERROR] Incorrect input,Please use [ /bin/bash add_patch.sh 'name of package' 'branch of package' 'path of patch file']" exit 1 fi } -function run() { - patch_files=$(ls ${patch_path}) - patch_ids="" - new_patches="" - for patch_file in ${patch_files[@]}; do - patch_file=${patch_path}/${patch_file} - git_patch_ids=$(cat "${patch_file}" | git patch-id --stable) - if [[ ! ${patch_ids} =~ ${git_patch_ids} ]]; then - new_patches="${new_patches} ${patch_file}" - patch_ids="${patch_ids} ${git_patch_ids}" +function pre_env_build() { + echo "[INFO] Create root path of rpmbuild" + if [[ ! -d ${root_build_path} ]]; then + mkdir -p ${root_build_path}/{BUILD,BUILDROOT,RPMS,SPECS,SOURCES,SRPMS} + fi +} + +function git_clone() { + echo "[INFO] Start to git clone ${rpm_name}" + cd ${source_path} + git clone -b ${rpm_branch} https://gitee.com/src-openeuler/${rpm_name}.git >/dev/null 2>&1 + rpm_path=$(ls | grep ${rpm_name}) + if [[ -z ${rpm_path} ]]; then + echo "400[ERROR] Access https://gitee.com/src-openeuler/${rpm_name}.git clone ${rpm_name} failed, please check path ${source_path}" + exit 1 + fi + + source_path=${source_path}/${rpm_name} +} + +function update_spec() { + echo "[INFO] Start to update spec file" + cd ${root_build_path}/SOURCES + spec_file=$(ls | grep *.spec | grep -v *.bak | grep -v *.update) + if [[ -z ${spec_file} ]]; then + echo "400[ERROR] spec file is not found" + exit 1 + fi + # backup spec + /bin/cp -rf ${spec_file} ${spec_file}.bak + # update Release + release_version=$(grep "Release:" ${spec_file} | awk -F " " '{print $NF}' | tr -cd "[0-9]") + new_release=$(expr ${release_version} + 1) + sed -i "s/Release:.*${release_version}/Release: ${new_release}/" ${spec_file} + # add Patch*** + last_patch=$(grep "Patch.*:" ${spec_file} | sed -n '$p') + if [[ -z ${last_patch} ]]; then + source_row=$(grep -n "Source.*:" ${spec_file} | sed -n '$p' | awk -F ':' '{print $1}') + sed -i -e "${source_row}G;${source_row}a Patch0000: ${path_file_name}" ${spec_file} + else + last_patch_row=$(grep -n "${last_patch}" ${spec_file} | awk -F ':' '{print $1}') + last_patch_num=$(echo ${last_patch} | awk -F ':' '{print $1}' | awk -F 'Patch' '{print $2}') + patch_name_len=${#last_patch_num} + new_patch_num=$(expr ${last_patch_num} + 1) + new_patch_num=$(printf "%0${patch_name_len}d" ${new_patch_num}) + sed -i "${last_patch_row}a Patch${new_patch_num}: ${path_file_name}" ${spec_file} + fi + # add %patch + last_patch_apply=$(grep "%patch.* " ${spec_file} | sed -n '$p') + if [[ -n ${last_patch_apply} ]]; then + last_patch_apply_row=$(grep -n "${last_patch_apply}" ${spec_file} | awk -F ':' '{print $1}') + last_patch_apply_num=$(echo ${last_patch_apply} | awk -F ' ' '{print $1}' | awk -F 'patch' '{print $2}') + ignore_level_num=$(echo ${last_patch_apply} | awk -F ' ' '{print $2}') + new_patch_apply_num=$(expr ${last_patch_apply_num} + 1) + sed -i "${last_patch_apply_row}a %patch${new_patch_apply_num} ${ignore_level_num} " ${spec_file} + fi + # add changelog + change_log_row=$(grep -n '%changelog' ${spec_file} | sed -n '$p' | awk -F ':' '{print $1}') + date_now=$(env LANG=en_US.UTF-8 date "+%a %b %d %Y") + version=$(grep 'Version:' ${spec_file} | awk -F ' ' '{print $NF}') + log_description="- add ${path_file_name}" + log_title="* ${date_now} robot - ${version}-${new_release}" + sed -i "${change_log_row}G" ${spec_file} + sed -i "${change_log_row}a ${log_description}" ${spec_file} + sed -i "${change_log_row}a ${log_title}" ${spec_file} + /bin/cp -rf ${root_build_path}/SOURCES/*.spec ${root_build_path}/SPECS + echo "[INFO] Update spec file success" +} + +function mv_source_file() { + echo "[INFO] Copy source file to ${root_build_path}" + cd ${source_path} + spec_file=$(ls | grep *.spec | grep -v *.bak | grep -v *.update) + /bin/cp -rf ${patches_path}/${cve_num}/* ${root_build_path}/SOURCES + /bin/cp -rf * ${root_build_path}/SOURCES +} + +function rpm_build() { + echo "[INFO] Start to rpmbuild" + install_rpm rpm-build rpm + apt-get -y build-dep ${rpm_name} >/dev/null 2>&1 + dnf builddep -y ${root_build_path}/SPECS/${spec_file} >/dev/null 2>&1 + if [[ $? -eq 0 ]]; then + echo "[INFO] build dependencies success !!!" + fi + rpmbuild -bp ${root_build_path}/SPECS/${spec_file} >./result.log 2>&1 + if [[ $? -eq 0 ]]; then + log_path="${root_build_path}/SOURCES/result.log" + fix_load=$(awk '(/fix-cve/){print $0}' $log_path) + if [[ -n ${fix_load} ]];then + echo "[INFO] build success !!!" + exit 0 else - echo "[INFO] ${patch_file} duplicate content" + echo "400补丁未能正确加载,请检查spec文件" + exit 1 fi - done - # Perform code download, modify spec file, package verification operation - /bin/bash ${current_path}/packing.sh "${rpm_name}" "${remote_branch}" "${new_patches}" "${source_path}" - add_patch_result=$? - if [[ ${add_patch_result} -eq 0 ]]; then - echo "[INFO] rpm: \"${rpm_name}\" of branch: \"${remote_branch}\" apply patch \"${new_patches}\" successfully" - exit 0 + elif [[ -n $(grep "Failed build dependencies" ./result.log) ]] && [[ ${install_retry_count} -le 2 ]]; then + requires_rpms=$(grep -r "is needed by" ./result.log | awk -F " " '{print $1}') + echo "${requires_rpms}" >./requires_rpms.log + for rpm in ${requires_rpms[@]}; do + install_rpm ${rpm} ${rpm} + done + install_retry_count=$(expr ${install_retry_count} + 1) + rpm_build else - echo "[ERROR] rpm: \"${rpm_name}\" of branch: \"${remote_branch}\" failed to apply patch \"${new_patches}\"" - exit 1 + # mv -f ${source_path}/${spec_file} ${source_path}/${spec_file}.update + echo "[ERROR] build failed,log is ${root_build_path}/SOURCES/result.log" + # exit 1 fi } -function main() { - # Executive total entrance - check - install_rpm git git - run +function get_error_msq(){ + echo "[INFO] Start to get_error_msq" + log_path="${root_build_path}/SOURCES/result.log" + error_msg=$(awk '(/^[0-9]{1,}/){print $0}' $log_path) + insert_num=$(awk '(/^[0-9]{1,}/){print $1}' $log_path) + local_num=$(awk '(/^[0-9]{1,}/){print $4}' $log_path) + fix_load=$(awk '(/fix-cve/){print $0}' $log_path) + download_right=$(awk '(/Only garbage was found in the patch input/){print $0}' $log_path) + if [[ -n ${fix_load} ]];then + : + if [[ -z ${download_right} ]];then + : + if [[ -n ${error_msg} ]]; then + : + if [[ "$insert_num" == "$local_num" ]];then + echo "400版本不受影响或补丁错误" + else + if [[ ${#insert_num} == 1 ]];then + echo "202补丁部分可用" + else + echo "400版本不受影响或补丁错误" + fi + fi + fi + else + echo "400补丁未能正确加载,请检查补丁内容是否正确下载" + fi + else + echo "400补丁未能正确加载,请检查spec文件中" + fi + exit 1 } -main +function main() { + check_patch + if [[ ${rpm_branch} == "no" ]]; then + check_local + pre_env_build + else + check_remote + pre_env_build + git_clone + fi + mv_source_file + if [[ -n `ls $patches_path/$cve_num` ]]; then + for patch in `ls ${patches_path[@]}/$cve_num`; do + if [[ ${patch} == fix-cve-*.patch ]]; then + path_file_name=$(echo "${patch}" | awk -F "/" '{print $NF}' | awk -F "\\" '{print $NF}') + update_spec + fi + done + rpm_build + get_error_msq + else + echo "400补丁未能下载,请尝试手动下载" + fi +} +main \ No newline at end of file diff --git a/cve-agency-manager/cve_tracking/core/verification/trash.sh b/cve-agency-manager/cve_tracking/core/verification/trash.sh new file mode 100755 index 0000000000000000000000000000000000000000..306ed0c2aeb6345bfa87e9aa111ffe965359cfec --- /dev/null +++ b/cve-agency-manager/cve_tracking/core/verification/trash.sh @@ -0,0 +1,16 @@ +#!/bin/bash +# shellcheck disable=SC2068 +# shellcheck disable=SC2164 +# shellcheck disable=SC2010 +# shellcheck disable=SC2063 +# shellcheck disable=SC2062 +# shellcheck disable=SC2035 +patch_path=$1 +home_path=$( + cd ~ + pwd -P +) +root_build_path=${home_path}/"rpmbuild" +rm $root_build_path/SOURCES/* -rf +rm $root_build_path/SPECS/* -rf +rm $patch_path/* -r \ No newline at end of file diff --git a/cve-agency-manager/cve_tracking/cve-tracking.yaml b/cve-agency-manager/cve_tracking/cve-tracking.yaml index 6e2873990201c4c190e3de1ec29ab7ba0f0b5660..45798377f67c4a586a5a0a40233af3fa961e6319 100644 --- a/cve-agency-manager/cve_tracking/cve-tracking.yaml +++ b/cve-agency-manager/cve_tracking/cve-tracking.yaml @@ -1,4 +1,7 @@ # Regular expression to match cve patch info +# rpm列表 +# platform: +# - ghostscript # 匹配cve补丁信息的正则表达式 regex: # A tag that matches the type of content, currently only "commit" "pr" "issue" is supported @@ -7,15 +10,26 @@ regex: # Regular expressions that match specific types of content, you can set multiple matching rules # 匹配特定类型内容的正则表达式,可以设置多个匹配规则 regular: - - http[s]?://(?:[-\w.\/;?])+(?:/rev|/ci|/commit[s]?)/(?:\?id=)?[0-9a-z]{8,40} - - http[s]?://(?:[-\w.\/;=&?])+a=commit(?:[\w;&?])+h=[0-9a-z]{8,40} + - http[s]?://(?:[-\w.\/;?\~?])+(?:/rev|/ci|/commit[s]?|/linus|/qtbase/\+|/c|/revision)/(?:\?id=)?[0-9a-z-]{1,40} + - /\?p=ghostpdl.git;a=commitdiff;h=[a-z0-9]{40};?(?:hp=[0-9a-z;=]{40})? + - http[s]?://[\w.\/\+]+patch_to_fix_[0-9]{0,8} - label: pr regular: - http[s]?://(?:[-\w.\/;?])+(?:pull[s]?|merge_requests)/[1-9][0-9]* - label: issue regular: - - http[s]?://(?:[-\w.\/;?])+issues/[0-9A-Z]+ + - http[s]?://(?:[-\w.\/;?])+issue[s]?/[0-9A-Z]+ +specialrpm: + - name: strongswan + url: https://download.strongswan.org/security + patch_url: https://download.strongswan.org/security/{}/ + request: false + - name: log4j12 + url: https://sources.debian.org/src/apache-log4j1.2 + patch_url: https://sources.debian.org/src/apache-log4j1.2/1.2.17-11/debian/patches/{}.patch/ + request: false + # Platforms to find bug fixes for cve, such as Debian, Bugzilla, Nvd, etc. # 查找cve漏洞修复的平台,例如 Debian、Bugzilla、Nvd等 platform: @@ -24,7 +38,7 @@ platform: - name: Cnnvd # The request address for finding cve details, "{cve num}" is a placeholder for string replacement in python (required) # 查找cve详情信息的请求地址,“{cve_num}”是python中字符串替换的占位符(必配项) - url: http://cnnvd.org.cn/web/vulnerability/queryLds.tag?qcvCnnvdid={cve_num} + url: http://www.cnnvd.org.cn/web/vulnerability/queryLds.tag?qcvCnnvdid={cve_num} # The method of sending the request, the default is get request, and it can also be specified as post request. # When the request method is get, it can not be configured here. # 发送请求的方式,默认为get请求,还可以指定为post请求,当请求方式为get时,此处可不配置 @@ -43,7 +57,7 @@ platform: # The prefix of the jump address, generally the domain name of the jump page, if the jump address matched by # the regular expression has a complete domain name + path, this configuration item can be left blank # 跳转地址的前缀,一般情况下为跳转页面的域名,如果正则表达式匹配到的跳转地址中有完整的域名+路径,此配置项可以不填写 - - prefix: http://cnnvd.org.cn + - prefix: http://www.cnnvd.org.cn # The regular expression that matches the page jump address, if it matches multiple values, get the last match # 匹配页面跳转地址的正则表达式,如果匹配多个值,则获取最后一个匹配项 regex: /web/xxk/ldxqById\.tag\?CNNVD=CNNVD[0-9-]+ @@ -54,11 +68,30 @@ platform: # If the request method is post and there is a request body, the body is required and must be in json format # 若请求方式为post且存在请求体时,body为必填项,且必须为json格式 body: - + - name: opencve + url: https://www.opencve.io/cve/{cve_num} + format: text + - name: Debian url: https://security-tracker.debian.org/tracker/{cve_num} + method: get + # body: format: text - + redirect: + - prefix: https://bugs.launchpad.net + regex: /mailman/\+bug/[0-9]{0,8} + method: get + body: + - prefix: https://community.otrs.com + regex: /security-advisory-[0-9]{4}-[0-9]{2} + method: get + body: + - prefix: https://git.ghostscript.com + regex: /\?p=ghostpdl.git;a=(?:commit|commitdiff);h=[a-z0-9]{40};?(?:hp=[0-9a-z;=]{40})? + method: get + body: + + - name: Ubuntn url: https://ubuntu.com/security/{cve_num} format: text @@ -75,6 +108,10 @@ platform: url: https://bugzilla.suse.com/show_bug.cgi?id={cve_num} format: text + - name: Redhat + url: https://bugzilla.redhat.com/show_bug.cgi?id={cve_num} + format: text + # Private token for api access, currently only supports "gitee" "github" "gitlab" # api访问时的私人令牌,当前只支持"gitee"/"github"/"gitlab" authentication: @@ -83,9 +120,9 @@ authentication: - name: gitee # A private token for accessing the api, which can be manually obtained and changed later # 访问api的私人令牌,可以手动获取后更改 - token: + token: 2be60f57f6eaef9efbac94f342685b59 - name: github - token: + token: 9c2290c567bd16064219cbbc0f61c8dc - name: gitlab token: glpat-24DGxkuTA8nTR1YQsXvZ diff --git "a/cve-agency-manager/cve_tracking/doc/images/\345\274\200\345\217\221\350\247\206\345\233\276.png" "b/cve-agency-manager/cve_tracking/doc/images/\345\274\200\345\217\221\350\247\206\345\233\276.png" index a1da01a4592a933bece141ed77d370be3b86eaa4..fa3ce2cdea002ca03ce3ad418cf98bacbb756511 100644 Binary files "a/cve-agency-manager/cve_tracking/doc/images/\345\274\200\345\217\221\350\247\206\345\233\276.png" and "b/cve-agency-manager/cve_tracking/doc/images/\345\274\200\345\217\221\350\247\206\345\233\276.png" differ diff --git "a/cve-agency-manager/cve_tracking/doc/images/\346\211\251\345\261\225\350\256\276\350\256\241.png" "b/cve-agency-manager/cve_tracking/doc/images/\346\211\251\345\261\225\350\256\276\350\256\241.png" index 57922ae14b4161e6be7d2cb2b8b5a833f4f73b4d..a3227baaa958cab69241265db28a2e3f821ec441 100644 Binary files "a/cve-agency-manager/cve_tracking/doc/images/\346\211\251\345\261\225\350\256\276\350\256\241.png" and "b/cve-agency-manager/cve_tracking/doc/images/\346\211\251\345\261\225\350\256\276\350\256\241.png" differ diff --git a/cve-agency-manager/cve_tracking/main.py b/cve-agency-manager/cve_tracking/main.py index 88cf3239db7b6e967da680881a13c0a6218835cc..8eb4003d77d9011a532309f7cf120cd69807e987 100644 --- a/cve-agency-manager/cve_tracking/main.py +++ b/cve-agency-manager/cve_tracking/main.py @@ -1,33 +1,32 @@ -#!/usr/bin/python3 -# ****************************************************************************** -# Copyright (c) Huawei Technologies Co., Ltd. 2021-2021. 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. -# ******************************************************************************/ -""" -Tool execution total entrance -""" -import asyncio - -from cli.base import CveTrackingCommand -from conf import CONFIG -from exception import CveException -from logger import logger - -try: - # Register all subcommands - for sub_class in CveTrackingCommand.__subclasses__(): - CveTrackingCommand.register_command(sub_class()) - - # Parallel execution of corresponding functions - loop = asyncio.get_event_loop() - loop.run_until_complete(CveTrackingCommand.run()) -except CveException as e: - logger.error( - f'Execution failed, message is : {e.message}, detail in {CONFIG.LOGGER_PATH}/{CONFIG.LOGGER_FILE_NAME}') +#!/usr/bin/python3 +# ****************************************************************************** +# Copyright (c) Huawei Technologies Co., Ltd. 2021-2021. 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. +# ******************************************************************************/ +""" +Tool execution total entrance +""" +import asyncio +from cli.base import CveTrackingCommand +from conf import CONFIG +from exception import CveException +from logger import logger + +try: + # Register all subcommands + for sub_class in CveTrackingCommand.__subclasses__(): + CveTrackingCommand.register_command(sub_class()) + + # Parallel execution of corresponding functions + loop = asyncio.get_event_loop() + loop.run_until_complete(CveTrackingCommand.run()) +except CveException as errors: + logger.error( + f'Execution failed, message is : {errors.message}, detail in {CONFIG.LOGGER_PATH}/{CONFIG.LOGGER_FILE_NAME}') \ No newline at end of file diff --git a/cve-agency-manager/cve_tracking/request.py b/cve-agency-manager/cve_tracking/request.py index b2859f473cc90ed60456d3495f95ef8da3b6bbe1..4e16b5578f77546ba131638a3e9baa6823abf3e9 100644 --- a/cve-agency-manager/cve_tracking/request.py +++ b/cve-agency-manager/cve_tracking/request.py @@ -11,20 +11,21 @@ # See the Mulan PSL v2 for more details. # ******************************************************************************/ import json -from asyncio import TimeoutError from http.client import RemoteDisconnected from urllib.error import HTTPError, URLError - +from asyncio import TimeoutError import aiohttp import wget from fake_useragent import UserAgent from retrying import retry - from constant import Constant from exception import RequestError class Response: + """ + Process the Response + """ def __init__(self, error=None, response: aiohttp.ClientResponse = None, text=None) -> None: self.error = error self.response = response @@ -32,14 +33,23 @@ class Response: @property def success(self): + """ + return successful response and status + """ return self.response and (self.response.status == 200 or self.response.status == 201) @property def status_code(self): + """ + return reponse and status + """ return self.response and self.response.status @property def json(self): + """ + return json + """ if not self.response: return None try: @@ -70,6 +80,9 @@ class Http: @property def headers(self): + """ + constitute headers + """ headers = { "User-Agent": self.user_agent.random } @@ -84,6 +97,9 @@ class Http: "Content-Type", "application/json") async def _get(self, url, params=None, **kwargs): + """ + get Methods using + """ self._set_headers(request_header=kwargs) @retry(stop_max_attempt_number=self._max_attempt_number, stop_max_delay=self._stop_max_delay) @@ -94,6 +110,9 @@ class Http: @classmethod async def get(cls, url, **kwargs) -> "Response": + """ + call _get + """ async with cls() as self: await self._get(url, **kwargs) return self._response @@ -104,12 +123,14 @@ class Http: self._response.text = await self._response.response.text() except aiohttp.ClientError as error: self._response.error = error - except TimeoutError as e: - raise RequestError(f'Call url timeout: {e}') + except TimeoutError as error: + raise RequestError(f'Call url timeout: {error}') async def _post(self, url, data=None, **kwargs): + """ + post Methods using + """ # self._set_headers(request_header=kwargs) - @retry(stop_max_attempt_number=self._max_attempt_number, stop_max_delay=self._stop_max_delay) async def _http_request(): return await self.session.post(url, data=data, timeout=Constant.CALL_MAX_DELAY, **kwargs) @@ -118,6 +139,9 @@ class Http: @classmethod async def post(cls, url, data=None, **kwargs) -> "Response": + """ + call _post + """ async with cls() as self: await self._post(url, data, **kwargs) return self._response @@ -127,19 +151,25 @@ class Http: def getfile(): file_name = wget.download(url, out_fname) if not file_name: - raise + raise return file_name return getfile() @staticmethod def download(url, out_fname, **kwargs): + """ + patch url downlad + """ max_attempt_number = kwargs.get("max_attempt_number", Constant.MAX_RETRY) stop_max_delay = kwargs.get("stop_max_delay", Constant.MAX_DOWNLOAD_DELAY) @retry(stop_max_attempt_number=max_attempt_number, stop_max_delay=stop_max_delay) def _dw(): - file_name = wget.download(url, out_fname) + try: + file_name = wget.download(url, out_fname) + except Exception as error: + return None if not file_name: raise RequestError(f'Call url:"{url}" failed') return file_name diff --git a/cve-agency-manager/cve_tracking/util/__init__.py b/cve-agency-manager/cve_tracking/util/__init__.py index 5a7ebcdc275716a52600da9e60838866db1f34a9..064ef9bbef4a868c6d1f2ccca6216fa092ff8d75 100644 --- a/cve-agency-manager/cve_tracking/util/__init__.py +++ b/cve-agency-manager/cve_tracking/util/__init__.py @@ -33,8 +33,8 @@ class Api: """ try: response = await http.post(url=url, data=values, **kwargs) - except RequestError as e: - logger.error(f'Call url {url} failed, message: {e.message}') + except RequestError as errors: + logger.error(f'Call url {url} failed, message: {errors.message}') return None if not response.success: @@ -55,8 +55,8 @@ class Api: """ try: response = await http.get(url=url, params=params, **kwargs) - except RequestError as e: - logger.error(f'Call url {url} failed, message: {e.message}') + except RequestError as errors: + logger.error(f'Call url {url} failed, message: {errors.message}') return None if not response.success: