Skip to content

Commit

Permalink
Merge pull request #49 from cisagov/feature/gui-updates
Browse files Browse the repository at this point in the history
feature: gui logic
  • Loading branch information
Dbones202 authored Sep 13, 2023
2 parents 1fd26ca + 85cd635 commit 4117039
Show file tree
Hide file tree
Showing 11 changed files with 386 additions and 91 deletions.
2 changes: 1 addition & 1 deletion src/navv/_version.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
"""This file defines the version of this module."""
__version__ = "3.1.0"
__version__ = "3.2.1"
2 changes: 1 addition & 1 deletion src/navv/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@

# Third-Party Libraries
import click
from navv.bll import get_inventory_report_df, get_snmp_df, get_zeek_df

# cisagov Libraries
from navv.gui.app import app
from navv.bll import get_inventory_report_df, get_snmp_df, get_zeek_df
from navv.message_handler import success_msg, warning_msg
from navv.spreadsheet_tools import (
auto_adjust_width,
Expand Down
65 changes: 54 additions & 11 deletions src/navv/gui/app.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import logging
import os

from flask import Flask, render_template, send_from_directory
from flask import Flask, render_template, request, send_file
from navv.gui.bll import generate

from navv.gui.utils import get_pcap_file

Expand All @@ -13,27 +14,69 @@

@app.route("/")
def index():
"""Home page."""
pcap_file, pcap_msg, pcap_msg_color = get_pcap_file()

return render_template(
"index.html",
"home.html",
pcap_file=pcap_file,
pcap_msg=pcap_msg,
pcap_msg_color=pcap_msg_color,
)


@app.route("/download")
@app.route("/new-analysis")
def new_analysis():
pcap_file, pcap_msg, pcap_msg_color = get_pcap_file()

return render_template(
"create_new.html",
pcap_file=pcap_file,
pcap_msg=pcap_msg,
pcap_msg_color=pcap_msg_color,
)


@app.route("/existing-analysis")
def existing_analysis():
pcap_file, pcap_msg, pcap_msg_color = get_pcap_file()

return render_template(
"update_existing.html",
pcap_file=pcap_file,
pcap_msg=pcap_msg,
pcap_msg_color=pcap_msg_color,
)


@app.route("/download", methods=["POST"])
def download():
"""Download the network analysis excel file."""
filename = "test-customer_network_analysis.xlsx"
current_path = os.getcwd()
# Get customer name
customer_name = request.form["customername"]
filename = f"{customer_name}_network_analysis.xlsx"

# Set output directory
output_dir = os.path.join(os.getcwd(), "_tmp")
if not os.path.isdir(output_dir):
os.mkdir(output_dir)

# set excel file
excel_file = request.files.get("spreadsheet")
if excel_file and excel_file.filename:
excel_file.save(os.path.join(output_dir, excel_file.filename))

if not os.path.isfile(os.path.join(current_path, filename)):
logger.error(f"File {filename} not found in {current_path}")
# Get pcap file and Zeek logs if available
pcap_file = request.files.get("pcapfile")
if pcap_file and pcap_file.filename:
pcap_file.save(os.path.join(output_dir, pcap_file.filename))

return send_from_directory(
current_path,
filename,
as_attachment=True,
zeek_logs = request.files.get("zeeklogs")
if zeek_logs and zeek_logs.filename:
zeek_logs.save(os.path.join(output_dir, zeek_logs.filename))

memfile = generate(
customer_name, output_dir, pcap_file, zeek_logs.filename, excel_file
)

return send_file(memfile, download_name=filename, as_attachment=True)
135 changes: 135 additions & 0 deletions src/navv/gui/bll.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
import io
import os
import shutil
from tempfile import NamedTemporaryFile
from zipfile import ZipFile

import openpyxl

from navv.bll import get_inventory_report_df, get_snmp_df, get_zeek_df
from navv.spreadsheet_tools import (
auto_adjust_width,
create_analysis_array,
get_inventory_data,
get_package_data,
get_segments_data,
get_workbook,
perform_analysis,
write_conn_states_sheet,
write_externals_sheet,
write_inventory_report_sheet,
write_snmp_sheet,
write_stats_sheet,
write_unknown_internals_sheet,
)
from navv.zeek import (
get_conn_data,
get_dns_data,
get_snmp_data,
run_zeek,
perform_zeekcut,
)
from navv.utilities import pushd


def generate(customer_name, output_dir, pcap, zeek_logs_zip, spreadsheet):
"""Generate excel sheet."""
with pushd(output_dir):
pass

if spreadsheet and spreadsheet.filename:
wb = openpyxl.load_workbook(os.path.join(output_dir, spreadsheet.filename))
else:
file_name = os.path.join(output_dir, customer_name + "_network_analysis.xlsx")
wb = get_workbook(file_name)

# Extract Zeek logs from zip file
if zeek_logs_zip:
with ZipFile(f"{output_dir}/{zeek_logs_zip}", "r") as zip_file:
zip_file.extractall(path=output_dir)
zeek_logs = os.path.join(output_dir, zeek_logs_zip[:-4])
os.remove(os.path.join(output_dir, zeek_logs_zip))
else:
zeek_logs = os.path.join(output_dir, "logs")

services, conn_states = get_package_data()
timer_data = dict()
segments = get_segments_data(wb["Segments"])
inventory = get_inventory_data(wb["Inventory Input"])

if pcap and pcap.filename:
run_zeek(os.path.join(output_dir, pcap.filename), zeek_logs, timer=timer_data)
else:
timer_data["run_zeek"] = "NOT RAN"

# Get zeek data from conn.log, dns.log and snmp.log
zeek_data = get_conn_data(zeek_logs)
snmp_data = get_snmp_data(zeek_logs)
dns_filtered = get_dns_data(customer_name, output_dir, zeek_logs)

# Get dns data for resolution
json_path = os.path.join(output_dir, f"{customer_name}_dns_data.json")

# Get zeek dataframes
zeek_df = get_zeek_df(zeek_data, dns_filtered)
snmp_df = get_snmp_df(snmp_data)

# Get inventory report dataframe
inventory_df = get_inventory_report_df(zeek_df)

# Turn zeekcut data into rows for spreadsheet
rows = create_analysis_array(zeek_data, timer=timer_data)

ext_IPs = set()
unk_int_IPs = set()
perform_analysis(
wb,
rows,
services,
conn_states,
inventory,
segments,
dns_filtered,
json_path,
ext_IPs,
unk_int_IPs,
timer=timer_data,
)

write_inventory_report_sheet(inventory_df, wb)

write_externals_sheet(ext_IPs, wb)

write_unknown_internals_sheet(unk_int_IPs, wb)

write_snmp_sheet(snmp_df, wb)

auto_adjust_width(wb["Analysis"])
times = (
perform_zeekcut(fields=["ts"], log_file=os.path.join(zeek_logs, "conn.log"))
.decode("utf-8")
.split("\n")[:-1]
)
forward = sorted(times)
start = float(forward[0])
end = float(forward[len(forward) - 1])
cap_time = end - start
timer_data[
"Length of Capture time"
] = "{} day(s) {} hour(s) {} minutes {} seconds".format(
int(cap_time / 86400),
int(cap_time % 86400 / 3600),
int(cap_time % 3600 / 60),
int(cap_time % 60),
)
write_stats_sheet(wb, timer_data)
write_conn_states_sheet(conn_states, wb)

memfile: io.BytesIO
with NamedTemporaryFile() as tmp:
wb.save(tmp.name)
tmp.seek(0)
memfile = io.BytesIO(tmp.read())

shutil.rmtree(output_dir)
return memfile
26 changes: 26 additions & 0 deletions src/navv/gui/static/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
function enableSpinner() {
var spinner = document.getElementById("createspinner")
spinner.classList.add("spinner-border")
}


(function () {
'use strict'

// Fetch all the forms we want to apply custom Bootstrap validation styles to
var forms = document.querySelectorAll('.needs-validation')

// Loop over them and prevent submission
Array.prototype.slice.call(forms)
.forEach(function (form) {
form.addEventListener('submit', function (event) {
if (!form.checkValidity()) {
event.preventDefault()
event.stopPropagation()
}

form.classList.add('was-validated')
}, false)
})

})();
18 changes: 18 additions & 0 deletions src/navv/gui/static/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,22 @@
.alert {
width: 40%;
font-size: .5rem;
}

.collapse {
&:not(.show) {
display: none;
}
}

.collapsing {
height: 0;
overflow: hidden;
@include transition($transition-collapse);

&.collapse-horizontal {
width: 0;
height: auto;
@include transition($transition-collapse-width);
}
}
68 changes: 68 additions & 0 deletions src/navv/gui/templates/base.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<!doctype html>
<html lang="en">

<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="">
<meta name="author" content="Idaho National Laboratory">
<title>NAVV</title>

<link rel="canonical" href="https://pypi.org/project/navv/">



<!-- Bootstrap core CSS -->
<link href="../static/css/bootstrap.min.css" rel="stylesheet">

<style>
.bd-placeholder-img {
font-size: 1.125rem;
text-anchor: middle;
-webkit-user-select: none;
-moz-user-select: none;
user-select: none;
}

@media (min-width: 768px) {
.bd-placeholder-img-lg {
font-size: 3.5rem;
}
}
</style>

<!-- Custom styles for this template -->
<link href="../static/styles.css" rel="stylesheet">
</head>

<body>

<main>
<div class="px-4 mt-3 text-center">
<img class="d-block mx-auto mb-4" src="../static/img/navv-logo.png" alt="" width="100" height="100">
<h1 class="display-5 fw-bold">NAVV</h1>
<div class="col-lg-6 mx-auto">
<h3 class="lead mb-3">Network Architecture Verification and Validation</h3>
</div>
</div>
{% block main %}
{% endblock %}
<div class="bg-dark text-secondary px-2 py-3 mt-5 text-center">
<div class="py-3">
<div class="col-lg-6 mx-auto">
<p class="fs-5 mb-3">This project uses <a
class="link-offset-2 link-underline link-underline-opacity-0"
href="https://zeek.org">Zeek</a>, a network security monitoring tool.
</p>
</div>
</div>
</div>
</main>

<script src="../static/js/bootstrap.bundle.min.js"></script>
<script src="../static/app.js"></script>


</body>

</html>
Loading

0 comments on commit 4117039

Please sign in to comment.