new: (WiP) Add support for multiple MISPs

multipleMISPS
Raphaël Vinot 2023-08-28 17:25:55 +02:00
parent f456947b62
commit 6034d10834
9 changed files with 327 additions and 163 deletions

View File

@ -23,16 +23,23 @@
"enabled": true,
"allow_auto_trigger": true
},
"MISP": {
"apikey": null,
"url": "https://misp.url",
"verify_tls_cert": true,
"timeout": 10,
"enable_lookup": false,
"enable_push": false,
"default_tags": [],
"auto_publish": false,
"allow_auto_trigger": false
"MultipleMISPs": {
"default": "MISP",
"instances": {
"MISP": {
"apikey": null,
"url": "https://misp.url",
"verify_tls_cert": true,
"timeout": 10,
"enable_lookup": false,
"enable_push": false,
"default_tags": [
"source:lookyloo"
],
"auto_publish": false,
"allow_auto_trigger": false
}
}
},
"UniversalWhois": {
"enabled": false,

View File

@ -52,7 +52,7 @@ from .helpers import (get_captures_dir, get_email_template,
uniq_domains, ParsedUserAgent, load_cookies, UserAgents,
get_useragent_for_requests)
from .indexing import Indexing
from .modules import (MISP, PhishingInitiative, UniversalWhois,
from .modules import (MISPs, MISP, PhishingInitiative, UniversalWhois,
UrlScan, VirusTotal, Phishtank, Hashlookup,
RiskIQ, RiskIQError, Pandora, URLhaus)
@ -106,9 +106,33 @@ class Lookyloo():
if not self.vt.available:
self.logger.warning('Unable to setup the VirusTotal module')
self.misp = MISP(get_config('modules', 'MISP'))
if not self.misp.available:
self.logger.warning('Unable to setup the MISP module')
# ## Initialize MISP(s)
try_old_config = False
if misps_config := get_config('modules', 'MultipleMISPs'):
# New config
self.misps = MISPs(misps_config)
if not self.misps.available:
self.logger.warning('Unable to setup the MISP module')
try_old_config = True
if try_old_config:
# Legacy MISP config, now use MultipleMISPs key to support more than one MISP instance
try:
if misp_config := get_config('modules', 'MISP'):
misps_config = {'default': 'MISP', 'instances': {'MISP': misp_config}}
self.misps = MISPs(misps_config)
if self.misps.available:
self.logger.warning('Please migrate the MISP config to the "MultipleMISPs" key in the config, and remove the "MISP" key')
else:
self.logger.warning('Unable to setup the MISP module')
except Exception:
# The key was removed from the config, and the sample config
pass
if not self.misps.available:
self.logger.info('The MISP module is not configured')
# ## Done with MISP(s)
self.uwhois = UniversalWhois(get_config('modules', 'UniversalWhois'))
if not self.uwhois.available:
@ -1129,9 +1153,9 @@ class Lookyloo():
# In the case, we want to have it as a FileObject in the export
filename, pseudofile = self.get_data(capture_uuid)
if filename:
event = self.misp.export(cache, self.is_public_instance, filename, pseudofile)
event = self.misps.export(cache, self.is_public_instance, filename, pseudofile)
else:
event = self.misp.export(cache, self.is_public_instance)
event = self.misps.export(cache, self.is_public_instance)
screenshot: MISPAttribute = event.add_attribute('attachment', 'screenshot_landing_page.png',
data=self.get_screenshot(cache.uuid),
disable_correlation=True) # type: ignore
@ -1179,8 +1203,18 @@ class Lookyloo():
return [event]
def get_misp_occurrences(self, capture_uuid: str, /) -> Optional[Dict[str, Set[str]]]:
if not self.misp.available:
def get_misp_instance(self, instance_name: Optional[str]=None) -> MISP:
if instance_name:
if misp := self.misps.get(instance_name):
return misp
self.logger.warning(f'Unable to connect to MISP Instance {instance_name}, falling back to default.')
return self.misps.default_misp
def get_misp_occurrences(self, capture_uuid: str, /, *, instance_name: Optional[str]=None) -> Optional[Tuple[Dict[str, Set[str]], str]]:
misp = self.get_misp_instance(instance_name)
if not misp.available:
return None
try:
ct = self.get_crawled_tree(capture_uuid)
@ -1190,12 +1224,12 @@ class Lookyloo():
nodes_to_lookup = ct.root_hartree.rendered_node.get_ancestors() + [ct.root_hartree.rendered_node]
to_return: Dict[str, Set[str]] = defaultdict(set)
for node in nodes_to_lookup:
hits = self.misp.lookup(node, ct.root_hartree.get_host_node_by_uuid(node.hostnode_uuid))
hits = misp.lookup(node, ct.root_hartree.get_host_node_by_uuid(node.hostnode_uuid))
for event_id, values in hits.items():
if not isinstance(values, set):
continue
to_return[event_id].update(values)
return to_return
return to_return, misp.client.root_url
def get_hashes_with_context(self, tree_uuid: str, /, algorithm: str, *, urls_only: bool=False) -> Union[Dict[str, Set[str]], Dict[str, List[URLNode]]]:
"""Build (on demand) hashes for all the ressources of the tree, using the alorighm provided by the user.

View File

@ -1,7 +1,7 @@
#!/usr/bin/env python3
from .fox import FOX # noqa
from .misp import MISP # noqa
from .misp import MISPs, MISP # noqa
from .pi import PhishingInitiative # noqa
from .sanejs import SaneJavaScript # noqa
from .urlscan import UrlScan # noqa

View File

@ -5,6 +5,7 @@ import re
from io import BytesIO
from collections import defaultdict
from collections.abc import Mapping
from typing import Any, Dict, List, Optional, Set, Union, TYPE_CHECKING
import requests
@ -19,6 +20,137 @@ if TYPE_CHECKING:
from ..capturecache import CaptureCache
class MISPs(Mapping):
def __init__(self, config: Dict[str, Any]):
self.logger = logging.getLogger(f'{self.__class__.__name__}')
self.logger.setLevel(get_config('generic', 'loglevel'))
if not config.get('default'):
self.available = False
self.logger.info('No default instance configured, disabling MISP.')
return
if not config.get('instances'):
self.available = False
self.logger.warning('No MISP instances configured, disabling MISP.')
return
self.default_instance = config['default']
if self.default_instance not in config['instances']:
self.available = False
self.logger.warning(f"The default MISP instance ({self.default_instance}) is missing in the instances ({', '.join(config['instances'].keys())}), disabling MISP.")
return
self.__misps: Dict[str, 'MISP'] = {}
for instance_name, instance_config in config['instances'].items():
if misp_connector := MISP(instance_config):
if misp_connector.available:
self.__misps[instance_name] = misp_connector
else:
self.logger.warning(f"MISP '{instance_name}' isn't available.")
else:
self.logger.warning(f"Unable to initialize the connector to '{instance_name}'. It won't be available.")
if not self.__misps.get(self.default_instance) or not self.__misps[self.default_instance].available:
self.available = False
self.logger.warning("Unable to initialize the connector to the default MISP instance, disabling MISP.")
return
self.available = True
def __getitem__(self, name: str) -> 'MISP':
return self.__misps[name]
def __iter__(self):
return iter(self.__misps)
def __len__(self):
return len(self.__misps)
@property
def default_misp(self) -> 'MISP':
return self.__misps[self.default_instance]
def export(self, cache: 'CaptureCache', is_public_instance: bool=False,
submitted_filename: Optional[str]=None,
submitted_file: Optional[BytesIO]=None) -> MISPEvent:
'''Export a capture in MISP format. You can POST the return of this method
directly to a MISP instance and it will create an event.'''
public_domain = get_config('generic', 'public_domain')
event = MISPEvent()
if cache.url.startswith('file'):
filename = cache.url.rsplit('/', 1)[-1]
event.info = f'Lookyloo Capture ({filename})'
# Create file object as initial
if hasattr(cache.tree.root_hartree.url_tree, 'body'):
# The file could be viewed in the browser
filename = cache.tree.root_hartree.url_tree.name
pseudofile = cache.tree.root_hartree.url_tree.body
elif submitted_filename:
# Impossible to get the file from the HAR.
filename = submitted_filename
pseudofile = submitted_file
else:
raise Exception('We must have a file here.')
initial_file = FileObject(pseudofile=pseudofile, filename=filename)
initial_file.comment = 'This is a capture of a file, rendered in the browser'
initial_obj = event.add_object(initial_file)
else:
event.info = f'Lookyloo Capture ({cache.url})'
initial_url = URLObject(cache.url)
initial_url.comment = 'Submitted URL'
self.__misp_add_ips_to_URLObject(initial_url, cache.tree.root_hartree.hostname_tree)
initial_obj = event.add_object(initial_url)
lookyloo_link: MISPAttribute = event.add_attribute('link', f'https://{public_domain}/tree/{cache.uuid}') # type: ignore
if not is_public_instance:
lookyloo_link.distribution = 0
initial_obj.add_reference(lookyloo_link, 'captured-by', 'Capture on lookyloo')
redirects: List[URLObject] = []
for nb, url in enumerate(cache.redirects):
if url == cache.url:
continue
obj = URLObject(url)
obj.comment = f'Redirect {nb}'
self.__misp_add_ips_to_URLObject(obj, cache.tree.root_hartree.hostname_tree)
redirects.append(obj)
if redirects:
redirects[-1].comment = f'Last redirect ({nb})'
if redirects:
prec_object = initial_url
for u_object in redirects:
prec_object.add_reference(u_object, 'redirects-to')
prec_object = u_object
for u_object in redirects:
event.add_object(u_object)
final_redirect = event.objects[-1]
try:
fo = FileObject(pseudofile=cache.tree.root_hartree.rendered_node.body, filename=cache.tree.root_hartree.rendered_node.filename)
fo.comment = 'Content received for the final redirect (before rendering)'
fo.add_reference(final_redirect, 'loaded-by', 'URL loading that content')
event.add_object(fo)
except Har2TreeError:
pass
except AttributeError:
# No `body` in rendered node
pass
return event
def __misp_add_ips_to_URLObject(self, obj: URLObject, hostname_tree: HostNode) -> None:
hosts = obj.get_attributes_by_relation('host')
if hosts:
hostnodes = hostname_tree.search_nodes(name=hosts[0].value)
if hostnodes and hasattr(hostnodes[0], 'resolved_ips'):
obj.add_attributes('ip', *hostnodes[0].resolved_ips)
class MISP():
def __init__(self, config: Dict[str, Any]):
@ -159,81 +291,3 @@ class MISP():
return {'info': 'No hits.'}
else:
return {'error': 'Module not available or lookup not enabled.'}
def __misp_add_ips_to_URLObject(self, obj: URLObject, hostname_tree: HostNode) -> None:
hosts = obj.get_attributes_by_relation('host')
if hosts:
hostnodes = hostname_tree.search_nodes(name=hosts[0].value)
if hostnodes and hasattr(hostnodes[0], 'resolved_ips'):
obj.add_attributes('ip', *hostnodes[0].resolved_ips)
def export(self, cache: 'CaptureCache', is_public_instance: bool=False,
submitted_filename: Optional[str]=None,
submitted_file: Optional[BytesIO]=None) -> MISPEvent:
'''Export a capture in MISP format. You can POST the return of this method
directly to a MISP instance and it will create an event.'''
public_domain = get_config('generic', 'public_domain')
event = MISPEvent()
if cache.url.startswith('file'):
filename = cache.url.rsplit('/', 1)[-1]
event.info = f'Lookyloo Capture ({filename})'
# Create file object as initial
if hasattr(cache.tree.root_hartree.url_tree, 'body'):
# The file could be viewed in the browser
filename = cache.tree.root_hartree.url_tree.name
pseudofile = cache.tree.root_hartree.url_tree.body
elif submitted_filename:
# Impossible to get the file from the HAR.
filename = submitted_filename
pseudofile = submitted_file
else:
raise Exception('We must have a file here.')
initial_file = FileObject(pseudofile=pseudofile, filename=filename)
initial_file.comment = 'This is a capture of a file, rendered in the browser'
initial_obj = event.add_object(initial_file)
else:
event.info = f'Lookyloo Capture ({cache.url})'
initial_url = URLObject(cache.url)
initial_url.comment = 'Submitted URL'
self.__misp_add_ips_to_URLObject(initial_url, cache.tree.root_hartree.hostname_tree)
initial_obj = event.add_object(initial_url)
lookyloo_link: MISPAttribute = event.add_attribute('link', f'https://{public_domain}/tree/{cache.uuid}') # type: ignore
if not is_public_instance:
lookyloo_link.distribution = 0
initial_obj.add_reference(lookyloo_link, 'captured-by', 'Capture on lookyloo')
redirects: List[URLObject] = []
for nb, url in enumerate(cache.redirects):
if url == cache.url:
continue
obj = URLObject(url)
obj.comment = f'Redirect {nb}'
self.__misp_add_ips_to_URLObject(obj, cache.tree.root_hartree.hostname_tree)
redirects.append(obj)
if redirects:
redirects[-1].comment = f'Last redirect ({nb})'
if redirects:
prec_object = initial_url
for u_object in redirects:
prec_object.add_reference(u_object, 'redirects-to')
prec_object = u_object
for u_object in redirects:
event.add_object(u_object)
final_redirect = event.objects[-1]
try:
fo = FileObject(pseudofile=cache.tree.root_hartree.rendered_node.body, filename=cache.tree.root_hartree.rendered_node.filename)
fo.comment = 'Content received for the final redirect (before rendering)'
fo.add_reference(final_redirect, 'loaded-by', 'URL loading that content')
event.add_object(fo)
except Har2TreeError:
pass
except AttributeError:
# No `body` in rendered node
pass
return event

View File

@ -354,32 +354,61 @@ def stats(tree_uuid: str):
@app.route('/tree/<string:tree_uuid>/misp_lookup', methods=['GET'])
@flask_login.login_required
def web_misp_lookup_view(tree_uuid: str):
hits = lookyloo.get_misp_occurrences(tree_uuid)
if hits:
misp_root_url = lookyloo.misp.client.root_url
else:
misp_root_url = ''
return render_template('misp_lookup.html', uuid=tree_uuid, hits=hits, misp_root_url=misp_root_url)
if not lookyloo.misps.available:
flash('There are no MISP instances available.', 'error')
return redirect(url_for('tree', tree_uuid=tree_uuid))
misps_occurrences = {}
for instance_name in lookyloo.misps:
if occurrences := lookyloo.get_misp_occurrences(tree_uuid, instance_name=instance_name):
misps_occurrences[instance_name] = occurrences
return render_template('misp_lookup.html', uuid=tree_uuid,
current_misp=lookyloo.misps.default_instance,
misps_occurrences=misps_occurrences)
@app.route('/tree/<string:tree_uuid>/misp_push', methods=['GET', 'POST'])
@flask_login.login_required
def web_misp_push_view(tree_uuid: str):
error = False
if not lookyloo.misp.available:
flash('MISP module not available.', 'error')
if not lookyloo.misps.available:
flash('There are no MISP instances available.', 'error')
return redirect(url_for('tree', tree_uuid=tree_uuid))
elif not lookyloo.misp.enable_push:
flash('Push not enabled in MISP module.', 'error')
return redirect(url_for('tree', tree_uuid=tree_uuid))
else:
event = lookyloo.misp_export(tree_uuid)
if isinstance(event, dict):
flash(f'Unable to generate the MISP export: {event}', 'error')
return redirect(url_for('tree', tree_uuid=tree_uuid))
if request.method == 'POST':
event = lookyloo.misp_export(tree_uuid)
if isinstance(event, dict):
flash(f'Unable to generate the MISP export: {event}', 'error')
return redirect(url_for('tree', tree_uuid=tree_uuid))
if request.method == 'GET':
# Initialize settings that will be displayed on the template
misp_instances_settings = {}
for name, instance in lookyloo.misps.items():
# the 1st attribute in the event is the link to lookyloo
misp_instances_settings[name] = {
'default_tags': instance.default_tags,
'fav_tags': [tag.name for tag in instance.get_fav_tags()],
'auto_publish': instance.auto_publish
}
if existing_misp_url := instance.get_existing_event_url(event[-1].attributes[0].value):
misp_instances_settings[name]['existing_event'] = existing_misp_url
cache = lookyloo.capture_cache(tree_uuid)
return render_template('misp_push_view.html',
current_misp=lookyloo.misps.default_instance,
tree_uuid=tree_uuid,
event=event[0],
misp_instances_settings=misp_instances_settings,
has_parent=True if cache and cache.parent else False)
elif request.method == 'POST':
# event is a MISPEvent at this point
misp_instance_name = request.form.get('misp_instance_name')
if not misp_instance_name or misp_instance_name not in lookyloo.misps:
flash(f'MISP instance {misp_instance_name} is unknown.', 'error')
return redirect(url_for('tree', tree_uuid=tree_uuid))
misp = lookyloo.misps[misp_instance_name]
if not misp.enable_push:
flash('Push not enabled in MISP module.', 'error')
return redirect(url_for('tree', tree_uuid=tree_uuid))
# Submit the event
tags = request.form.getlist('tags')
error = False
@ -406,30 +435,17 @@ def web_misp_push_view(tree_uuid: str):
events[-1].info = request.form.get('event_info')
try:
new_events = lookyloo.misp.push(events, True if request.form.get('force_push') else False,
True if request.form.get('auto_publish') else False)
new_events = misp.push(events, True if request.form.get('force_push') else False,
True if request.form.get('auto_publish') else False)
except MISPServerError:
flash(f'MISP returned an error, the event(s) might still have been created on {lookyloo.misp.client.root_url}', 'error')
flash(f'MISP returned an error, the event(s) might still have been created on {misp.client.root_url}', 'error')
else:
if isinstance(new_events, dict):
flash(f'Unable to create event(s): {new_events}', 'error')
else:
for e in new_events:
flash(f'MISP event {e.id} created on {lookyloo.misp.client.root_url}', 'success')
flash(f'MISP event {e.id} created on {misp.client.root_url}', 'success')
return redirect(url_for('tree', tree_uuid=tree_uuid))
else:
# the 1st attribute in the event is the link to lookyloo
existing_misp_url = lookyloo.misp.get_existing_event_url(event[-1].attributes[0].value)
fav_tags = lookyloo.misp.get_fav_tags()
cache = lookyloo.capture_cache(tree_uuid)
return render_template('misp_push_view.html', tree_uuid=tree_uuid,
event=event[0], fav_tags=fav_tags,
existing_event=existing_misp_url,
auto_publish=lookyloo.misp.auto_publish,
has_parent=True if cache and cache.parent else False,
default_tags=lookyloo.misp.default_tags)
@app.route('/tree/<string:tree_uuid>/modules', methods=['GET'])
@ -786,8 +802,8 @@ def tree(tree_uuid: str, node_uuid: Optional[str]=None):
enable_context_by_users=enable_context_by_users,
enable_categorization=enable_categorization,
enable_bookmark=enable_bookmark,
misp_push=lookyloo.misp.available and lookyloo.misp.enable_push,
misp_lookup=lookyloo.misp.available and lookyloo.misp.enable_lookup,
misp_push=lookyloo.misps.available and lookyloo.misps.default_misp.enable_push,
misp_lookup=lookyloo.misps.available and lookyloo.misps.default_misp.enable_lookup,
blur_screenshot=blur_screenshot, urlnode_uuid=hostnode_to_highlight,
auto_trigger_modules=auto_trigger_modules,
confirm_message=confirm_message if confirm_message else 'Tick to confirm.',

View File

@ -180,6 +180,7 @@ misp_push_fields = api.model('MISPPushFields', {
@api.route('/json/<string:capture_uuid>/misp_push')
@api.route('/json/<string:capture_uuid>/misp_push/<string:instance_name>')
@api.doc(description='Push an event to a pre-configured MISP instance',
params={'capture_uuid': 'The UUID of the capture'},
security='apikey')
@ -188,20 +189,21 @@ class MISPPush(Resource):
@api.param('with_parents', 'Also push the parents of the capture (if any)')
@api.param('allow_duplicates', 'Push the event even if it is already present on the MISP instance')
def get(self, capture_uuid: str):
def get(self, capture_uuid: str, instance_name: Optional[str]=None):
with_parents = True if request.args.get('with_parents') else False
allow_duplicates = True if request.args.get('allow_duplicates') else False
to_return: Dict = {}
if not lookyloo.misp.available:
misp = self.get_misp_instance(instance_name)
if not misp.available:
to_return['error'] = 'MISP module not available.'
elif not lookyloo.misp.enable_push:
elif not misp.enable_push:
to_return['error'] = 'Push not enabled in MISP module.'
else:
event = lookyloo.misp_export(capture_uuid, with_parents)
if isinstance(event, dict):
to_return['error'] = event
else:
new_events = lookyloo.misp.push(event, allow_duplicates)
new_events = misp.push(event, allow_duplicates)
if isinstance(new_events, dict):
to_return['error'] = new_events
else:
@ -213,22 +215,23 @@ class MISPPush(Resource):
return to_return
@api.doc(body=misp_push_fields)
def post(self, capture_uuid: str):
def post(self, capture_uuid: str, instance_name: Optional[str]=None):
parameters: Dict = request.get_json(force=True)
with_parents = True if parameters.get('with_parents') else False
allow_duplicates = True if parameters.get('allow_duplicates') else False
to_return: Dict = {}
if not lookyloo.misp.available:
misp = self.get_misp_instance(instance_name)
if not misp.available:
to_return['error'] = 'MISP module not available.'
elif not lookyloo.misp.enable_push:
elif not misp.enable_push:
to_return['error'] = 'Push not enabled in MISP module.'
else:
event = lookyloo.misp_export(capture_uuid, with_parents)
if isinstance(event, dict):
to_return['error'] = event
else:
new_events = lookyloo.misp.push(event, allow_duplicates)
new_events = misp.push(event, allow_duplicates)
if isinstance(new_events, dict):
to_return['error'] = new_events
else:

View File

@ -1,11 +1,24 @@
{% from "macros.html" import shorten_string %}
<center>
<h1 class="display-4">MISP hits</h1>
<h6>Searching on URL, domain, IPs, and CNAMEs for all the nodes up to the rendered page.</h6>
<h6>Skips the entries in warnings lists enabled on your MISP instance.</h6>
{% if misps_occurrences|length > 1 %}
</br>
<hr/>
<label for="mispSelector">Select the MISP instance to search in</label>
</br>
<div class="btn-group" role="group" aria-label="MISP Selector" id="mispSelector">
{%for name in misps_occurrences %}
<button type="button" value="{{name}}" class="btn btn-outline-primary {%if name == current_misp%}active{%endif%}" href="#">{{name}}</a></li>
{%endfor%}
</div>
{%endif%}
</center>
<div>
<center>
<h1 class="display-4">MISP hits</h1>
<h6>Searching on URL, domain, IPs, and CNAMEs for all the nodes up to the rendered page.</h6>
<h6>Skips the entries in warnings lists enabled on your MISP instance.</h6>
</center>
<div id="allInstances">
{% for name, occurrences in misps_occurrences.items() %}
<div id="{{name}}" {%if name != current_misp%}style="display:none"{%endif%}>
{% set hits, root_url = occurrences %}
{% if hits %}
<ul>
{% for event_id, values in hits.items() %}
@ -21,3 +34,6 @@
{% else %}
No hits
{% endif %}
</div>
{% endfor %}
</div>

View File

@ -1,6 +1,24 @@
<div>
<p>Default tags: {{', '.join(default_tags)}}</p>
<form role="form" action="{{ url_for('web_misp_push_view', tree_uuid=tree_uuid) }}" method=post enctype=multipart/form-data>
{% if misp_instances_settings|length > 1 %}
<center>
<label for="mispSelector">Select the MISP instance to push to</label>
</br>
<div class="btn-group" role="group" aria-label="MISP Selector" id="mispSelector">
{%for name in misp_instances_settings %}
<button type="button" value="{{name}}" class="btn btn-outline-primary {%if name == current_misp%}active{%endif%}" href="#">{{name}}</a></li>
{%endfor%}
</div>
</center>
{%endif%}
<div id="allInstances">
{%for name, misp_settings in misp_instances_settings.items() %}
<div id="{{name}}" {%if name != current_misp%}style="display:none"{%endif%}>
<form role="form" action="{{ url_for('web_misp_push_view', tree_uuid=tree_uuid) }}"
method=post enctype=multipart/form-data>
<label for="misp_instance_name" class="col-sm-2 col-form-label">Submit event to:</label>
<input type="text" class="form-control" name="misp_instance_name" value="{{name}}" readonly>
<label for="defaultTags" class="col-sm-6 col-form-label">Tags attached to the event by default</label>
<input type="text" class="form-control" name="defaultTags" value="{{', '.join(misp_settings['default_tags'])}}" disabled readonly>
<div class="row mb-3">
<div class="col-sm-10">
<label for="url" class="col-sm-2 col-form-label">Event info:</label>
@ -12,20 +30,21 @@
<div class="col-sm-10">
<label for="tags" class="col-sm-2 col-form-label">Available tags:</label>
<select class="form-control" name="tags" id="tags" multiple>
{% for tag in fav_tags %}
<option value="{{ tag.name }}">{{ tag.name }}</option>
{% for tag_name in misp_settings['fav_tags'] %}
<option value="{{ tag_name }}">{{ tag_name }}</option>
{% endfor %}
</select>
</div>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" name="auto_publish" {%if auto_publish %} checked {% endif %}></input>
<input class="form-check-input" type="checkbox" name="auto_publish"
{%if misp_settings.auto_publish %} checked {% endif %}></input>
<label for="auto_publish" class="form-check-label">Publish the event automatically</label>
</div>
{% if existing_event %}
<p>There is already an <a href="{{existing_event}}">event on your MISP instance</a> with this lookyloo capture.</p>
{% if misp_settings.existing_event %}
<p>There is already an <a href="{{misp_settings.existing_event}}">event on your MISP instance</a> with this lookyloo capture.</p>
<div class="form-check">
<input class="form-check-input" type="checkbox" name="force_push" onchange="document.getElementById('btn-misp-push').disabled = !this.checked;"></input>
<input class="form-check-input" type="checkbox" name="force_push" onchange="document.getElementById('btn-misp-push-{{name}}').disabled = !this.checked;"></input>
<label for="force_push" class="form-check-label">Tick this box if you want to push anyway</label>
</div>
{% endif %}
@ -35,6 +54,9 @@
<label for="with_parents" class="form-check-label">Also push the parents</label>
</div>
{% endif %}
<button type="submit" class="btn btn-primary" id="btn-misp-push" {% if existing_event %}disabled=true{% endif %}>Push to MISP</button>
<button type="submit" class="btn btn-primary" id="btn-misp-push-{{name}}"
{% if misp_settings.existing_event %}disabled=true{% endif %}>Push to {{name}}</button>
</form>
</div>
{%endfor%}
</div>

View File

@ -94,14 +94,26 @@
$('#mispPushModal').on('show.bs.modal', function(e) {
var button = $(e.relatedTarget);
var modal = $(this);
modal.find('.modal-body').load(button.data("remote"));
modal.find('.modal-body').load(button.data("remote"), function(result){
$('#mispSelector button').on('click', function(e){
var thisBtn = $(this);
thisBtn.addClass('active').siblings().removeClass('active');
$('#'+thisBtn.val()).show().siblings().hide()
});
});
});
</script>
<script>
$('#mispLookupModal').on('show.bs.modal', function(e) {
var button = $(e.relatedTarget);
var modal = $(this);
modal.find('.modal-body').load(button.data("remote"));
modal.find('.modal-body').load(button.data("remote"), function(result){
$('#mispSelector button').on('click', function(e){
var thisBtn = $(this);
thisBtn.addClass('active').siblings().removeClass('active');
$('#'+thisBtn.val()).show().siblings().hide()
});
});
});
</script>
<script>