Skip to content

Commit 4117039

Browse files
authored
Merge pull request #49 from cisagov/feature/gui-updates
feature: gui logic
2 parents 1fd26ca + 85cd635 commit 4117039

File tree

11 files changed

+386
-91
lines changed

11 files changed

+386
-91
lines changed

src/navv/_version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
"""This file defines the version of this module."""
2-
__version__ = "3.1.0"
2+
__version__ = "3.2.1"

src/navv/commands.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@
55

66
# Third-Party Libraries
77
import click
8-
from navv.bll import get_inventory_report_df, get_snmp_df, get_zeek_df
98

109
# cisagov Libraries
1110
from navv.gui.app import app
11+
from navv.bll import get_inventory_report_df, get_snmp_df, get_zeek_df
1212
from navv.message_handler import success_msg, warning_msg
1313
from navv.spreadsheet_tools import (
1414
auto_adjust_width,

src/navv/gui/app.py

Lines changed: 54 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import logging
22
import os
33

4-
from flask import Flask, render_template, send_from_directory
4+
from flask import Flask, render_template, request, send_file
5+
from navv.gui.bll import generate
56

67
from navv.gui.utils import get_pcap_file
78

@@ -13,27 +14,69 @@
1314

1415
@app.route("/")
1516
def index():
17+
"""Home page."""
1618
pcap_file, pcap_msg, pcap_msg_color = get_pcap_file()
1719

1820
return render_template(
19-
"index.html",
21+
"home.html",
2022
pcap_file=pcap_file,
2123
pcap_msg=pcap_msg,
2224
pcap_msg_color=pcap_msg_color,
2325
)
2426

2527

26-
@app.route("/download")
28+
@app.route("/new-analysis")
29+
def new_analysis():
30+
pcap_file, pcap_msg, pcap_msg_color = get_pcap_file()
31+
32+
return render_template(
33+
"create_new.html",
34+
pcap_file=pcap_file,
35+
pcap_msg=pcap_msg,
36+
pcap_msg_color=pcap_msg_color,
37+
)
38+
39+
40+
@app.route("/existing-analysis")
41+
def existing_analysis():
42+
pcap_file, pcap_msg, pcap_msg_color = get_pcap_file()
43+
44+
return render_template(
45+
"update_existing.html",
46+
pcap_file=pcap_file,
47+
pcap_msg=pcap_msg,
48+
pcap_msg_color=pcap_msg_color,
49+
)
50+
51+
52+
@app.route("/download", methods=["POST"])
2753
def download():
2854
"""Download the network analysis excel file."""
29-
filename = "test-customer_network_analysis.xlsx"
30-
current_path = os.getcwd()
55+
# Get customer name
56+
customer_name = request.form["customername"]
57+
filename = f"{customer_name}_network_analysis.xlsx"
58+
59+
# Set output directory
60+
output_dir = os.path.join(os.getcwd(), "_tmp")
61+
if not os.path.isdir(output_dir):
62+
os.mkdir(output_dir)
63+
64+
# set excel file
65+
excel_file = request.files.get("spreadsheet")
66+
if excel_file and excel_file.filename:
67+
excel_file.save(os.path.join(output_dir, excel_file.filename))
3168

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

35-
return send_from_directory(
36-
current_path,
37-
filename,
38-
as_attachment=True,
74+
zeek_logs = request.files.get("zeeklogs")
75+
if zeek_logs and zeek_logs.filename:
76+
zeek_logs.save(os.path.join(output_dir, zeek_logs.filename))
77+
78+
memfile = generate(
79+
customer_name, output_dir, pcap_file, zeek_logs.filename, excel_file
3980
)
81+
82+
return send_file(memfile, download_name=filename, as_attachment=True)

src/navv/gui/bll.py

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
import io
2+
import os
3+
import shutil
4+
from tempfile import NamedTemporaryFile
5+
from zipfile import ZipFile
6+
7+
import openpyxl
8+
9+
from navv.bll import get_inventory_report_df, get_snmp_df, get_zeek_df
10+
from navv.spreadsheet_tools import (
11+
auto_adjust_width,
12+
create_analysis_array,
13+
get_inventory_data,
14+
get_package_data,
15+
get_segments_data,
16+
get_workbook,
17+
perform_analysis,
18+
write_conn_states_sheet,
19+
write_externals_sheet,
20+
write_inventory_report_sheet,
21+
write_snmp_sheet,
22+
write_stats_sheet,
23+
write_unknown_internals_sheet,
24+
)
25+
from navv.zeek import (
26+
get_conn_data,
27+
get_dns_data,
28+
get_snmp_data,
29+
run_zeek,
30+
perform_zeekcut,
31+
)
32+
from navv.utilities import pushd
33+
34+
35+
def generate(customer_name, output_dir, pcap, zeek_logs_zip, spreadsheet):
36+
"""Generate excel sheet."""
37+
with pushd(output_dir):
38+
pass
39+
40+
if spreadsheet and spreadsheet.filename:
41+
wb = openpyxl.load_workbook(os.path.join(output_dir, spreadsheet.filename))
42+
else:
43+
file_name = os.path.join(output_dir, customer_name + "_network_analysis.xlsx")
44+
wb = get_workbook(file_name)
45+
46+
# Extract Zeek logs from zip file
47+
if zeek_logs_zip:
48+
with ZipFile(f"{output_dir}/{zeek_logs_zip}", "r") as zip_file:
49+
zip_file.extractall(path=output_dir)
50+
zeek_logs = os.path.join(output_dir, zeek_logs_zip[:-4])
51+
os.remove(os.path.join(output_dir, zeek_logs_zip))
52+
else:
53+
zeek_logs = os.path.join(output_dir, "logs")
54+
55+
services, conn_states = get_package_data()
56+
timer_data = dict()
57+
segments = get_segments_data(wb["Segments"])
58+
inventory = get_inventory_data(wb["Inventory Input"])
59+
60+
if pcap and pcap.filename:
61+
run_zeek(os.path.join(output_dir, pcap.filename), zeek_logs, timer=timer_data)
62+
else:
63+
timer_data["run_zeek"] = "NOT RAN"
64+
65+
# Get zeek data from conn.log, dns.log and snmp.log
66+
zeek_data = get_conn_data(zeek_logs)
67+
snmp_data = get_snmp_data(zeek_logs)
68+
dns_filtered = get_dns_data(customer_name, output_dir, zeek_logs)
69+
70+
# Get dns data for resolution
71+
json_path = os.path.join(output_dir, f"{customer_name}_dns_data.json")
72+
73+
# Get zeek dataframes
74+
zeek_df = get_zeek_df(zeek_data, dns_filtered)
75+
snmp_df = get_snmp_df(snmp_data)
76+
77+
# Get inventory report dataframe
78+
inventory_df = get_inventory_report_df(zeek_df)
79+
80+
# Turn zeekcut data into rows for spreadsheet
81+
rows = create_analysis_array(zeek_data, timer=timer_data)
82+
83+
ext_IPs = set()
84+
unk_int_IPs = set()
85+
perform_analysis(
86+
wb,
87+
rows,
88+
services,
89+
conn_states,
90+
inventory,
91+
segments,
92+
dns_filtered,
93+
json_path,
94+
ext_IPs,
95+
unk_int_IPs,
96+
timer=timer_data,
97+
)
98+
99+
write_inventory_report_sheet(inventory_df, wb)
100+
101+
write_externals_sheet(ext_IPs, wb)
102+
103+
write_unknown_internals_sheet(unk_int_IPs, wb)
104+
105+
write_snmp_sheet(snmp_df, wb)
106+
107+
auto_adjust_width(wb["Analysis"])
108+
times = (
109+
perform_zeekcut(fields=["ts"], log_file=os.path.join(zeek_logs, "conn.log"))
110+
.decode("utf-8")
111+
.split("\n")[:-1]
112+
)
113+
forward = sorted(times)
114+
start = float(forward[0])
115+
end = float(forward[len(forward) - 1])
116+
cap_time = end - start
117+
timer_data[
118+
"Length of Capture time"
119+
] = "{} day(s) {} hour(s) {} minutes {} seconds".format(
120+
int(cap_time / 86400),
121+
int(cap_time % 86400 / 3600),
122+
int(cap_time % 3600 / 60),
123+
int(cap_time % 60),
124+
)
125+
write_stats_sheet(wb, timer_data)
126+
write_conn_states_sheet(conn_states, wb)
127+
128+
memfile: io.BytesIO
129+
with NamedTemporaryFile() as tmp:
130+
wb.save(tmp.name)
131+
tmp.seek(0)
132+
memfile = io.BytesIO(tmp.read())
133+
134+
shutil.rmtree(output_dir)
135+
return memfile

src/navv/gui/static/app.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
function enableSpinner() {
2+
var spinner = document.getElementById("createspinner")
3+
spinner.classList.add("spinner-border")
4+
}
5+
6+
7+
(function () {
8+
'use strict'
9+
10+
// Fetch all the forms we want to apply custom Bootstrap validation styles to
11+
var forms = document.querySelectorAll('.needs-validation')
12+
13+
// Loop over them and prevent submission
14+
Array.prototype.slice.call(forms)
15+
.forEach(function (form) {
16+
form.addEventListener('submit', function (event) {
17+
if (!form.checkValidity()) {
18+
event.preventDefault()
19+
event.stopPropagation()
20+
}
21+
22+
form.classList.add('was-validated')
23+
}, false)
24+
})
25+
26+
})();

src/navv/gui/static/styles.css

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,22 @@
77
.alert {
88
width: 40%;
99
font-size: .5rem;
10+
}
11+
12+
.collapse {
13+
&:not(.show) {
14+
display: none;
15+
}
16+
}
17+
18+
.collapsing {
19+
height: 0;
20+
overflow: hidden;
21+
@include transition($transition-collapse);
22+
23+
&.collapse-horizontal {
24+
width: 0;
25+
height: auto;
26+
@include transition($transition-collapse-width);
27+
}
1028
}

src/navv/gui/templates/base.html

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
<!doctype html>
2+
<html lang="en">
3+
4+
<head>
5+
<meta charset="utf-8">
6+
<meta name="viewport" content="width=device-width, initial-scale=1">
7+
<meta name="description" content="">
8+
<meta name="author" content="Idaho National Laboratory">
9+
<title>NAVV</title>
10+
11+
<link rel="canonical" href="https://pypi.org/project/navv/">
12+
13+
14+
15+
<!-- Bootstrap core CSS -->
16+
<link href="../static/css/bootstrap.min.css" rel="stylesheet">
17+
18+
<style>
19+
.bd-placeholder-img {
20+
font-size: 1.125rem;
21+
text-anchor: middle;
22+
-webkit-user-select: none;
23+
-moz-user-select: none;
24+
user-select: none;
25+
}
26+
27+
@media (min-width: 768px) {
28+
.bd-placeholder-img-lg {
29+
font-size: 3.5rem;
30+
}
31+
}
32+
</style>
33+
34+
<!-- Custom styles for this template -->
35+
<link href="../static/styles.css" rel="stylesheet">
36+
</head>
37+
38+
<body>
39+
40+
<main>
41+
<div class="px-4 mt-3 text-center">
42+
<img class="d-block mx-auto mb-4" src="../static/img/navv-logo.png" alt="" width="100" height="100">
43+
<h1 class="display-5 fw-bold">NAVV</h1>
44+
<div class="col-lg-6 mx-auto">
45+
<h3 class="lead mb-3">Network Architecture Verification and Validation</h3>
46+
</div>
47+
</div>
48+
{% block main %}
49+
{% endblock %}
50+
<div class="bg-dark text-secondary px-2 py-3 mt-5 text-center">
51+
<div class="py-3">
52+
<div class="col-lg-6 mx-auto">
53+
<p class="fs-5 mb-3">This project uses <a
54+
class="link-offset-2 link-underline link-underline-opacity-0"
55+
href="https://zeek.org">Zeek</a>, a network security monitoring tool.
56+
</p>
57+
</div>
58+
</div>
59+
</div>
60+
</main>
61+
62+
<script src="../static/js/bootstrap.bundle.min.js"></script>
63+
<script src="../static/app.js"></script>
64+
65+
66+
</body>
67+
68+
</html>

0 commit comments

Comments
 (0)