-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Add "All Finding Groups" page #12814
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: dev
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,20 +2,28 @@ | |
|
||
from django.contrib import messages | ||
from django.contrib.admin.utils import NestedObjects | ||
from django.core.paginator import Page, Paginator | ||
from django.db.models import Count, Min, Q, QuerySet, Subquery | ||
from django.db.utils import DEFAULT_DB_ALIAS | ||
from django.http import HttpRequest | ||
from django.http.response import HttpResponse, HttpResponseRedirect, JsonResponse | ||
from django.shortcuts import get_object_or_404, render | ||
from django.urls.base import reverse | ||
from django.views import View | ||
from django.views.decorators.http import require_POST | ||
|
||
import dojo.jira_link.helper as jira_helper | ||
from dojo.authorization.authorization import user_has_permission_or_403 | ||
from dojo.authorization.authorization_decorators import user_is_authorized | ||
from dojo.authorization.roles_permissions import Permissions | ||
from dojo.filters import FindingFilter, FindingFilterWithoutObjectLookups | ||
from dojo.filters import ( | ||
FindingFilter, | ||
FindingFilterWithoutObjectLookups, | ||
FindingGroupsFilter, | ||
) | ||
from dojo.finding.queries import prefetch_for_findings | ||
from dojo.forms import DeleteFindingGroupForm, EditFindingGroupForm, FindingBulkUpdateForm | ||
from dojo.models import Engagement, Finding, Finding_Group, GITHUB_PKey, Product | ||
from dojo.models import Dojo_Group, Engagement, Finding, Finding_Group, GITHUB_PKey, Global_Role, Product | ||
from dojo.utils import Product_Tab, add_breadcrumb, get_page_items, get_setting, get_system_setting, get_words_for_field | ||
|
||
logger = logging.getLogger(__name__) | ||
|
@@ -204,3 +212,118 @@ def push_to_jira(request, fgid): | |
"Error pushing to JIRA", | ||
extra_tags="alert-danger") | ||
return HttpResponse(status=500) | ||
|
||
|
||
class ListFindingGroups(View): | ||
filter_name: str = "All" | ||
|
||
SEVERITY_ORDER = { | ||
"Critical": 4, | ||
"High": 3, | ||
"Medium": 2, | ||
"Low": 1, | ||
"Info": 0, | ||
} | ||
|
||
def get_template(self) -> str: | ||
return "dojo/finding_groups_list.html" | ||
|
||
def order_field(self, request: HttpRequest, group_findings_queryset: QuerySet[Finding_Group]) -> QuerySet[Finding_Group]: | ||
order_field_param: str | None = request.GET.get("o") | ||
if order_field_param: | ||
reverse_order = order_field_param.startswith("-") | ||
order_field_param = order_field_param[1:] if reverse_order else order_field_param | ||
if order_field_param in {"name", "creator", "findings_count", "sla_deadline"}: | ||
prefix = "-" if reverse_order else "" | ||
group_findings_queryset = group_findings_queryset.order_by(f"{prefix}{order_field_param}") | ||
return group_findings_queryset | ||
|
||
def filters(self, request: HttpRequest) -> tuple[str, str | None, list[str], list[str]]: | ||
name_filter: str = request.GET.get("name", "").lower() | ||
min_severity_filter: str | None = request.GET.get("severity") | ||
engagement_filter: list[str] = request.GET.getlist("engagement") | ||
product_filter: list[str] = request.GET.getlist("product") | ||
return name_filter, min_severity_filter, engagement_filter, product_filter | ||
|
||
def filter_check(self, request: HttpRequest) -> Q: | ||
name_filter, min_severity_filter, engagement_filter, product_filter = self.filters(request) | ||
q_objects = Q() | ||
if name_filter: | ||
q_objects &= Q(name__icontains=name_filter) | ||
if product_filter: | ||
q_objects &= Q(findings__test__engagement__product__id__in=product_filter) | ||
if engagement_filter: | ||
q_objects &= Q(findings__test__engagement__id__in=engagement_filter) | ||
if min_severity_filter: | ||
min_severity_order_value = self.SEVERITY_ORDER.get(min_severity_filter, -1) | ||
valid_severities_for_filter = [ | ||
sev for sev, order in self.SEVERITY_ORDER.items() if order >= min_severity_order_value | ||
] | ||
q_objects &= Q(findings__severity__in=valid_severities_for_filter) | ||
return q_objects | ||
|
||
def get_findings(self, products: QuerySet[Product] | None) -> tuple[QuerySet[Finding], QuerySet[Finding]]: | ||
filters: dict = {} | ||
if products: | ||
filters["test__engagement__product__in"] = products | ||
user_findings_qs = Finding.objects.filter(**filters) | ||
return user_findings_qs, user_findings_qs.filter(active=True) | ||
|
||
def get_finding_groups(self, request: HttpRequest, products: QuerySet[Product] | None = None) -> QuerySet[Finding_Group]: | ||
finding_groups_queryset = Finding_Group.objects.all() | ||
if products is not None: | ||
user_findings, _ = self.get_findings(products) | ||
finding_groups_queryset = finding_groups_queryset.filter(findings__id__in=Subquery(user_findings.values("id"))).distinct() | ||
request_filters_q = self.filter_check(request) | ||
finding_groups_queryset = finding_groups_queryset.filter(request_filters_q).distinct() | ||
finding_groups_queryset = finding_groups_queryset.annotate( | ||
findings_count=Count("findings", distinct=True), | ||
sla_deadline=Min("findings__sla_expiration_date"), | ||
) | ||
return self.order_field(request, finding_groups_queryset) | ||
|
||
def paginate_queryset(self, queryset: QuerySet[Finding_Group], request: HttpRequest) -> Page: | ||
page_size = int(request.GET.get("page_size", 25)) | ||
paginator = Paginator(queryset, page_size) | ||
page_number = request.GET.get("page") | ||
return paginator.get_page(page_number) | ||
|
||
def get(self, request: HttpRequest) -> HttpResponse: | ||
global_role = Global_Role.objects.filter(user=request.user).first() | ||
user_groups = Dojo_Group.objects.filter(users=request.user) | ||
products = Product.objects.filter(Q(members=request.user) | Q(authorization_groups__in=user_groups)).distinct() | ||
if request.user.is_superuser or (global_role and global_role.role): | ||
Comment on lines
+292
to
+295
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would expect some calls to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. or maybe there should be an |
||
finding_groups = self.get_finding_groups(request) | ||
elif products.exists(): | ||
finding_groups = self.get_finding_groups(request, products) | ||
else: | ||
finding_groups = Finding_Group.objects.none() | ||
|
||
paginated_finding_groups = self.paginate_queryset(finding_groups, request) | ||
|
||
context = { | ||
"filter_name": self.filter_name, | ||
"filtered": FindingGroupsFilter(request.GET), | ||
"finding_groups": paginated_finding_groups, | ||
} | ||
|
||
add_breadcrumb(title="Finding Group", top_level=not request.GET, request=request) | ||
return render(request, self.get_template(), context) | ||
|
||
|
||
class ListOpenFindingGroups(ListFindingGroups): | ||
filter_name: str = "Open" | ||
|
||
def get_finding_groups(self, request: HttpRequest, products: QuerySet[Product] | None = None) -> QuerySet[Finding_Group]: | ||
finding_groups_queryset = super().get_finding_groups(request, products) | ||
_, active_findings = self.get_findings(products) | ||
return finding_groups_queryset.filter(findings__id__in=Subquery(active_findings.values("id"))).distinct() | ||
|
||
|
||
class ListClosedFindingGroups(ListFindingGroups): | ||
filter_name: str = "Closed" | ||
|
||
def get_finding_groups(self, request: HttpRequest, products: QuerySet[Product] | None = None) -> QuerySet[Finding_Group]: | ||
finding_groups_queryset = super().get_finding_groups(request, products) | ||
_, active_findings = self.get_findings(products) | ||
return finding_groups_queryset.exclude(findings__id__in=Subquery(active_findings.values("id"))).distinct() |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
{% extends "base.html" %} | ||
{% load navigation_tags %} | ||
{% load display_tags %} | ||
{% load static %} | ||
{% block content %} | ||
{% comment %} All/Open/Closed Finding Groups {% endcomment %} | ||
{% include "dojo/finding_groups_list_snippet.html" %} | ||
{% endblock %} |
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should this be some case insensitive like
name = CharFilter(lookup_expr="icontains", label="Name")