Skip to content

Commit

Permalink
Create findings report (#2393)
Browse files Browse the repository at this point in the history
Signed-off-by: Donny Peeters <[email protected]>
Co-authored-by: stephanie0x00 <[email protected]>
Co-authored-by: Donny Peeters <[email protected]>
Co-authored-by: Ammar <[email protected]>
Co-authored-by: Jan Klopper <[email protected]>
  • Loading branch information
5 people authored Mar 1, 2024
1 parent 3a4242f commit 9cd33d3
Show file tree
Hide file tree
Showing 10 changed files with 440 additions and 71 deletions.
Empty file.
69 changes: 69 additions & 0 deletions rocky/reports/report_types/findings_report/report.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
{% load i18n %}

<div class="horizontal-scroll">
{% if data %}
{% include "partials/report_severity_totals.html" with data=data.summary %}

<h2>{% translate "Findings" %}</h2>
<div class="horizontal-scroll">
<table>
<caption class="visually-hidden">{% translate "Other findings found:" %}</caption>
<thead>
<tr>
<th scope="col">{% translate "FindingType" %}</th>
<th scope="col">{% translate "Type" %}</th>
<th scope="col">{% translate "Risk level" %}</th>
<th scope="col">{% translate "Occurrences" %}</th>
<th scope="col">{% translate "Details" %}</th>
</tr>
</thead>
<tbody>
{% for info in data.finding_types %}
<tr>
<td>{{ info.finding_type.id }}</td>
<td>{{ info.finding_type.object_type }}</td>
<td>
<span class="{{ info.finding_type.risk_severity }}">{{ info.finding_type.risk_severity|capfirst }}</span>
</td>
<td>{{ info.occurrences|length }}</td>
<td class="actions">
<button class="expando-button"
data-icon-open-class="icon ti-chevron-down"
data-icon-close-class="icon ti-chevron-up"
data-close-label="{% translate "Close details" %}">
{% translate "Open details" %}
</button>
</td>
</tr>
<tr class="expando-row">
<td colspan="5">
<h2 class="heading-normal">{% translate "Description" %}</h2>
<p>{{ info.finding_type.description }}</p>
<h2 class="heading-normal">{% translate "Impact" %}</h2>
<p>{{ info.finding_type.impact }}</p>
<h2 class="heading-normal">{% translate "Recommendation" %}</h2>
<p>{{ info.finding_type.recommendation }}</p>
<h2 class="heading-normal">{% translate "Occurrences" %}</h2>
<ul class="accordion break-title">
{% for occurrence in info.occurrences %}
<li>
<button aria-expanded="false">{{ occurrence.finding.ooi.human_readable }}</button>
<div aria-labelledby="finding-details">
<h2>{% translate "First seen" %}</h2>
<p>{{ occurrence.first_seen }}</p>
<h2>{% translate "Description" %}</h2>
<p>{{ occurrence.finding.description }}</p>
</div>
</li>
{% endfor %}
</ul>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<p>{% translate "No findings have been found." %}</p>
{% endif %}
</div>
84 changes: 84 additions & 0 deletions rocky/reports/report_types/findings_report/report.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
from datetime import datetime
from logging import getLogger
from typing import Any

from django.utils.translation import gettext_lazy as _

from octopoes.models import Reference
from octopoes.models.exception import ObjectNotFoundException
from octopoes.models.ooi.findings import Finding, RiskLevelSeverity
from octopoes.models.types import ALL_TYPES
from reports.report_types.definitions import Report

logger = getLogger(__name__)

TREE_DEPTH = 9
SEVERITY_OPTIONS = [severity.value for severity in RiskLevelSeverity]


class FindingsReport(Report):
id = "findings-report"
name = _("Findings Report")
description = _("Shows all the finding types and their occurrences.")
plugins = {"required": [], "optional": []}
input_ooi_types = ALL_TYPES
template_path = "findings_report/report.html"

def get_finding_valid_time_history(self, reference: str) -> list[datetime]:
transaction_record = self.octopoes_api_connector.get_history(reference=reference)
valid_time_history = [transaction.valid_time for transaction in transaction_record]
return valid_time_history

def generate_data(self, input_ooi: str, valid_time: datetime) -> dict[str, Any]:
reference = Reference.from_str(input_ooi)
findings = []
finding_types: dict[str, Any] = {}
total_by_severity = {}
total_by_severity_per_finding_type = {}

for severity in SEVERITY_OPTIONS:
total_by_severity[severity] = 0
total_by_severity_per_finding_type[severity] = 0

tree = self.octopoes_api_connector.get_tree(
reference, depth=TREE_DEPTH, types={Finding}, valid_time=valid_time
).store

for ooi in tree.values():
if ooi.ooi_type == "Finding":
findings.append(ooi)

for finding in findings:
try:
finding_type = self.octopoes_api_connector.get(Reference.from_str(finding.finding_type), valid_time)
severity = finding_type.risk_severity.name.lower()
total_by_severity[severity] += 1

time_history = self.get_finding_valid_time_history(finding.primary_key)

if time_history:
first_seen = str(time_history[0])

finding = {"finding": finding, "first_seen": first_seen}

if finding_type.id in finding_types:
finding_types[finding_type.id]["occurrences"].append(finding)
else:
finding_types[finding_type.id] = {"finding_type": finding_type, "occurrences": [finding]}
total_by_severity_per_finding_type[severity] += 1

except ObjectNotFoundException:
logger.error("No Finding Type found for Finding '%s' on date %s.", finding, str(valid_time))

sorted_finding_types: list[Any] = sorted(
finding_types.values(), key=lambda x: x["finding_type"].risk_score or 0, reverse=True
)

summary = {
"total_by_severity": total_by_severity,
"total_by_severity_per_finding_type": total_by_severity_per_finding_type,
"total_finding_types": len(sorted_finding_types),
"total_occurrences": sum(total_by_severity.values()),
}

return {"finding_types": sorted_finding_types, "summary": summary}
2 changes: 2 additions & 0 deletions rocky/reports/report_types/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from reports.report_types.aggregate_organisation_report.report import AggregateOrganisationReport
from reports.report_types.definitions import AggregateReport, MultiReport, Report
from reports.report_types.dns_report.report import DNSReport
from reports.report_types.findings_report.report import FindingsReport
from reports.report_types.ipv6_report.report import IPv6Report
from reports.report_types.mail_report.report import MailReport
from reports.report_types.multi_organization_report.report import MultiOrganizationReport
Expand All @@ -27,6 +28,7 @@
OpenPortsReport,
IPv6Report,
VulnerabilityReport,
FindingsReport,
]
AGGREGATE_REPORTS = [AggregateOrganisationReport]

