diff options
Diffstat (limited to 'lib/cve_checker/tables.py')
-rwxr-xr-x | lib/cve_checker/tables.py | 695 |
1 files changed, 695 insertions, 0 deletions
diff --git a/lib/cve_checker/tables.py b/lib/cve_checker/tables.py new file mode 100755 index 00000000..252d109f --- /dev/null +++ b/lib/cve_checker/tables.py @@ -0,0 +1,695 @@ +# +# ex:ts=4:sw=4:sts=4:et +# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- +# +# Security Response Tool Implementation +# +# Copyright (C) 2023 Wind River Systems +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +# +# NOTICE: Important ToasterTable implementation concepts and limitations +# +# 1) The order of table method execution. This implies that data added +# to the table object in "get_context_data" is NOT persistent. +# +# a) __init__ +# b) get_context_data +# c) __init__ (second call reason unknown) +# d) setup_queryset +# e) setup_filters (if present) +# f) setup_columns +# g) apply_row_customization (if present) +# +# 2) Named URL path arguments from "urls.py" are accessible via kwargs +# WARNING: these values not NOT available in "__init__" +# +# Example: +# urls.ps : url(r'^foo/(?P<my_value>\d+)$', +# tables.py: my_value = int(kwargs['my_value']) +# +# 3) Named URL query arguments the table's url are accessible via the request +# +# Example: +# url : http://.../foo/bar/42605?my_value=25 +# tables.py: my_value = self.request.GET.get('my_value','0') +# +# 4) The context[] values are NOT present in the individual "setup_columns" context +# They must be explicitly implemented into the individual column data without Django translation +# +# 5) The HTML page's templatetags are NOT present in the "setup_columns" context +# They must be explicitly added into the template code +# +# Example: +# static_data_template = ''' +# {% load jobtags %}<span onclick="toggle_select(\'box_{{data.id}}\');">{{data.recommend|recommend_display}}</span> +# ''' +# +# WARNING: because there is no context (#4), you cannot for example use dictionary lookup filters +# use apply_row_customization() method instead, and set the self.dict_name in setup_columns() +# + +import os +import re +import json +from datetime import timedelta, datetime, date +import pytz +import traceback + +from django.db.models import Q, Max, Sum, Count, When, Case, Value, IntegerField +from django.urls import re_path as url +from django.urls import reverse, resolve +from django.http import HttpResponse +from django.views.generic import TemplateView + +from srtgui.widgets import ToasterTable +from cve_checker.models import Ck_Audit, Ck_Package, Ck_Product, Ck_Layer, CkPackage2CkProduct, CkPackage2Cve, CkUploadManager +from orm.models import Cve, Product +from orm.models import Notify, NotifyAccess, NotifyCategories +from orm.models import DataSource, SrtSetting, Job +from users.models import SrtUser, UserSafe +from srtgui.api import execute_process + +from srtgui.tablefilter import TableFilter +from srtgui.tablefilter import TableFilterActionToggle +from srtgui.tablefilter import TableFilterActionDateRange +from srtgui.tablefilter import TableFilterActionDay + +# quick development/debugging support +from srtgui.api import _log + +class CveCheckerAuditsTable(ToasterTable): + """Table of All CvecheckerRecord audits""" + + def __init__(self, *args, **kwargs): + super(CveCheckerAuditsTable, self).__init__(*args, **kwargs) + self.default_orderby = "-id" + + def get_context_data(self, **kwargs): + create_time = datetime.now(pytz.utc) + context = super(CveCheckerAuditsTable, self).get_context_data(**kwargs) + context['orm_products'] = Product.objects.all().order_by('name') + context['ab_sets'] = ("master","nanbield","mickledore","langdale","kirkstone","dunfell") + context['new_audit_name'] = 'audit_%s' % (create_time.strftime('%Y%m%d')) + context['default_product'] = 'Yocto Project master' + context['srt_cvechecker_update'] = SrtSetting.get_setting('SRT_CVECHECKER_UPDATE','') + context['mru'] = Job.get_recent() + context['mrj_type'] = 'all' + # Enforce at least the "Upload" import + ck_import_obj,created = CkUploadManager.objects.get_or_create(name='Upload') + if created: + ck_import_obj.order = 1 + ck_import_obj.import_mode = 'Upload' + ck_import_obj.path = '' + ck_import_obj.pem = '' + ck_import_obj.repo = '' + ck_import_obj.branch = '' + ck_import_obj.auto_refresh = False + ck_import_obj.select_refresh = datetime.now(pytz.utc) + ck_import_obj.select_list = "master|nanbield|mickledore|langdale|kirkstone|dunfell" + ck_import_obj.save() + ck_import_obj,created = CkUploadManager.objects.get_or_create(name='Import from Auto Builder scan') + if created: + ck_import_obj.order = 2 + ck_import_obj.import_mode = 'Repo' + ck_import_obj.path = 'yocto-metrics/cve-check' + ck_import_obj.pem = '' + ck_import_obj.repo = 'git://git.yoctoproject.org/yocto-metrics' + ck_import_obj.branch = '' + ck_import_obj.auto_refresh = True + ck_import_obj.select_refresh = datetime.now(pytz.utc) + ck_import_obj.select_list = "master|nanbield|mickledore|langdale|kirkstone|dunfell" + ck_import_obj.save() + context['ckuploadmanager'] = CkUploadManager.objects.all().order_by('order') + # Update the Import select tables + cmnd = ["bin/cve_checker/srtool_cvechecker.py","--update-imports","-f"] + result_returncode,result_stdout,result_stderr = execute_process(*cmnd) + if 0 != result_returncode: + _log(f"ERROR:{cmnd}: {result_stderr}:{result_stdout}:") + return context + + def setup_queryset(self, *args, **kwargs): + self.queryset = Ck_Audit.objects.all() + self.queryset = self.queryset.order_by('-id') + + def setup_filters(self, *args, **kwargs): + pass + + def setup_columns(self, *args, **kwargs): + + self.add_column(title="Id", + field_name="id", + hideable=False, + orderable=True, + ) + + name_template = ''' + <span id="audit_name-disp-{{data.id}}"><td><a href="{% url 'cvechecker_audit' data.id %}">{{data.name}}</a></td></span> + <span id="audit_name-edit-{{data.id}}" style="display:none;"> + <input type="text" id="audit_name-text-{{data.id}}" value="{{data.name}}" size="50"> + </span> + ''' + self.add_column(title="Name", + orderable=True, + static_data_name="name", + static_data_template=name_template, + ) + + self.add_column(title="Create Time", + field_name="create_time", + hideable=True, + hidden=True, + orderable=True, + ) + + ck_package_link_template = ''' + <td><a href="{% url 'cvechecker_audit' data.id %}">{{data.get_package_count}}</a></td> + ''' + self.add_column(title="Package Count", + static_data_name="count", + static_data_template=ck_package_link_template, + ) + + self.add_column(title="Unpatched CVE", + static_data_name="unpatched_count", + static_data_template='<b><label style="color:DarkRed">{{data.get_unpatched_count}}</label></b>', + ) + self.add_column(title="Ignored CVE", + static_data_name="ignored_count", + static_data_template='<label style="color:green">{{data.get_ignored_count}}</label>', + ) + self.add_column(title="Patched CVE", + static_data_name="patched_count", + static_data_template='<label style="color:green">{{data.get_patched_count}}</label>', + ) + self.add_column(title="Undefined CVE", + static_data_name="undefined_count", + static_data_template='<label style="color:DarkRed">{{data.get_undefined_count}}</label>', + hideable=True, + hidden=True, + ) + + self.add_column(title="YP Release", + static_data_name="orm_product__profile", + static_data_template='{{data.orm_product.long_name}}', + orderable=True, + ) + + if UserSafe.is_contributor(self.request.user): + manage_link_template = ''' + <span class="glyphicon glyphicon-edit edit-ck-entry" id="edit-entry-{{data.id}}" x-data="{{data.id}}"></span> + <span class="glyphicon glyphicon glyphicon glyphicon-ok save-ck-entry" id="save-entry-{{data.id}}" x-data="{{data.id}}" style="display:none;color: Chartreuse;"></span> + + <span class="glyphicon glyphicon glyphicon glyphicon-remove cancel-ck-entry" id="cancel-entry-{{data.id}}" x-data="{{data.id}}" style="display:none;color: Crimson;"></span> + <span class="glyphicon glyphicon-trash trash-audit" x-data="{{data.create_time}}|{{data.id}}"></span> + ''' + self.add_column(title="Manage", + hideable=True, + static_data_name="manage", + static_data_template=manage_link_template, + ) + + +class CveCheckerAuditTable(ToasterTable): + """Table of All entries in CvecheckerRecord""" + + def __init__(self, *args, **kwargs): + super(CveCheckerAuditTable, self).__init__(*args, **kwargs) + self.default_orderby = "name" + + def get_context_data(self, **kwargs): + context = super(CveCheckerAuditTable, self).get_context_data(**kwargs) + audit_id = int(kwargs['audit_id']) + context['Ck_Audit'] = Ck_Audit.objects.get(id=audit_id) + context['mru'] = Job.get_recent() + context['mrj_type'] = 'all' + return context + + def setup_queryset(self, *args, **kwargs): + audit_id = int(kwargs['audit_id']) + self.queryset = Ck_Package.objects.filter(ck_audit_id=audit_id) + self.queryset = self.queryset.order_by(self.default_orderby) + + def setup_filters(self, *args, **kwargs): + # Status filter + is_status = TableFilter(name="is_status", title="Status") + audit_id = int(kwargs['audit_id']) + status_filter = TableFilterActionToggle( + "unpatched", + "Unpatched", + Q(unpatched_cnt__gt=0)) + is_status.add_action(status_filter) + status_filter = TableFilterActionToggle( + "patched/ignored", + "Patched/Ignored", + Q(unpatched_cnt=0)) + is_status.add_action(status_filter) + self.add_filter(is_status) + + def setup_columns(self, *args, **kwargs): + + self.add_column(title="Id", + field_name="id", + hideable=True, + hidden=True, + ) + + self.add_column(title="Name", + field_name="name", + hideable=False, + orderable=True, + ) + + self.add_column(title="Version", + field_name="version", + hideable=False, + orderable=True, + ) + + self.add_column(title="Layer", + field_name="ck_layer", + hideable=False, + orderable=True, + static_data_name="ck_layer", + static_data_template="{{data.ck_layer.name}}", + ) + + issue_link_template = ''' + <a href="{% url 'cvechecker_issue' data.id %}">{{data.get_issue_count}}</a> + ''' + self.add_column(title="Issues", + static_data_name="issue_count", + static_data_template=issue_link_template, + ) + + unpatched_link_template = ''' + <label style="color:{% if data.unpatched_cnt %}DarkRed{% else %}green{% endif %}">{{data.unpatched_cnt}}</label> + ''' + self.add_column(title="Unpatched CVE", + filter_name="is_status", + static_data_name="unpatched_count", + static_data_template=unpatched_link_template, + ) + + product_link_template = ''' + <td><a href="{% url 'cvechecker_product' data.id %}">{{data.get_product_names}}</a></td> + ''' + self.add_column(title="Products (cvesInRecord)", + static_data_name="product_count", + static_data_template=product_link_template, + ) + + +class CveCheckerAuditCveTable(ToasterTable): + """Table of All entries in CvecheckerRecord""" + + def __init__(self, *args, **kwargs): + super(CveCheckerAuditCveTable, self).__init__(*args, **kwargs) + self.default_orderby = "orm_cve__name" + + def get_context_data(self, **kwargs): + context = super(CveCheckerAuditCveTable, self).get_context_data(**kwargs) + audit_id = int(kwargs['audit_id']) + context['Ck_Audit'] = Ck_Audit.objects.get(id=audit_id) + context['mru'] = Job.get_recent() + context['mrj_type'] = 'all' + return context + + def setup_queryset(self, *args, **kwargs): + audit_id = int(kwargs['audit_id']) + self.queryset = CkPackage2Cve.objects.filter(ck_audit_id=audit_id) + self.queryset = self.queryset.order_by(self.default_orderby) + + def setup_filters(self, *args, **kwargs): + # Status filter + is_status = TableFilter(name="is_status", title="Status") + for status_id in range(CkPackage2Cve.UNPATCHED,CkPackage2Cve.PATCHED+1): + status_filter = TableFilterActionToggle( + CkPackage2Cve.CK_STATUS[status_id][1], + CkPackage2Cve.CK_STATUS[status_id][1], + Q(ck_status=status_id)) + is_status.add_action(status_filter) + self.add_filter(is_status) + + def setup_columns(self, *args, **kwargs): + + self.add_column(title="Id", + field_name="id", + hideable=True, + hidden=True, + ) + + cve_link_template = ''' + <a href="{% url 'cve' data.orm_cve.name %}" target="_blank">{{data.orm_cve.name}}</a> + ''' + self.add_column(title="Name", + static_data_name="orm_cve__name", + static_data_template=cve_link_template, + hideable=False, + orderable=True, + ) + + self.add_column(title="Status", + filter_name="is_status", + static_data_name="status", + static_data_template="{{data.get_status_text}}", + ) + + self.add_column(title="V3 Severity", + orderable=True, + static_data_name="orm_cve__cvssV3_baseSeverity", + static_data_template="{{data.orm_cve.cvssV3_baseSeverity}}", + ) + + self.add_column(title="V3 Score", + orderable=True, + static_data_name="orm_cve__cvssV3_baseScore", + static_data_template="{{data.orm_cve.cvssV3_baseScore}}", + ) + + self.add_column(title="V2 Severity", + orderable=True, + static_data_name="data.orm_cve__cvssV2_severity", + static_data_template="{{data.orm_cve.cvssV2_severity}}", + ) + + self.add_column(title="V2 Score", + orderable=True, + static_data_name="data.orm_cve__cvssV2_baseScore", + static_data_template="{{data.orm_cve.cvssV2_baseScore}}", + ) + + self.add_column(title="Published", + static_data_name="data.orm_cve__publishedDate", + static_data_template="{{data.orm_cve.publishedDate}}", + ) + + self.add_column(title="Package", + orderable=True, + static_data_name="ck_package__name", + static_data_template="{{data.ck_package.name}}", + ) + + +class CveCheckerIssueTable(ToasterTable): + """Table of Issues in CvecheckerRecord""" + + def __init__(self, *args, **kwargs): + super(CveCheckerIssueTable, self).__init__(*args, **kwargs) + self.default_orderby = "orm_cve__name" + + def get_context_data(self, **kwargs): + context = super(CveCheckerIssueTable, self).get_context_data(**kwargs) + package_id = int(kwargs['package_id']) + context['Ck_Package'] = Ck_Package.objects.get(id=package_id) + context['mru'] = Job.get_recent() + context['mrj_type'] = 'all' + return context + + def setup_queryset(self, *args, **kwargs): + package_id = int(kwargs['package_id']) + self.queryset = CkPackage2Cve.objects.filter(ck_package_id=package_id) + self.queryset = self.queryset.order_by(self.default_orderby) + + def setup_filters(self, *args, **kwargs): + # Status filter + is_status = TableFilter(name="is_status", title="Status") + for status_id in range(CkPackage2Cve.UNPATCHED,CkPackage2Cve.PATCHED+1): + status_filter = TableFilterActionToggle( + CkPackage2Cve.CK_STATUS[status_id][1], + CkPackage2Cve.CK_STATUS[status_id][1], + Q(ck_status=status_id)) + is_status.add_action(status_filter) + self.add_filter(is_status) + + def setup_columns(self, *args, **kwargs): + + cve_link_template = ''' + <a href="{% url 'cve' data.orm_cve.name %}" target="_blank">{{data.orm_cve.name}}</a> + ''' + self.add_column(title="Issue", + static_data_name="orm_cve__name", + static_data_template=cve_link_template, + hideable=False, + orderable=True, + ) + + self.add_column(title="CK Status", + filter_name="is_status", + static_data_name="ck_status", + static_data_template="{{data.get_status_text}}", + hideable=False, + orderable=True, + ) + + self.add_column(title="description", + static_data_name="orm_cve__description", + static_data_template="{{data.orm_cve.description}}", + hideable=False, + orderable=True, + ) + + self.add_column(title="V3 Score", + static_data_name="orm_cve__cvssV3_baseScore", + static_data_template="{{data.orm_cve.cvssV3_baseScore}}", + hideable=False, + orderable=True, + ) + + self.add_column(title="V3 Severity", + static_data_name="orm_cve__cvssV3_baseSeverity", + static_data_template="{{data.orm_cve.cvssV3_baseSeverity}}", + hideable=False, + ) + + self.add_column(title="V2 Score", + static_data_name="orm_cve__cvssV2_baseScore", + static_data_template="{{data.orm_cve.cvssV2_baseScore}}", + hideable=True, + ) + + self.add_column(title="V2 Severity", + static_data_name="orm_cve__cvssV2_severity", + static_data_template="{{data.orm_cve.cvssV2_severity}}", + hideable=True, + ) + + self.add_column(title="Publish Date", + static_data_name="orm_cve__publishedDate", + static_data_template="{{data.orm_cve.publishedDate}}", + hideable=True, + ) + + self.add_column(title="Last Modified Date", + static_data_name="orm_cve__lastModifiedDate", + static_data_template="{{data.orm_cve.lastModifiedDate}}", + hideable=True, + ) + + +class CveCheckerProductTable(ToasterTable): + """Table of All entries in CvecheckerRecord""" + + def __init__(self, *args, **kwargs): + super(CveCheckerProductTable, self).__init__(*args, **kwargs) + self.default_orderby = "ck_product__name" + + def get_context_data(self, **kwargs): + context = super(CveCheckerProductTable, self).get_context_data(**kwargs) + package_id = int(kwargs['package_id']) + context['Ck_Package'] = Ck_Package.objects.get(id=package_id) + context['mru'] = Job.get_recent() + context['mrj_type'] = 'all' + return context + + def setup_queryset(self, *args, **kwargs): + package_id = int(kwargs['package_id']) + self.queryset = CkPackage2CkProduct.objects.filter(ck_package_id=package_id) + self.queryset = self.queryset.order_by(self.default_orderby) + + def setup_filters(self, *args, **kwargs): + pass + + def setup_columns(self, *args, **kwargs): + + self.add_column(title="Product", + static_data_name="ck_product__name", + static_data_template="{{data.ck_product.name}}", + hideable=False, + orderable=True, + ) + + self.add_column(title="CvesInRecord", + static_data_name="cvesInRecord", + static_data_template="{{data.cvesInRecord}}", + hideable=False, + orderable=True, + ) + + +class CveCheckerImportManagementTable(ToasterTable): + """Table of Audit import meta-management """ + + def __init__(self, *args, **kwargs): + super(CveCheckerImportManagementTable, self).__init__(*args, **kwargs) + self.default_orderby = "order" + + def get_context_data(self, **kwargs): + context = super(CveCheckerImportManagementTable, self).get_context_data(**kwargs) + return context + + def setup_queryset(self, *args, **kwargs): + self.queryset = CkUploadManager.objects.all() + self.queryset = self.queryset.order_by(self.default_orderby) + + def setup_filters(self, *args, **kwargs): + pass + + def setup_columns(self, *args, **kwargs): + + if UserSafe.is_admin(self.request.user): + self.add_column(title="ID", + field_name="id", + hideable=True, + hidden = True, + ) + + order_template = ''' + <span id="audit_order-disp-{{data.id}}"><td><a href="{% url 'cvechecker_audit' data.id %}">{{data.order}}</a></td></span> + <span id="audit_order-edit-{{data.id}}" style="display:none;"> + <input type="text" id="audit_order-text-{{data.id}}" value="{{data.order}}" size="10"> + </span> + ''' + self.add_column(title="Order", + static_data_name="order", + static_data_template=order_template, + orderable=True, + ) + + name_template = ''' + <span id="audit_name-disp-{{data.id}}">{{data.name}}</span> + <span id="audit_name-edit-{{data.id}}" style="display:none;"> + <input type="text" id="audit_name-text-{{data.id}}" value="{{data.name}}" size="20"> + </span> + ''' + self.add_column(title="Title", + static_data_name="name", + static_data_template=name_template, + ) + + mode_template = ''' + <span id="audit_mode-disp-{{data.id}}">{{data.import_mode}}</span> + <span id="audit_mode-edit-{{data.id}}" style="display:none;"> + <select id="audit_mode-text-{{data.id}}" name="audit_mode-text-{{data.id}}"> + <option value="Repo" {% if "Repo" == data.import_mode %}selected{% endif %} >Repo</option> + <option value="SSL" {% if "SSL" == data.import_mode %}selected{% endif %} >SSL</option> + <option value="File" {% if "File" == data.import_mode %}selected{% endif %} >File</option> + </select> + </span> + ''' + self.add_column(title="Mode", + static_data_name="import_mode", + static_data_template=mode_template, + ) + + repo_template = ''' + <span id="audit_repo-disp-{{data.id}}">{{data.repo}}</span> + <span id="audit_repo-edit-{{data.id}}" style="display:none;"> + <input type="text" id="audit_repo-text-{{data.id}}" value="{{data.repo}}" size="30"> + </span> + ''' + self.add_column(title="Repo URL", + static_data_name="repo", + static_data_template=repo_template, + ) + + path_template = ''' + <span id="audit_path-disp-{{data.id}}">{{data.path}}</span> + <span id="audit_path-edit-{{data.id}}" style="display:none;"> + <input type="text" id="audit_path-text-{{data.id}}" value="{{data.path}}" size="30"> + </span> + ''' + self.add_column(title="Path", + static_data_name="path", + static_data_template=path_template, + ) + + pem_template = ''' + <span id="audit_pem-disp-{{data.id}}">{{data.pem}}</span> + <span id="audit_pem-edit-{{data.id}}" style="display:none;"> + <input type="text" id="audit_pem-text-{{data.id}}" value="{{data.pem}}" size="20"> + </span> + ''' + self.add_column(title="Pem File", + static_data_name="pem_file", + static_data_template=pem_template, + ) + + branch_template = ''' + <span id="audit_branch-disp-{{data.id}}">{{data.branch}}</span> + <span id="audit_branch-edit-{{data.id}}" style="display:none;"> + <input type="text" id="audit_branch-text-{{data.id}}" value="{{data.branch}}" size="10"> + </span> + ''' + self.add_column(title="Branch", + static_data_name="branch", + static_data_template=branch_template, + ) + + if False: + refresh_template = ''' + {% if "Upload" == data.name %}{% else %} + <span id="audit_refresh-disp-{{data.id}}">{{data.auto_refresh}}</span> + <span id="audit_refresh-edit-{{data.id}}" style="display:none;"> + <select id="audit_refresh-text-{{data.id}}" name="audit_mode-text-{{data.id}}"> + <option value="False" {% if False == data.auto_refresh %}selected{% endif %} >Absolute choice</option> + <option value="True" {% if True == data.auto_refresh %}selected{% endif %} >Automatic refresh choices</option> + </select> + + </span> + {% endif %} + ''' + self.add_column(title="Auto Refresh", + static_data_name="auto_refresh", + static_data_template=refresh_template, + ) + + self.add_column(title="Select Refresh", + field_name="select_refresh", + hideable=True, + hidden = True, + ) + self.add_column(title="Select List", + field_name="select_list", + hideable=True, + hidden = True, + ) + + if UserSafe.is_contributor(self.request.user): + manage_link_template = ''' + {% if "Upload" == data.name %}Built-in{% else %} + <span class="glyphicon glyphicon-edit edit-ck-entry" id="edit-entry-{{data.id}}" x-data="{{data.id}}"></span> + <span class="glyphicon glyphicon glyphicon glyphicon-ok save-ck-entry" id="save-entry-{{data.id}}" x-data="{{data.id}}" style="display:none;color: Chartreuse;"></span> + + <span class="glyphicon glyphicon glyphicon glyphicon-remove cancel-ck-entry" id="cancel-entry-{{data.id}}" x-data="{{data.id}}" style="display:none;color: Crimson;"></span> + + <span class="glyphicon glyphicon-trash trash-import" x-data="{{data.name}}|{{data.id}}"></span> + {% endif %} + ''' + self.add_column(title="Manage", + hideable=True, + static_data_name="manage", + static_data_template=manage_link_template, + ) |