moved code around for packaging, working on CSS with grid

This commit is contained in:
Joost Agterhoek 2025-04-15 14:32:48 +02:00
parent 5c2eefc17d
commit 486c855e18
23 changed files with 1155 additions and 0 deletions

View File

@ -0,0 +1,32 @@
import os
from flask import Flask, redirect, url_for, render_template
from dotenv import load_dotenv
# Instead of turning 'lookup' into a Blueprint, maybe I could make a history of previous lookups a Blueprint?
def create_app(test_config=None):
app = Flask(__name__)
app.config.from_pyfile("config.py")
load_dotenv()
app.secret_key = os.getenv("SECRET_KEY")
if test_config is None:
app.config.from_pyfile("config.py", silent=True)
else:
app.config.update(test_config)
try:
os.makedirs(app.instance_path)
except OSError:
pass
@app.route("/")
def index():
return render_template("index.html")
from . import lookup
app.register_blueprint(lookup.bp)
return app

0
flask_soc_site/config.py Normal file
View File

31
flask_soc_site/lookup.py Normal file
View File

@ -0,0 +1,31 @@
from flask import Blueprint, request, render_template, flash
from .src import host_lookup
from markupsafe import escape
bp = Blueprint("lookup", __name__)
@bp.route("/lookup", methods=(["GET", "POST"]))
def lookup():
hosts = []
results = []
if request.method == "GET":
return render_template("lookup.html")
elif request.method == "POST" and any(request.form.get("host")):
print(f'REQUEST.FORM.GET("HOST") IS {request.form.get("host")}')
user_input = escape(request.form.get("host").strip())
hosts, errors = host_lookup.process_input(user_input)
for host in hosts:
result = host_lookup.Lookedup(host)
results.append(result)
return render_template("results.html", hosts=results, errors=errors)
else:
flash("Nothing entered! Try again?", "error")
return render_template("lookup.html")
# elif request.method == "POST" and "file" in request.files:
# file = request.files["file"]
# extracted = upload.extract(file)
# hosts = host_lookup.process_file(extracted)
# return render_template("results.html", hosts=hosts)

View File

@ -0,0 +1,52 @@
from base64 import decode
import json
import os
import requests
import requests_cache
from dotenv import load_dotenv
from pprint import pprint
class API_error(Exception):
pass
def environment():
requests_cache.install_cache(expire_after=360, allowable_methods=("POST"))
load_dotenv()
api_key = os.getenv("ABUSEIPDB_API")
return api_key
def lookup(api_key, host):
url = "https://api.abuseipdb.com/api/v2/check"
payload = {"ipAddress": "", "maxAgeInDays": "90"}
payload.update({"ipAddress": host})
headers = {"Accept": "application/json", "Key": api_key}
response = requests.request(
method="GET", url=url, params=payload, headers=headers, verify=False
) # TODO: remove SSL verify=False and add signed certificate if possible.
# Figure out how caching functions here: https://requests-cache.readthedocs.io/en/stable/examples.html
response_dict = json.loads(response.text)
lookup = dict.fromkeys(
["score", "last_reported", "IP_address", "CDN", "Tor", "total_reports"]
)
print(response_dict)
lookup["score"] = response_dict["data"]["abuseConfidenceScore"]
lookup["last_reported"] = response_dict["data"]["lastReportedAt"]
lookup["IP_address"] = response_dict["data"]["ipAddress"]
lookup["usage"] = response_dict["data"]["usageType"]
lookup["Tor"] = response_dict["data"]["isTor"]
lookup["total_reports"] = response_dict["data"]["totalReports"]
print(requests_cache.get_cache())
print("Cached:")
print("\n".join(requests_cache.get_cache().urls()))
return lookup
def analyse(host):
api_key = environment()
result = lookup(api_key, host)
return result

View File

@ -0,0 +1,30 @@
LOGCONF = {
"version": 1,
"formatters": {
"default": {
"format": "[%(asctime)s] %(levelname)s in %(module)s: %(message)s",
}
},
"handlers": {
"console": {
"class": "logging.StreamHandler",
"stream": "ext://sys.stdout",
"formatter": "default",
},
"file": {
"class": "logging.FileHandler",
"filename": "flask.log",
"formatter": "default",
},
},
"root": {"level": "DEBUG", "handlers": ["console", "file"]},
}
EMAIL = "email address"
URL = "url"
DOMAIN = "domain"
IPV4 = "ipv4"
IPV6 = "ipv6"
NO_HOST = "no host"
domain_lookup = {}

View File

@ -0,0 +1,43 @@
from ipaddress import ip_address
from checkdmarc.dmarc import check_dmarc
from checkdmarc.spf import check_spf
import validators
from ipwhois import IPWhois
from whois import whois
from .constants import DOMAIN, EMAIL, IPV4, IPV6, URL
def determine(host):
host_type = ""
if validators.url(host):
host_type = URL
elif validators.domain(host):
host_type = DOMAIN
elif validators.ip_address.ipv4(host):
host_type = IPV4
elif validators.ip_address.ipv6(host):
host_type = IPV6
elif validators.email(host):
host_type = EMAIL
else:
print("NO HOST TYPE")
return host_type
def domain(host):
result = dict(whois(host))
if type(result["domain_name"]) is list:
result["domain_name"] = result["domain_name"][0]
return result
def emailsec(host):
spf = ""
dmarc = ""
result_spf = check_spf(host)
if result_spf["valid"]:
spf = result_spf["record"]
result_dmarc = check_dmarc(host)
if result_dmarc["valid"]:
dmarc = result_dmarc["record"]
return spf, dmarc

View File

@ -0,0 +1,154 @@
# TODO: make this module only have wrapper functions, no direct lookups or imports. Move all the direct lookup functions (emailsec) into (a) separate module(s).
import re
from ipaddress import ip_address
from checkdmarc.dmarc import check_dmarc
from checkdmarc.spf import check_spf
import validators
from ipwhois import IPWhois
from whois import whois
# from constants import DOMAIN, EMAIL, IPV4, IPV6, URL
from . import abuseipdb_api, virustotal_api
import socket
from urllib.parse import urlparse
from . import host_data
import tldextract
class Lookedup(object):
def __init__(self, host):
self.host = host
self.host_type = determine(self.host)
self = self.specific()
# TODO: consolidate all below functions if possible
def url_lookup(self):
self.domain = urlparse(self.host).netloc
self.ip_address = socket.gethostbyname(self.domain)
self.metadata = domain(self.domain)
self.email_security = spf_dmarc(self.domain)
self.vt, self.vt_dict = virustotal_api.analyse2(self.host, self.host_type)
self.abuseipdb = abuseipdb_api.analyse(self.ip_address)
return self
def ip_lookup(self):
self.metadata = domain(self.host)
self.domain = self.metadata["domain_name"]
self.email_security = spf_dmarc(self.domain)
self.vt, self.vt_dict = virustotal_api.analyse2(self.host, self.host_type)
self.abuseipdb = abuseipdb_api.analyse(self.host)
return self
def domain_lookup(self):
self.ip_address = socket.gethostbyname(self.host)
self.metadata = domain(self.host)
self.domain = self.metadata["domain_name"]
self.email_security = spf_dmarc(self.domain)
self.vt, self.vt_dict = virustotal_api.analyse2(self.host, self.host_type)
self.abuseipdb = abuseipdb_api.analyse(self.ip_address)
return self
def email_lookup(self):
self.domain = self.host.split("@")[1]
self.metadata = domain(self.domain)
self.ip_address = socket.gethostbyname(self.domain)
self.email_security = spf_dmarc(self.domain)
self.vt, self.vt_dict = virustotal_api.analyse2(self.domain, self.host_type)
self.abuseipdb = abuseipdb_api.analyse(self.ip_address)
return self
def specific(self):
if self.host_type == "url":
return self.url_lookup()
elif self.host_type == "domain":
return self.domain_lookup()
elif self.host_type == "ip":
return self.ip_lookup()
elif self.host_type == "email address":
return self.email_lookup()
def sanitize(user_input):
sanitized = []
if user_input.strip() != "":
sanitized = re.split("; |, | |\n", user_input)
return sanitized
def determine(host):
host_type = ""
if validators.url(host):
host_type = "url"
elif validators.domain(host):
host_type = "domain"
elif validators.ip_address.ipv4(host):
host_type = "ip"
elif validators.ip_address.ipv6(host):
host_type = "ip"
elif validators.email(host):
host_type = "email address"
else:
host_type = "no host"
return host_type
def extract(user_input):
hosts = []
errors = []
for item in user_input:
if validators.url(item):
hosts.append(item)
elif validators.domain(item):
hosts.append(item)
elif validators.ip_address.ipv4(item):
hosts.append(item)
elif validators.ip_address.ipv6(item):
hosts.append(item)
elif validators.email(item):
hosts.append(item)
else:
errors.append(item)
return hosts, errors
def domain(host):
result = dict(whois(host))
if type(result["creation_date"]) is list:
result["creation_date"] = result["creation_date"][0].strftime("%d-%m-%Y")
else:
result["creation_date"] = result["creation_date"].strftime("%d-%m-%Y")
if type(result["domain_name"]) is list:
result["domain_name"] = result["domain_name"][0]
return result
def spf_dmarc(domain):
spf = ""
dmarc = ""
result_spf = check_spf(domain)
if result_spf["valid"]:
spf = result_spf["record"]
result_dmarc = check_dmarc(domain)
if result_dmarc["valid"]:
dmarc = result_dmarc["record"]
return spf, dmarc
def process_input(user):
results = []
sanitized = sanitize(user)
hosts, errors = extract(sanitized)
# for host in hosts:
# host_analyzed = Host(host).lookup()
# results.append(host_analyzed)
# return results
return hosts, errors
def process_file(file_content):
hosts = []
for host in file_content:
result = Host(host)
hosts.append(result)
return hosts

View File

@ -0,0 +1,111 @@
import json
import time
import os
import requests
from dotenv import load_dotenv
from .constants import URL, DOMAIN, IPV4, IPV6, domain_lookup
def environment():
load_dotenv()
api_key = os.getenv("VT_API")
return api_key
def analysis_object(api_key, host):
url = "https://www.virustotal.com/api/v3/urls"
payload = {"url": ""}
payload.update({"url": host})
headers = {
"accept": "application/json",
"content-type": "application/x-www-form-urlencoded",
"x-apikey": api_key,
}
response = requests.post(url, data=payload, headers=headers)
response_dict = json.loads(response.text)
response_id = response_dict["data"]["id"]
return response_id
def analyse_domain(api_key, host):
url = "https://www.virustotal.com/api/v3/domains/" + host
headers = {
"accept": "application/json",
"content-type": "application/x-www-form-urlencoded",
"x-apikey": api_key,
}
analysis_json = requests.get(url, headers=headers)
response_dict = json.loads(analysis_json.text)
return response_dict, analysis_json
def analyse_URL(api_key, response_id):
url = "https://www.virustotal.com/api/v3/analyses/{}".format(response_id)
headers = {"accept": "application/json", "x-apikey": api_key}
analysis_json = requests.get(url, headers=headers)
analysis_dict = json.loads(analysis_json.text)
return analysis_dict, analysis_json
# This returns a differently shaped JSON and therefore dict:
# analysis_dict keys 'data', 'meta'
# analysis_dict['data'] keys 'id', 'type', 'links', 'attributes'
# analysis_dict['data']['attributes'] keys 'stats', (numbers) 'results', (all the AV engine results) 'date', (Linux epoch timestamp) 'status'
def analyse_IP(api_key, host):
analysis_url = "https://www.virustotal.com/api/v3/ip_addresses/{}".format(host)
headers = {"accept": "application/json", "x-apikey": api_key}
analysis_json = requests.get(analysis_url, headers=headers)
response_dict = json.loads(analysis_json.text)
# Implement this: https://docs.virustotal.com/reference/ip-info
return response_dict, analysis_json
def analyse(host, host_type):
api_key = environment()
if host_type == URL:
response_id = analysis_object(api_key, host)
result, analysis_json = analyse_URL(api_key, response_id)
elif host_type == DOMAIN:
result, analysis_json = analyse_domain(api_key, host)
elif host_type == IPV4 or IPV6:
result, analysis_json = analyse_IP(api_key, host)
return result, analysis_json
def analyse2(host, host_type):
api_key = environment()
if host_type == "url":
response_id = analysis_object(api_key, host)
result, analysis_json = analyse_URL(api_key, response_id)
elif host_type == "domain" or host_type == "email address":
result, analysis_json = analyse_domain(api_key, host)
elif host_type == "ip":
result, analysis_json = analyse_IP(api_key, host)
if host_type == "url":
vt_stats = result["data"]["attributes"]["stats"]
vt_results = result["data"]["attributes"]["results"]
last_update = result["data"]["attributes"]["date"]
elif host_type == "domain" or host_type == "email address" or host_type == "ip":
vt_stats = result["data"]["attributes"]["last_analysis_stats"]
vt_results = result["data"]["attributes"]["last_analysis_results"]
last_update = result["data"]["attributes"]["last_analysis_date"]
summary = dict.fromkeys(["total", "score", "vendors", "last_update"])
total = 0
vendors = []
for key, value in vt_stats.items():
total += value
for key, value in vt_results.items():
if value["category"] == "malicious":
vendors.append(key)
summary["total"] = total
summary["score"] = vt_stats["malicious"]
summary["vendors"] = vendors
summary["last_update"] = time.strftime(
"%d-%m-%Y",
time.gmtime(last_update),
)
return summary, analysis_json

