moved code around for packaging, working on CSS with grid
This commit is contained in:
parent
5c2eefc17d
commit
486c855e18
32
flask_soc_site/__init__.py
Normal file
32
flask_soc_site/__init__.py
Normal 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
0
flask_soc_site/config.py
Normal file
31
flask_soc_site/lookup.py
Normal file
31
flask_soc_site/lookup.py
Normal 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)
|
52
flask_soc_site/src/abuseipdb_api.py
Normal file
52
flask_soc_site/src/abuseipdb_api.py
Normal 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
|
30
flask_soc_site/src/constants.py
Normal file
30
flask_soc_site/src/constants.py
Normal 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 = {}
|
43
flask_soc_site/src/host_data.py
Normal file
43
flask_soc_site/src/host_data.py
Normal 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
|
154
flask_soc_site/src/host_lookup.py
Normal file
154
flask_soc_site/src/host_lookup.py
Normal 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
|
111
flask_soc_site/src/virustotal_api.py
Normal file
111
flask_soc_site/src/virustotal_api.py
Normal 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
|
BIN
flask_soc_site/static/background/cool-background-trianglify.png
Normal file
BIN
flask_soc_site/static/background/cool-background-trianglify.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 263 KiB |
12
flask_soc_site/static/javascript.js
Normal file
12
flask_soc_site/static/javascript.js
Normal 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();
|
||||
}
|
||||
});
|
||||
});
|
379
flask_soc_site/static/styles/style.css
Normal file
379
flask_soc_site/static/styles/style.css
Normal 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;
|
||||
}
|
10
flask_soc_site/templates/empty_form.html
Normal file
10
flask_soc_site/templates/empty_form.html
Normal 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>
|
19
flask_soc_site/templates/forms.html
Normal file
19
flask_soc_site/templates/forms.html
Normal 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>
|
4
flask_soc_site/templates/history.html
Normal file
4
flask_soc_site/templates/history.html
Normal file
@ -0,0 +1,4 @@
|
||||
<div class="test">
|
||||
|
||||
<button type="button">History</button>
|
||||
</div>
|
25
flask_soc_site/templates/howto.html
Normal file
25
flask_soc_site/templates/howto.html
Normal 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>
|
3
flask_soc_site/templates/index.html
Normal file
3
flask_soc_site/templates/index.html
Normal file
@ -0,0 +1,3 @@
|
||||
{% extends "layout.html" %}
|
||||
|
||||
{% include "test.html" %}
|
41
flask_soc_site/templates/layout.html
Normal file
41
flask_soc_site/templates/layout.html
Normal 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>
|
14
flask_soc_site/templates/lookup.html
Normal file
14
flask_soc_site/templates/lookup.html
Normal file
@ -0,0 +1,14 @@
|
||||
{% extends "layout.html" %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<!-- {% include "forms.html" %} -->
|
||||
|
||||
{% include "test.html" %}
|
||||
|
||||
<!-- {% include "howto.html" %} -->
|
||||
|
||||
{% include "empty_form.html" %}
|
||||
|
||||
{% endblock %}
|
||||
|
0
flask_soc_site/templates/results-detail.html
Normal file
0
flask_soc_site/templates/results-detail.html
Normal file
98
flask_soc_site/templates/results-overview-new.html
Normal file
98
flask_soc_site/templates/results-overview-new.html
Normal 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 %}
|
20
flask_soc_site/templates/results.html
Normal file
20
flask_soc_site/templates/results.html
Normal 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 %}
|
58
flask_soc_site/templates/test-grid-and-flex.html
Normal file
58
flask_soc_site/templates/test-grid-and-flex.html
Normal 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>
|
19
flask_soc_site/templates/test.html
Normal file
19
flask_soc_site/templates/test.html
Normal 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>
|
Loading…
x
Reference in New Issue
Block a user