Expand Down
11 changes: 11 additions & 0 deletions rocky/reports/templates/partials/report_severity_totals.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{% load i18n %}

<section>
<div>
<h3>{% translate "Findings overview" %}</h3>
<div class="column-2">
{% include "partials/report_severity_totals_table.html" %}

</div>
</div>
</section>
58 changes: 58 additions & 0 deletions rocky/reports/templates/partials/report_severity_totals_table.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
{% load i18n %}

<div class="horizontal-scroll">
<table>
<caption class="visually-hidden">{% translate "Total per severity overview:" %}</caption>
<thead>
<tr>
<th>{% translate "Risk level" %}</th>
<th>{% translate "FindingTypes" %}</th>
<th>{% translate "Occurrences" %}</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<span class="critical">Critical</span>
</td>
<td class="number">{{ data.total_by_severity_per_finding_type.critical }}</td>
<td class="number">{{ data.total_by_severity.critical }}</td>
</tr>
<tr>
<td>
<span class="high">High</span>
</td>
<td class="number">{{ data.total_by_severity_per_finding_type.high }}</td>
<td class="number">{{ data.total_by_severity.high }}</td>
</tr>
<tr>
<td>
<span class="medium">Medium</span>
</td>
<td class="number">{{ data.total_by_severity_per_finding_type.medium }}</td>
<td class="number">{{ data.total_by_severity.medium }}</td>
</tr>
<tr>
<td>
<span class="low">Low</span>
</td>
<td class="number">{{ data.total_by_severity_per_finding_type.low }}</td>
<td class="number">{{ data.total_by_severity.low }}</td>
</tr>
<tr>
<td>
<span class="recommendation">Recommendation</span>
</td>
<td class="number">{{ data.total_by_severity_per_finding_type.recommendation }}</td>
<td class="number">{{ data.total_by_severity.recommendation }}</td>
</tr>
<tfoot>
<tr>
<td>Total</td>
<td class="number">{{ data.total_finding_types }}</td>
<td class="number">{{ data.total_occurrences }}</td>
</tr>
</tfoot>
</tbody>
</table>
</div>
Loading

0 comments on commit 9cd33d3

Please sign in to comment.