diff --git a/Kunlun_M/middleware.py b/Kunlun_M/middleware.py index 84797fb0..05fe8cc9 100644 --- a/Kunlun_M/middleware.py +++ b/Kunlun_M/middleware.py @@ -10,7 +10,7 @@ ''' -from web.index.models import ScanTask, ScanResultTask, Rules, Tampers, Project +from web.index.models import ScanTask, ScanResultTask, Rules, Tampers, Project, VendorVulns class SDataMiddleware: @@ -21,12 +21,13 @@ def __call__(self, request): response = self.get_response(request) if request.user.is_authenticated: - request.session["rules_count"] = Rules.objects.all().count() - request.session["project_count"] = Rules.objects.all().count() - request.session["tasks_count"] = ScanTask.objects.all().count() + request.session["rules_count"] = Rules.objects.count() + request.session["project_count"] = Project.objects.count() + request.session["tasks_count"] = ScanTask.objects.count() request.session["tasks_finished_count"] = ScanTask.objects.filter(is_finished=True).count() request.session["tampers_count"] = Tampers.objects.all().count() + request.session["vendor_vuls_count"] = VendorVulns.objects.count() - request.session["vul_count"] = ScanResultTask.objects.all().count() + request.session["vul_count"] = ScanResultTask.objects.filter(is_active=1).count() return response diff --git a/core/__init__.py b/core/__init__.py index 8fb6f01c..df0bc3ff 100644 --- a/core/__init__.py +++ b/core/__init__.py @@ -21,7 +21,8 @@ from django.core.management import call_command from utils.log import log, logger, log_add, log_rm -from utils.utils import get_mainstr_from_filename, get_scan_id +from utils.utils import get_mainstr_from_filename +from utils.status import get_scan_id from utils.web import upload_log from utils.file import load_kunlunmignore diff --git a/core/__version__.py b/core/__version__.py index 1af4262e..4895fc33 100644 --- a/core/__version__.py +++ b/core/__version__.py @@ -7,7 +7,7 @@ __issue_page__ = 'https://github.com/LoRexxar/Kunlun-M/issues/new' __python_version__ = sys.version.split()[0] __platform__ = platform.platform() -__version__ = '2.6.0' +__version__ = '2.6.1' __author__ = 'LoRexxar' __author_email__ = 'LoRexxar@gmail.com' __license__ = 'MIT License' diff --git a/core/cli.py b/core/cli.py index 0b505b1f..3912ff81 100644 --- a/core/cli.py +++ b/core/cli.py @@ -23,7 +23,7 @@ from core.pretreatment import ast_object from utils.export import write_to_file -from utils.log import logger +from utils.log import logger, logger_console from utils.file import Directory, load_kunlunmignore from utils.utils import show_context from utils.utils import ParseArgs @@ -140,7 +140,8 @@ def display_result(scan_id, is_ask=False): logger.info("[Chain] Vul {}".format(sr.id)) for rf in rfs: logger.info("[Chain] {}, {}, {}:{}".format(rf.node_type, rf.node_content, rf.node_path, rf.node_lineno)) - show_context(rf.node_path, rf.node_lineno) + if not show_context(rf.node_path, rf.node_lineno): + logger_console.info(rf.node_source) logger.info( "[SCAN] ending\r\n -------------------------------------------------------------------------") diff --git a/core/console.py b/core/console.py index 28710c6c..a077e6d2 100644 --- a/core/console.py +++ b/core/console.py @@ -27,7 +27,8 @@ from utils.log import logger, logger_console, log, log_add from utils import readlineng as readline -from utils.utils import get_mainstr_from_filename, get_scan_id, file_output_format, show_context +from utils.utils import get_mainstr_from_filename, file_output_format, show_context +from utils.status import get_scan_id from Kunlun_M.settings import HISTORY_FILE_PATH, MAX_HISTORY_LENGTH from Kunlun_M.settings import RULES_PATH, PROJECT_DIRECTORY, LOGS_PATH @@ -1222,17 +1223,21 @@ def command_show(self, *args, **kwargs): # show Vuls Chain ResultFlow = get_resultflow_class(int(self.result_task_id)) - rfs = ResultFlow.objects.filter(vul_id=sr.id) - - if rfs: - logger.info("[Chain] Vul {}".format(sr.id)) - for rf in rfs: - logger.info("[Chain] {}, {}, {}:{}".format(rf.node_type, rf.node_content, - rf.node_path, rf.node_lineno)) - show_context(rf.node_path, rf.node_lineno) - logger.info("[SCAN] ending\r\n -------------------------------------------------------------------------") - logger.warn("[Console] Use 'del vuls ' could delete Wrong vul.") - return + + if ResultFlow: + rfs = ResultFlow.objects.filter(vul_id=sr.id) + + if rfs: + logger.info("[Chain] Vul {}".format(sr.id)) + for rf in rfs: + logger.info("[Chain] {}, {}, {}:{}".format(rf.node_type, rf.node_content, + rf.node_path, rf.node_lineno)) + if not show_context(rf.node_path, rf.node_lineno): + logger_console.info(rf.node_source) + + logger.info("[SCAN] ending\r\n -------------------------------------------------------------------------") + logger.warn("[Console] Use 'del vuls ' could delete Wrong vul.") + return else: logger.error("[Console] ScanTask {} not found id {}. please check you result id.".format(self.result_task_id, key)) diff --git a/core/core_engine/php/parser.py b/core/core_engine/php/parser.py index 3c23226a..f56a0b94 100644 --- a/core/core_engine/php/parser.py +++ b/core/core_engine/php/parser.py @@ -19,7 +19,7 @@ # from asgiref.sync import sync_to_async, async_to_sync from utils.log import logger -from utils.utils import SCAN_ID +from utils.status import SCAN_ID from core.pretreatment import ast_object from core.internal_defines.php.functions import function_dict as php_function_dict diff --git a/core/engine.py b/core/engine.py index 35e8c27f..fff0f9f3 100644 --- a/core/engine.py +++ b/core/engine.py @@ -38,7 +38,7 @@ from utils.utils import show_context from utils.file import FileParseAll, get_line from utils.log import logger -from utils.utils import get_scan_id +from utils.status import get_scan_id from web.index.models import ScanResultTask, NewEvilFunc from web.index.models import get_resultflow_class, check_update_or_new_scanresult diff --git a/core/pretreatment.py b/core/pretreatment.py index 0cc56e78..6167aa66 100644 --- a/core/pretreatment.py +++ b/core/pretreatment.py @@ -344,6 +344,9 @@ async def pre_ast(self): new_filepath = filepath + ".pretty" try: + # 添加新限制,如果js文件内容大于一定程度,则不解析 + if len(code_content) > 3000 or code_content.count('\n') > 500 or code_content.count('\n') < 10: + continue if not os.path.isfile(new_filepath): fi2 = codecs.open(new_filepath, "w", encoding='utf-8', errors='ignore') diff --git a/core/vendors.py b/core/vendors.py index 3d2919b0..661eec8f 100644 --- a/core/vendors.py +++ b/core/vendors.py @@ -23,54 +23,12 @@ from utils.log import logger from utils.file import check_filepath +from utils.utils import compare_vendor, abstract_version from Kunlun_M.const import VENDOR_FILE_DICT, VENDOR_CVIID, vendor_source_match from web.index.models import ProjectVendors, update_and_new_project_vendor, update_and_new_vendor_vuln -from web.index.models import Project, VendorVulns, check_update_or_new_scanresult - - -def abstract_version(vendor_version): - version_reg = '([0-9]+(\.[0-9]+)*)' - result_version = '' - - if re.search(version_reg, vendor_version, re.I): - - p = re.compile(version_reg) - matchs = p.finditer(vendor_version) - - for match in matchs: - result_version = match.group(1) - else: - result_version = False - - return result_version - - -def compare_vendor(vendor_version, compare_version): - - # vendor_version = abstract_version(vendor_version) - compare_version = abstract_version(compare_version) - - vendor_version_list = vendor_version.split('.') - compare_version_list = compare_version.split('.') - - is_smaller_vendor = False - smallest_range = len(vendor_version_list) if len(compare_version_list) > len(vendor_version_list) else len(compare_version_list) - - for i in range(smallest_range): - if int(vendor_version_list[i]) < int(compare_version_list[i]): - is_smaller_vendor = True - return is_smaller_vendor - - if int(vendor_version_list[i]) > int(compare_version_list[i]): - is_smaller_vendor = False - return is_smaller_vendor - - if len(compare_version_list) >= len(vendor_version_list): - is_smaller_vendor = True - - return is_smaller_vendor +from web.index.models import Project, VendorVulns, check_update_or_new_scanresult, get_resultflow_class def get_project_vendor_by_name(vendor_name): @@ -140,9 +98,9 @@ def get_project_by_version(vendor_name, vendor_version): pvs = get_project_vendor_by_name(vendor_name.strip()) for pv in pvs: - pv_version = abstract_version(pv.version) + # pv_version = abstract_version(pv.version) - if not is_need_version_check or compare_vendor(pv_version, vendor_version): + if not is_need_version_check or compare_vendor(pv.version, vendor_version): pid = pv.project_id project = Project.objects.filter(id=pid).first() @@ -167,14 +125,14 @@ def check_and_save_result(task_id, language, vendor_name, vendor_version): :return: """ vvs = get_vendor_vul_by_name(vendor_name.strip()) - vendor_version = abstract_version(vendor_version) + # vendor_version = abstract_version(vendor_version) result_list = [] for vv in vvs: if not vendor_version or compare_vendor(vendor_version, vv.vendor_version): if task_id: - check_update_or_new_scanresult( + sr = check_update_or_new_scanresult( scan_task_id=task_id, cvi_id=VENDOR_CVIID, language=language, @@ -184,6 +142,16 @@ def check_and_save_result(task_id, language, vendor_name, vendor_version): is_unconfirm=False, is_active=True ) + # save into get_resultflow_class + ResultFlow = get_resultflow_class(int(task_id)) + + if sr: + node_source = vv.description + rf = ResultFlow(vul_id=sr.id, node_type='sca_scan', + node_content=vv.title, node_path=vv.reference, + node_source=node_source, node_lineno=0) + rf.save() + else: result_list.append(vv) diff --git a/docs/changelog.md b/docs/changelog.md index 566f2943..9d0bf22c 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -262,4 +262,12 @@ - 将默认日志重置为Debug模式 - 新增组件安全扫描功能@LuckyC4t #158 #144 - 新增search命令用于快速搜索包含某个组件的项目列表,并支持*语法 - - 优化了底层数据结构 \ No newline at end of file + - 优化了底层数据结构 +- 2021-08-13 + - KunLun-M 2.6.1 + - 更新了Web模式页面以适配组件漏洞扫描结果展示 + - 修复了数据可能会导致重复入库的bug + - 更新了web模式的api数据 + - 为基础扫描添加去重功能,现在同一文件泄露的同一问题会被去重。 + - 为JS的语义分析扫描添加硬限制以应对混淆代码。 + \ No newline at end of file diff --git a/rules/base/CVI_5001.py b/rules/base/CVI_5001.py index b6096dc7..9802a1e3 100644 --- a/rules/base/CVI_5001.py +++ b/rules/base/CVI_5001.py @@ -30,7 +30,7 @@ def __init__(self): # 部分配置 self.match_mode = "only-regex" - self.match = ['((password)\\b[\'"]?\\s*[:=(,]+\\s*[\'"]?(\\w{3,})[\'"]?\\b)'] + self.match = ['((password)\\b[\'"]?\\s*[:=(,]+\\s*[\'"]?([^\'"\\s]{3,})[\'"]?\\b)'] # for solidity self.match_name = None diff --git a/templates/dashboard/base.html b/templates/dashboard/base.html index fc9ec89b..f28b2bd1 100644 --- a/templates/dashboard/base.html +++ b/templates/dashboard/base.html @@ -167,6 +167,19 @@
  • Tasks List
  • +
  • + Vendors + + + + + + +
  • Rules diff --git a/templates/dashboard/index.html b/templates/dashboard/index.html index 93a4f981..d5f1e1d9 100644 --- a/templates/dashboard/index.html +++ b/templates/dashboard/index.html @@ -1,7 +1,7 @@ {% extends "dashboard/base.html" %} {% block body %}
    -
    +
    @@ -16,7 +16,7 @@

    {% if request.session.tasks_count %}{{ request.session.tasks_count }}{% else

    -
    +
    @@ -31,7 +31,7 @@

    {% if request.session.project_count %}{{ request.session.project_count }}{

    -
    +
    @@ -47,7 +47,7 @@

    {% if request.session.vul_count %}{{ request.session.vul_count }}{% else %}0

    -
    +
    diff --git a/templates/dashboard/projects/project_detail.html b/templates/dashboard/projects/project_detail.html index be2c4034..39d8a882 100644 --- a/templates/dashboard/projects/project_detail.html +++ b/templates/dashboard/projects/project_detail.html @@ -98,8 +98,9 @@

    Results

    ID CVI ID Language - VulFile Path + VulFile Path/Title Source + Level Type Is Confirm Operate @@ -110,7 +111,12 @@

    Results

    {{ taskresult.cvi_id }} {{ taskresult.language }} {{ taskresult.vulfile_path }} + {% if taskresult.result_type == 'vendor_source_match' %} + {{ taskresult.source_code }} + {% else %} {{ taskresult.source_code }} + {% endif %} + {{ taskresult.level }} {{ taskresult.result_type }} diff --git a/templates/dashboard/projects/projects_list.html b/templates/dashboard/projects/projects_list.html index af052c6e..3344614c 100644 --- a/templates/dashboard/projects/projects_list.html +++ b/templates/dashboard/projects/projects_list.html @@ -35,7 +35,7 @@

    Projects List

    +{% endblock %} + +{% block script %} + + +{% endblock %} \ No newline at end of file diff --git a/templates/dashboard/vendors/vendor_vuln_detail.html b/templates/dashboard/vendors/vendor_vuln_detail.html new file mode 100644 index 00000000..725d0307 --- /dev/null +++ b/templates/dashboard/vendors/vendor_vuln_detail.html @@ -0,0 +1,123 @@ +{% extends "dashboard/base.html" %} +{% block title %}Vendor Vuls Detail{% endblock %} + +{% block body %} +
    + +
    +
    +

    Vendor Vuls Details

    +
    +
    + + +
    + + +
    +
    + +
    + +
    + +
    + +
    + + +
    +
    + +
    +
    {{ vvuln.title }}
    +
    + +
    + +
    + + +
    +
    + +
    +
    {{ vvuln.vendor_name }}
    +
    + +
    + +
    + + +
    +
    + +
    +
    {{ vvuln.vendor_version }}
    +
    + +
    + + +
    + + +
    + +
    + + + + +
    + +
    + + +
    + {% if vvuln.severity > 7 %} + HigH + {% elif vvuln.severity > 4 %} + Medium + {% elif vvuln.severity > 0 %} + Low + {% else %} + {{ vvuln.severity }} + {% endif %} +
    + +
    + +
    + + +
    + + +
    + +
    + + + +
    +{% endblock %} + +{% block script %} + + +{% endblock %} \ No newline at end of file diff --git a/templates/dashboard/vendors/vendors_list.html b/templates/dashboard/vendors/vendors_list.html new file mode 100644 index 00000000..bfe8e434 --- /dev/null +++ b/templates/dashboard/vendors/vendors_list.html @@ -0,0 +1,65 @@ +{% extends "dashboard/base.html" %} +{% block title %}Vendors list{% endblock %} + +{% block body %} +
    +
    +
    +
    +

    Vendors List

    +
    + +
    + + + + + + + + + + {% for vendor in vendors %} + + + + + + + + + + {% endfor %} +
    IDProjectNameVersionLanguageExt
    {{ vendor.id }}{{ vendor.project_id }} {{ vendor.project_name }}{{ vendor.name }}{{ vendor.version }}{{ vendor.language }}{{ vendor.ext }}
    +
    + + + +
    + +
    +
    +{% endblock %} + +{% block script %} + + + + +{% endblock %} \ No newline at end of file diff --git a/templates/dashboard/vendors/vendors_vuln_list.html b/templates/dashboard/vendors/vendors_vuln_list.html new file mode 100644 index 00000000..b71ad261 --- /dev/null +++ b/templates/dashboard/vendors/vendors_vuln_list.html @@ -0,0 +1,62 @@ +{% extends "dashboard/base.html" %} +{% block title %}Vendor Vulns list{% endblock %} + +{% block body %} +
    +
    +
    +
    +

    Vendor Vulns List

    +
    + +
    + + + + + + + + + + + {% for vendorvuln in vendorvulns %} + + + + + + + + + + + + {% endfor %} +
    IDVuln IDTitleSeverityCveVendor NameVendor Version
    {{ vendorvuln.id }}{{ vendorvuln.vuln_id }}{{ vendorvuln.title }}{{ vendorvuln.severity }}{{ vendorvuln.cves }}{{ vendorvuln.vendor_name }}{{ vendorvuln.vendor_version }}
    +
    + + + +
    + +
    +
    +{% endblock %} + +{% block script %} + + + + +{% endblock %} \ No newline at end of file diff --git a/utils/status.py b/utils/status.py new file mode 100644 index 00000000..fe586153 --- /dev/null +++ b/utils/status.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python +# encoding: utf-8 +''' +@author: LoRexxar +@contact: lorexxar@gmail.com +@file: status.py +@time: 2021/8/11 15:31 +@desc: + +''' + +from web.index.models import ScanTask + + +SCAN_ID = -1 + + +def get_scan_id(): + global SCAN_ID + + if SCAN_ID > 0: + return SCAN_ID + else: + s = ScanTask.objects.order_by("-id").first() + SCAN_ID = s.id + + return SCAN_ID diff --git a/utils/utils.py b/utils/utils.py index e3661479..516a0001 100644 --- a/utils/utils.py +++ b/utils/utils.py @@ -21,7 +21,6 @@ import ast from Kunlun_M.settings import RULES_PATH, PROJECT_DIRECTORY -from web.index.models import ScanTask from utils.log import logger, logger_console from utils.file import check_filepath, get_line @@ -37,20 +36,6 @@ OUTPUT_MODE_STREAM = 'stream' PY2 = sys.version_info[0] == 2 -SCAN_ID = -1 - - -def get_scan_id(): - global SCAN_ID - - if SCAN_ID > 0: - return SCAN_ID - else: - s = ScanTask.objects.order_by("-id").first() - SCAN_ID = s.id - - return SCAN_ID - class ParseArgs(object): def __init__(self, target, formatter, output, special_rules=None, language=None, black_path=None, a_sid=None): @@ -709,6 +694,9 @@ def show_context(filename, line_number, show_line=3, is_back=False): if not show_line: return "" + if not int(line_number): + return False + filename = check_filepath(PROJECT_DIRECTORY, filename) line_number = line_number if line_number else 0 @@ -758,3 +746,55 @@ def del_sensitive_for_config(param_config): last_param = param return "".join(result_list) + + +def abstract_version(vendor_version): + version_reg = '([0-9]+(\.[0-9]+)*)' + result_version = '' + + if re.search(version_reg, vendor_version, re.I): + + p = re.compile(version_reg) + matchs = p.finditer(vendor_version) + + for match in matchs: + result_version = match.group(1) + else: + result_version = False + + return result_version + + +def compare_vendor(vendor_version, compare_version): + """ + 返回True则vendor_vernsion < compare_version + :param vendor_version: + :param compare_version: + :return: + """ + + if vendor_version == 'latest': + return False + + vendor_version = abstract_version(vendor_version) + compare_version = abstract_version(compare_version) + + vendor_version_list = vendor_version.split('.') + compare_version_list = compare_version.split('.') + + is_smaller_vendor = False + smallest_range = len(vendor_version_list) if len(compare_version_list) > len(vendor_version_list) else len(compare_version_list) + + for i in range(smallest_range): + if int(vendor_version_list[i]) < int(compare_version_list[i]): + is_smaller_vendor = True + return is_smaller_vendor + + if int(vendor_version_list[i]) > int(compare_version_list[i]): + is_smaller_vendor = False + return is_smaller_vendor + + if len(compare_version_list) >= len(vendor_version_list): + is_smaller_vendor = True + + return is_smaller_vendor \ No newline at end of file diff --git a/web/api/urls.py b/web/api/urls.py index 6878430c..dfdc4690 100644 --- a/web/api/urls.py +++ b/web/api/urls.py @@ -30,4 +30,9 @@ path("rule/list", views.RuleListApiView.as_view(), name="rule_list"), # rule details path("rule/", views.RuleDetailApiView.as_view(), name="rule_detail"), + + # vendor vul list + path("vendorvul/list", views.VendorVulListApiView.as_view(), name="vendor_vul_list"), + # vendor vul details + path("vendorvul/", views.VendorVuLDetailApiView.as_view(), name="vendor_vul_detail"), ] diff --git a/web/api/views.py b/web/api/views.py index fa4f5339..5d75d04e 100644 --- a/web/api/views.py +++ b/web/api/views.py @@ -17,7 +17,7 @@ from django.views.generic import TemplateView from django.views import View -from web.index.models import ScanTask, ScanResultTask, Rules, Tampers, NewEvilFunc, Project, ProjectVendors +from web.index.models import ScanTask, VendorVulns, Rules, Tampers, NewEvilFunc, Project, ProjectVendors from web.index.models import get_and_check_scantask_project_id, get_resultflow_class, get_and_check_scanresult from utils.utils import show_context @@ -154,6 +154,31 @@ class RuleDetailApiView(View): @staticmethod @api_token_required def get(request, rule_cviid): + rules = Rules.objects.filter(svid=rule_cviid).values() return JsonResponse({"code": 200, "status": True, "message": list(rules)}) + + +class VendorVulListApiView(View): + """展示组件漏洞列表""" + + @staticmethod + @api_token_required + def get(request): + vendorvuls = VendorVulns.objects.filter()[:100].values() + + return JsonResponse( + {"code": 200, "status": True, "message": list(vendorvuls)}) + + +class VendorVuLDetailApiView(View): + """展示当前规则细节""" + + @staticmethod + @api_token_required + def get(request, vendor_vul_id): + + vendorvuls = VendorVulns.objects.filter(id=vendor_vul_id).values() + + return JsonResponse({"code": 200, "status": True, "message": list(vendorvuls)}) diff --git a/web/dashboard/controller/project.py b/web/dashboard/controller/project.py index cc7c73a2..34423c00 100644 --- a/web/dashboard/controller/project.py +++ b/web/dashboard/controller/project.py @@ -19,10 +19,12 @@ from django.shortcuts import render, redirect from Kunlun_M.settings import SUPER_ADMIN +from Kunlun_M.const import VUL_LEVEL, VENDOR_VUL_LEVEL + from web.index.controller import login_or_token_required from utils.utils import del_sensitive_for_config -from web.index.models import ScanTask, ScanResultTask, Rules, Tampers, NewEvilFunc, Project, ProjectVendors +from web.index.models import ScanTask, ScanResultTask, Rules, Tampers, NewEvilFunc, Project, ProjectVendors, VendorVulns from web.index.models import get_and_check_scanresult, get_and_check_evil_func @@ -98,6 +100,20 @@ def get(request, project_id): for taskresult in taskresults: taskresult.is_unconfirm = int(taskresult.is_unconfirm) + taskresult.level = 0 + + if taskresult.cvi_id == '9999': + vender_vul_id = taskresult.vulfile_path.split(":")[-1] + + if vender_vul_id: + vv = VendorVulns.objects.filter(id=vender_vul_id).first() + + taskresult.vulfile_path = "[{}:{}]{}".format(vv.vendor_name, vv.vendor_version, vv.title) + taskresult.level = VENDOR_VUL_LEVEL[vv.severity] + + else: + r = Rules.objects.filter(svid=taskresult.cvi_id).first() + taskresult.level = VUL_LEVEL[r.level] if not project: return HttpResponseNotFound('Project Not Found.') diff --git a/web/dashboard/controller/tasks.py b/web/dashboard/controller/tasks.py index 4e72b7d7..c85e710a 100644 --- a/web/dashboard/controller/tasks.py +++ b/web/dashboard/controller/tasks.py @@ -14,10 +14,12 @@ from django.shortcuts import render, redirect from Kunlun_M.settings import SUPER_ADMIN +from Kunlun_M.const import VENDOR_VUL_LEVEL, VUL_LEVEL + from web.index.controller import login_or_token_required from utils.utils import del_sensitive_for_config -from web.index.models import ScanTask, ScanResultTask, Rules, Tampers, NewEvilFunc, Project +from web.index.models import ScanTask, VendorVulns, Rules, Tampers, NewEvilFunc, Project from web.index.models import get_and_check_scantask_project_id, get_and_check_scanresult, get_and_check_evil_func @@ -82,6 +84,20 @@ def get(request, task_id): for taskresult in taskresults: taskresult.is_unconfirm = int(taskresult.is_unconfirm) + taskresult.level = 0 + + if taskresult.cvi_id == '9999': + vender_vul_id = taskresult.vulfile_path.split(":")[-1] + + if vender_vul_id: + vv = VendorVulns.objects.filter(id=vender_vul_id).first() + + taskresult.vulfile_path = "[{}:{}]{}".format(vv.vendor_name, vv.vendor_version, vv.title) + taskresult.level = VENDOR_VUL_LEVEL[vv.severity] + + else: + r = Rules.objects.filter(svid=taskresult.cvi_id).first() + taskresult.level = VUL_LEVEL[r.level] if not task: return HttpResponseNotFound('Task Not Found.') diff --git a/web/dashboard/controller/vendor.py b/web/dashboard/controller/vendor.py new file mode 100644 index 00000000..8ada46f6 --- /dev/null +++ b/web/dashboard/controller/vendor.py @@ -0,0 +1,153 @@ +#!/usr/bin/env python +# encoding: utf-8 +''' +@author: LoRexxar +@contact: lorexxar@gmail.com +@file: vendor.py +@time: 2021/8/11 17:05 +@desc: + +''' + +import ast + +from django.contrib.auth.decorators import login_required +from django.http import JsonResponse, HttpResponseNotFound +from django.views.generic import TemplateView +from django.views import View +from django.shortcuts import render, redirect + +from Kunlun_M.settings import SUPER_ADMIN +from Kunlun_M.const import VENDOR_VUL_LEVEL, VUL_LEVEL + +from web.index.controller import login_or_token_required +from utils.utils import del_sensitive_for_config +from core.vendors import get_vendor_vul_by_name, get_project_vendor_by_name + +from web.index.models import ScanTask, VendorVulns, Rules, ProjectVendors, Project +from web.index.models import get_and_check_scantask_project_id, get_and_check_scanresult, get_and_check_evil_func + + +class VendorListView(TemplateView): + """展示所有的组件""" + template_name = "dashboard/vendors/vendors_list.html" + + def get_context_data(self, **kwargs): + context = super(VendorListView, self).get_context_data(**kwargs) + vendor_count = ProjectVendors.objects.all().count() + + if 'p' in self.request.GET: + page = int(self.request.GET['p']) + else: + page = 1 + + # check page + if page*100 > vendor_count: + page = 1 + + rows = ProjectVendors.objects.all()[(page-1)*100: page*100] + + context['vendors'] = rows + + context['page'] = page + max_page = vendor_count / 100 if vendor_count % 100 == 0 else (vendor_count / 100)+1 + context['max_page'] = max_page + context['page_range'] = range(int(max_page))[1:] + + for row in context['vendors']: + project = Project.objects.filter(id=row.project_id).first() + + row.project_name = project.project_name + + return context + + +class VendorVulnListView(TemplateView): + """展示所有的组件漏洞""" + template_name = "dashboard/vendors/vendors_vuln_list.html" + + def get_context_data(self, **kwargs): + context = super(VendorVulnListView, self).get_context_data(**kwargs) + vendor_vulns_count = VendorVulns.objects.all().count() + + if 'p' in self.request.GET: + page = int(self.request.GET['p']) + else: + page = 1 + + # check page + if page*100 > vendor_vulns_count: + page = 1 + + rows = VendorVulns.objects.all()[(page-1)*100: page*100] + + context['vendorvulns'] = rows + + context['page'] = page + max_page = vendor_vulns_count / 100 if vendor_vulns_count % 100 == 0 else (vendor_vulns_count / 100)+1 + context['max_page'] = max_page + context['page_range'] = range(int(max_page))[1:] + + for vendorvul in context['vendorvulns']: + vendorvul.severity = VENDOR_VUL_LEVEL[vendorvul.severity] + vendorvul.cves = ','.join(ast.literal_eval(vendorvul.cves)) + + return context + + +class VendorDetailView(View): + """展示当前组件细节""" + + @staticmethod + @login_or_token_required + def get(request): + + if "vendorname" in request.GET: + vendor_name = request.GET['vendorname'] + else: + redirect('dashboard:vendors_list') + return + + vs = get_project_vendor_by_name(vendor_name) + projects = [] + vvulns = [] + + for v in vs: + project_id = v.project_id + v_name = v.name + vvs = get_vendor_vul_by_name(v_name.strip()) + p = Project.objects.filter(id=project_id).first() + p.vendor_name = v_name + p.vendor_version = v.version + + projects.append(p) + vvulns.extend(list(vvs)) + + if not len(vs): + return HttpResponseNotFound('Vendor Not Found.') + else: + data = { + 'vendor_name': vendor_name, + 'vendors': vs, + 'projects': projects, + 'vvulns': vvulns, + } + return render(request, 'dashboard/vendors/vendor_detail.html', data) + + +class VendorVulnDetailView(View): + """展示当前组件漏洞细节""" + + @staticmethod + @login_or_token_required + def get(request, vendor_vul_id): + + vvuln = VendorVulns.objects.filter(id=vendor_vul_id).first() + + if not vvuln: + return HttpResponseNotFound('Vendor vuls Not Found.') + else: + data = { + 'vvuln': vvuln, + } + return render(request, 'dashboard/vendors/vendor_vuln_detail.html', data) diff --git a/web/dashboard/urls.py b/web/dashboard/urls.py index 082185d2..b72690d8 100644 --- a/web/dashboard/urls.py +++ b/web/dashboard/urls.py @@ -9,7 +9,7 @@ from django.contrib.auth.decorators import login_required from web.dashboard import views -from web.dashboard.controller import tasks, rules, tampers, project +from web.dashboard.controller import tasks, rules, tampers, project, vendor from web.dashboard.interface import scanresult app_name = "dashboard" @@ -32,6 +32,12 @@ path('tampers/list', login_required(tampers.TamperListView.as_view()), name='tampers_list'), path('tampers/detail/', tampers.TamperDetailView.as_view(), name="tamper_detail"), + # vendor + path('vendors/search', login_required(vendor.VendorDetailView.as_view()), name='vendor_details'), + path('vendors/list', login_required(vendor.VendorListView.as_view()), name='vendors_list'), + path('vendorvulns/', login_required(vendor.VendorVulnDetailView.as_view()), name='vendor_vulns_details'), + path('vendorvulns/list', login_required(vendor.VendorVulnListView.as_view()), name='vendor_vulns_list'), + # docs path("docs", views.docs, name="docs"), diff --git a/web/index/models.py b/web/index/models.py index 51bcab40..15eb162b 100644 --- a/web/index/models.py +++ b/web/index/models.py @@ -2,7 +2,7 @@ from __future__ import unicode_literals import traceback -from MySQLdb._exceptions import IntegrityError +from django.db.utils import IntegrityError from datetime import datetime from django.db import models @@ -13,6 +13,7 @@ from Kunlun_M.const import TAMPER_TYPE from utils.log import logger +from utils.utils import compare_vendor, abstract_version import json import uuid @@ -86,8 +87,18 @@ class VendorVulns(models.Model): def update_and_new_vendor_vuln(vendor, vuln): - v = VendorVulns.objects.filter(vuln_id=vuln["vuln_id"], vendor_name=vendor["name"], vendor_version=vendor["version"]).first() + # v = VendorVulns.objects.filter(vuln_id=vuln["vuln_id"], vendor_name=vendor["name"], vendor_version=vendor["version"]).first() + v = VendorVulns.objects.filter(vuln_id=vuln["vuln_id"]).first() + + # 检查版本比较 if v: + if vuln["version"] and compare_vendor(v.vendor_version, vuln["version"]): + v.vendor_version = vuln["version"] + try: + v.save() + except IntegrityError: + logger.warn("[Model Save] vuln model not changed") + if vuln["title"] != v.title or vuln["cves"] != v.cves: logger.debug("[Vendors] Vuln {} update".format(v.vuln_id)) v.title = vuln["title"] @@ -231,6 +242,10 @@ def get_and_check_scanresult(scan_task_id): def check_update_or_new_scanresult(scan_task_id, cvi_id, language, vulfile_path, source_code, result_type, is_unconfirm, is_active): + # 优化基础扫描结果 + if str(cvi_id).startswith('5'): + vulfile_path = vulfile_path.split(':')[0] + # 如果漏洞hash存在,那么更新信息,如果hash不存在,那么新建漏洞 scan_project_id = get_and_check_scantask_project_id(scan_task_id) vul_hash = md5("{},{},{},{},{}".format(scan_project_id, cvi_id, language, vulfile_path, source_code)) @@ -246,7 +261,7 @@ def check_update_or_new_scanresult(scan_task_id, cvi_id, language, vulfile_path, sr.source_code = source_code sr.result_type = result_type sr.is_unconfirm = is_unconfirm - # sr.is_active =is_active + try: sr.save() except IntegrityError: