Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions docs/content/en/usage/features.md
Original file line number Diff line number Diff line change
Expand Up @@ -557,6 +557,9 @@ Product Type Counts

![Product Type Counts](../../images/met_2.png)

Product Tag Counts
: Same as above, but for a group of products sharing a tag.

Simple Metrics
: Provides tabular data for all Product Types. The data displayed in
this view is the total number of S0, S1, S2, S3, S4, Opened This
Expand Down
20 changes: 18 additions & 2 deletions dojo/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -2111,21 +2111,37 @@ def get_years():
return [(now.year, now.year), (now.year - 1, now.year - 1), (now.year - 2, now.year - 2)]


class ProductTypeCountsForm(forms.Form):
class ProductCountsFormBase(forms.Form):
month = forms.ChoiceField(choices=list(MONTHS.items()), required=True, error_messages={
'required': '*'})
year = forms.ChoiceField(choices=get_years, required=True, error_messages={
'required': '*'})


class ProductTypeCountsForm(ProductCountsFormBase):
product_type = forms.ModelChoiceField(required=True,
queryset=Product_Type.objects.none(),
error_messages={
'required': '*'})

def __init__(self, *args, **kwargs):
super(ProductTypeCountsForm, self).__init__(*args, **kwargs)
super().__init__(*args, **kwargs)
self.fields['product_type'].queryset = get_authorized_product_types(Permissions.Product_Type_View)


class ProductTagCountsForm(ProductCountsFormBase):
product_tag = forms.ModelChoiceField(required=True,
queryset=Product.tags.tag_model.objects.none().order_by('name'),
error_messages={
'required': '*'})

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
prods = get_authorized_products(Permissions.Product_View)
tags_available_to_user = Product.tags.tag_model.objects.filter(product__in=prods)
self.fields['product_tag'].queryset = tags_available_to_user


class APIKeyForm(forms.ModelForm):
id = forms.IntegerField(required=True,
widget=forms.widgets.HiddenInput())
Expand Down
4 changes: 4 additions & 0 deletions dojo/locale/en/LC_MESSAGES/django.po
Original file line number Diff line number Diff line change
Expand Up @@ -2692,6 +2692,10 @@ msgstr ""
msgid "Product Type Counts"
msgstr ""

#: dojo/templates/base.html
msgid "Product Tag Counts"
msgstr ""

#: dojo/templates/base.html
msgid "Users"
msgstr ""
Expand Down
2 changes: 2 additions & 0 deletions dojo/metrics/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
views.metrics, name='product_type_metrics'),
re_path(r'^metrics/product/type/counts$',
views.product_type_counts, name='product_type_counts'),
re_path(r'^metrics/product/tag/counts$',
views.product_tag_counts, name='product_tag_counts'),
re_path(r'^metrics/engineer$', views.engineer_metrics,
name='engineer_metrics'),
re_path(r'^metrics/engineer/(?P<eid>\d+)$', views.view_engineer,
Expand Down
164 changes: 161 additions & 3 deletions dojo/metrics/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
from django.utils import timezone

from dojo.filters import MetricsFindingFilter, UserFilter, MetricsEndpointFilter
from dojo.forms import SimpleMetricsForm, ProductTypeCountsForm
from dojo.forms import SimpleMetricsForm, ProductTypeCountsForm, ProductTagCountsForm
from dojo.models import Product_Type, Finding, Product, Engagement, Test, \
Risk_Acceptance, Dojo_User, Endpoint_Status
from dojo.utils import get_page_items, add_breadcrumb, findings_this_period, opened_in_period, count_findings, \
Expand Down Expand Up @@ -586,13 +586,13 @@ def product_type_counts(request):
end_date.month, end_date.day,
tzinfo=timezone.get_current_timezone())

oip = opened_in_period(start_date, end_date, pt)
oip = opened_in_period(start_date, end_date, test__engagement__product__prod_type=pt)

# trending data - 12 months
for x in range(12, 0, -1):
opened_in_period_list.append(
opened_in_period(start_date + relativedelta(months=-x), end_of_month + relativedelta(months=-x),
pt))
test__engagement__product__prod_type=pt))

opened_in_period_list.append(oip)

Expand Down Expand Up @@ -697,6 +697,164 @@ def product_type_counts(request):
)