Binary file not shown.

After

Width:  |  Height:  |  Size: 263 KiB

View File

@ -0,0 +1,12 @@
$(function() {
$("td[colspan=7]").find("p").hide();
$("table").click(function(event) {
event.stopPropagation();
var $target = $(event.target);
if ( $target.closest("td").attr("colspan") > 1 ) {
$target.slideUp();
} else {
$target.closest("tr").next().find("p").slideToggle();
}
});
});

View File

@ -0,0 +1,379 @@
@import url('https://fonts.googleapis.com/css2?family=DM+Mono:wght@300;400;500&display=swap');
body {
min-height: 100vh;
padding: 0;
margin: 0;
background-color: #17141d;
color: white;
font-family: 'DM Mono', monospace;
}
.no_bullets {
list-style-type: none;
padding: 0;
margin: 0;
}
.headings {
color: #fdc105;
display: flex;
justify-content: center;
}
.lookup_container {
display: flex;
justify-content: center;
flex-direction: column;
margin: 10px auto;
}
.input_container {
/* padding: 30px 30px; */
/* flex: 1 1 0; */
display: grid;
justify-content: center;
}
.forms_container {
display: grid;
justify-content: center;
align-items: center;
flex-direction: column;
/* padding: 75px 250px; */
/* flex: 1 1 0; */
color: #fff;
background-image: url(/static/background/cool-background-trianglify.png);
/* background: linear-gradient(to bottom right, red, yellow); */
border: 0;
/* transition: 0.2s; */
/* overflow: hidden; */
width: min(25em, 100%);
}
.input_form {
/* padding: 5px 5px; */
/* display: flex; */
margin: auto;
}
.upload_form {
padding: 5px 5px;
display: flex;
}
.grid_test_container {
display: grid;
grid-gap: 10px;
grid-template-columns: repeat(5, minmax(0, 1fr));
grid-gap: 10px;
background-color: #2196F3;
padding: 10px;
}
.grid_test_container>.grid_test {
color: blue;
grid-column-start: span 2, 3;
background-color: #ffc100;
text-align: center;
padding: 20px 0;
font-size: 30px;
}
.flex_in_grid_container {
display: grid;
grid-template-columns: 1fr 1fr;
grid-auto-rows: 75px;
grid-gap: 10px;
}
.flex_in_grid {
display: flex;
align-items: center;
justify-content: center;
}
.grid_container>div {
background-color: rbga(255, 255, 255, 0.8);
text-align: center;
padding: 20px 0;
font-size: 30px;
}
.test_container {
/* for some reason the line below keeps the form a little less wide? */
display: grid;
justify-content: center;
justify-items: center;
margin: auto;
/*
-ms-box-sizing: content-box;
-moz-box-sizing: content-box;
-webkit-box-sizing: content-box;
box-sizing: content-box;
*/
}
.test_input_container {
/*
display: grid;
justify-items: center;
justify-content: center;
width: min(25em, 100%);
*/
background-color: #ffc100;
/*
-ms-box-sizing: content-box;
-moz-box-sizing: content-box;
-webkit-box-sizing: content-box;
box-sizing: content-box;
*/
/*
-ms-box-sizing: border-box;
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
box-sizing: border-box;
*/
justify-content: center;
justify-items: center;
align-items: center;
text-align: center;
width: min(25em, 100%);
margin: auto;
}
.test_forms_container {
/*
display: grid;
justify-content: center;
justify-items: center;
/* background-image: url(/static/background/cool-background-trianglify.png);*/
*/
/*
background-color: #ffc100;
*/
}
.test_text_container {
display: grid;
justify-content: center;
}
.test {
display: grid;
margin: 5px auto;
background-color: #ffc100;
justify-content: center;
justify-items: center;
width: min(25em, 100%);
text-align: center;
}
.general_container {
display: flex;
justify-content: center;
flex-direction: column;
}
.error_container {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
/* padding: 75px 250px; */
/* color: #fff; */
border: 0;
/* transition: 0.2s; */
/* overflow: hidden; */
}
.error_form_empty {
padding: 15px 15px;
display: flex;
border-style: dashed;
border-color: #fad253;
/* background-color: #fad253; */
background-color: #ffc100;
border: 5;
}
.howto_container {
/*
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
margin: 10px auto;
background-image: url(/static/background/cool-background-trianglify.png);
overflow: hidden;
*/
display: grid;
justify-content: center;
margin: auto;
}
.howto_text {
/*
display: flex;
justify-content: center;
border-style: dashed;
border-color: #fad253;
background-color: #ffc100;
border: 5;
margin: 10px auto;
*/
display: grid;
margin: 5px auto;
background-color: #ffc100;
justify-content: center;
justify-items: center;
width: min(35em, 100%);
}
.btn-warning {
position: relative;
padding: 11px 16px;
font-size: 15px;
line-height: 1.5;
border-radius: 3px;
color: #000000;
background-color: #ffc100;
border: 0;
transition: 0.2s;
opacity: 50;
overflow: hidden;
}
.btn-warning input[type="file"] {
color: transparent;
cursor: pointer;
position: absolute;
left: 0%;
top: 0%;
transform: scale(3);
opacity: 50;
transition: 0.2s;
}
.btn-warning:hover {
background-color: #eed519;
}
.submit-button {
position: relative;
padding: 11px 16px;
font-size: 15px;
line-height: 1.5;
border-radius: 3px;
color: #000000;
background-color: #ffc100;
border: 0;
transition: 0.2s;
opacity: 50;
overflow: hidden;
}
.submit-button:hover {
background-color: #eed519;
}
.table-container {
/* padding: 50px 50px; */
flex: 1 1 0;
border-radius: 3px;
}
.errors-container {
display: flex;
justify-content: center;
/* padding: 10px 10px; */
flex: 1 1 0;
border-radius: 3px;
}
.results-table {
padding: 50px 50px;
border-radius: 3px;
}
#myTable {
background-color: #fad253;
border-collapse: collapse;
width: 100%;
border: 1px solid #ddd;
font-size: 14px;
}
#myTable th {
background-color: #fad253;
text-align: left;
/* Left-align text */
padding: 12px;
/* Add padding */
}
#myTable tr {
/* Add a bottom border to all table rows */
border-bottom: 1px solid #ddd;
}
/*#myTable tr.header,*/
#myTable tr:hover {
/* Add a grey background color to the table header and on hover */
background-color: #ec974f;
}
#myTable td[colspan] {
padding-top: 0;
padding-bottom: 0;
}
#myTable td[colspan] p {
padding-top: 12px;
padding-bottom: 12px;
}
/* TEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEST */
#data {
background-color: #fad253;
border-collapse: collapse;
width: 100%;
border: 1px solid #ddd;
font-size: 14px;
}
#data th {
background-color: #fad253;
text-align: left;
/* Left-align text */
padding: 12px;
/* Add padding */
}
#data tr {
/* Add a bottom border to all table rows */
border-bottom: 1px solid #ddd;
}
/*#myTable tr.header,*/
#data tr:hover {
/* Add a grey background color to the table header and on hover */
background-color: #ec974f;
}
#data td[colspan] {
padding-top: 0;
padding-bottom: 0;
}
#data td[colspan] p {
padding-top: 12px;
padding-bottom: 12px;
}

View File

@ -0,0 +1,10 @@
{% with messages=get_flashed_messages() %} {% if messages %}
<div class="error_container">
<div class="error_form_empty">
<p class="text-center">
{% for message in messages %}
{{message}}
{% endfor %} {% endif %} {% endwith %}
</p>
</div>
</div>

View File

@ -0,0 +1,19 @@
<div class="input_container">
<div class="forms_container">
<div class="input_form">
<form host="/lookup" method="POST">
<input type="text" class="input-field" placeholder="Type it in.." name="host">
<input type="submit" class="submit-button" value="Look em' up!">
</form>
</div>
<div class="upload_YT">
<form action="/lookup" host="/lookup" method="POST" enctype="multipart/form-data">
<button type="button" class="btn-warning">
<i class="fa fa-upload"></i> Upload CSV
<input type="file" name="file">
</button>
<input type="submit" class="submit-button" value='Submit'>
</form>
</div>
</div>
</div>

View File

@ -0,0 +1,4 @@
<div class="test">
<button type="button">History</button>
</div>

View File

@ -0,0 +1,25 @@
<div class="howto_container">
<div class="howto_text">
<p>This is a simple one-page Flask application to help my day-to-day work as a security analyst. Above you can
type
in the following:</p>
</div>
<div class="howto_text">
<ul>
<li>URL</li>
<li>email address</li>
<li>domain</li>
</ul>
</div>
<div class="howto_text">
Submit and you will get:
</div>
<div class="howto_text">
<ul>
<li>VirusTotal score and details</li>
<li>AbuseIPDB score and details</li>
<li>Domain and IP information (registration date, country, registrar)</li>
<li>Email security information (DMARC, DKIM)</li>
</ul>
</div>
</div>

View File

@ -0,0 +1,3 @@
{% extends "layout.html" %}
{% include "test.html" %}

View File

@ -0,0 +1,41 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Got something to look up? I got you!</title>
<link rel="stylesheet" type="text/css" href="https://use.fontawesome.com/releases/v6.5.1/css/all.css" />
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='styles/style.css')}}" />
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.1/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-+0n0xVW2eSR5OomGNYDnhzAbDsOXxcvSN1TPprVMTNDbiYZCxYbOOl7+AMvyTG2x" crossorigin="anonymous" />
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.10.25/css/dataTables.bootstrap5.css" />
<!-- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" /> -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.1/css/all.min.css"
integrity="sha512-+4zCK9k+qNFUR5X+cKL9EIR+ZOhtIloNl9GIKS57V1MyNsYpYcUrUeQc9vNfzsWfV28IaLL3i96P9sdNyeRssA=="
crossorigin="anonymous" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.1/js/all.min.js"
integrity="sha512-F5QTlBqZlvuBEs9LQPqc1iZv2UMxcVXezbHzomzS6Df4MZMClge/8+gXrKw2fl5ysdk4rWjR0vKS7NNkfymaBQ=="
crossorigin="anonymous"></script>
</head>
<body>
{% block content %} {% endblock %}
<script type="text/javascript" charset="utf8" src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script type="text/javascript" charset="utf8"
src="https://cdn.datatables.net/1.10.25/js/jquery.dataTables.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<link href="https://cdn.datatables.net/v/dt/dt-1.10.16/r-2.2.1/datatables.min.css" rel="stylesheet" />
<script src="https://cdn.datatables.net/v/dt/dt-1.10.16/r-2.2.1/datatables.min.js"></script>
<script type="text/javascript" charset="utf8"
src="https://cdn.datatables.net/1.10.25/js/dataTables.bootstrap5.js"></script>
<script type="text/javascript" href="/static/javascript.js"></script>
{% block scripts %}{% endblock %}
</body>
</html>

View File

@ -0,0 +1,14 @@
{% extends "layout.html" %}
{% block content %}
<!-- {% include "forms.html" %} -->
{% include "test.html" %}
<!-- {% include "howto.html" %} -->
{% include "empty_form.html" %}
{% endblock %}

View File

@ -0,0 +1,98 @@
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="static/javascript.js"></script>
<div class="errors-container">
{% if error in errors %}
<p>
<i>
You may have mistyped: </i><b>{{errors|join(',')}}</b><i>. Try again!</i>
</i>
{% endif %}
</div>
<div class="table-container">
<div class="results-table-new">
<table id="data" class="table table-striped">
<thead>
<th>IP address</th>
<th>VirusTotal</th>
<th>AbuseIPDB</th>
<th>Domain</th>
<th>Registration country</th>
<th>Registration date</th>
<th>Email security</th>
</thead>
<tbody>
{% for host in hosts %}
<tr>
<td>{{host.host}}</td>
<td>Score: {{host.vt['score']}} / {{host.vt['total']}}<br>
Last updated: {{host.vt['last_update']}}<br>
</td>
<td>Score: {{host.abuseipdb['score']}}
Last reported: {{host.abuseipdb['last_reported']}}</td>
<td>{{host.domain}}</td>
<td>{{host.metadata['registrar_country']}}</td>
<td>{{host.metadata['creation_date']}}</td>
<td>SPF: {{host.email_security[0]}}, DMARC: {{host.email_security[1]}}</td>
</tr>
<tr>
<td colspan="7" id="info">
<p>
<b>VirusTotal</b>
<br>
{% if host.vt['score'] > 0 %}
Vendor marked this host as malicious or suspicious:
{{host.vt['vendors']|join(', ')}}
{{host.vt['score']}}
{% else %}
No vendors marked this host as malicious or suspicious.
{% endif %}
<br>
<b>AbuseIPDB</b>
<br>
IP address is: {{host.abuseipdb['IP_address']}}<br>
Is this Tor: {{host.abuseipdb['Tor']}}<br>
Usage: {{host.abuseipdb['usage']}}<br>
<br>
<!--Display some of the metadata info-->
</p>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% block scripts %}
<script>
$(document).ready(function () {
$('#data').DataTable({
paging: true,
searching: true,
info: true,
columnDefs: [{
targets: '_all',
createdCell: function (td, cellData, rowData, row, col) {
if (cellData == 'outlook.com' && col == 0) {
$(td).css({'background-color': 'red', 'color': 'white'});
console.log(cellData);
}
if (cellData.startsWith('Score: 0') && col == 1) {
console.log("TEST", cellData);
$(td).css({'background-color': 'green', 'color': 'white'});
}
else if (!cellData.startsWith('Score: 0') && col == 1) {
$(td).css({'background-color': 'red', 'color': 'white'});
}
}
}]
});
});
</script>
{% endblock %}

View File

@ -0,0 +1,20 @@
{% extends "layout.html" %}
{% block content %}
{# For multiple results, maybe make a top results overview and then a navigation menu to specific URL pages? See: https://realpython.com/primer-on-jinja-templating/#include-a-navigation-menu #}
<!-- {% include "forms.html" %} -->
{% include "test.html" %}
{# {% include "results-overview.html" %} #}
{% include "history.html" %}
{% include "results-overview-new.html" %}
{% include "results-detail.html" %}
{% include "empty_form.html" %}
{% endblock %}

View File

@ -0,0 +1,58 @@
<div class="grid_test_container">
<div class="item1">
1
</div>
<div class="item2">
2
</div>
<div class="item3">
3
</div>
<div class="item4">
4
</div>
<div>TEST</div>
<div>TEST</div>
<div>TEST</div>
<div class="grid_test">
GRID TEST
</div>
</div>
<div class="flex_in_grid_container">
<div class="flex_in_grid">
<table id="test_table" class="data-table-striped">
<thead>
<th>head 1</th>
<th>head 2</th>
<th>head 3</th>
<th>head 4</th>
<th>head 5</th>
<th>head 6</th>
<th>head 7</th>
<th>head 8</th>
</thead>
<tbody>
<tr>
<td>table data 1</td>
<td>table data 2</td>
<td>table data 3</td>
<td>table data 4</td>
<td>table data 5</td>
<td>table data 6</td>
<td>table data 7</td>
<td>table data 8</td>
</tr>
<tr>
<td>table data 1</td>
<td>table data 2</td>
<td>table data 3</td>
<td>table data 4</td>
<td>table data 5</td>
<td>table data 6</td>
<td>table data 7</td>
<td>table data 8</td>
</tr>
</tbody>
</table>
</div>
</div>

View File

@ -0,0 +1,19 @@
<div class="test_container">
<!-- <div class="test_forms_container"> -->
<!-- <div class="test_input_container"> -->
<div class="test">
<form host="/lookup" method="POST">
<input type="text" class="input-field" placeholder="Type it in.." name="host">
<input type="submit" class="submit-button" value="And submit!">
</form>
</div>
<!-- </div> -->
<div class="test">
<p>Put in your <b>IP address, URL, domain or email address</b>...</p>
<p>and see <b>AbuseIPDB and VirusTotal scores,
registrar information and DMARC, DKIM and SPF information</b>.</p>
</div>
<div class="test">
<p>This is a simple one-page Flask application to help my day-to-day work as a security analyst.</p>
</div>
</div>