new: Very basic RiskIQ support for PDNS.

pull/463/head
Raphaël Vinot 2022-07-15 18:53:49 +02:00
parent 5f329e4d7b
commit c0bde711d2
10 changed files with 293 additions and 18 deletions

View File

@ -51,6 +51,11 @@
"url": "https://hashlookup.circl.lu/",
"allow_auto_trigger": true
},
"RiskIQ": {
"user": null,
"apikey": null,
"allow_auto_trigger": false
},
"_notes": {
"apikey": "null disables the module. Pass a string otherwise.",
"autosubmit": "Automatically submits the URL to the 3rd party service.",
@ -63,6 +68,7 @@
"UrlScan": "Module to query urlscan.io",
"Phishtank": "Module to query Phishtank Lookup (https://github.com/Lookyloo/phishtank-lookup). URL set to none means querying the public instance.",
"Hashlookup": "Module to query Hashlookup (https://github.com/adulau/hashlookup-server). URL set to none means querying the public instance.",
"FOX": "Submission only interface by and for CCCS"
"FOX": "Submission only interface by and for CCCS",
"RiskIQ": "Module to query RiskIQ (https://community.riskiq.com/)"
}
}

View File

@ -189,7 +189,7 @@ def get_useragent_for_requests():
return f'Lookyloo / {version}'
def get_cache_directory(root: Path, identifier: str, namespace: Optional[str] = None) -> Path:
def get_cache_directory(root: Path, identifier: str, namespace: Optional[Union[str, Path]] = None) -> Path:
m = hashlib.md5()
m.update(identifier.encode())
digest = m.hexdigest()

View File