def product_tag_counts(request):
form = ProductTagCountsForm()
opened_in_period_list = []
oip = None
cip = None
aip = None
all_current_in_pt = None
top_ten = None
pt = None
today = timezone.now()
first_of_month = today.replace(day=1, hour=0, minute=0, second=0, microsecond=0)
mid_month = first_of_month.replace(day=15, hour=23, minute=59, second=59, microsecond=999999)
end_of_month = mid_month.replace(day=monthrange(today.year, today.month)[1], hour=23, minute=59, second=59,
microsecond=999999)
start_date = first_of_month
end_date = end_of_month

if request.method == 'GET' and 'month' in request.GET and 'year' in request.GET and 'product_tag' in request.GET:
form = ProductTagCountsForm(request.GET)
if form.is_valid():
prods = get_authorized_products(Permissions.Product_View)

pt = form.cleaned_data['product_tag']
month = int(form.cleaned_data['month'])
year = int(form.cleaned_data['year'])
first_of_month = first_of_month.replace(month=month, year=year)

month_requested = datetime(year, month, 1)

end_of_month = month_requested.replace(day=monthrange(month_requested.year, month_requested.month)[1],
hour=23, minute=59, second=59, microsecond=999999)
start_date = first_of_month
start_date = datetime(start_date.year,
start_date.month, start_date.day,
tzinfo=timezone.get_current_timezone())
end_date = end_of_month
end_date = datetime(end_date.year,
end_date.month, end_date.day,
tzinfo=timezone.get_current_timezone())

oip = opened_in_period(start_date, end_date,
test__engagement__product__tags__name=pt,
test__engagement__product__in=prods)

# trending data - 12 months
for x in range(12, 0, -1):
opened_in_period_list.append(
opened_in_period(start_date + relativedelta(months=-x), end_of_month + relativedelta(months=-x),
test__engagement__product__tags__name=pt, test__engagement__product__in=prods))

opened_in_period_list.append(oip)

closed_in_period = Finding.objects.filter(mitigated__date__range=[start_date, end_date],
test__engagement__product__tags__name=pt,
test__engagement__product__in=prods,
severity__in=('Critical', 'High', 'Medium', 'Low')).values(
'numerical_severity').annotate(Count('numerical_severity')).order_by('numerical_severity')

total_closed_in_period = Finding.objects.filter(mitigated__date__range=[start_date, end_date],
test__engagement__product__tags__name=pt,
test__engagement__product__in=prods,
severity__in=(
'Critical', 'High', 'Medium', 'Low')).aggregate(
total=Sum(
Case(When(severity__in=('Critical', 'High', 'Medium', 'Low'),
then=Value(1)),
output_field=IntegerField())))['total']

overall_in_pt = Finding.objects.filter(date__lt=end_date,
verified=True,
false_p=False,
duplicate=False,
out_of_scope=False,
mitigated__isnull=True,
test__engagement__product__tags__name=pt,
test__engagement__product__in=prods,
severity__in=('Critical', 'High', 'Medium', 'Low')).values(
'numerical_severity').annotate(Count('numerical_severity')).order_by('numerical_severity')

total_overall_in_pt = Finding.objects.filter(date__lte=end_date,
verified=True,
false_p=False,
duplicate=False,
out_of_scope=False,
mitigated__isnull=True,
test__engagement__product__tags__name=pt,
test__engagement__product__in=prods,
severity__in=('Critical', 'High', 'Medium', 'Low')).aggregate(
total=Sum(
Case(When(severity__in=('Critical', 'High', 'Medium', 'Low'),
then=Value(1)),
output_field=IntegerField())))['total']

all_current_in_pt = Finding.objects.filter(date__lte=end_date,
verified=True,
false_p=False,
duplicate=False,
out_of_scope=False,
mitigated__isnull=True,
test__engagement__product__tags__name=pt,
test__engagement__product__in=prods,
severity__in=(
'Critical', 'High', 'Medium', 'Low')).prefetch_related(
'test__engagement__product',
'test__engagement__product__prod_type',
'test__engagement__risk_acceptance',
'reporter').order_by(
'numerical_severity')

top_ten = Product.objects.filter(engagement__test__finding__date__lte=end_date,
engagement__test__finding__verified=True,
engagement__test__finding__false_p=False,
engagement__test__finding__duplicate=False,
engagement__test__finding__out_of_scope=False,
engagement__test__finding__mitigated__isnull=True,
engagement__test__finding__severity__in=(
'Critical', 'High', 'Medium', 'Low'),
tags__name=pt, engagement__product__in=prods)
top_ten = severity_count(top_ten, 'annotate', 'engagement__test__finding__severity').order_by('-critical', '-high', '-medium', '-low')[:10]

cip = {'S0': 0,
'S1': 0,
'S2': 0,
'S3': 0,
'Total': total_closed_in_period}

