diff --git a/cve-agency-manager/cve_tracking/.gitignore b/cve-agency-manager/cve_tracking/.gitignore
index 2027f52cd9ebda8e294616082ebbf3cec258f56a..11614af2870733183efe883810764d8708bddf8f 100644
--- a/cve-agency-manager/cve_tracking/.gitignore
+++ b/cve-agency-manager/cve_tracking/.gitignore
@@ -1,4 +1,115 @@
-.DS_Store
-*/.DS_Store
-*.pyc
-*.vscode
\ No newline at end of file
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
+
+# C extensions
+*.so
+
+# Distribution / packaging
+.Python
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+wheels/
+share/python-wheels/
+*.egg-info/
+.installed.cfg
+*.egg
+MANIFEST
+
+# PyInstaller
+# Usually these files are written by a python script from a template
+# before PyInstaller builds the exe, so as to inject date/other infos into it.
+*.manifest
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.nox/
+.coverage
+.coverage.*
+.cache
+nosetests.xml
+coverage.xml
+*.cover
+.hypothesis/
+.pytest_cache/
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+*.log
+local_settings.py
+db.sqlite3
+
+# Flask stuff:
+instance/
+.webassets-cache
+
+# Scrapy stuff:
+.scrapy
+
+# Sphinx documentation
+docs/_build/
+
+# PyBuilder
+target/
+
+# Jupyter Notebook
+.ipynb_checkpoints
+
+# IPython
+profile_default/
+ipython_config.py
+
+# pyenv
+.python-version
+
+# celery beat schedule file
+celerybeat-schedule
+
+# SageMath parsed files
+*.sage.py
+
+# Environments
+.env
+.venv
+env/
+venv/
+ENV/
+env.bak/
+venv.bak/
+
+# Spyder project settings
+.spyderproject
+.spyproject
+
+# Rope project settings
+.ropeproject
+
+# mkdocs documentation
+/site
+
+# mypy
+.mypy_cache/
+.dmypy.json
+dmypy.json
+
+# Pyre type checker
+.pyre/
diff --git a/cve-agency-manager/cve_tracking/README.md b/cve-agency-manager/cve_tracking/README.md
index c9903c9370c6a3cee156489fbde6856ce83904a1..45d962b3a2b9634e06951249eae032abf782c621 100644
--- a/cve-agency-manager/cve_tracking/README.md
+++ b/cve-agency-manager/cve_tracking/README.md
@@ -1,23 +1,110 @@
# cve_tracking
#### 介绍
-cve补丁自动获取工具
+
+cve 补丁自动获取工具,该工具将会根据 cve 和 rpm 包自动在上游社区查找补丁并反馈结果,同时也可以下载查找到的补丁以及验证补丁的可用性。
#### 软件架构
-软件架构说明
+python 可执行代码
#### 安装教程
-1. xxxx
-2. xxxx
-3. xxxx
+1. 下载代码
+
+ ```shell
+ git clone https://gitee.com/openeuler/cve-manager.git
+ ```
+
+2. 进入工具执行目录
+
+ ```shell
+ cd xxx(上述代码下载目录)/cve-manager/cve-agency-manager/cve_tracking
+ ```
+
+3. 在 cve-tracking.yaml 的 authentication 中设置 gitee(gitee 的私人令牌)和 gitlab(gitlab 的私人令牌,默认设置了一个临时令牌,可临时使用),github(github 的私人令牌)可不设置。
+
+4. 安装依赖包
+
+ ```
+ pip3 install -r requirements.txt
+ ```
+
+5. 根据使用说明执行工具
#### 使用说明
-1. xxxx
-2. xxxx
-3. xxxx
+1. 补丁查找及评论 issue
+
+ ```shell
+ python3 main.py comment -c cve_num -r rpm_name -i issue_num
+ ```
+
+ > 参数说明:
+ >
+ > -c cve 的编号
+ >
+ > -r rpm 包名称
+ >
+ > -i 需要评论的 issue 编号
+ >
+ > 注意:默认仓库为 src-openeuler,如果要更改,请修改 main.py 同目录下 constant.py 中的 DEFAULT_OWNER 的值。
+
+2. 补丁查找及下载(验证)
+
+ ```
+ python3 main.py download -c cve_num -r rpm_name [-f patch_save_path] [-s source_path] [-p] [-b branch]
+ ```
+
+ > 参数说明:
+ >
+ > -c cve 的编号
+ >
+ > -r rpm 包名称
+ >
+ > -f 补丁文件的下载目录,不设置默认为/opt/cve_tracking/patches
+ >
+ > -s 源码包下载路径,不设置默认为/opt/cve_tracking/source_code
+ >
+ > -b 源码包所在的 gitee 的 src-openeuler 仓库的分支,默认为 master
+ >
+ > -p 是否进行补丁应用,默认为不应用,若需要应用,添加该参数。
+
+3. 补丁验证
+
+ ```
+ python3 main.py packing -r rpm_name -f patch_save_path -s source_path -b branch [-nd]
+ ```
+
+ > 参数说明:
+ >
+ > -r rpm 包名称
+ >
+ > -f 补丁文件路径(指定到补丁所在的文件夹即可)
+ >
+ > -s 源码包路径,如果无需下载指定为本地源码包的路径;如果需要下载指定为需要下载源码包的路径即可
+ >
+ > -b 源码包所在 gitee 中 src-openeuler 仓库的分支,不设置默认为 master
+ >
+ > -nd 是否需要下载源代码,默认为需要下载,若无需下载添加该参数
+
+4. 意见反馈
+
+ 若工具没有查找到补丁且人工查找到补丁信息,可通过该功能进行反馈,将会在配置的平台中提交 issue。将发现新 patch 的平台信息上报至指定的仓库,便于 CVE 修复人员完善工具的查找。指定的仓库可以在 conf.ini 配置文件的【FEEDBACK】中进行配置
+
+ ```shell
+ python3 main.py feedback -c cve_num -p cve_platform -u patch_url -i issue_warehouse
+ ```
+
+ > 参数说明:
+ >
+ > -c CVE 的编号
+ >
+ > -p 查找 CVE 所在的平台,可按照平台名称简写
+ >
+ > -u 修复 CVE 的补丁链接
+ >
+ > -i 管理 issue 的仓库,只能是(gitee、gitlab、github)中任选其一
#### 参与贡献
@@ -26,10 +113,9 @@ cve补丁自动获取工具
3. 提交代码
4. 新建 Pull Request
-
#### 特技
-1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md
+1. 使用 Readme_XXX.md 来支持不同的语言,例如 Readme_en.md, Readme_zh.md
2. Gitee 官方博客 [blog.gitee.com](https://blog.gitee.com)
3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解 Gitee 上的优秀开源项目
4. [GVP](https://gitee.com/gvp) 全称是 Gitee 最有价值开源项目,是综合评定出的优秀开源项目
diff --git a/cve-agency-manager/cve_tracking/cli/__init__.py b/cve-agency-manager/cve_tracking/cli/__init__.py
index cefefd513232a95053d521436dcea1949b32d5f5..41ef326fdecdf0c1e0a9812ed47acd15b8fd5d74 100644
--- a/cve-agency-manager/cve_tracking/cli/__init__.py
+++ b/cve-agency-manager/cve_tracking/cli/__init__.py
@@ -12,10 +12,12 @@
# ******************************************************************************/
from .base import CveTrackingCommand
from .comment import CommentCommand
+from .feedback import FeedbackCommand
from .save import SaveCommand
from .verify import VerifyCommand
__all__ = ("CveTrackingCommand",
"CommentCommand",
"SaveCommand",
- "VerifyCommand")
+ "VerifyCommand",
+ "FeedbackCommand")
diff --git a/cve-agency-manager/cve_tracking/cli/comment.py b/cve-agency-manager/cve_tracking/cli/comment.py
index b254b79296f0924e8e21b667eb5989e5222a330f..b45186377c580cd21e2b634f98cf5afd0776ca5e 100644
--- a/cve-agency-manager/cve_tracking/cli/comment.py
+++ b/cve-agency-manager/cve_tracking/cli/comment.py
@@ -11,7 +11,7 @@
# See the Mulan PSL v2 for more details.
# ******************************************************************************/
from cli.base import CveTrackingCommand
-from constant import Constant
+from conf import CONFIG
from core.comment.issue_comment import issue_comment
from core.crawler.patch import Patch
from exception import InputError
@@ -26,10 +26,20 @@ class CommentCommand(CveTrackingCommand):
def __init__(self):
super(CommentCommand, self).__init__()
- self.parser = CveTrackingCommand.sub_parse.add_parser('comment', help='Add a comment to the issue on gitee')
+ self.parser = CveTrackingCommand.sub_parse.add_parser(
+ "comment", help="Add a comment to the issue on gitee"
+ )
self._add_common_param()
- self.parser.add_argument('-i', '--issue', metavar='issue', type=str, required=True, action='store',
- default=None, help='CVE related issue number')
+ 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):
@@ -38,9 +48,9 @@ class CommentCommand(CveTrackingCommand):
:param args: command line params
:return: None
"""
- logger.info('Start to perform search and comment functions')
+ 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)
@@ -48,6 +58,6 @@ class CommentCommand(CveTrackingCommand):
# comment cve issue
gitee = Gitee()
- gitee.set_attr(owner=Constant.DEFAULT_OWNER, repo=args.rpm_name)
+ gitee.set_attr(owner=CONFIG.DEFAULT_OWNER, repo=args.rpm_name)
await issue_comment(patch_details=patch_details, number=args.issue, gitee=gitee)
- logger.info('End to perform search and comment functions')
+ logger.info("End to perform search and comment functions")
diff --git a/cve-agency-manager/cve_tracking/cli/feedback.py b/cve-agency-manager/cve_tracking/cli/feedback.py
new file mode 100644
index 0000000000000000000000000000000000000000..1bd42ff5dd67946ef5c1269a6aa712fe9c458652
--- /dev/null
+++ b/cve-agency-manager/cve_tracking/cli/feedback.py
@@ -0,0 +1,112 @@
+#!/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 import CveTrackingCommand
+from conf import CONFIG
+from constant import Constant
+from core.feedback.issue_mode import IssueMode
+from exception import InputError
+from logger import logger
+
+
+class FeedbackCommand(CveTrackingCommand):
+ """
+ Cve tool feedback function command line
+ """
+
+ def __init__(self):
+ super(FeedbackCommand, self).__init__()
+ self.parser = CveTrackingCommand.sub_parse.add_parser(
+ name="feedback",
+ help="Submit an issue to the designated warehouse, and the feedback tool will find the result",
+ )
+ self._add_params()
+
+ def _add_params(self):
+ """
+ Add input parameters required for feedback function
+ :return: None
+ """
+ self.parser.add_argument(
+ "-c",
+ "--cve_num",
+ metavar="cve",
+ type=str,
+ required=True,
+ action="store",
+ default=None,
+ help="cve number",
+ )
+ self.parser.add_argument(
+ "-p",
+ "--cve_platform",
+ type=str,
+ metavar="cve_platform",
+ required=True,
+ action="store",
+ default=None,
+ help="Correct cve reference URL",
+ )
+ self.parser.add_argument(
+ "-u",
+ "--patch_url",
+ type=str,
+ metavar="patch_url",
+ required=True,
+ action="store",
+ default=None,
+ help="Correct patch url",
+ )
+ self.parser.add_argument(
+ "-i",
+ "--issue_platform",
+ type=str,
+ metavar="issue_platform",
+ required=False,
+ action="store",
+ choices=Constant.CODE_PLATFORM,
+ default=CONFIG.FEEDBACK_PLATFORM,
+ help="Issue submission platform, default is gitee",
+ )
+
+ @staticmethod
+ async def run_command(args):
+ """
+ Feedback function execution entrance
+ :param args: input params
+ :return: None
+ """
+ logger.info(
+ f"Start add issue about cve:{args.cve_num} on platform:{args.issue_platform}"
+ )
+ if not all(
+ [args.cve_num, args.cve_platform, args.patch_url, args.issue_platform]
+ ):
+ raise InputError(msg="Feedback command params, please check and try again.")
+
+ issue_mode = IssueMode(
+ cve_num=args.cve_num,
+ cve_platform=args.cve_platform,
+ patch_url=args.patch_url,
+ issue_platform=args.issue_platform,
+ )
+ result = await issue_mode.create_feedback_issue()
+
+ if result:
+ logger.info(
+ f"Success to add issue about cve:{args.cve_num} on platform:{args.issue_platform}"
+ )
+ else:
+ logger.error(
+ f"Failed to add issue about cve:{args.cve_num} on platform:{args.issue_platform}, "
+ f"content is cve_platform:{args.cve_platform}, patch_url:{args.patch_url}"
+ )
diff --git a/cve-agency-manager/cve_tracking/conf/__init__.py b/cve-agency-manager/cve_tracking/conf/__init__.py
index 221fcf9d05ce88ef5aa55bbd0a2723eb4e4eb98f..683c506a89390bb5cd51fb2ad98d1e29fde7a4f3 100644
--- a/cve-agency-manager/cve_tracking/conf/__init__.py
+++ b/cve-agency-manager/cve_tracking/conf/__init__.py
@@ -10,61 +10,10 @@
# PURPOSE.
# See the Mulan PSL v2 for more details.
# ******************************************************************************/
-import configparser
-import os
-
-from conf.setting import DefaultConfig
-from exception import ConfigNotFoundError
-
-CONFIG_FILE = os.path.join(os.path.dirname(
- os.path.abspath(os.path.dirname(__file__))), 'config.ini')
-
-
-class Config:
- """
- Configuration item load and get value
- """
-
- def __init__(self):
- self._setattr_default()
- self._setattr_customize()
-
- def _setattr_default(self):
- """
- Set default configuration items and their values
- :return: self
- """
- for attr in dir(DefaultConfig):
- if not str(attr).startswith('_'):
- setattr(self, attr, getattr(DefaultConfig, attr))
-
- def _setattr_customize(self):
- """
- Set customize configuration items and their values
- :return: self
- """
- if not os.path.exists(CONFIG_FILE):
- raise ConfigNotFoundError(CONFIG_FILE)
-
- conf_parser = configparser.ConfigParser()
- conf_parser.read(CONFIG_FILE)
- for section in conf_parser.sections():
- for key_value in conf_parser.items(section):
- if key_value[1]:
- setattr(self, key_value[0].upper(), key_value[1])
-
- def __getattr__(self, item):
- """
- When the configuration cannot be found, reload the configuration file
- :param item: config key
- :return: value
- """
- self._setattr_default()
- self._setattr_customize()
- try:
- return self.__dict__[item]
- except KeyError:
- return None
+from .setting import Config, YamlConfiguration
CONFIG = Config()
+settings = YamlConfiguration()
+
+__all__ = ("CONFIG", "settings")
diff --git a/cve-agency-manager/cve_tracking/conf/setting.py b/cve-agency-manager/cve_tracking/conf/setting.py
index 233b05432fe0a2c99d4016e8ff2a0b4007e437a7..dcb5660551dfacda8a65567357945a5525906484 100644
--- a/cve-agency-manager/cve_tracking/conf/setting.py
+++ b/cve-agency-manager/cve_tracking/conf/setting.py
@@ -10,6 +10,11 @@
# PURPOSE.
# See the Mulan PSL v2 for more details.
# ******************************************************************************/
+import configparser
+import os
+import yaml
+from exception import ConfigNotFoundError
+
class DefaultConfig:
"""
@@ -38,3 +43,118 @@ class DefaultConfig:
# Source code package and patch file storage path,
# used to download gitee source code package and package verification.
SOURCE_CODE_PATH = "/opt/cve_tracking/source_code"
+
+ # **********
+ # Feedback issue related configuration
+ # **********
+
+ FEEDBACK_PLATFORM = "github"
+ # Feedback to the owner of the issue
+ FEEDBACK_ISSUE_OWNER = "liheavy"
+ # Feedback to the repo of the issue
+ FEEDBACK_ISSUE_REPO = "cve_tracking"
+
+ DEFAULT_OWNER = "src-openeuler"
+
+
+class Config:
+ """
+ Configuration item load and get value
+ """
+
+ config_file = os.path.join(
+ os.path.dirname(os.path.abspath(os.path.dirname(__file__))), "config.ini"
+ )
+
+ def __init__(self):
+ self._setattr_default()
+ self._setattr_customize()
+
+ def _setattr_default(self):
+ """
+ Set default configuration items and their values
+ :return: self
+ """
+ for attr in dir(DefaultConfig):
+ if not str(attr).startswith("_"):
+ setattr(self, attr, getattr(DefaultConfig, attr))
+
+ def _setattr_customize(self):
+ """
+ Set customize configuration items and their values
+ :return: self
+ """
+ if not os.path.exists(self.config_file):
+ raise ConfigNotFoundError(self.config_file)
+
+ conf_parser = configparser.ConfigParser()
+ conf_parser.read(self.config_file)
+ for section in conf_parser.sections():
+ for key_value in conf_parser.items(section):
+ if key_value[1]:
+ setattr(self, key_value[0].upper(), key_value[1])
+
+ def __getattr__(self, item):
+ """
+ When the configuration cannot be found, reload the configuration file
+ :param item: config key
+ :return: value
+ """
+ self._setattr_default()
+ self._setattr_customize()
+ try:
+ return self.__dict__[item]
+ except KeyError:
+ return None
+
+
+class YamlConfiguration:
+ yaml = os.path.join(
+ os.path.dirname(os.path.abspath(os.path.dirname(__file__))), "cve-tracking.yaml"
+ )
+
+ def __init__(self) -> None:
+ if not os.path.exists(self.yaml):
+ raise FileNotFoundError("file not found: %s" % self.yaml)
+ self.__dict__.update(self._parse_yaml())
+
+ def _parse_yaml(self):
+ with open(self.yaml, "r", encoding="utf-8") as file:
+ try:
+ configs = yaml.load(file.read(), Loader=yaml.FullLoader)
+ except yaml.YAMLError as error:
+ raise ValueError(
+ "The format of the yaml configuration "
+ "file is wrong please check and try again:{0}".format(error)
+ )
+ else:
+ return configs
+
+ def __getattr__(self, name):
+ if name not in self.__dict__:
+ return None
+
+ return self.__dict__[name]
+
+ def get_platform(self, name=None):
+ if name is None:
+ return self.platform
+ platform = filter(lambda x: x["name"] == name, self.platform)
+ if platform:
+ return list(platform)[-1]
+
+ def get_regex(self, label=None):
+ if label is None:
+ regulars = [reg for reg in self.regex]
+ 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):
+ token = filter(lambda x: x["name"] == name, self.authentication)
+ if token:
+ return list(token)[-1]["token"]
+
+ @property
+ def configuration(self):
+ return self.__dict__
diff --git a/cve-agency-manager/cve_tracking/config.ini b/cve-agency-manager/cve_tracking/config.ini
index 9bdd816e6253a662b6c06d4eb17a93b00a55797d..f4b3f755aa24715e620fce02b75ba1533f1d7b76 100644
--- a/cve-agency-manager/cve_tracking/config.ini
+++ b/cve-agency-manager/cve_tracking/config.ini
@@ -23,21 +23,6 @@ LOGGER_BUFFER = 5242880
; Number of log files saved
LOGGER_FILE_COUNT = 20
-;Gitee related configuration
-[GITEE]
-;Token used to access gitee api
-GITEE_TOKEN =
-
-;Github related configuration
-[GITHUB]
-;Token used to access github api
-GITHUB_TOKEN =
-
-;Gitlab related configuration
-[GITLAB]
-;Token used to access gitlab api
-GITLAB_TOKEN = glpat-24DGxkuTA8nTR1YQsXvZ
-
;Path related configuration
[PATH]
;Patch file download and save path.
@@ -45,4 +30,16 @@ PATCH_SAVE_PATH = /opt/cve_tracking/patches
;Source code package and patch file storage path, used to download gitee source code package and package verification.
SOURCE_CODE_PATH = /opt/cve_tracking/source_code
+;Feedback issue related configuration
+[FEEDBACK]
+;Feedback issue platform,Currently supports "gitee" "github" "gitlab",default is "gitee"
+;Please configure the token of the corresponding platform synchronously in cve-tracking.yaml
+FEEDBACK_PLATFORM = github
+;Feedback to the owner of the issue
+FEEDBACK_ISSUE_OWNER = liheavy
+;Feedback to the repo of the issue
+FEEDBACK_ISSUE_REPO = cve_tracking
+
+[WAREHOUSE]
+DEFAULT_OWNER = src-openeuler
\ No newline at end of file
diff --git a/cve-agency-manager/cve_tracking/constant.py b/cve-agency-manager/cve_tracking/constant.py
index 44c2635b221f9b1fdd3be49693a8cd14ca2fd7e5..1bcd29c71c151722ac951a0fa62380db0a3c95dd 100644
--- a/cve-agency-manager/cve_tracking/constant.py
+++ b/cve-agency-manager/cve_tracking/constant.py
@@ -11,35 +11,12 @@
# See the Mulan PSL v2 for more details.
# ******************************************************************************/
+
class Constant:
"""
Constant class
"""
- """
- CVE reference URL related
- """
- # CVE reference URL that supports query
- CVE_PLATFORM = ['Debian', 'Ubuntu', 'Bugzilla', 'Nvd', 'Suse']
- # CVE reference URL - NVD
- NVD_BASE_URL = "https://nvd.nist.gov/vuln/detail/{cve_num}"
- # CVE reference URL - Bugzilla
- BUGZILLA_BASE_URL = "https://bugzilla.redhat.com/rest/bug/{cve_num}/comment"
- # CVE reference URL - Ubuntu
- UBUNTU_BASE_URL = "https://ubuntu.com/security/{cve_num}"
- # CVE reference URL - Debian
- DEBIAN_BASE_URL = "https://security-tracker.debian.org/tracker/{cve_num}"
- # CVE reference URL - Suse
- SUSE_BASE_URL = "https://bugzilla.suse.com/show_bug.cgi?id={cve_num}"
- """
- Patch related
- """
- # commit format
- COMMIT_REGEX = r"http[s]?://(?:[-\w.\/;?])+(?:/rev|/ci|/commit[s]?)/(?:\?id=)?[0-9a-z]{8,40}"
- GIT_COMMIT_REGEX = r"http[s]?://(?:[-\w.\/;=&?])+a=commit(?:[\w;&?])+h=[0-9a-z]{8,40}"
- # pr format
- PR_REGEX = r"http[s]?://(?:[-\w.\/;?])+(?:pull[s]?|merge_requests)/[1-9][0-9]*"
- # issue format
- ISSUE_REGEX = r"http[s]?://(?:[-\w.\/;?])+issues/[0-9A-Z]+"
+
# gitlab host format
GITLAB_HOST_REGEX = r"http[s]?://gitlab(?:[-\w.?])+"
"""
@@ -49,8 +26,8 @@ class Constant:
GITHUB = "github"
# gitlab
GITLAB = "gitlab"
- # default owner is src_openEuler
- DEFAULT_OWNER = "src-openeuler"
+ # Supported code hosting platforms
+ CODE_PLATFORM = ["gitee", "github", "gitlab"]
"""
Other
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 9836c2837545c496ef6b08dc1620d843cceacd8d..1a4e2c59e33ffd54f7b62ef9901c6069a8feec6d 100644
--- a/cve-agency-manager/cve_tracking/core/comment/issue_comment.py
+++ b/cve-agency-manager/cve_tracking/core/comment/issue_comment.py
@@ -34,14 +34,14 @@ class Table:
def _rowspan(num, platform):
return """
{} | """.format(str(num), platform)
- def span_content(self, pr, commits):
+ def span_content(self, pr, pr_status, commits):
"""
Splicing comment content
:param pr: pull request
:param commits: commits
:return: content spliced
"""
- contents = self._td(pr) + self._td(",".join(commits))
+ contents = self._td(pr) + self._td(pr_status) + self._td("
".join(commits))
return contents
def _table_name(self):
@@ -49,10 +49,17 @@ class Table:
Splicing header
:return: header
"""
- table_names = ["参考网址", "关联pr", "补丁链接"]
+ table_names = ["参考网址", "关联pr", "状态", "补丁链接"]
table_names = " ".join(map(self._th, table_names))
return self._tr(table_names)
+ @staticmethod
+ def _row_span(detail):
+ number = 0
+ for issues in detail:
+ number += len(issues["issue"].get("prs", list()))
+ return number
+
def table(self, lst):
"""
Splicing table
@@ -64,20 +71,26 @@ class Table:
platform_index = 0
platform = platforms.get("platform")
details = platforms.get("details")
- rowspan_content = self._rowspan(len(details), platform)
+ rowspan_content = self._rowspan(self._row_span(details), platform)
+
if not details:
- td_cont = self._tr(self._td(platform) + self._td("") * 2)
- contents += td_cont
-
- for pr, commits in details.items():
- content = self.span_content(pr, commits)
- if platform_index == 0:
- rowspan_content = rowspan_content + content
- span_content = self._tr(rowspan_content)
- platform_index += 1
- else:
- span_content = self._tr(content)
- contents += span_content
+ 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(
+ pr["url"],
+ pr["status"],
+ pr.get("commits", []),
+ )
+ if platform_index == 0:
+ rowspan_content = rowspan_content + content
+ span_content = self._tr(rowspan_content)
+ platform_index += 1
+ else:
+ span_content = self._tr(content)
+ contents += span_content
return self._table(self._table_name() + contents)
@@ -93,7 +106,7 @@ async def issue_comment(patch_details, number, gitee: Gitee):
table = Table().table(patch_details)
instruction = "\n" * 2 + "> 说明: 抱歉,当前工具暂未找到推荐补丁,请人工查找或者之后再尝试。"
for cve_info in patch_details:
- if cve_info.get('details'):
+ if cve_info.get("details"):
instruction = "\n" * 2 + "> 说明:补丁链接仅供初步排查参考,实际可用性请人工再次确认。"
break
diff --git a/cve-agency-manager/cve_tracking/core/crawler/patch.py b/cve-agency-manager/cve_tracking/core/crawler/patch.py
index 06120b97b032cf4496c4eb0c75b156af7e553a67..97c44ffc27da8c19f73360112a68e3b685eb96c3 100644
--- a/cve-agency-manager/cve_tracking/core/crawler/patch.py
+++ b/cve-agency-manager/cve_tracking/core/crawler/patch.py
@@ -11,7 +11,6 @@
# See the Mulan PSL v2 for more details.
# ******************************************************************************/
import re
-
import asyncio
from collections import namedtuple
@@ -20,6 +19,7 @@ from core.platform import CvePlatform
from logger import logger
from util.github_api import Github
from util.gitlab_api import Gitlab
+from conf import settings
class Patch:
@@ -33,10 +33,19 @@ class Patch:
self.patch_info_list = list()
self.patch_detail_list = list()
self.issue_pr_dict = dict()
+ self.pr_status_dict = dict()
self.github_api = Github()
self.gitlab_api = Gitlab()
- self._SummaryInfo = namedtuple('SummaryInfo', ['pr_list', 'issues_list'])
- self._PatchDetail = namedtuple('PatchDetail', ['platform', 'details'])
+ self._SummaryInfo = namedtuple("SummaryInfo", ["pr_list", "issues_list"])
+ self._PatchDetail = namedtuple("PatchDetail", ["platform", "details"])
+
+ @property
+ def issue_relation_info(self):
+ return {"issue": {"url": None, "prs": []}}
+
+ @property
+ def pr_relation_info(self):
+ return {"url": None, "status": None, "commits": []}
async def find_patches_detail(self):
"""
@@ -47,26 +56,35 @@ class Patch:
# 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', []))
+ 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 = [asyncio.create_task(self._get_issue_link_pr(issue)) for issue in
- list(set(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())
- # Find pr contain commits
if summary_info.pr_list:
- pr_task_list = [asyncio.create_task(self._get_pr_contain_commits(pr)) for pr in
- list(set(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}')
+ logger.info(
+ f'Find the patch information of cve "{self.cve_num}" as: \n{self.patch_detail_list}'
+ )
return self.patch_detail_list
async def _find_patches_info(self):
@@ -74,12 +92,37 @@ class Patch:
Implementation method of preliminary search for CVE patch information
:return: self.patch_info_list
"""
- logger.info(f'Start to obtain patches info of {self.cve_num} for {self.rpm_name}')
- cve_platforms = CvePlatform.__subclasses__()
- crawler_task = [asyncio.create_task(cve_platform(self.cve_num).crawling_patch())
- for cve_platform in cve_platforms if cve_platform.__name__ in Constant.CVE_PLATFORM]
+ logger.info(
+ f"Start to obtain patches info of {self.cve_num} for {self.rpm_name}"
+ )
+ crawler_task = [
+ asyncio.create_task(
+ CvePlatform(
+ cve_platform,
+ self.cve_num,
+ cve_platform["url"],
+ cve_platform["format"],
+ ).crawling_patch()
+ )
+ for cve_platform in settings.get_platform()
+ ]
done_task, _ = await asyncio.wait(crawler_task)
- self.patch_info_list.extend([task.result() for task in done_task if task.result()])
+ self.patch_info_list.extend(
+ [task.result() for task in done_task if task.result()]
+ )
+
+ @staticmethod
+ async def _create_async_task(method, issue_pr_list):
+ """
+ Create asynchronous tasks
+ :param method: Method to be executed
+ :param issue_pr_list: issue or pr list
+ :return: task list
+ """
+ task_list = []
+ for issue_or_pr in list(set(issue_pr_list)):
+ task_list.append(asyncio.create_task(method(issue_or_pr)))
+ return task_list
async def _get_issue_link_pr(self, issue):
"""
@@ -92,49 +135,94 @@ class Patch:
pr_list = await self.github_api.issue_relevance_pull(issue_url=issue)
elif Constant.GITLAB in issue:
try:
- issue_info = issue.split('/')
+ issue_info = issue.split("/")
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')
+ response = await self.gitlab_api.issue_relevance_pull(
+ issue_id=issue_num
+ )
+ 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')
+ 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)}')
+ logger.info(f"According to issue {issue} get pr: {str(pr_list)}")
self.issue_pr_dict[issue] = pr_list
return pr_list
+ async def _get_pr_status(self, pr):
+ """
+ Query pr status
+ :param pr: pr
+ :return: self.pr_status_dict
+ """
+ pr_number = self._set_owner_repo_by_pr(pr)
+ if not pr_number:
+ logger.info("This is not effective pr:%s" % pr)
+ return
+ if Constant.GITHUB in pr:
+ response = await self.github_api.get_pull_request(pr_number)
+ if response:
+ self.pr_status_dict[pr] = response.get("state")
+
+ elif Constant.GITLAB in pr:
+ self._set_gitlab_host(pr)
+ response = await self.gitlab_api.get_single_mr(pr_number)
+ if response:
+ self.pr_status_dict[pr] = response.get("merge_status")
+
async def _get_pr_contain_commits(self, pr):
"""
Get the commits contained in pr and record them
:param pr: pull requests
:return: self.issue_pr_dict
"""
- pr_info = pr.split('/')
- pr_number = pr_info[-1]
- commits_list = []
+ pr_number = self._set_owner_repo_by_pr(pr)
+ if pr_number:
+ commits_list = []
+ if Constant.GITHUB in pr:
+ response = await self.github_api.get_pull_commits(pr_number)
+ commits_list = self._convert_api_response(response, "html_url")
+ elif Constant.GITLAB in pr:
+ self._set_gitlab_host(pr)
+ response = await self.gitlab_api.get_mr_context_commits(
+ merge_request_iid=pr_number
+ )
+ commits_list = self._convert_api_response(response, "web_url")
+
+ logger.info(f"According to pull {pr} get commits: {str(commits_list)}")
+
+ self.issue_pr_dict[pr] = commits_list
+
+ def _set_owner_repo_by_pr(self, pr):
+ """
+ Set the owner and repo required by the api through the pr link
+ :param pr: pull request
+ :return: pr number
+ """
+ pr_info = pr.split("/")
try:
+ pr_number = pr_info[-1]
if Constant.GITHUB in pr:
self.github_api.set_attr(owner=pr_info[-4], repo=pr_info[-3])
- response = await self.github_api.get_pull_commits(pr_number)
- commits_list = self._convert_api_response(response, 'html_url')
elif Constant.GITLAB in pr:
self.gitlab_api.set_attr(owner=pr_info[-5], repo=pr_info[-4])
- self._set_gitlab_host(pr)
- response = await self.gitlab_api.get_mr_context_commits(merge_request_iid=pr_number)
- commits_list = self._convert_api_response(response, 'web_url')
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(f'The pull link: {pr} does not conform to the standard format')
- logger.info(f'According to pull {pr} get commits: {str(commits_list)}')
+ logger.warning(
+ f"The pull link: {pr} does not conform to the standard format"
+ )
+ return None
- self.issue_pr_dict[pr] = commits_list
+ return pr_number
@staticmethod
def _convert_api_response(response, url_key):
@@ -146,7 +234,9 @@ class Patch:
"""
pr_commits_list = list()
if response:
- pr_commits_list.extend([commit_info.get(url_key) for commit_info in response])
+ pr_commits_list.extend(
+ [commit_info.get(url_key) for commit_info in response]
+ )
return pr_commits_list
def _set_gitlab_host(self, issue_pr):
@@ -159,22 +249,49 @@ class Patch:
_host = _host_match.group()
self.gitlab_api.host = _host
+ def _set_pr_relation_info(self, pr):
+ pr_relation_info = self.pr_relation_info
+ pr_relation_info["url"] = pr
+ pr_relation_info["commits"] = self.issue_pr_dict.get(pr, [])
+ pr_relation_info["status"] = self.pr_status_dict.get(pr)
+ return pr_relation_info
+
def _convert_path_detail(self):
"""
Process data and generate patch details
:return: self.patch_detail_list
"""
for patch_info in self.patch_info_list:
- patch_detail = self._PatchDetail(platform=patch_info.get('platform'), details=dict())
+ patch_detail = self._PatchDetail(
+ platform=patch_info.get("platform"), details=list()
+ )
+ issue_relation_info = self.issue_relation_info
+
# First, If there is commits information, pr and issue information will be ignored
- if patch_info.get('commits'):
- patch_detail.details[None] = patch_info.get('commits')
+ if patch_info.get("commits"):
+ pr_relation_info = self.pr_relation_info
+ pr_relation_info["commits"] = patch_info.get("commits")
+ issue_relation_info["issue"]["prs"].append(pr_relation_info)
+
# Second, processing pr information
- for pr in patch_info.get('pr'):
- patch_detail.details[pr] = self.issue_pr_dict.get(pr, [])
+ for pr in patch_info.get("pr"):
+ issue_relation_info["issue"]["prs"].append(
+ self._set_pr_relation_info(pr)
+ )
+
+ # None Issue associated patch
+ if issue_relation_info["issue"]["prs"]:
+ patch_detail.details.append(issue_relation_info)
+
# Third, processing issue information
- for issue in patch_info.get('issue'):
+ for issue in patch_info.get("issue"):
+ issue_relation_info = self.issue_relation_info
+ issue_relation_info["issue"]["url"] = issue
for pr in self.issue_pr_dict.get(issue, []):
- patch_detail.details[pr] = self.issue_pr_dict.get(pr, [])
+ issue_relation_info["issue"]["prs"].append(
+ self._set_pr_relation_info(pr)
+ )
+
+ patch_detail.details.append(issue_relation_info)
self.patch_detail_list.append(dict(patch_detail._asdict()))
diff --git a/cve-agency-manager/cve_tracking/core/download/save.py b/cve-agency-manager/cve_tracking/core/download/save.py
index 3abdab537a0ff139c1c9068e4ee33efd4525e786..302c9bc4832f68a7aa0250535c7e61b1d7bfbef1 100644
--- a/cve-agency-manager/cve_tracking/core/download/save.py
+++ b/cve-agency-manager/cve_tracking/core/download/save.py
@@ -26,10 +26,12 @@ def _file(cve, file):
"""
file = os.path.join(file, cve)
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) + ".patch"
- return file_name
+ file_name = (
+ "fix-cve-"
+ + str(sum([os.path.isfile(list_index) for list_index in os.listdir(file)]) + 1)
+ + ".patch"
+ )
+ return os.path.join(file, file_name)
def save_patch(patch_details, cve, path=CONFIG.PATCH_SAVE_PATH):
@@ -42,8 +44,9 @@ def save_patch(patch_details, cve, path=CONFIG.PATCH_SAVE_PATH):
"""
commits = set()
for patch in patch_details:
- for _, commit in patch.get("details", dict()).items():
- commits.update(commit)
+ 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:
diff --git a/cve-agency-manager/cve_tracking/core/platform/suse.py b/cve-agency-manager/cve_tracking/core/feedback/__init__.py
similarity index 70%
rename from cve-agency-manager/cve_tracking/core/platform/suse.py
rename to cve-agency-manager/cve_tracking/core/feedback/__init__.py
index f5d6a5e3777bd54863ec1062266e45ead7279cb2..3e26da5218904e6406ea988ff828312ffb3f9fe2 100644
--- a/cve-agency-manager/cve_tracking/core/platform/suse.py
+++ b/cve-agency-manager/cve_tracking/core/feedback/__init__.py
@@ -10,14 +10,3 @@
# PURPOSE.
# See the Mulan PSL v2 for more details.
# ******************************************************************************/
-from constant import Constant
-from core.platform import CvePlatform
-
-
-class Suse(CvePlatform):
- """
- cve refer to the URL suse to find the patch implementation class
- """
-
- def __init__(self, cve_num, base_url=Constant.SUSE_BASE_URL):
- super(Suse, self).__init__(cve_num, base_url)
diff --git a/cve-agency-manager/cve_tracking/core/platform/bugzila.py b/cve-agency-manager/cve_tracking/core/feedback/issue_mode.py
similarity index 33%
rename from cve-agency-manager/cve_tracking/core/platform/bugzila.py
rename to cve-agency-manager/cve_tracking/core/feedback/issue_mode.py
index 81852a2427deca60a1a764789849e3b6f7194edd..ec2250d065d5082eb85ba97e0f8e9299f450a669 100644
--- a/cve-agency-manager/cve_tracking/core/platform/bugzila.py
+++ b/cve-agency-manager/cve_tracking/core/feedback/issue_mode.py
@@ -10,33 +10,43 @@
# PURPOSE.
# See the Mulan PSL v2 for more details.
# ******************************************************************************/
-import json
-from json import JSONDecodeError
-
-from constant import Constant
-from core.platform.cve_platform import CvePlatform
+from conf import CONFIG
from logger import logger
+from util.gitee_api import Gitee
+from util.github_api import Github
+from util.gitlab_api import Gitlab
-class Bugzilla(CvePlatform):
+class IssueMode:
"""
- cve refer to the URL bugzilla to find the patch implementation class
+ Feedback by creating an issue
"""
- def __init__(self, cve_num, base_url=Constant.BUGZILLA_BASE_URL):
- super(Bugzilla, self).__init__(cve_num, base_url)
+ def __init__(self, cve_num, cve_platform, patch_url, issue_platform):
+ self.cve_num = cve_num
+ self.cve_platform = cve_platform
+ self.patch_url = patch_url
+ self.issue_platform = issue_platform
+ self._support_issue_platform = {'gitee': Gitee,
+ 'github': Github,
+ 'gitlab': Gitlab}
- @staticmethod
- def format_text(text):
+ async def create_feedback_issue(self):
"""
- Rewrite the formatted web page to get the content method
- :param text: content of web page
- :return: formatted content
+ Create an issue about tool feedback
+ :return: True or False
"""
- try:
- text_dict = json.loads(text)
- return json.dumps(text_dict, indent=4)
- except JSONDecodeError as e:
- logger.error(f'The format of the content obtained by bugzilla website is incorrect, '
- f'content is {text}, message is {e.msg}')
- return None
+ platform_api_class = self._support_issue_platform.get(self.issue_platform)
+ if platform_api_class is None:
+ logger.error(f'Issue hosting platform: {self.issue_platform} does not support')
+ return False
+ if not all([CONFIG.FEEDBACK_ISSUE_OWNER, CONFIG.FEEDBACK_ISSUE_REPO]):
+ logger.error(f'"FEEDBACK_ISSUE_OWNER" or "FEEDBACK_ISSUE_REPO" no setting, Please set in file "config.ini"')
+ return False
+
+ title = f"CVE: {self.cve_num} patch information"
+ description = f"Cve reference URL: {self.cve_platform}\nPatch url: {self.patch_url}"
+ platform_api = platform_api_class()
+ platform_api.set_attr(CONFIG.FEEDBACK_ISSUE_OWNER, CONFIG.FEEDBACK_ISSUE_REPO)
+ response = await platform_api.create_issue(title=title, body=description)
+ return response is not None
diff --git a/cve-agency-manager/cve_tracking/core/platform/__init__.py b/cve-agency-manager/cve_tracking/core/platform/__init__.py
index 4299ebe45353d6248097191b3c9f1bea8377dc1c..d7d26770ced7f7dbc9adb8fa2c600ecfc8c44d04 100644
--- a/cve-agency-manager/cve_tracking/core/platform/__init__.py
+++ b/cve-agency-manager/cve_tracking/core/platform/__init__.py
@@ -11,15 +11,5 @@
# See the Mulan PSL v2 for more details.
# ******************************************************************************/
from .cve_platform import CvePlatform
-from .nvd import Nvd
-from .bugzila import Bugzilla
-from .debian import Debian
-from .suse import Suse
-from .ubuntu import Ubuntu
-__all__ = ("CvePlatform",
- "Nvd",
- "Bugzilla",
- "Debian",
- "Ubuntu",
- "Suse")
+__all__ = ("CvePlatform",)
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 bc3202a3bc422f2cc95d40bb6407869cb841e7f7..37d23c10fce2e4e59b5ec10f074a36c02e8f37fb 100644
--- a/cve-agency-manager/cve_tracking/core/platform/cve_platform.py
+++ b/cve-agency-manager/cve_tracking/core/platform/cve_platform.py
@@ -10,6 +10,8 @@
# PURPOSE.
# See the Mulan PSL v2 for more details.
# ******************************************************************************/
+import json
+from json import JSONDecodeError
import re
from collections import namedtuple
from functools import wraps
@@ -20,6 +22,7 @@ from constant import Constant
from exception import RequestError
from logger import logger
from request import http
+from conf import settings
def match(regexes, component):
@@ -33,7 +36,7 @@ def match(regexes, component):
def _inner_decorator(func):
@wraps(func)
def _inner_match(crawler_text, patch_info):
- if patch_info.get('commits') or patch_info.get('pr'):
+ if patch_info.get("commits") or patch_info.get("pr"):
return func(crawler_text, patch_info)
match_list = list()
for regex in regexes:
@@ -41,7 +44,7 @@ def match(regexes, component):
try:
patch_info[component].extend(list(set(match_list)))
except KeyError:
- logger.error(f'Patch info does not exist in field {component}')
+ logger.error(f"Patch info does not exist in field {component}")
return func(crawler_text, patch_info)
return _inner_match
@@ -54,10 +57,18 @@ class CvePlatform:
The parent class of cve reference URL
"""
- def __init__(self, cve_num=None, base_url=None):
+ def __init__(
+ self,
+ platform,
+ cve_num=None,
+ base_url=None,
+ format_text="text",
+ ):
+ self._platform = platform
self.cve_num = cve_num
self.base_url = base_url
- self._Patch = namedtuple('Patch', ['platform', 'commits', 'pr', 'issue'])
+ self._format = format_text
+ self._Patch = namedtuple("Patch", ["platform", "commits", "pr", "issue"])
@property
def crawler_url(self):
@@ -75,32 +86,74 @@ class CvePlatform:
"""
return self._Patch(platform=self.crawler_url, commits=[], pr=[], issue=[])
+ async def _rule_redirct(self, response):
+ """
+ Page multi layer jump data parsing
+ :param response: http response data
+ :return: response data
+ """
+ for redirct_rule in self._platform.get("redirct", []):
+ format_text = self.format_text(response.text)
+ target_val = list(
+ set(
+ re.findall(
+ pattern=redirct_rule.get("regex", ""), string=format_text or ""
+ )
+ )
+ )
+ try:
+ url = redirct_rule["prefix"] + target_val[-1]
+ except IndexError:
+ url = redirct_rule["prefix"]
+ response = await self._method(redirct_rule)(
+ url, data=redirct_rule.get("body")
+ )
+
+ return response
+
+ @staticmethod
+ def _method(rule):
+ method = http.get if rule.get("method", "get") == "get" else http.post
+ return method
+
async def crawling_patch(self):
"""
Crawl patch information from the cve reference website
:return: patch info
"""
try:
- _response = await http.get(self.crawler_url)
+ _response = await self._method(self._platform)(
+ self.crawler_url, data=self._platform.get("body")
+ )
+ if "redirct" in self._platform:
+ _response = await self._rule_redirct(response=_response)
except RequestError:
return None
if _response.error or not _response.text:
- logger.error(f'Failed to access URL {self.crawler_url}, detail: {_response.error}')
+ logger.error(
+ f"Failed to access URL {self.crawler_url}, detail: {_response.error}"
+ )
return None
- formatted_text = self.format_text(_response.text)
+ formatted_text = (
+ self.format_text(_response.text)
+ if self._format == "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()))
+ patch_info_dict = self.match_patch(
+ formatted_text, dict(self.patch_info._asdict())
+ )
return patch_info_dict
@staticmethod
- @match([Constant.COMMIT_REGEX, Constant.GIT_COMMIT_REGEX], 'commits')
- @match([Constant.PR_REGEX], 'pr')
- @match([Constant.ISSUE_REGEX], 'issue')
+ @match(settings.get_regex(label="commit"), "commits")
+ @match(settings.get_regex(label="pr"), "pr")
+ @match(settings.get_regex(label="issue"), "issue")
def match_patch(text, patch_info):
"""
Matching patch related links,Use decorator to find
@@ -113,7 +166,7 @@ class CvePlatform:
if isinstance(value, list):
patch_info[key] = list(set(value))
- logger.info(f'Find patch: {patch_info}')
+ logger.info(f"Find patch: {patch_info}")
return patch_info
@staticmethod
@@ -122,5 +175,24 @@ class CvePlatform:
Format the content obtained from the URL
:return: Formatted content
"""
- html_text = BeautifulSoup(text, 'html.parser')
+ if not text:
+ return None
+ html_text = BeautifulSoup(text, "html.parser")
return html_text.prettify()
+
+ @staticmethod
+ def json(text):
+ """
+ Rewrite the formatted web page to get the content method
+ :param text: content of web page
+ :return: formatted content
+ """
+ try:
+ text_dict = json.loads(text)
+ return json.dumps(text_dict, indent=4)
+ except JSONDecodeError as e:
+ logger.error(
+ f"The format of the content obtained by bugzilla website is incorrect, "
+ f"content is {text}, message is {e.msg}"
+ )
+ return None
diff --git a/cve-agency-manager/cve_tracking/core/platform/debian.py b/cve-agency-manager/cve_tracking/core/platform/debian.py
deleted file mode 100644
index a50d2807475a65bc45868eae91dad6f06d1db2b2..0000000000000000000000000000000000000000
--- a/cve-agency-manager/cve_tracking/core/platform/debian.py
+++ /dev/null
@@ -1,23 +0,0 @@
-#!/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 constant import Constant
-from core.platform.cve_platform import CvePlatform
-
-
-class Debian(CvePlatform):
- """
- cve refer to the URL debian to find the patch implementation class
- """
-
- def __init__(self, cve_num, base_url=Constant.DEBIAN_BASE_URL):
- super(Debian, self).__init__(cve_num, base_url)
diff --git a/cve-agency-manager/cve_tracking/core/platform/nvd.py b/cve-agency-manager/cve_tracking/core/platform/nvd.py
deleted file mode 100644
index 98a5efc26ec19663a5b4d918a691cd4d4bf681d7..0000000000000000000000000000000000000000
--- a/cve-agency-manager/cve_tracking/core/platform/nvd.py
+++ /dev/null
@@ -1,23 +0,0 @@
-#!/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 constant import Constant
-from core.platform.cve_platform import CvePlatform
-
-
-class Nvd(CvePlatform):
- """
- cve refer to the URL nvd to find the patch implementation class
- """
-
- def __init__(self, cve_num, base_url=Constant.NVD_BASE_URL):
- super(Nvd, self).__init__(cve_num, base_url)
diff --git a/cve-agency-manager/cve_tracking/core/platform/ubuntu.py b/cve-agency-manager/cve_tracking/core/platform/ubuntu.py
deleted file mode 100644
index d3446bb3bd10af80e30465eb2a1b1b67cf42fa50..0000000000000000000000000000000000000000
--- a/cve-agency-manager/cve_tracking/core/platform/ubuntu.py
+++ /dev/null
@@ -1,23 +0,0 @@
-#!/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 constant import Constant
-from core.platform.cve_platform import CvePlatform
-
-
-class Ubuntu(CvePlatform):
- """
- cve refer to the URL ubuntu to find the patch implementation class
- """
-
- def __init__(self, cve_num, base_url=Constant.UBUNTU_BASE_URL):
- super(Ubuntu, self).__init__(cve_num, base_url)
diff --git a/cve-agency-manager/cve_tracking/core/verification/packing.sh b/cve-agency-manager/cve_tracking/core/verification/packing.sh
index 9b8f8ee9a540546e6a4914e10cba46b35ba0af3c..8ab7db464c3137f4c8dafd12c1bc3ddbdf2ed956 100644
--- a/cve-agency-manager/cve_tracking/core/verification/packing.sh
+++ b/cve-agency-manager/cve_tracking/core/verification/packing.sh
@@ -103,7 +103,7 @@ function update_spec() {
fi
# add %patch
last_patch_apply=$(grep "%patch.* " ${spec_file} | sed -n '$p')
- if [[ -n ${last_patch_apply} ]];then
+ 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}')
@@ -135,6 +135,8 @@ function mv_source_file() {
function rpm_build() {
echo "[INFO] Start to rpmbuild"
install_rpm rpm-build rpm
+ apt-get build-dep ${rpm_name} >/dev/null 2>&1
+ dnf builddep ${root_build_path}/SPECS/${spec_file} >/dev/null 2>&1
rpmbuild -bp ${root_build_path}/SPECS/${spec_file} >./result.log 2>&1
if [[ $? -eq 0 ]]; then
echo "[INFO] build success !!!"
diff --git a/cve-agency-manager/cve_tracking/cve-tracking.yaml b/cve-agency-manager/cve_tracking/cve-tracking.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..a456929ea95a18bcc6d3c8d211a1adb1337fea83
--- /dev/null
+++ b/cve-agency-manager/cve_tracking/cve-tracking.yaml
@@ -0,0 +1,71 @@
+# 匹配cve补丁信息的正则表达式
+regex:
+ # 匹配内容的类型的标签,当前只支持 "commit"/"pr"/"issue"
+ - label: commit
+ # 匹配特定类型内容的正则表达式,可以设置多个匹配规则
+ 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}
+ - label: pr
+ regular:
+ - http[s]?://(?:[-\w.\/;?])+(?:pull[s]?|merge_requests)/[1-9][0-9]*
+ - label: issue
+ regular:
+ - http[s]?://(?:[-\w.\/;?])+issues/[0-9A-Z]+
+
+# 查找cve漏洞修复的平台,例如 Debian、Bugzilla、Nvd等
+platform:
+ # The name of the platform, similar to a label
+ # 平台的名称(必配项)
+ - name: Cnnvd
+ # Request address to find CVE information,{cve_num} is a placeholder for string substitution in Python
+ # 查找cve详情信息的请求地址,“{cve_num}”是python中字符串替换的占位符(必配项)
+ url: http://cnnvd.org.cn/web/vulnerability/queryLds.tag?qcvCnnvdid={cve_num}
+ # 发送请求的方式,默认为get请求,还可以指定为post请求,当请求方式为get时,此处可不配置
+ method: get
+ # 若请求方式为post且存在请求体时,body为必填项,且必须为json格式,当请求方式为get时,此处可不配置
+ body:
+ # 解析响应体的方式,当前只支持“text”或“json”(必配项)
+ format: text
+ # 当请求无法直接获取cve信息时(根据页面中特定链接多次跳转),可以指定页面跳转的方式来获取进一步的信息
+ redirct:
+ # 跳转地址的前缀,一般情况下为跳转页面的域名,如果正则表达式匹配到的跳转地址中有完整的域名+路径,此配置项可以不填写
+ - prefix: http://cnnvd.org.cn
+ # 匹配页面跳转地址的正则表达式,如果匹配多个值,则获取最后一个匹配项
+ regex: /web/xxk/ldxqById\.tag\?CNNVD=CNNVD[0-9-]+
+ # 送请求的方法,默认为get请求,还可以指定为post请求,当请求为get时,此处可不配置
+ method: get
+ # 若请求方式为post且存在请求体时,body为必填项,且必须为json格式
+ body:
+
+ - name: Debian
+ url: https://security-tracker.debian.org/tracker/{cve_num}
+ format: text
+
+ - name: Ubuntn
+ url: https://ubuntu.com/security/{cve_num}
+ format: text
+
+ - name: Bugzilla
+ url: https://bugzilla.redhat.com/rest/bug/{cve_num}/comment
+ format: json
+
+ - name: Nvd
+ url: https://nvd.nist.gov/vuln/detail/{cve_num}
+ format: text
+
+ - name: Suse
+ url: https://bugzilla.suse.com/show_bug.cgi?id={cve_num}
+ format: text
+
+# Private token for API access
+# api访问时的私人令牌,当前只支持"gitee"/"github"/"gitlab"
+authentication:
+ # api名称,此值不可更改
+ - name: gitee
+ # 访问api的私人令牌,可以手动获取后更改
+ token:
+ - name: github
+ token:
+ - name: gitlab
+ token: glpat-24DGxkuTA8nTR1YQsXvZ
diff --git "a/cve-agency-manager/cve_tracking/doc/CVE\350\241\245\344\270\201\350\207\252\345\212\250\350\216\267\345\217\226\345\267\245\345\205\267\350\256\276\350\256\241\346\226\207\346\241\243.md" "b/cve-agency-manager/cve_tracking/doc/CVE\350\241\245\344\270\201\350\207\252\345\212\250\350\216\267\345\217\226\345\267\245\345\205\267\350\256\276\350\256\241\346\226\207\346\241\243.md"
index 013dbb02e3643987a1b7b6623d12b921ea7f486b..914f2f5c7f0b26791e348c799635ef08cc6a405d 100644
--- "a/cve-agency-manager/cve_tracking/doc/CVE\350\241\245\344\270\201\350\207\252\345\212\250\350\216\267\345\217\226\345\267\245\345\205\267\350\256\276\350\256\241\346\226\207\346\241\243.md"
+++ "b/cve-agency-manager/cve_tracking/doc/CVE\350\241\245\344\270\201\350\207\252\345\212\250\350\216\267\345\217\226\345\267\245\345\205\267\350\256\276\350\256\241\346\226\207\346\241\243.md"
@@ -23,15 +23,22 @@
| python3-retrying | requests请求重试依赖程序库 | openEuler已集成 |
| python3-concurrent-log-handler | python日志程序库,包括日志记录,日志转储 | openEuler已集成 |
| python3-pyyaml | python处理yaml文件程序库 | openEuler已集成 |
+| python3-asyncio | python异步执行io操作程序库 | openEuler已集成 |
+| python3-bs4 | python处理html格式程序库 | openEuler已集成 |
+| python3-aiohttp | python异步请求web资源操作程序库 | openEuler已集成 |
+| python3-wget | python下载web资源程序库 | openEuler已集成 |
+| python3-fake_useragent | python请求api设置代理程序库 | openEuler已集成 |
+| python3-lxml | python解析xml程序库 | openEuler已集成 |
### 1.4、特性需求
-| 需求编号 | 需求描述 | 特性描述 |
-| -------- | --------- | ----------------------------------------------- |
-| 1 | 补丁查找 | 通过cve参考网址和查找规则进行补丁链接匹配查找。 |
-| 2 | issue评论 | 将查找到的补丁链接添加到相关issue的评论中。 |
-| 3 | 补丁下载 | 通过查找到的补丁链接,将补丁文件下载保存。 |
-| 4 | 补丁验证 | 下载补丁文件后,进行补丁打包验证。 |
+| 需求编号 | 需求描述 | 特性描述 |
+| -------- | --------- | ------------------------------------------------------------ |
+| 1 | 补丁查找 | 通过cve参考网址和查找规则进行补丁链接匹配查找。 |
+| 2 | issue评论 | 将查找到的补丁链接添加到相关issue的评论中。 |
+| 3 | 补丁下载 | 通过查找到的补丁链接,将补丁文件下载保存。 |
+| 4 | 补丁验证 | 下载补丁文件后,进行补丁打包验证。 |
+| 5 | 补丁反馈 | 若工具为找到补丁,通过命令向指定仓库提交issue,方便数据统计和后续工具优化。 |
@@ -53,34 +60,35 @@
> 评论格式
>
-> | 参考网址 | 关联pr | 补丁链接 |
-> | --------------------------------------------------------- | ---------------------------------------------- | ------------------------------------------------------------ |
-> | https://bugzilla.redhat.com/show_bug.cgi?id=CVE-2020-1735 | https://github.com/ansible/ansible/pull/68780/ | https://github.com/ansible/ansible/pull/68780/commits/5292482553dc409081f7f4368398358cbf9f8672,https://github.com/ansible/ansible/pull/68780/commits/5292482553dc409081f7f4368398358cbf9f8673 |
-> | https://ubuntu.com/security/CVE-2019-10156 | | https://github.com/ansible/ansible/pull/57188/commits/8254c266f962d5febe46396d5083bb9c1da74840,https://github.com/ansible/ansible/pull/57188/commits/fbda0028750a17a032d83dad9d1fb284f9ea68a4 |
+> | 参考网址 | 关联pr | 状态 | 补丁链接 |
+> | --------------------------------------------------------- | ---------------------------------------------- | ----- | ------------------------------------------------------------ |
+> | https://bugzilla.redhat.com/show_bug.cgi?id=CVE-2020-1735 | https://github.com/ansible/ansible/pull/68780/ | close | https://github.com/ansible/ansible/pull/68780/commits/5292482553dc409081f7f4368398358cbf9f8672,https://github.com/ansible/ansible/pull/68780/commits/5292482553dc409081f7f4368398358cbf9f8673 |
+> | https://ubuntu.com/security/CVE-2019-10156 | | | https://github.com/ansible/ansible/pull/57188/commits/8254c266f962d5febe46396d5083bb9c1da74840,https://github.com/ansible/ansible/pull/57188/commits/fbda0028750a17a032d83dad9d1fb284f9ea68a4 |
-- 本地使用:下载工具后,通过python调用,该方式支持下载补丁和验证补丁的操作。
+ 反馈功能:如果工具未找到补丁信息,但是人工可以找到,可以通过在评论中添加一下内容进行反馈:
+
+```shell
+/feedback "cve参考网址" "补丁url"
+```
+
+ 该操作将在https://gitee.com/liheavy/cve_tracking仓库中提交相关issue。可通过config.ini中的"FEEDBACK"中的配置来更改提交issue所在的仓库。
+
+- 本地使用:下载工具后,通过python调用,该方式支持下载补丁和验证补丁的操作,也支持补丁反馈功能,具体操作见readme指导文档。
### 3.2、用例视图
-
+
> 说明:
>
-> - 社区维护人员是该工具的主要使用者,用于快速查找补丁,及时修复cve。
-> - 社区开发人员可以使用补丁验证模块,快速打包验证。
+> - 社区维护人员是该工具的主要使用者,用于快速查找补丁,及时修复cve,当工具未找到相关补丁时可使用反馈功能进行反馈。
+> - 社区开发人员可以使用补丁验证模块,快速打包验证,可根据反馈信息不断提升工具的查找率和正确率。
> - 版本经理主要是利用该工具,能更好保障版本的顺利发布。
### 3.3、逻辑视图

-> 说明:
->
-> - 补丁查找模块:通过输入cve编号和软件包信息,在cve参考网址中查找相关内容,然后通过过滤查找补丁链接。
-> - 评论模块:向cve的issue提交补丁链接相关内容。
-> - 下载模块:根据找到的补丁链接,进行补丁文件下载保存。
-> - 验证模块:根据下载的补丁文件和源代码进行打包验证。
-
### 3.4、代码结构

@@ -91,35 +99,127 @@
### 3.6、开发视图
-
+
+
+
### 3.7、质量设计
##### 3.7.1、性能规格
-| 规格项 | 规格指标 |
-| -------- | ------------------------------------------------------------ |
-| 提交评论 | 由于补丁平台的网络访问可能存在不稳定性,将重试三次,总时间限制为5min |
-| 补丁下载 | 由于补丁平台的网络访问可能存在不稳定性以及补丁大小区别,将重试三次,总时间限制为10min |
-| 补丁验证 | 由软件包编译流程和编译环境决定,无时间限制。 |
+| 规格项 | 功能点 | 规格指标 |
+| ------ | ---------------------- | ------------------------------------------------------------ |
+| 效率 | 补丁下载 | 由于补丁平台的网络访问可能存在不稳定性以及补丁大小区别,将重试三次,每次重试2min,总时间限制为10min |
+| 效率 | 提交评论
工具反馈 | 由于补丁平台的网络访问可能存在不稳定性,将重试三次,每次重试时间为1min,总时间限制为3min |
+| 效率 | 补丁验证 | 由软件包编译流程和编译环境决定,无时间限制。 |
-**性能提升策略:**
+**补丁查找性能提升策略:**

##### 3.7.2、可靠性设计
-结果展示多个平台的相关内容,使结果更具参考性。
+| 影响因素 | 使用场景 | 可靠性说明 |
+| ---------- | :--------- | ------------------------------------------------------------ |
+| 高并发 | 在线、本地 | 基于当前主要使用场景为在线使用,每次执行只输入单个cve,所以多个请求也只会顺序执行,资源控制由操作系统调度。 |
+| 进程异常 | 在线、本地 | 当前工具为python调用方式,当系统故障或者进程异常退出,执行中断,后续执行不会受影响。 |
+| 结果正确性 | 在线、本地 | 工具采用多个cve参考平台共同查找的方式,平台信息各自展示,可通过交叉对比提升结果准确性。同时提供反馈功能,不断提升工具准确性。 |
##### 3.7.3、安全性设计
-| 功能点 | 涉及安全项 | 解决策略 |
-| ---------------- | -------------------- | ------------------------------------------------------------ |
-| 上游社区查找补丁 | 上游社区用户名,密码 | 不需要登录的将直接访问,必须登录的将支持用户在配置文件配置临时用户名密码。 |
-| issue评论 | gitee用户名,密码 | 主要使用场景为和cve_manager集成,将使用机器人账号密码。本地执行支持用户配置临时全局环境变量。 |
-| 补丁验证 | 软件包安装,编译 | 必须使用root权限的操作,需用户保证拥有对应权限,不涉及用户名密码保存。 |
+| 功能点 | 涉及安全项 | 解决策略 |
+| ---------------- | ---------------- | ------------------------------------------------------------ |
+| 上游社区信息获取 | 上游社区token | 不需要登录的将直接访问,必须登录的将支持用户在配置文件配置临时token。 |
+| issue评论 | gitee私人令牌 | 主要使用场景为和cve_manager集成,将使用机器人令牌。本地执行支持用户配置临时全局环境变量。 |
+| 补丁验证 | 软件包安装,编译 | 必须使用root权限的操作,需用户保证拥有对应权限,不涉及用户名密码保存。 |
+| 补丁反馈 | 提交issue | 无需用户名密码,需要token,token可设置环境变量和写入配置文件,由用户自己保存。 |
+
+### 3.8、扩展性设计
+
+#### 3.8.1、查找平台扩展
+
+工具采用yaml配置查找平台及对应的查找规则,支持动态扩展,开发者只要按照定义的yaml配置文件(cve-tracking.yaml)规则和说明配置对应平台信息即可,当yaml配置项不满足时可调整yaml解析类进行扩展。
+
+```yaml
+# 匹配cve补丁信息的正则表达式
+regex:
+ # 匹配内容的类型的标签,当前只支持 "commit"/"pr"/"issue"
+ - label: commit
+ # 匹配特定类型内容的正则表达式,可以设置多个匹配规则
+ 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}
+ - label: pr
+ regular:
+ - http[s]?://(?:[-\w.\/;?])+(?:pull[s]?|merge_requests)/[1-9][0-9]*
+ - label: issue
+ regular:
+ - http[s]?://(?:[-\w.\/;?])+issues/[0-9A-Z]+
+
+# 查找cve漏洞修复的平台,例如 Debian、Bugzilla、Nvd等
+platform:
+ # 平台的名称(必配项)
+ - name: Cnnvd
+ # 查找cve详情信息的请求地址,“{cve_num}”是python中字符串替换的占位符(必配项)
+ url: http://cnnvd.org.cn/web/vulnerability/queryLds.tag?qcvCnnvdid={cve_num}
+ # 发送请求的方式,默认为get请求,还可以指定为post请求,当请求方式为get时,此处可不配置
+ method: get
+ # 若请求方式为post且存在请求体时,body为必填项,且必须为json格式,当请求方式为get时,此处可不配置
+ body:
+ # 解析响应体的方式,当前只支持“text”或“json”(必配项)
+ format: text
+ # 当请求无法直接获取cve信息时(根据页面中特定链接多次跳转),可以指定页面跳转的方式来获取进一步的信息
+ redirct:
+ # 跳转地址的前缀,一般情况下为跳转页面的域名,如果正则表达式匹配到的跳转地址中有完整的域名+路径,此配置项可以不填写
+ - prefix: http://cnnvd.org.cn
+ # 匹配页面跳转地址的正则表达式,如果匹配多个值,则获取最后一个匹配项
+ regex: /web/xxk/ldxqById\.tag\?CNNVD=CNNVD[0-9-]+
+ # 送请求的方法,默认为get请求,还可以指定为post请求,当请求为get时,此处可不配置
+ method: get
+ # 若请求方式为post且存在请求体时,body为必填项,且必须为json格式
+ body:
+
+ - name: Debian
+ url: https://security-tracker.debian.org/tracker/{cve_num}
+ format: text
+
+ - name: Ubuntn
+ url: https://ubuntu.com/security/{cve_num}
+ format: text
+
+ - name: Bugzilla
+ url: https://bugzilla.redhat.com/rest/bug/{cve_num}/comment
+ format: json
+
+ - name: Nvd
+ url: https://nvd.nist.gov/vuln/detail/{cve_num}
+ format: text
+
+ - name: Suse
+ url: https://bugzilla.suse.com/show_bug.cgi?id={cve_num}
+ format: text
+
+# api访问时的私人令牌,当前只支持"gitee"/"github"/"gitlab"
+authentication:
+ # api名称,此值不可更改
+ - name: gitee
+ # 访问api的私人令牌,可以手动获取后更改
+ token: 9c2290c567bd16064219cbbc0f61c8dc
+ - name: github
+ token: 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
+ - name: gitlab
+ token: glpat-24DGxkuTA8nTR1YQsXvZ
+
-### 3.8、特性清单
+```
+
+#### 3.8.2、功能扩展
+
+工具采用工厂模式设计模式,当命令行和功能模块扩展时,只需增量添加对应的类即可。
+
+
+
+### 3.9、特性清单
**AR:补丁查找**
@@ -147,9 +247,9 @@
| -------: | ------------------------------------------------------------ | ------ |
| 补丁验证 | 根据下载的补丁文件,以及软件包的gitee地址,下载源代码,修改spec文件,进行软件包打包验证。 | 0.5k |
-### 3.9、接口清单
+### 3.10、接口清单
-#### 3.9.1、查找补丁信息 find_patches_info()
+#### 3.10.1、查找补丁信息 find_patches_info()
> 说明:在各个cve参考网址爬取cve的补丁信息,先初步记录参考网址上列出的网址。
@@ -178,7 +278,7 @@ patch_info:
}
```
-#### 3.9.2、查找补丁链接 find_patches_detail()
+#### 3.10.2、查找补丁链接 find_patches_detail()
> 说明:对find_patches_info查询的信息做进一步处理,获取补丁链接。
@@ -220,23 +320,39 @@ patch_details:
{
"plantform": "nvd_url",
"details": {
- "pr_url1":["commit11","commit12"],
- "pr_url2":["commit21","commit22"],
- None:["commit31","commit32"]
+ "issue1":{
+ "url": "issue1_url",
+ "status": "close",
+ "prs":[
+ {
+ "url": "pr1_url",
+ "status": "open",
+ "commits": ["commit1", "commit2"]
+ }
+ ]
+ }
}
},
{
"plantform": "buzila_url",
"details": {
- "pr_url1":["commit11","commit12"],
- "pr_url2":["commit21","commit22"],
- None:["commit31","commit32"]
+ "issue1":{
+ "url": "",
+ "status": "",
+ "prs":[
+ {
+ "url": "",
+ "status": "",
+ "commits": ["commit1", "commit2"]
+ }
+ ]
+ }
}
}
]
```
-#### 3.9.3、评论issue add_comment()
+#### 3.10.3、评论issue add_comment()
> 说明:根据patch_details获取的补丁详细信息,拼接issue评论内容,添加issue评论。
@@ -254,7 +370,7 @@ patch_details:
| code | str | 调用gitee api添加issue评论返回的状态码 |
| text | str | 调用gitee api添加issue评论返回的信息 |
-#### 3.9.4、下载保存 save_patch()
+#### 3.10.4、下载保存 save_patch()
> 说明:根据find_patches_detail获取到的补丁信息,进行补丁文件下载。
@@ -270,7 +386,7 @@ patch_details:
> 无返回值,下载信息记录日志
-#### 3.9.5、打包验证 packing_source()
+#### 3.10.5、打包验证 packing_source()
> 说明:根据save_patch保存的补丁文件,以及软件包信息,从gitee拉取源代码,修改spec,进行打包测试。
@@ -284,4 +400,19 @@ patch_details:
返回值:
-> 无返回值,编译过程记录文件,编译结果记录日志。
\ No newline at end of file
+> 无返回值,编译过程记录文件,编译结果记录日志。
+
+#### 3.10.6、补丁信息反馈 create_feedback_issue()
+
+> 说明:反馈cve参考网址和补丁url信息,到指定仓库下提交issue
+
+入参:
+
+| 名称 | 类型 | 说明 |
+| -------------- | ---- | -------------------------------------- |
+| cve_num | str | cve编号 |
+| cve_platform | str | cve参考网址 |
+| patch_url | str | 补丁url链接 |
+| issue_platform | str | 提交issue的平台 |
+| *owner | str | 提交issue所在的owner,在配置文件中配置 |
+| *repo | str | 提交issue所在的repo, 在配置文件中配置 |
\ No newline at end of file
diff --git "a/cve-agency-manager/cve_tracking/doc/images/\344\273\243\347\240\201\347\273\223\346\236\204.png" "b/cve-agency-manager/cve_tracking/doc/images/\344\273\243\347\240\201\347\273\223\346\236\204.png"
index 38310dfce4981d997ef1f3ad221740798e71d757..ed8b9633710875acbf53e360c137567f4ea2d830 100644
Binary files "a/cve-agency-manager/cve_tracking/doc/images/\344\273\243\347\240\201\347\273\223\346\236\204.png" and "b/cve-agency-manager/cve_tracking/doc/images/\344\273\243\347\240\201\347\273\223\346\236\204.png" differ
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"
new file mode 100644
index 0000000000000000000000000000000000000000..a1da01a4592a933bece141ed77d370be3b86eaa4
Binary files /dev/null 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"
new file mode 100644
index 0000000000000000000000000000000000000000..57922ae14b4161e6be7d2cb2b8b5a833f4f73b4d
Binary files /dev/null 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/doc/images/\347\224\250\344\276\213\345\233\276.png" "b/cve-agency-manager/cve_tracking/doc/images/\347\224\250\344\276\213\345\233\276.png"
index 5bddf5b9257e2a46d2773be414e5ef57a2bf8660..53cacf2c001132d41489e19b355def525c6f26fc 100644
Binary files "a/cve-agency-manager/cve_tracking/doc/images/\347\224\250\344\276\213\345\233\276.png" and "b/cve-agency-manager/cve_tracking/doc/images/\347\224\250\344\276\213\345\233\276.png" differ
diff --git "a/cve-agency-manager/cve_tracking/doc/images/\351\200\273\350\276\221\350\247\206\345\233\276.png" "b/cve-agency-manager/cve_tracking/doc/images/\351\200\273\350\276\221\350\247\206\345\233\276.png"
index 8cfbd43c05be01b61406abe44fbf519a9e4f4eb9..6edec76befbac3f31b17c8931fbd5b0f211f4998 100644
Binary files "a/cve-agency-manager/cve_tracking/doc/images/\351\200\273\350\276\221\350\247\206\345\233\276.png" and "b/cve-agency-manager/cve_tracking/doc/images/\351\200\273\350\276\221\350\247\206\345\233\276.png" differ
diff --git a/cve-agency-manager/cve_tracking/requirements.txt b/cve-agency-manager/cve_tracking/requirements.txt
index 8386667d5b9a6ae86a2efe0ef3e1f46fba84f6ef..c1697f7e8de4b843ef1e29900b5f455dd6e8918b 100644
--- a/cve-agency-manager/cve_tracking/requirements.txt
+++ b/cve-agency-manager/cve_tracking/requirements.txt
@@ -6,4 +6,5 @@ wget
fake_useragent
retrying
concurrent_log_handler
-lxml
\ No newline at end of file
+lxml
+pyyaml
\ No newline at end of file
diff --git a/cve-agency-manager/cve_tracking/util/__init__.py b/cve-agency-manager/cve_tracking/util/__init__.py
index b6c2caed55ed482594c023ed1eac25210359a0fa..5a7ebcdc275716a52600da9e60838866db1f34a9 100644
--- a/cve-agency-manager/cve_tracking/util/__init__.py
+++ b/cve-agency-manager/cve_tracking/util/__init__.py
@@ -17,9 +17,14 @@ from logger import logger
class Api:
- def __init__(self, repo=None, owner=None) -> None:
+ def __init__(self, repo=None, owner=None, token=None) -> None:
self._repo = repo
self._owner = owner
+ self.token = token
+
+ def _set_token(self):
+ parameters = {"access_token": self.token} if self.token else {}
+ return parameters
@staticmethod
async def _post(url, values, **kwargs):
@@ -35,6 +40,8 @@ class Api:
if not response.success:
logger.warning("reuqest url: %s status code: %s error info: %s"
% (url, response.status_code, response.error))
+ return None
+
return response.json
def set_attr(self, owner, repo):
diff --git a/cve-agency-manager/cve_tracking/util/gitee_api.py b/cve-agency-manager/cve_tracking/util/gitee_api.py
index 52608ae9739f4b6e4d912633f7a4c886453591c1..24aa3151256d5411f2799a31fbe2f666c59486ca 100644
--- a/cve-agency-manager/cve_tracking/util/gitee_api.py
+++ b/cve-agency-manager/cve_tracking/util/gitee_api.py
@@ -15,7 +15,7 @@ This is a helper script for working with gitee.com
"""
import os
-from conf import CONFIG
+from conf import settings
from exception import ConfigNotFoundError
from logger import logger
from . import Api
@@ -25,71 +25,42 @@ class Gitee(Api):
"""
Gitee is a helper class to abstract gitee.com api
"""
- token = os.getenv("TRACK_GITEE_TOKEN", CONFIG.GITEE_TOKEN)
- time_format = "%Y-%m-%dT%H:%M:%S%z"
+ host = "https://gitee.com/api/v5/repos"
def __init__(self):
+ super(Gitee, self).__init__()
+ self.token = os.getenv("TRACK_GITEE_TOKEN", settings.token(name="gitee"))
if not self.token:
raise ConfigNotFoundError(
- "Please set the 'TRACK_GITEE_TOKEN' environment variables of gitee.")
- self.src_openeuler_url = "https://gitee.com/src-openeuler/{repo}/raw/{br}/"
- self.advisor_url = "https://gitee.com/openeuler/openEuler-Advisor/raw/master/"
- super(Gitee, self).__init__()
+ "Please set the 'TRACK_GITEE_TOKEN' environment variables of gitee."
+ )
- def _set_token(self):
- parameters = {"access_token": self.token}
- return parameters
-
- async def _post_issue(self, title, body):
+ async def create_issue(self, title, body):
"""
- Post new issue
+ Create issue in gitee
"""
- issues_url = f"https://gitee.com/api/v5/repos/{self._owner}/issues"
+ issues_url = f"{self.host}/{self._owner}/issues"
parameters = self._set_token()
parameters["repo"] = self._repo
parameters["title"] = title
parameters["body"] = body
return await self._post(issues_url, parameters)
- async def create_issue(
- self,
- version="",
- branch="master",
- title=None,
- body=None,
- ):
- """
- Create issue in gitee
- """
- title = title or f"Upgrade {self._repo} to {version} in {branch}"
- body = (
- body
- or """This issue is automatically created by openEuler-Advisor.
- Please check the correspond PR is accepted before close it.
- Thanks.
- Yours openEuler-Advisor."""
- )
- return await self._post_issue(title, body)
-
async def create_issue_comment(self, number, body):
"""
create issue comment
"""
- url = (
- f"https://gitee.com/api/v5/repos/{self._owner}/{self._repo}/issues/{number}/comments"
- )
+ url = f"{self.host}/{self._owner}/{self._repo}/issues/{number}/comments"
values = self._set_token()
values["body"] = body
- logger.info(f'Add comment to issue url: {url}')
+ logger.info(f"Add comment to issue url: {url}")
return await self._post(url, values)
async def create_pr_comment(self, number, body):
"""
Post comment to the given specific PR
"""
- url = (
- f"https://gitee.com/api/v5/repos/{self._owner}/{self._repo}/pulls/{number}/comments"
- )
+ url = f"{self.host}/{self._owner}/{self._repo}/pulls/{number}/comments"
values = self._set_token()
values["body"] = body
return self._post(url, values)
diff --git a/cve-agency-manager/cve_tracking/util/github_api.py b/cve-agency-manager/cve_tracking/util/github_api.py
index 312ca95c05aca8605283c564eae192d7d3b2b215..b607a3c3b85c745403894ab66b46dbce8d3027ae 100644
--- a/cve-agency-manager/cve_tracking/util/github_api.py
+++ b/cve-agency-manager/cve_tracking/util/github_api.py
@@ -10,24 +10,29 @@
# PURPOSE.
# See the Mulan PSL v2 for more details.
# ******************************************************************************/
+import os
+
from lxml import etree
-from exception import RequestError
-from logger import logger
+from conf import settings
from . import Api
class Github(Api):
- host = "https://api.github.com"
+ host = "https://api.github.com/repos"
def __init__(self) -> None:
super(Github, self).__init__()
+ self.token = os.getenv("GITHUB_TOKEN", settings.token(name="github"))
@property
def _headers(self):
- return {
- "accept": "application/vnd.github.v3+json",
+ headers = {
+ "Accept": "application/vnd.github.v3+json",
}
+ if self.token:
+ headers["Authorization"] = f"token {self.token}"
+ return headers
async def get_pull_commits(self, pull_number, page=100, curpage=1):
"""
@@ -37,8 +42,17 @@ class Github(Api):
:param curpage: current page number
:return: response
"""
- url = f"{self.host}/repos/{self._owner}/{self._repo}/pulls/{pull_number}/commits"
- return await self._get(url, params=dict(per_page=page, page=curpage), headers=self._headers)
+ url = f"{self.host}/{self._owner}/{self._repo}/pulls/{pull_number}/commits"
+ return await self._get(
+ url, params=dict(per_page=page, page=curpage), headers=self._headers
+ )
+
+ async def get_pull_request(self, pull_number):
+ """
+ Get a pull request
+ """
+ url = f"{self.host}/{self._owner}/{self._repo}/pulls/{pull_number}"
+ return await self._get(url, headers=self._headers)
async def get_issue(self, issue_number):
"""
@@ -46,7 +60,7 @@ class Github(Api):
:param issue_number: issue num
:return: response
"""
- url = f"{self.host}/repos/{self._owner}/{self._repo}/issues/{issue_number}"
+ url = f"{self.host}/{self._owner}/{self._repo}/issues/{issue_number}"
return await self._get(url, headers=self._headers)
async def get_issue_comments(self, issue_number):
@@ -55,16 +69,24 @@ class Github(Api):
:param issue_number: issue num
:return: response
"""
- url = f"{self.host}/repos/{self._owner}/{self._repo}/issues/{issue_number}/comments"
+ url = f"{self.host}/{self._owner}/{self._repo}/issues/{issue_number}/comments"
+
return await self._get(url, headers=self._headers)
+ async def create_issue(self, title, body):
+ url = f"{self.host}/{self._owner}/{self._repo}/issues"
+ params = {"title": title, "body": body}
+ return await self._post(
+ url=url, values=None, json=params, headers=self._headers
+ )
+
async def check_pull_merged(self, pull_number):
"""
Check pr status
:param pull_number: pull number
:return: response
"""
- url = f"{self.host}/repos/{self._owner}/{self._repo}/pulls/{pull_number}/merge"
+ url = f"{self.host}/{self._owner}/{self._repo}/pulls/{pull_number}/merge"
response = await self._get(url, headers=self._headers)
if response.status_code == 204:
return True
@@ -89,19 +111,23 @@ class Github(Api):
if issue_url:
response = await self._get(url=issue_url, text=True)
else:
- response = await self._get(url=f"https://github.com/{self._owner}/{self._repo}/issues/{issue_number}",
- text=True)
+ response = await self._get(
+ url=f"https://github.com/{self._owner}/{self._repo}/issues/{issue_number}",
+ text=True,
+ )
if not response:
return []
html = etree.HTML(response)
link = html.xpath(
"//div[contains(@class,'discussion-sidebar-item') and contains(@class,'js-discussion-sidebar-item') "
- "and last() ]/form/div[contains(@class,'css-truncate')]")
+ "and last() ]/form/div[contains(@class,'css-truncate')]"
+ )
if link:
return self._pull(pulls=link[-1].xpath("./a/@href"))
# Search for operation logs
pulls = html.xpath(
- "//div[@class='TimelineItem']/div[@class='TimelineItem-body']//div[contains(@class,'flex-auto')]/a/@href")
+ "//div[@class='TimelineItem']/div[@class='TimelineItem-body']//div[contains(@class,'flex-auto')]/a/@href"
+ )
return self._pull(pulls=pulls)
diff --git a/cve-agency-manager/cve_tracking/util/gitlab_api.py b/cve-agency-manager/cve_tracking/util/gitlab_api.py
index 97b985fd2d2bfe5f4c9f8ed3186c1820dad3a17d..a127e8a63741bc46079e6a965818bf00e4b9ad03 100644
--- a/cve-agency-manager/cve_tracking/util/gitlab_api.py
+++ b/cve-agency-manager/cve_tracking/util/gitlab_api.py
@@ -13,23 +13,23 @@
import os
from exception import ConfigNotFoundError
-from conf import CONFIG
+from conf import CONFIG, settings
from . import Api
class Gitlab(Api):
- host = "https://gitlab.com"
+ host = "https://gitlab.com/api/v4/projects"
def __init__(self) -> None:
- if not CONFIG.GITLAB_TOKEN and not os.getenv("GITLAB-TOKEN", None):
- raise ConfigNotFoundError("Please set the 'GITLAB-TOKEN' environment variable for gitlab.")
+ if not settings.token(name="gitlab") and not os.getenv("GITLAB-TOKEN", None):
+ raise ConfigNotFoundError(
+ "Please set the 'GITLAB-TOKEN' environment variable for gitlab."
+ )
super(Gitlab, self).__init__()
@property
def _headers(self):
- return {
- "PRIVATE-TOKEN": os.getenv("GITLAB-TOKEN", CONFIG.GITLAB_TOKEN)
- }
+ return {"PRIVATE-TOKEN": os.getenv("GITLAB-TOKEN", settings.token('gitlab'))}
async def get_commit_comments(self, commit_sha):
"""
@@ -37,7 +37,7 @@ class Gitlab(Api):
:param commit_sha: The commit hash or name of a repository branch or tag
"""
- url = f"{self.host}/api/v4/projects/{self._owner}%2F{self._repo}/repository/commits/{commit_sha}/comments"
+ url = f"{self.host}/{self._owner}%2F{self._repo}/repository/commits/{commit_sha}/comments"
return await self._get(url, headers=self._headers)
async def get_single_mr(self, merge_request_iid):
@@ -46,7 +46,7 @@ class Gitlab(Api):
:param merge_request_iid: The internal ID of the merge request.
"""
- url = f"{self.host}/api/v4/projects/{self._owner}%2F{self._repo}/merge_requests/{merge_request_iid}"
+ url = f"{self.host}/{self._owner}%2F{self._repo}/merge_requests/{merge_request_iid}"
return await self._get(url, headers=self._headers)
async def get_mr_comments(self, merge_request_iid):
@@ -56,7 +56,7 @@ class Gitlab(Api):
:param merge_request_iid: The internal ID of the merge request.
"""
- url = f"{self.host}/api/v4/projects/{self._owner}%2F{self._repo}/merge_requests/{merge_request_iid}/notes"
+ url = f"{self.host}/{self._owner}%2F{self._repo}/merge_requests/{merge_request_iid}/notes"
return await self._get(url, headers=self._headers)
async def get_mr_context_commits(self, merge_request_iid):
@@ -66,7 +66,7 @@ class Gitlab(Api):
:param merge_request_iid: The internal ID of the merge request.
"""
- url = f"{self.host}/api/v4/projects/{self._owner}%2F{self._repo}/merge_requests/{merge_request_iid}/commits"
+ url = f"{self.host}/{self._owner}%2F{self._repo}/merge_requests/{merge_request_iid}/commits"
return await self._get(url, headers=self._headers)
@@ -76,5 +76,17 @@ class Gitlab(Api):
:param issue_id: The internal ID of a project issue
"""
- url = f"{self.host}/api/v4/projects/{self._owner}%2F{self._repo}/issues/{issue_id}/closed_by"
+ url = f"{self.host}/{self._owner}%2F{self._repo}/issues/{issue_id}/closed_by"
return await self._get(url, headers=self._headers)
+
+ async def create_issue(self, title, body):
+ """
+ Creates a new project issue.
+ :param title: issue title
+ :param body: issue body
+ :return:
+ """
+ url = f"{self.host}/{self._owner}%2F{self._repo}/issues"
+ params = {'title': title,
+ 'description': body}
+ return await self._post(url, headers=self._headers, values=params)