@ -34,7 +34,8 @@ from .helpers import (CaptureStatus, get_captures_dir, get_email_template,
uniq_domains, ParsedUserAgent)
from .indexing import Indexing
from .modules import (MISP, PhishingInitiative, UniversalWhois,
UrlScan, VirusTotal, Phishtank, Hashlookup)
UrlScan, VirusTotal, Phishtank, Hashlookup,
RiskIQ)
class Lookyloo():
@ -82,6 +83,10 @@ class Lookyloo():
if not self.hashlookup.available:
self.logger.warning('Unable to setup the Hashlookup module')
self.riskiq = RiskIQ(get_config('modules', 'RiskIQ'))
if not self.riskiq.available:
self.logger.warning('Unable to setup the RiskIQ module')
self.logger.info('Initializing context...')
self.context = Context()
self.logger.info('Context initialized.')
@ -281,6 +286,22 @@ class Lookyloo():
to_return['urlscan']['result'] = result
return to_return
def get_historical_lookups(self, capture_uuid: str, /, force: bool=False) -> Dict:
# this method is only trigered when the user wants to get more details about the capture
# by looking at Passive DNS systems, check if there are hits in the current capture
# in another one and things like that. The trigger_modules method is for getting
# information about the current status of the capture in other systems.
try:
ct = self.get_crawled_tree(capture_uuid)
except LookylooException:
self.logger.warning(f'Unable to get the modules responses unless the tree ({capture_uuid}) is cached.')
return None
to_return: Dict[str, Any] = {}
if self.riskiq.available:
self.riskiq.capture_default_trigger(ct)
to_return['riskiq'] = self.riskiq.get_passivedns(ct.root_hartree.rendered_node.hostname)
return to_return
def hide_capture(self, capture_uuid: str, /) -> None:
"""Add the capture in the hidden pool (not shown on the front page)
NOTE: it won't remove the correlations until they are rebuilt.

View File

@ -9,3 +9,4 @@ from .uwhois import UniversalWhois # noqa
from .vt import VirusTotal # noqa
from .phishtank import Phishtank # noqa
from .hashlookup import HashlookupModule as Hashlookup # noqa
from .riskiq import RiskIQ # noqa

View File

@ -0,0 +1,84 @@
#!/usr/bin/env python3
import json
from datetime import date
from typing import Any, Dict
from har2tree import CrawledTree
from passivetotal import AccountClient, DnsRequest, WhoisRequest
from ..default import ConfigError, get_homedir
from ..helpers import get_cache_directory
class RiskIQ():
def __init__(self, config: Dict[str, Any]):
if not (config.get('user') and config.get('apikey')):
self.available = False
return
self.available = True
self.allow_auto_trigger = False
test_client = AccountClient(username=config.get('user'), api_key=config.get('apikey'))
# Check account is working
details = test_client.get_account_details()
if 'message' in details and details['message'] == 'invalid credentials':
self.available = False
raise ConfigError('RiskIQ not available, invalid credentials')
return
self.client_dns = DnsRequest(username=config.get('user'), api_key=config.get('apikey'))
self.client_whois = WhoisRequest(username=config.get('user'), api_key=config.get('apikey'))
if config.get('allow_auto_trigger'):
self.allow_auto_trigger = True
self.storage_dir_riskiq = get_homedir() / 'riskiq'
self.storage_dir_riskiq.mkdir(parents=True, exist_ok=True)
def get_passivedns(self, query: str) -> Dict[str, Any]:
# The query can be IP or Hostname. For now, we only do it on domains.
url_storage_dir = get_cache_directory(self.storage_dir_riskiq, query, 'pdns')
print(url_storage_dir)
if not url_storage_dir.exists():
return None
cached_entries = sorted(url_storage_dir.glob('*'), reverse=True)
if not cached_entries:
return None
with cached_entries[0].open() as f:
return json.load(f)
def capture_default_trigger(self, crawled_tree: CrawledTree, /, *, force: bool=False, auto_trigger: bool=False) -> Dict:
'''Run the module on all the nodes up to the final redirect'''
if not self.available:
return {'error': 'Module not available'}
if auto_trigger and not self.allow_auto_trigger:
return {'error': 'Auto trigger not allowed on module'}
self.pdns_lookup(crawled_tree.root_hartree.rendered_node.hostname, force)
return {'success': 'Module triggered'}
def pdns_lookup(self, hostname: str, force: bool=False) -> None:
'''Lookup an hostname on RiskIQ Passive DNS
Note: force means re-fetch the entry RiskIQ even if we already did it today
'''
if not self.available:
raise ConfigError('RiskIQ not available, probably no API key')
url_storage_dir = get_cache_directory(self.storage_dir_riskiq, hostname, 'pdns')
url_storage_dir.mkdir(parents=True, exist_ok=True)
riskiq_file = url_storage_dir / date.today().isoformat()
if not force and riskiq_file.exists():
return
pdns_info = self.client_dns.get_passive_dns(query=hostname)
print(pdns_info)
if not pdns_info:
return
with riskiq_file.open('w') as _f:
json.dump(pdns_info, _f)

114
poetry.lock generated
View File

@ -276,6 +276,18 @@ category = "dev"
optional = false
python-versions = "*"
[[package]]
name = "filelock"
version = "3.7.1"
description = "A platform independent file lock."
category = "main"
optional = false
python-versions = ">=3.7"
[package.extras]
docs = ["furo (>=2021.8.17b43)", "sphinx (>=4.1)", "sphinx-autodoc-typehints (>=1.12)"]
testing = ["covdefaults (>=1.2.0)", "coverage (>=4)", "pytest (>=4)", "pytest-cov", "pytest-timeout (>=1.4.2)"]
[[package]]
name = "filetype"
version = "1.1.0"
@ -286,7 +298,7 @@ python-versions = "*"
[[package]]
name = "flask"
version = "2.1.2"
version = "2.1.3"
description = "A simple framework for building complex web applications."
category = "main"
optional = false
@ -356,6 +368,14 @@ category = "main"
optional = false
python-versions = ">=3.7"
[[package]]
name = "future"
version = "0.18.2"
description = "Clean single-source support for Python 3 and 2"
category = "main"
optional = false
python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
[[package]]
name = "greenlet"
version = "1.1.2"
@ -645,6 +665,23 @@ python-versions = ">=3.6"
qa = ["flake8 (==3.8.3)", "mypy (==0.782)"]
testing = ["docopt", "pytest (<6.0.0)"]
[[package]]
name = "passivetotal"
version = "2.5.9"
description = "Library for the RiskIQ PassiveTotal and Illuminate API"
category = "main"
optional = false
python-versions = "*"
[package.dependencies]
future = "*"
python-dateutil = "*"
requests = "*"
tldextract = "*"
[package.extras]
pandas = ["pandas"]
[[package]]
name = "pexpect"
version = "4.8.0"
@ -678,7 +715,7 @@ tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "pa
[[package]]
name = "playwright"
version = "1.23.0"
version = "1.23.1"
description = "A high-level API to automate web browsers"
category = "main"
optional = false
@ -980,6 +1017,18 @@ urllib3 = ">=1.21.1,<1.27"
socks = ["PySocks (>=1.5.6,!=1.5.7)"]
use_chardet_on_py3 = ["chardet (>=3.0.2,<6)"]
[[package]]
name = "requests-file"
version = "1.5.1"
description = "File transport adapter for Requests"
category = "main"
optional = false
python-versions = "*"
[package.dependencies]
requests = ">=1.0.0"
six = "*"
[[package]]
name = "rich"
version = "12.5.1"
@ -1028,6 +1077,20 @@ pure-eval = "*"
[package.extras]
tests = ["pytest", "typeguard", "pygments", "littleutils", "cython"]
[[package]]
name = "tldextract"
version = "3.3.1"
description = "Accurately separates a URL's subdomain, domain, and public suffix, using the Public Suffix List (PSL). By default, this includes the public ICANN TLDs and their exceptions. You can optionally support the Public Suffix List's private domains as well."
category = "main"
optional = false
python-versions = ">=3.7"
[package.dependencies]
filelock = ">=3.0.8"
idna = "*"
requests = ">=2.1.0"
requests-file = ">=1.4"
[[package]]
name = "tomli"
version = "2.0.1"
@ -1137,7 +1200,7 @@ python-versions = "*"
[[package]]
name = "types-requests"
version = "2.28.0"
version = "2.28.1"
description = "Typing stubs for requests"
category = "dev"
optional = false
@ -1148,7 +1211,7 @@ types-urllib3 = "<1.27"
[[package]]
name = "types-setuptools"
version = "62.6.1"
version = "63.2.0"
description = "Typing stubs for setuptools"
category = "dev"
optional = false
@ -1328,7 +1391,7 @@ misp = ["python-magic", "pydeep2"]
[metadata]
lock-version = "1.1"
python-versions = ">=3.8,<3.11"
content-hash = "aacc3fc7b768656db70e2d898fadbe68a13f2915b2e58997d96385bd749f8ba6"
content-hash = "e36f3a483a9a3869b446e0633b005628b3cf832ed1836610a7fcf6af894800df"
[metadata.files]
aiohttp = [
@ -1540,13 +1603,17 @@ executing = [
{file = "executing-0.8.3-py2.py3-none-any.whl", hash = "sha256:d1eef132db1b83649a3905ca6dd8897f71ac6f8cac79a7e58a1a09cf137546c9"},
{file = "executing-0.8.3.tar.gz", hash = "sha256:c6554e21c6b060590a6d3be4b82fb78f8f0194d809de5ea7df1c093763311501"},
]
filelock = [
{file = "filelock-3.7.1-py3-none-any.whl", hash = "sha256:37def7b658813cda163b56fc564cdc75e86d338246458c4c28ae84cabefa2404"},
{file = "filelock-3.7.1.tar.gz", hash = "sha256:3a0fd85166ad9dbab54c9aec96737b744106dc5f15c0b09a6744a445299fcf04"},
]
filetype = [
{file = "filetype-1.1.0-py2.py3-none-any.whl", hash = "sha256:117e25a50988d1a03a32ed510f4a15353e7291e683e94c63930497dd2c66ce24"},
{file = "filetype-1.1.0.tar.gz", hash = "sha256:afe4a00029601f66d239b72688065cc7c219dec1c927994f90b825e9e53d8f93"},
]
flask = [
{file = "Flask-2.1.2-py3-none-any.whl", hash = "sha256:fad5b446feb0d6db6aec0c3184d16a8c1f6c3e464b511649c8918a9be100b4fe"},
{file = "Flask-2.1.2.tar.gz", hash = "sha256:315ded2ddf8a6281567edb27393010fe3406188bafbfe65a3339d5787d89e477"},
{file = "Flask-2.1.3-py3-none-any.whl", hash = "sha256:9013281a7402ad527f8fd56375164f3aa021ecfaff89bfe3825346c24f87e04c"},
{file = "Flask-2.1.3.tar.gz", hash = "sha256:15972e5017df0575c3d6c090ba168b6db90259e620ac8d7ea813a396bad5b6cb"},
]
flask-cors = [
{file = "Flask-Cors-3.0.10.tar.gz", hash = "sha256:b60839393f3b84a0f3746f6cdca56c1ad7426aa738b70d6c61375857823181de"},
@ -1621,6 +1688,9 @@ frozenlist = [
{file = "frozenlist-1.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:772965f773757a6026dea111a15e6e2678fbd6216180f82a48a40b27de1ee2ab"},
{file = "frozenlist-1.3.0.tar.gz", hash = "sha256:ce6f2ba0edb7b0c1d8976565298ad2deba6f8064d2bebb6ffce2ca896eb35b0b"},
]
future = [
{file = "future-0.18.2.tar.gz", hash = "sha256:b1bead90b70cf6ec3f0710ae53a525360fa360d306a86583adc6bf83a4db537d"},
]
greenlet = [
{file = "greenlet-1.1.2-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:58df5c2a0e293bf665a51f8a100d3e9956febfbf1d9aaf8c0677cf70218910c6"},
{file = "greenlet-1.1.2-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:aec52725173bd3a7b56fe91bc56eccb26fbdff1386ef123abb63c84c5b43b63a"},
@ -2005,6 +2075,10 @@ parso = [
{file = "parso-0.8.3-py2.py3-none-any.whl", hash = "sha256:c001d4636cd3aecdaf33cbb40aebb59b094be2a74c556778ef5576c175e19e75"},
{file = "parso-0.8.3.tar.gz", hash = "sha256:8c07be290bb59f03588915921e29e8a50002acaf2cdc5fa0e0114f91709fafa0"},
]
passivetotal = [
{file = "passivetotal-2.5.9-py3-none-any.whl", hash = "sha256:070c408181bf294f1cf4d49bd7184a00c9419b2bac7a3405f247f786db45ed8f"},
{file = "passivetotal-2.5.9.tar.gz", hash = "sha256:f5f1b7843257bc1ed5ae951c48902eb809a4a632947a57d6f8ad199428b13251"},
]
pexpect = [
{file = "pexpect-4.8.0-py2.py3-none-any.whl", hash = "sha256:0b48a55dcb3c05f3329815901ea4fc1537514d6ba867a152b581d69ae3710937"},
{file = "pexpect-4.8.0.tar.gz", hash = "sha256:fc65a43959d153d0114afe13997d439c22823a27cefceb5ff35c2178c6784c0c"},
@ -2073,7 +2147,15 @@ pillow = [
{file = "Pillow-9.2.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:0030fdbd926fb85844b8b92e2f9449ba89607231d3dd597a21ae72dc7fe26927"},
{file = "Pillow-9.2.0.tar.gz", hash = "sha256:75e636fd3e0fb872693f23ccb8a5ff2cd578801251f3a4f6854c6a5d437d3c04"},
]
playwright = []
playwright = [
{file = "playwright-1.23.1-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:438c15c20930cbdb0a8d1d8d6e37f2d8f35e810303f10ac18f12fca0b15c2e11"},
{file = "playwright-1.23.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:4559648db378752072965bce139993720212328fe1a009474e3136df381216a3"},
{file = "playwright-1.23.1-py3-none-macosx_11_0_universal2.whl", hash = "sha256:34ec1c952e0dfe203e45e52109f490b6bd90ccecdfaa389dc551ed580c7d94b4"},
{file = "playwright-1.23.1-py3-none-manylinux1_x86_64.whl", hash = "sha256:c928a6c2af723f7196dc0c243d857ac96b0951360166cc5cb114ae76c42c359e"},
{file = "playwright-1.23.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b8573ab3aa669c951421dd4cfc4c99f3f6c3f3a2a0318c6b91c45c0f5fa2f7c3"},
{file = "playwright-1.23.1-py3-none-win32.whl", hash = "sha256:f337c8fc4be9b5ebd63297ae235aae4b8e0cb694adfbef88478fc78712afca09"},
{file = "playwright-1.23.1-py3-none-win_amd64.whl", hash = "sha256:72f2e21effda5f3f8a0a23988bc4b49b78ca6a78303159e6ee4aa514c2451ca0"},
]
playwrightcapture = [
{file = "PlaywrightCapture-1.13.1-py3-none-any.whl", hash = "sha256:d2d0a8b74ab023b782e37a4d089a6835d413b8cbfa276549a7ac2a6f4afb1f1c"},
{file = "PlaywrightCapture-1.13.1.tar.gz", hash = "sha256:dcfca2b121134a648cdc54440c3e9e9b38c9ae1a459ad70ad6a767362ce4ccfd"},
@ -2274,6 +2356,10 @@ requests = [
{file = "requests-2.28.1-py3-none-any.whl", hash = "sha256:8fefa2a1a1365bf5520aac41836fbee479da67864514bdb821f31ce07ce65349"},
{file = "requests-2.28.1.tar.gz", hash = "sha256:7c5599b102feddaa661c826c56ab4fee28bfd17f5abca1ebbe3e7f19d7c97983"},
]
requests-file = [
{file = "requests-file-1.5.1.tar.gz", hash = "sha256:07d74208d3389d01c38ab89ef403af0cfec63957d53a0081d8eca738d0247d8e"},
{file = "requests_file-1.5.1-py2.py3-none-any.whl", hash = "sha256:dfe5dae75c12481f68ba353183c53a65e6044c923e64c24b2209f6c7570ca953"},
]
rich = [
{file = "rich-12.5.1-py3-none-any.whl", hash = "sha256:2eb4e6894cde1e017976d2975ac210ef515d7548bc595ba20e195fb9628acdeb"},
{file = "rich-12.5.1.tar.gz", hash = "sha256:63a5c5ce3673d3d5fbbf23cd87e11ab84b6b451436f1b7f19ec54b6bc36ed7ca"},
@ -2290,6 +2376,10 @@ stack-data = [
{file = "stack_data-0.3.0-py3-none-any.whl", hash = "sha256:aa1d52d14d09c7a9a12bb740e6bdfffe0f5e8f4f9218d85e7c73a8c37f7ae38d"},
{file = "stack_data-0.3.0.tar.gz", hash = "sha256:77bec1402dcd0987e9022326473fdbcc767304892a533ed8c29888dacb7dddbc"},
]
tldextract = [
{file = "tldextract-3.3.1-py3-none-any.whl", hash = "sha256:35a0260570e214d8d3cfeeb403992fe9e2b686925f63c9b03c5933408ac2aa5a"},
{file = "tldextract-3.3.1.tar.gz", hash = "sha256:fe15ac3205e5a25b61689369f98cb45c7778a8f2af113d7c11559ece5195f2d6"},
]
tomli = [
{file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"},
{file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"},
@ -2336,12 +2426,12 @@ types-redis = [
{file = "types_redis-4.3.4-py3-none-any.whl", hash = "sha256:f84dfe570ac729fb51735357f807a9e59b4732ddd7050708a7a7e32782b91047"},
]
types-requests = [
{file = "types-requests-2.28.0.tar.gz", hash = "sha256:9863d16dfbb3fa55dcda64fa3b989e76e8859033b26c1e1623e30465cfe294d3"},
{file = "types_requests-2.28.0-py3-none-any.whl", hash = "sha256:85383b4ef0535f639c3f06c5bbb6494bbf59570c4cd88bbcf540f0b2ac1b49ab"},
{file = "types-requests-2.28.1.tar.gz", hash = "sha256:acd8ed78509d27bdf04cddcc05f7066dfde4d30dd7dba67b808cdb1141d62ffe"},
{file = "types_requests-2.28.1-py3-none-any.whl", hash = "sha256:b097692e124001f0ed5e4490245bb090f5e8e929819972f9ace84f9c3e146e8c"},
]
types-setuptools = [
{file = "types-setuptools-62.6.1.tar.gz", hash = "sha256:affd968a3a7218e1c96f1806eb457f4027eac803b3caaddccf98a4e5776b1724"},
{file = "types_setuptools-62.6.1-py3-none-any.whl", hash = "sha256:b0341c29c7f44f7625204532b8829bd92ebb5fd48aa9f3e2052177e743e990b1"},
{file = "types-setuptools-63.2.0.tar.gz", hash = "sha256:fc9a6c4776a398d0f57b259ca893748342174c52a35d593d08b56f52aa99c1a4"},
{file = "types_setuptools-63.2.0-py3-none-any.whl", hash = "sha256:f9d0d0443dd344cad78da04320a3fb7837d4dd3f3ef4b25d3e0958edea6da812"},
]
types-urllib3 = []
types-werkzeug = [

View File

@ -37,7 +37,7 @@ start_website = "bin.start_website:main"
[tool.poetry.dependencies]
python = ">=3.8,<3.11"
requests = "^2.28.1"
flask = "^2.1.2"
flask = "^2.1.3"
gunicorn = "^20.1.0"
cchardet = "^2.1.7"
redis = {version = "^4.3.4", extras = ["hiredis"]}
@ -65,6 +65,7 @@ ua-parser = "^0.15.0"
Flask-Login = "^0.6.1"
har2tree = "^1.13.1"
playwrightcapture = "^1.13.1"
passivetotal = "^2.5.9"
[tool.poetry.extras]
misp = ['python-magic', 'pydeep2']
@ -73,13 +74,13 @@ misp = ['python-magic', 'pydeep2']
mypy = "^0.961"
ipython = "^8.4.0"
types-redis = "^4.3.4"
types-requests = "^2.28.0"
types-requests = "^2.28.1"
types-Flask = "^1.1.6"
types-pkg-resources = "^0.1.3"
types-Deprecated = "^1.2.9"
types-python-dateutil = "^2.8.18"
types-beautifulsoup4 = "^4.11.3"
types-setuptools = "^62.6.1"
types-setuptools = "^63.2.0"
types-Pillow = "^9.2.0"
[build-system]

View File

@ -251,6 +251,14 @@ def trigger_modules(tree_uuid: str):
return redirect(url_for('modules', tree_uuid=tree_uuid))
@app.route('/tree/<string:tree_uuid>/historical_lookups', methods=['GET'])
def historical_lookups(tree_uuid: str):
force = True if (request.args.get('force') and request.args.get('force') == 'True') else False
data = lookyloo.get_historical_lookups(tree_uuid, force)
return render_template('historical_lookups.html', tree_uuid=tree_uuid,
riskiq=data['riskiq'])
@app.route('/tree/<string:tree_uuid>/categories_capture/', defaults={'query': ''})
@app.route('/tree/<string:tree_uuid>/categories_capture/<string:query>', methods=['GET'])
def categories_capture(tree_uuid: str, query: str):

View File

@ -0,0 +1,31 @@
{% from "macros.html" import shorten_string %}
<div>
{% if riskiq %}
<hr>
<center>
<h1 class="display-4">RiskIQ</h1>
<div>
<h3>{{riskiq['queryValue']}}</h3>
<h4>{{riskiq['firstSeen']}} - {{ riskiq['lastSeen']}}</h4>
<table class="table">
<thead>
<tr>
<th scope="col">First seen</th>
<th scope="col">Last seen</th>
<th scope="col">Resolve</th>
<th scope="col">Type</th>
</thead>
<tbody>
{% for entry in riskiq['results'] %}
<tr>
<td>{{entry['firstSeen']}}</td>
<td>{{entry['lastSeen']}}</td>
<td>{{entry['resolve']}}</td>
<td>{{entry['recordType']}}</td>
</tr>
{% endfor %}
</table>
</center>
{% endif%}
</div>

View File

@ -47,6 +47,11 @@
var modal = $(this);
modal.find('.modal-body').load(button.data("remote"));
});
$('#historyModal').on('show.bs.modal', function(e) {
var button = $(e.relatedTarget);
var modal = $(this);
modal.find('.modal-body').load(button.data("remote"));
});
$('.modulesForceRefresh').on('click',function(){
$('#modulesModal .modal-body').load("{{ url_for('trigger_modules', tree_uuid=tree_uuid, force=True) }}", function(){
$('#modulesModal').modal({show:true});
@ -213,6 +218,13 @@
data-bs-toggle="modal" data-bs-target="#modulesModal" role="button">Third Party Reports</a>
</li>
{% if current_user.is_authenticated %}
<?-- This bloc will be available for everyone soon-->
<li>
<a href="#historyModal" data-remote="{{ url_for('historical_lookups', tree_uuid=tree_uuid, force=False) }}"
data-bs-toggle="modal" data-bs-target="#historyModal" role="button">Historical lookups</a>
</li>
{% endif %}
{% if current_user.is_authenticated %}
<li>
<a href="#hashlookupModal" data-remote="{{ url_for('hashlookup', tree_uuid=tree_uuid) }}"
data-bs-toggle="modal" data-bs-target="#hashlookupModal" role="button">Hashlookup hits</a>
@ -531,6 +543,27 @@
</div>
</div>
<div class="modal fade" id="historyModal" tabindex="-1" role="dialog">
<div class="modal-dialog modal-xl" role="document">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title" id="historyModalLabel">
Historical data and contex about this capture
</h4>
</br>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
</br>
<div class="modal-body">
... loading results historical context ...
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
<div class="modal fade" id="hashlookupModal" tabindex="-1" role="dialog">
<div class="modal-dialog modal-xl" role="document">
<div class="modal-content">