aip = {'S0': 0,
'S1': 0,
'S2': 0,
'S3': 0,
'Total': total_overall_in_pt}

for o in closed_in_period:
cip[o['numerical_severity']] = o['numerical_severity__count']

for o in overall_in_pt:
aip[o['numerical_severity']] = o['numerical_severity__count']
else:
messages.add_message(request, messages.ERROR, _("Please choose month and year and the Product Tag."),
extra_tags='alert-danger')

add_breadcrumb(title=_("Bi-Weekly Metrics"), top_level=True, request=request)

return render(request,
'dojo/pt_counts.html',
{'form': form,
'start_date': start_date,
'end_date': end_date,
'opened_in_period': oip,
'trending_opened': opened_in_period_list,
'closed_in_period': cip,
'overall_in_pt': aip,
'all_current_in_pt': all_current_in_pt,
'top_ten': top_ten,
'pt': pt}
)


def engineer_metrics(request):
# only superusers can select other users to view
if request.user.is_superuser:
Expand Down
5 changes: 5 additions & 0 deletions dojo/templates/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,11 @@
{% trans "Product Type Counts" %}
</a>
</li>
<li>
<a href="{% url 'product_tag_counts' %}">
{% trans "Product Tag Counts" %}
</a>
</li>
<li>
<a href="{% url 'simple_metrics' %}">
{% trans "Simple Metrics" %}
Expand Down
10 changes: 7 additions & 3 deletions dojo/templates/dojo/pt_counts.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,20 @@
{% block content %}
{{ block.super }}

<form class="biweekly-metrics" action="{% url 'product_type_counts' %}" method="get">
<form class="biweekly-metrics" method="get">
{{ form.as_p }}
<input class="btn btn-sm btn-primary" type="submit" value="{% trans "Generate Metrics For Selected Period" %}"/>
</form>
<br/>
{% if pt %}
<h2>{% blocktrans with start_date=start_date.date end_date=end_date.date%}Finding Information For Period of {{ start_date }} - {{ end_date }}
{% endblocktrans %}</h2>
<h3 class="inline-block">{{ pt.name }}</h3> [
<a href="{% url 'product_type_metrics' pt.id %}" class="inline-block">{% trans "View Details" %}</a>]
<h3 class="inline-block">{{ pt.name }}</h3>
{% if pt|class_name == "Product_Type" %}
[<a href="{% url 'product_type_metrics' pt.id %}" class="inline-block">{% trans "View Details" %}</a>]
{% elif pt|class_name == "Tagulous_Product_tags" %}
[<a href="{% url 'product' %}?tags={{ pt }}" class="inline-block">{% trans "View Details" %}</a>]
{% endif %}
<div class="panel panel-default table-responsive">
<div class="panel-heading">
<h4>{% trans "Total Security Bug Count In Period" %}</h4>
Expand Down
10 changes: 5 additions & 5 deletions dojo/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -1082,7 +1082,7 @@ def get_period_counts(findings,
}


def opened_in_period(start_date, end_date, pt):
def opened_in_period(start_date, end_date, **kwargs):
start_date = datetime(
start_date.year,
start_date.month,
Expand All @@ -1095,7 +1095,7 @@ def opened_in_period(start_date, end_date, pt):
tzinfo=timezone.get_current_timezone())
opened_in_period = Finding.objects.filter(
date__range=[start_date, end_date],
test__engagement__product__prod_type=pt,
**kwargs,
verified=True,
false_p=False,
duplicate=False,
Expand All @@ -1107,7 +1107,7 @@ def opened_in_period(start_date, end_date, pt):
Count('numerical_severity')).order_by('numerical_severity')
total_opened_in_period = Finding.objects.filter(
date__range=[start_date, end_date],
test__engagement__product__prod_type=pt,
**kwargs,
verified=True,
false_p=False,
duplicate=False,
Expand Down Expand Up @@ -1139,7 +1139,7 @@ def opened_in_period(start_date, end_date, pt):
'closed':
Finding.objects.filter(
mitigated__date__range=[start_date, end_date],
test__engagement__product__prod_type=pt,
**kwargs,
severity__in=('Critical', 'High', 'Medium', 'Low')).aggregate(
total=Sum(
Case(
Expand All @@ -1155,7 +1155,7 @@ def opened_in_period(start_date, end_date, pt):
duplicate=False,
out_of_scope=False,
mitigated__isnull=True,
test__engagement__product__prod_type=pt,
**kwargs,
severity__in=('Critical', 'High', 'Medium', 'Low')).count()
}

Expand Down