mirror of
https://github.com/ail-project/ail-framework.git
synced 2024-11-30 01:37:17 +00:00
chg: [tracker] add experimental report generator
This commit is contained in:
parent
a282354fce
commit
dbde04caa3
11 changed files with 496 additions and 59 deletions
|
@ -1055,6 +1055,23 @@ def api_delete_tracker(data, user_id):
|
||||||
tracker = Tracker(tracker_uuid)
|
tracker = Tracker(tracker_uuid)
|
||||||
return tracker.delete(), 200
|
return tracker.delete(), 200
|
||||||
|
|
||||||
|
def api_tracker_add_object(data, user_id):
|
||||||
|
tracker_uuid = data.get('uuid')
|
||||||
|
res = api_check_tracker_acl(tracker_uuid, user_id)
|
||||||
|
if res:
|
||||||
|
return res
|
||||||
|
tracker = Tracker(tracker_uuid)
|
||||||
|
object_gid = data.get('gid')
|
||||||
|
date = data.get('date')
|
||||||
|
if date:
|
||||||
|
if not Date.validate_str_date(date):
|
||||||
|
date = None
|
||||||
|
try:
|
||||||
|
obj_type, subtype, obj_id = object_gid.split(':', 2)
|
||||||
|
except (AttributeError, IndexError):
|
||||||
|
return {"status": "error", "reason": "Invalid Object"}, 400
|
||||||
|
return tracker.add(obj_type, subtype, obj_id, date=date), 200
|
||||||
|
|
||||||
def api_tracker_remove_object(data, user_id):
|
def api_tracker_remove_object(data, user_id):
|
||||||
tracker_uuid = data.get('uuid')
|
tracker_uuid = data.get('uuid')
|
||||||
res = api_check_tracker_acl(tracker_uuid, user_id)
|
res = api_check_tracker_acl(tracker_uuid, user_id)
|
||||||
|
|
|
@ -322,7 +322,7 @@ def get_threads_metas(threads):
|
||||||
def get_username_meta_from_global_id(username_global_id):
|
def get_username_meta_from_global_id(username_global_id):
|
||||||
_, instance_uuid, username_id = username_global_id.split(':', 2)
|
_, instance_uuid, username_id = username_global_id.split(':', 2)
|
||||||
username = Usernames.Username(username_id, instance_uuid)
|
username = Usernames.Username(username_id, instance_uuid)
|
||||||
return username.get_meta()
|
return username.get_meta(options={'icon'})
|
||||||
|
|
||||||
# TODO Filter
|
# TODO Filter
|
||||||
## Instance type
|
## Instance type
|
||||||
|
@ -386,6 +386,8 @@ def get_user_account_chats_meta(user_id, chats, subchannels):
|
||||||
c_subtype, c_id = chat_g_id.split(':', 1)
|
c_subtype, c_id = chat_g_id.split(':', 1)
|
||||||
chat = Chats.Chat(c_id, c_subtype)
|
chat = Chats.Chat(c_id, c_subtype)
|
||||||
chat_meta = chat.get_meta(options={'icon', 'info', 'nb_participants', 'tags_safe', 'username'})
|
chat_meta = chat.get_meta(options={'icon', 'info', 'nb_participants', 'tags_safe', 'username'})
|
||||||
|
if chat_meta['username']:
|
||||||
|
chat_meta['username'] = get_username_meta_from_global_id(chat_meta['username'])
|
||||||
chat_meta['nb_messages'] = len(chat.get_user_messages(user_id))
|
chat_meta['nb_messages'] = len(chat.get_user_messages(user_id))
|
||||||
chat_meta['subchannels'] = []
|
chat_meta['subchannels'] = []
|
||||||
for subchannel_gid in chat.get_subchannels():
|
for subchannel_gid in chat.get_subchannels():
|
||||||
|
@ -425,6 +427,39 @@ def get_user_account_nb_all_week_messages(user_id, chats, subchannels):
|
||||||
nb_day += 1
|
nb_day += 1
|
||||||
return stats
|
return stats
|
||||||
|
|
||||||
|
def _get_chat_card_meta_options():
|
||||||
|
return {'created_at', 'icon', 'info', 'nb_participants', 'origin_link', 'subchannels', 'tags_safe', 'threads', 'translation', 'username'}
|
||||||
|
|
||||||
|
def _get_message_bloc_meta_options():
|
||||||
|
return {'chat', 'content', 'files-names', 'icon', 'images', 'language', 'link', 'parent', 'parent_meta', 'reactions','thread', 'translation', 'user-account'}
|
||||||
|
|
||||||
|
def get_message_report(l_mess): # TODO Force language + translation
|
||||||
|
translation_target = 'en'
|
||||||
|
chats = {}
|
||||||
|
messages = []
|
||||||
|
mess_options = _get_message_bloc_meta_options()
|
||||||
|
|
||||||
|
l_mess = sorted(l_mess, key=lambda x: x[2])
|
||||||
|
|
||||||
|
for m in l_mess:
|
||||||
|
message = Messages.Message(m[2])
|
||||||
|
meta = message.get_meta(options=mess_options, translation_target=translation_target)
|
||||||
|
if meta['chat'] not in chats:
|
||||||
|
chat = Chats.Chat(meta['chat'], message.get_chat_instance())
|
||||||
|
meta_chat = chat.get_meta(options=_get_chat_card_meta_options(), translation_target=translation_target)
|
||||||
|
if meta_chat['username']:
|
||||||
|
meta_chat['username'] = get_username_meta_from_global_id(meta_chat['username'])
|
||||||
|
chats[chat.id] = meta_chat
|
||||||
|
|
||||||
|
# stats
|
||||||
|
chats[chat.id]['t_messages'] = 1
|
||||||
|
else:
|
||||||
|
chats[meta['chat']]['t_messages'] += 1
|
||||||
|
|
||||||
|
messages.append(meta)
|
||||||
|
|
||||||
|
return chats, messages
|
||||||
|
|
||||||
#### FIX ####
|
#### FIX ####
|
||||||
|
|
||||||
def fix_correlations_subchannel_message():
|
def fix_correlations_subchannel_message():
|
||||||
|
|
|
@ -56,6 +56,13 @@ class Chat(AbstractChatObject):
|
||||||
url = f'{baseurl}/correlation/show?type={self.type}&subtype={self.subtype}&id={self.id}'
|
url = f'{baseurl}/correlation/show?type={self.type}&subtype={self.subtype}&id={self.id}'
|
||||||
return url
|
return url
|
||||||
|
|
||||||
|
def get_origin_link(self):
|
||||||
|
if self.subtype == '00098785-7e70-5d12-a120-c5cdc1252b2b':
|
||||||
|
username = self.get_username()
|
||||||
|
if username:
|
||||||
|
username = username.split(':', 2)[2]
|
||||||
|
return f'https://t.me/{username}'
|
||||||
|
|
||||||
def get_svg_icon(self): # TODO
|
def get_svg_icon(self): # TODO
|
||||||
# if self.subtype == 'telegram':
|
# if self.subtype == 'telegram':
|
||||||
# style = 'fab'
|
# style = 'fab'
|
||||||
|
@ -100,6 +107,8 @@ class Chat(AbstractChatObject):
|
||||||
meta['threads'] = self.get_threads()
|
meta['threads'] = self.get_threads()
|
||||||
if 'tags_safe' in options:
|
if 'tags_safe' in options:
|
||||||
meta['tags_safe'] = self.is_tags_safe(meta['tags'])
|
meta['tags_safe'] = self.is_tags_safe(meta['tags'])
|
||||||
|
if 'origin_link' in options:
|
||||||
|
meta['origin_link'] = self.get_origin_link()
|
||||||
return meta
|
return meta
|
||||||
|
|
||||||
def get_misp_object(self):
|
def get_misp_object(self):
|
||||||
|
|
|
@ -71,6 +71,10 @@ class Message(AbstractObject):
|
||||||
def get_basename(self):
|
def get_basename(self):
|
||||||
return os.path.basename(self.id)
|
return os.path.basename(self.id)
|
||||||
|
|
||||||
|
def get_chat_instance(self):
|
||||||
|
c_id = self.id.split('/')
|
||||||
|
return c_id[0]
|
||||||
|
|
||||||
def get_content(self, r_type='str'): # TODO ADD cache # TODO Compress content ???????
|
def get_content(self, r_type='str'): # TODO ADD cache # TODO Compress content ???????
|
||||||
"""
|
"""
|
||||||
Returns content
|
Returns content
|
||||||
|
@ -259,7 +263,7 @@ class Message(AbstractObject):
|
||||||
else:
|
else:
|
||||||
timestamp = float(timestamp)
|
timestamp = float(timestamp)
|
||||||
timestamp = datetime.utcfromtimestamp(float(timestamp))
|
timestamp = datetime.utcfromtimestamp(float(timestamp))
|
||||||
meta['date'] = timestamp.strftime('%Y/%m/%d')
|
meta['date'] = timestamp.strftime('%Y-%m-%d')
|
||||||
meta['hour'] = timestamp.strftime('%H:%M:%S')
|
meta['hour'] = timestamp.strftime('%H:%M:%S')
|
||||||
meta['full_date'] = timestamp.isoformat(' ')
|
meta['full_date'] = timestamp.isoformat(' ')
|
||||||
if 'last_full_date' in options:
|
if 'last_full_date' in options:
|
||||||
|
|
|
@ -150,7 +150,7 @@ class UserAccount(AbstractSubtypeObject):
|
||||||
if meta['username']:
|
if meta['username']:
|
||||||
_, username_account_subtype, username_account_id = meta['username'].split(':', 3)
|
_, username_account_subtype, username_account_id = meta['username'].split(':', 3)
|
||||||
if 'username_meta' in options:
|
if 'username_meta' in options:
|
||||||
meta['username'] = Usernames.Username(username_account_id, username_account_subtype).get_meta()
|
meta['username'] = Usernames.Username(username_account_id, username_account_subtype).get_meta(options={'icon'})
|
||||||
else:
|
else:
|
||||||
meta['username'] = {'type': 'username', 'subtype': username_account_subtype, 'id': username_account_id}
|
meta['username'] = {'type': 'username', 'subtype': username_account_subtype, 'id': username_account_id}
|
||||||
if 'usernames' in options:
|
if 'usernames' in options:
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#!/usr/bin/python3
|
#!/usr/bin/python3
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
|
import time
|
||||||
from calendar import monthrange
|
from calendar import monthrange
|
||||||
|
|
||||||
from dateutil.rrule import rrule, MONTHLY
|
from dateutil.rrule import rrule, MONTHLY
|
||||||
|
@ -91,6 +92,10 @@ def get_current_week_day():
|
||||||
start = dt - datetime.timedelta(days=dt.weekday())
|
start = dt - datetime.timedelta(days=dt.weekday())
|
||||||
return start.strftime("%Y%m%d")
|
return start.strftime("%Y%m%d")
|
||||||
|
|
||||||
|
def get_current_utc_full_time():
|
||||||
|
timestamp = datetime.datetime.fromtimestamp(time.time())
|
||||||
|
return timestamp.strftime('%Y-%m-%d %H:%M:%S')
|
||||||
|
|
||||||
def get_month_dates(date=None):
|
def get_month_dates(date=None):
|
||||||
if date:
|
if date:
|
||||||
date = convert_date_str_to_datetime(date)
|
date = convert_date_str_to_datetime(date)
|
||||||
|
|
|
@ -360,5 +360,9 @@ def show_relationship():
|
||||||
else:
|
else:
|
||||||
dict_object["subtype"] = ''
|
dict_object["subtype"] = ''
|
||||||
dict_object["metadata_card"] = ail_objects.get_object_card_meta(obj_type, subtype, obj_id)
|
dict_object["metadata_card"] = ail_objects.get_object_card_meta(obj_type, subtype, obj_id)
|
||||||
|
dict_object["metadata_card"]['tags_safe'] = True
|
||||||
return render_template("show_relationship.html", dict_object=dict_object, bootstrap_label=bootstrap_label,
|
return render_template("show_relationship.html", dict_object=dict_object, bootstrap_label=bootstrap_label,
|
||||||
tags_selector_data=Tag.get_tags_selector_data())
|
tags_selector_data=Tag.get_tags_selector_data(),
|
||||||
|
meta=dict_object["metadata_card"],
|
||||||
|
ail_tags=dict_object["metadata_card"]["add_tags_modal"])
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,7 @@ sys.path.append(os.environ['AIL_BIN'])
|
||||||
##################################
|
##################################
|
||||||
from lib import ail_core
|
from lib import ail_core
|
||||||
from lib.objects import ail_objects
|
from lib.objects import ail_objects
|
||||||
|
from lib import chats_viewer
|
||||||
from lib import item_basic
|
from lib import item_basic
|
||||||
from lib import Tracker
|
from lib import Tracker
|
||||||
from lib import Tag
|
from lib import Tag
|
||||||
|
@ -372,6 +373,27 @@ def get_json_tracker_graph():
|
||||||
res = Tracker.get_trackers_graph_by_day([tracker_uuid])
|
res = Tracker.get_trackers_graph_by_day([tracker_uuid])
|
||||||
return jsonify(res)
|
return jsonify(res)
|
||||||
|
|
||||||
|
@hunters.route('/tracker/object/add', methods=['GET'])
|
||||||
|
@login_required
|
||||||
|
@login_admin
|
||||||
|
def tracker_object_add():
|
||||||
|
user_id = current_user.get_id()
|
||||||
|
tracker_uuid = request.args.get('uuid')
|
||||||
|
object_global_id = request.args.get('gid')
|
||||||
|
if object_global_id.startswith('messages::'):
|
||||||
|
obj = ail_objects.get_obj_from_global_id(object_global_id)
|
||||||
|
date = obj.get_date()
|
||||||
|
else:
|
||||||
|
date = request.args.get('date') # TODO check daterange
|
||||||
|
res = Tracker.api_tracker_add_object({'uuid': tracker_uuid, 'gid': object_global_id, 'date': date}, user_id)
|
||||||
|
if res[1] != 200:
|
||||||
|
return create_json_response(res[0], res[1])
|
||||||
|
else:
|
||||||
|
if request.referrer:
|
||||||
|
return redirect(request.referrer)
|
||||||
|
else:
|
||||||
|
return redirect(url_for('hunters.show_tracker', uuid=tracker_uuid))
|
||||||
|
|
||||||
@hunters.route('/tracker/object/remove', methods=['GET'])
|
@hunters.route('/tracker/object/remove', methods=['GET'])
|
||||||
@login_required
|
@login_required
|
||||||
@login_analyst
|
@login_analyst
|
||||||
|
@ -389,6 +411,41 @@ def tracker_object_remove():
|
||||||
return redirect(url_for('hunters.show_tracker', uuid=tracker_uuid))
|
return redirect(url_for('hunters.show_tracker', uuid=tracker_uuid))
|
||||||
|
|
||||||
|
|
||||||
|
@hunters.route('/tracker/objects', methods=['GET'])
|
||||||
|
@login_required
|
||||||
|
@login_admin
|
||||||
|
def tracker_objects():
|
||||||
|
user_id = current_user.get_id()
|
||||||
|
tracker_uuid = request.args.get('uuid', None)
|
||||||
|
res = Tracker.api_is_allowed_to_edit_tracker(tracker_uuid, user_id)
|
||||||
|
if res[1] != 200: # invalid access
|
||||||
|
return Response(json.dumps(res[0], indent=2, sort_keys=True), mimetype='application/json'), res[1]
|
||||||
|
|
||||||
|
tracker = Tracker.Tracker(tracker_uuid)
|
||||||
|
meta = tracker.get_meta(options={'description', 'sparkline', 'tags', 'nb_objs'})
|
||||||
|
if meta['type'] == 'yara':
|
||||||
|
yara_rule_content = Tracker.get_yara_rule_content(meta['tracked'])
|
||||||
|
else:
|
||||||
|
yara_rule_content = None
|
||||||
|
|
||||||
|
chats, messages = chats_viewer.get_message_report(tracker.get_objs())
|
||||||
|
|
||||||
|
meta['date'] = Date.get_current_utc_full_time()
|
||||||
|
|
||||||
|
return render_template("messages_report.html", meta=meta, yara_rule_content=yara_rule_content,
|
||||||
|
chats=chats, messages=messages, bootstrap_label=bootstrap_label)
|
||||||
|
|
||||||
|
# TODO
|
||||||
|
|
||||||
|
# Manual - Title
|
||||||
|
# - Summary
|
||||||
|
|
||||||
|
# Messages table
|
||||||
|
|
||||||
|
# Timeline messages by chats - line
|
||||||
|
# pie charts NB messages all chats
|
||||||
|
# Barchart NB messages by days
|
||||||
|
|
||||||
####################
|
####################
|
||||||
# RETRO HUNT #
|
# RETRO HUNT #
|
||||||
####################
|
####################
|
||||||
|
|
|
@ -13,11 +13,13 @@
|
||||||
<text x="15" y="15" text-anchor="middle" dominant-baseline="central" class="{{ meta["svg_icon"]["style"] }}" font-size="16px">{{ meta["svg_icon"]["icon"] }}</text>
|
<text x="15" y="15" text-anchor="middle" dominant-baseline="central" class="{{ meta["svg_icon"]["style"] }}" font-size="16px">{{ meta["svg_icon"]["icon"] }}</text>
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
{% if meta['username'] %}{{ meta["username"]["id"] }} {% else %} {{ meta['name'] }}{% endif %} : <small><a href="{{ url_for('chats_explorer.chats_explorer_chat') }}?subtype={{ meta['subtype'] }}&id={{ meta['id'] }}">{{ meta['id'] }}</a></small>
|
{% if meta['name'] %}{{ meta['name'] }}{% endif %}{% if not report_mode %} : <small><a href="{{ url_for('chats_explorer.chats_explorer_chat') }}?subtype={{ meta['subtype'] }}&id={{ meta['id'] }}">{{ meta['id'] }}</a></small>{% endif %}
|
||||||
</h4>
|
</h4>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body py-0">
|
<div class="card-body py-0">
|
||||||
<span class="">
|
|
||||||
|
<div class="d-flex align-items-center">
|
||||||
|
<div>
|
||||||
{% if meta["tags_safe"] %}
|
{% if meta["tags_safe"] %}
|
||||||
{% if meta['icon'] %}
|
{% if meta['icon'] %}
|
||||||
<span><img src="{{ url_for('objects_image.image', filename=meta['icon'])}}" class="my-1" alt="{{ meta['id'] }}" width="200" height="200"></span>
|
<span><img src="{{ url_for('objects_image.image', filename=meta['icon'])}}" class="my-1" alt="{{ meta['id'] }}" width="200" height="200"></span>
|
||||||
|
@ -28,43 +30,70 @@
|
||||||
<i class="fas fa-stack-2x fa-ban" style="color:Red"></i>
|
<i class="fas fa-stack-2x fa-ban" style="color:Red"></i>
|
||||||
</span>
|
</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</span>
|
</div>
|
||||||
|
|
||||||
<span>
|
|
||||||
<span class="badge badge-dark">
|
<div>
|
||||||
|
{% if meta['username'] %}
|
||||||
|
<div class="mx-2">
|
||||||
|
<svg height="30" width="30">
|
||||||
|
<g class="nodes">
|
||||||
|
<circle cx="15" cy="15" r="15" fill="{{ meta["username"]["icon"]["color"] }}"></circle>
|
||||||
|
<text x="15" y="15" text-anchor="middle" dominant-baseline="central" class="{{ meta["username"]["icon"]["style"] }}" font-size="16px">{{ meta["username"]["icon"]["icon"] }}</text>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
{{ meta['username']['id'] }}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
|
||||||
|
<div class="badge badge-dark mx-2 my-1">
|
||||||
<span class="badge badge-info" style="font-size: 0.8rem;">
|
<span class="badge badge-info" style="font-size: 0.8rem;">
|
||||||
<i class="fas fa-hourglass-start"></i>
|
<i class="fas fa-hourglass-start"></i>
|
||||||
</span>
|
</span>
|
||||||
{{meta["first_seen"]}}
|
{{meta["first_seen"][0:4]}}-{{meta["first_seen"][4:6]}}-{{meta["first_seen"][6:8]}}
|
||||||
<span class="badge badge-light mx-1" style="font-size: 1rem;">
|
<span class="badge badge-light mx-1" style="font-size: 1rem;">
|
||||||
<i class="far fa-calendar-alt"></i>
|
<i class="far fa-calendar-alt"></i>
|
||||||
</span>
|
</span>
|
||||||
{{meta["last_seen"]}}
|
{{meta["last_seen"][0:4]}}-{{meta["last_seen"][4:6]}}-{{meta["last_seen"][6:8]}}
|
||||||
<span class="badge badge-secondary" style="font-size: 0.8rem;">
|
<span class="badge badge-secondary" style="font-size: 0.8rem;">
|
||||||
<i class="fas fa-hourglass-end"></i>
|
<i class="fas fa-hourglass-end"></i>
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</div>
|
||||||
|
{# <div class="mx-2">#}
|
||||||
|
{# <span class="badge badge-dark">#}
|
||||||
|
{# <span class="badge badge-info" style="font-size: 0.8rem;">#}
|
||||||
|
{# <i class="fas fa-calendar-plus"></i>#}
|
||||||
|
{# </span>#}
|
||||||
|
{# {{meta["created_at"]}}#}
|
||||||
|
{# </span>#}
|
||||||
|
{# </div>#}
|
||||||
|
<div class="mx-2">
|
||||||
<span class="badge badge-dark">
|
<span class="badge badge-dark">
|
||||||
<span class="badge badge-info" style="font-size: 0.8rem;">
|
<span class="badge badge-info" style="font-size: 0.8rem;">
|
||||||
<i class="far fa-comments"></i>
|
<i class="far fa-comments"></i> Subchannels
|
||||||
</span>
|
</span>
|
||||||
{{meta["nb_subchannels"]}}
|
{{meta["nb_subchannels"]}}
|
||||||
<span class="badge badge-info" style="font-size: 0.8rem;">
|
<span class="badge badge-info" style="font-size: 0.8rem;">
|
||||||
<i class="fas fa-user-circle"></i>
|
<i class="fas fa-user-circle"></i> Participants
|
||||||
</span>
|
</span>
|
||||||
{{meta["nb_participants"]}}
|
{{meta["nb_participants"]}}
|
||||||
</span>
|
</span>
|
||||||
|
{% if "nb_messages" in meta %}
|
||||||
<span class="badge badge-dark">
|
<span class="badge badge-dark">
|
||||||
<span class="badge badge-info" style="font-size: 0.8rem;">
|
<span class="badge badge-info" style="font-size: 0.8rem;">
|
||||||
<i class="fas fa-user-circle"></i>
|
<i class="fas fa-user-circle"></i>
|
||||||
<i class="far fa-comment-dots"></i>
|
<i class="far fa-comment-dots"></i>
|
||||||
</span>
|
</span>
|
||||||
{{meta["nb_messages"]}}
|
{{ meta["nb_messages"] }}
|
||||||
</span>
|
|
||||||
</span>
|
</span>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="">
|
<div>
|
||||||
{{ meta['info'] }}
|
<pre class="my-0" style="white-space: pre-wrap;">{{ meta['info'] }}</pre>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="">
|
<div class="">
|
||||||
|
@ -120,5 +149,7 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
{% if not report_mode %}
|
||||||
{% include 'objects/block_object_footer_small.html' %}
|
{% include 'objects/block_object_footer_small.html' %}
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
|
@ -22,15 +22,16 @@
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div class="chat-message-left pb-1">
|
<div class="chat-message-left pb-1">
|
||||||
<div>
|
<div>
|
||||||
<a href="{{ url_for('chats_explorer.objects_user_account')}}?subtype={{ message['user-account']['subtype'] }}&id={{ message['user-account']['id'] }}">
|
<a href="{{ url_for('chats_explorer.objects_user_account')}}?subtype={{ message['user-account']['subtype'] }}&id={{ message['user-account']['id'] }}">
|
||||||
<img src="{% if message['user-account']['icon'] %}{{ url_for('objects_image.image', filename=message['user-account']['icon'])}}{% else %}{{ url_for('static', filename='image/ail-icon.png') }}{% endif %}"
|
<img src="{% if message['user-account']['icon'] %}{{ url_for('objects_image.image', filename=message['user-account']['icon'])}}{% else %}{{ url_for('static', filename='image/ail-icon.png') }}{% endif %}"
|
||||||
class="rounded-circle mr-1" alt="{{ message['user-account']['id'] }}" width="40" height="40">
|
class="rounded-circle mr-1" alt="{{ message['user-account']['id'] }}" width="60" height="60">
|
||||||
</a>
|
</a>
|
||||||
<div class="text-muted small text-nowrap mt-2">{{ message['hour'] }}</div>
|
<div class="text-center">
|
||||||
|
<div class="text-muted small text-nowrap">{{ message['date'] }}</div>
|
||||||
|
<div class="text-muted small text-nowrap" style="font-size: 90%">{{ message['hour'] }}</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-shrink-1 bg-light rounded py-2 px-3 ml-4 pb-4" style="overflow-x: auto">
|
<div class="flex-shrink-1 bg-light rounded py-2 px-3 ml-4 pb-4" style="overflow-x: auto">
|
||||||
<div class="font-weight-bold mb-1">
|
<div class="font-weight-bold mb-1">
|
||||||
|
@ -77,13 +78,13 @@
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if not message['extracted'] %}
|
{% if not message['extracted'] %}
|
||||||
<pre class="my-0">{{ message['content'] }}</pre>
|
<pre class="my-0" style="white-space: pre-wrap;">{{ message['content'] }}</pre>
|
||||||
{% else %}
|
{% else %}
|
||||||
<pre class="my-0">{{ message['content'][:message['extracted'][0][0]] }}{% for row in message['extracted'] %}<span class="hg-text" data-toggle="popover" data-trigger="hover" data-html="true" title="Extracted:" data-content="<ul class="list-group">{% for r in row[3] %}<li class="list-group-item"><div><svg height="26" width="26"><g class="nodes"><circle cx="13" cy="13" r="13" fill="{{ message['extracted_matches'][r[0]]['icon']['color'] }}"></circle><text x="13" y="13" text-anchor="middle" dominant-baseline="central" class="{{ message['extracted_matches'][r[0]]['icon']['style'] }}" font-size="16px">{{ message['extracted_matches'][r[0]]['icon']['icon'] }}</text></g></svg> {{ message['extracted_matches'][r[0]]['subtype'] }}</div>{{ message['extracted_matches'][r[0]]['id'] }} <div><b>{{ r[1] }}</b></div></li>{% endfor %}</ul>" id="{{ row[0] }}:{{ row[1] }}">{{ message['content'][row[0]:row[1]] }}</span>{% if loop.index + 1 > message['extracted']|length %}{{ message['content'][message['extracted'][-1][1]:] }}{% else %}{{ message['content'][row[1]:message['extracted'][loop.index][0]] }}{% endif %}{% endfor %}</pre>
|
<pre class="my-0" style="white-space: pre-wrap;">{{ message['content'][:message['extracted'][0][0]] }}{% for row in message['extracted'] %}<span class="hg-text" data-toggle="popover" data-trigger="hover" data-html="true" title="Extracted:" data-content="<ul class="list-group">{% for r in row[3] %}<li class="list-group-item"><div><svg height="26" width="26"><g class="nodes"><circle cx="13" cy="13" r="13" fill="{{ message['extracted_matches'][r[0]]['icon']['color'] }}"></circle><text x="13" y="13" text-anchor="middle" dominant-baseline="central" class="{{ message['extracted_matches'][r[0]]['icon']['style'] }}" font-size="16px">{{ message['extracted_matches'][r[0]]['icon']['icon'] }}</text></g></svg> {{ message['extracted_matches'][r[0]]['subtype'] }}</div>{{ message['extracted_matches'][r[0]]['id'] }} <div><b>{{ r[1] }}</b></div></li>{% endfor %}</ul>" id="{{ row[0] }}:{{ row[1] }}">{{ message['content'][row[0]:row[1]] }}</span>{% if loop.index + 1 > message['extracted']|length %}{{ message['content'][message['extracted'][-1][1]:] }}{% else %}{{ message['content'][row[1]:message['extracted'][loop.index][0]] }}{% endif %}{% endfor %}</pre>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if message['translation'] %}
|
{% if message['translation'] %}
|
||||||
<hr class="m-1">
|
<hr class="m-1">
|
||||||
<pre class="my-0 text-secondary">{{ message['translation'] }}</pre>
|
<pre class="my-0 text-secondary" style="white-space: pre-wrap;">{{ message['translation'] }}</pre>
|
||||||
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% for reaction in message['reactions'] %}
|
{% for reaction in message['reactions'] %}
|
||||||
|
|
274
var/www/templates/hunter/messages_report.html
Normal file
274
var/www/templates/hunter/messages_report.html
Normal file
|
@ -0,0 +1,274 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<title>Report - AIL</title>
|
||||||
|
<link rel="icon" href="{{ url_for('static', filename='image/ail-icon.png') }}">
|
||||||
|
|
||||||
|
<!-- Core CSS -->
|
||||||
|
<link href="{{ url_for('static', filename='css/bootstrap4.min.css') }}" rel="stylesheet">
|
||||||
|
<link href="{{ url_for('static', filename='css/font-awesome.min.css') }}" rel="stylesheet">
|
||||||
|
<link href="{{ url_for('static', filename='css/dataTables.bootstrap.min.css') }}" rel="stylesheet">
|
||||||
|
|
||||||
|
<!-- JS -->
|
||||||
|
<script src="{{ url_for('static', filename='js/jquery.js')}}"></script>
|
||||||
|
<script src="{{ url_for('static', filename='js/popper.min.js')}}"></script>
|
||||||
|
<script src="{{ url_for('static', filename='js/bootstrap4.min.js')}}"></script>
|
||||||
|
<script src="{{ url_for('static', filename='js/jquery.dataTables.min.js')}}"></script>
|
||||||
|
<script src="{{ url_for('static', filename='js/dataTables.bootstrap.min.js')}}"></script>
|
||||||
|
<script src="{{ url_for('static', filename='js/d3.min.js')}}"></script>
|
||||||
|
<script src="{{ url_for('static', filename='js/d3/heatmap_week_hour.js')}}"></script>
|
||||||
|
<script src="{{ url_for('static', filename='js/d3/sparklines.js') }}"></script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.chat-message-left,
|
||||||
|
.chat-message-right {
|
||||||
|
display: flex;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.chat-message-right {
|
||||||
|
flex-direction: row-reverse;
|
||||||
|
margin-left: auto
|
||||||
|
}
|
||||||
|
.divider:after,
|
||||||
|
.divider:before {
|
||||||
|
content: "";
|
||||||
|
flex: 1;
|
||||||
|
height: 2px;
|
||||||
|
background: #eee;
|
||||||
|
}
|
||||||
|
.object_image {
|
||||||
|
max-width: 50%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
{% include 'nav_bar.html' %}
|
||||||
|
|
||||||
|
<div class="container-fluid">
|
||||||
|
<div class="row">
|
||||||
|
|
||||||
|
<div class="col-12">
|
||||||
|
|
||||||
|
<h1>Tracker Report:</h1>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-8">
|
||||||
|
|
||||||
|
<div class="card my-2">
|
||||||
|
<div class="card-header bg-dark text-white">
|
||||||
|
<span class="badge badge-light lex-row-reverse float-right">
|
||||||
|
<span id="sparkline"></span>
|
||||||
|
</span>
|
||||||
|
<h4 class="card-title">
|
||||||
|
{% if meta['description'] %}
|
||||||
|
{{ meta['description'] }}
|
||||||
|
{% endif %}
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
<div class="card-body bg-light pt-2">
|
||||||
|
<table class="table table-borderless">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td class="text-right"><b>Type</b></td>
|
||||||
|
<td>
|
||||||
|
{% if meta['type'] == 'word' %}
|
||||||
|
<i class="fas fa-font"></i>
|
||||||
|
{% elif meta['type'] == 'set' %}
|
||||||
|
<i class="fas fa-layer-group"></i>
|
||||||
|
{% elif meta['type'] == 'regex' %}
|
||||||
|
<i class="fas fa-compass"></i>
|
||||||
|
{% elif meta['type'] == 'typosquatting' %}
|
||||||
|
<i class="fas fa-clone"></i>
|
||||||
|
{% elif meta['type'] == 'yara' %}
|
||||||
|
<span class="bg-danger text-white font-weight-bold" style="font-size: 120%"> { </span>
|
||||||
|
{% endif %}
|
||||||
|
{{ meta['type'] }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="text-right"><b>Generation Date</b></td>
|
||||||
|
<td>
|
||||||
|
<b>{{meta['date']}}</b>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="text-right"><b>First Seen <i class="fas fa-hourglass-start"></i></b></td>
|
||||||
|
<td>
|
||||||
|
{% if meta['first_seen'] %}
|
||||||
|
{{ meta['first_seen'][0:4] }} - {{ meta['first_seen'][4:6] }} - {{ meta['first_seen'][6:8] }}
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="text-right"><b>Last Seen <i class="fas fa-hourglass-end"></i></b></td>
|
||||||
|
<td>
|
||||||
|
{% if meta['last_seen'] %}
|
||||||
|
{{ meta['last_seen'][0:4] }} - {{ meta['last_seen'][4:6] }} - {{ meta['last_seen'][6:8] }}
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="text-right"><b>Tags</b></td>
|
||||||
|
<td>
|
||||||
|
{%for tag in meta['tags']%}
|
||||||
|
<span class="badge badge-{{ bootstrap_label[loop.index0 % 5] }}">{{ tag }}</span>
|
||||||
|
{%endfor%}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td class="text-right"><b>Objects Match</b></td>
|
||||||
|
<td>
|
||||||
|
{%for obj_type in meta['nb_objs']%}
|
||||||
|
<h4><span class="badge badge-{{ bootstrap_label[loop.index0 % 5] }}">
|
||||||
|
{{ obj_type }}
|
||||||
|
<span class="badge badge-light">{{ meta['nb_objs'][obj_type] }}</span>
|
||||||
|
</span></h4>
|
||||||
|
{%endfor%}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
{% if meta['type'] != 'yara' %}
|
||||||
|
<tr>
|
||||||
|
<td class="text-right"><b>Tracker</b></td>
|
||||||
|
<td>
|
||||||
|
{{ meta['tracked'] }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endif %}
|
||||||
|
</table>
|
||||||
|
|
||||||
|
{% if yara_rule_content %}
|
||||||
|
<h5 class="mb-0">Yara Rule:</h5>
|
||||||
|
<p class="my-0">
|
||||||
|
<pre class="border bg-white" style="white-space: pre-wrap;">{{ yara_rule_content }}</pre>
|
||||||
|
</p>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="col-4">
|
||||||
|
<div>
|
||||||
|
<img src="{{ url_for('static', filename='image/ail-project.png') }}" width="200">
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<img src="https://circl.lu/assets/images/circl-logo.png" width="200">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h3>Messages:</h3>
|
||||||
|
{% for message in messages %}
|
||||||
|
<div class="d-flex justify-content-between py-2 px-3 border-top" style="background-color: rgba(0,0,0,.03)">
|
||||||
|
<div>
|
||||||
|
<svg height="30" width="30">
|
||||||
|
<g class="nodes">
|
||||||
|
<circle cx="15" cy="15" r="15" fill="{{ chats[message['chat']]["svg_icon"]["color"] }}"></circle>
|
||||||
|
<text x="15" y="15" text-anchor="middle" dominant-baseline="central" class="{{ chats[message['chat']]["svg_icon"]["style"] }}" font-size="16px">{{ chats[message['chat']]["svg_icon"]["icon"] }}</text>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
{% if chats[message['chat']]['name'] %}{{ chats[message['chat']]['name'] }}{% endif %}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{% if chats[message['chat']]['origin_link'] %}
|
||||||
|
<span class="flex-row-reverse">{{ chats[message['chat']]['origin_link'] }}</span>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% with message=message,show_full_message=True %}
|
||||||
|
{% include 'chats_explorer/block_message.html' %}
|
||||||
|
{% endwith %}
|
||||||
|
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
<h3 class="mt-4">Chats Metadata:</h3>
|
||||||
|
{% for chat in chats %}
|
||||||
|
<div class="my-2">
|
||||||
|
{% with meta=chats[chat],report_mode=True %}
|
||||||
|
{% include 'chats_explorer/basic_card_chat.html' %}
|
||||||
|
{% endwith %}
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{# <h5 class="mx-5 mt-2 text-secondary">All Messages:</h5>#}
|
||||||
|
{# <div id="heatmapweekhourall"></div>#}
|
||||||
|
|
||||||
|
{# {% with translate_url=url_for('chats_explorer.chats_explorer_chat', subtype=chat['subtype']), obj_id=chat['id'], pagination=chat['pagination'] %}#}
|
||||||
|
{# {% include 'chats_explorer/block_translation.html' %}#}
|
||||||
|
{# {% endwith %}#}
|
||||||
|
|
||||||
|
{# {% if chat['messages'] %}#}
|
||||||
|
{##}
|
||||||
|
{# <div class="position-relative">#}
|
||||||
|
{# <div class="chat-messages p-2">#}
|
||||||
|
{##}
|
||||||
|
{# {% for date in chat['messages'] %}#}
|
||||||
|
{##}
|
||||||
|
{# <div class="divider d-flex align-items-center mb-4">#}
|
||||||
|
{# <p class="text-center h2 mx-3 mb-0" style="color: #a2aab7;">#}
|
||||||
|
{# <span class="badge badge-secondary mb-2" id="date_section_{{ date }}">{{ date }}</span>#}
|
||||||
|
{# </p>#}
|
||||||
|
{# </div>#}
|
||||||
|
{##}
|
||||||
|
{# {% for mess in chat['messages'][date] %}#}
|
||||||
|
{##}
|
||||||
|
{# {% with message=mess %}#}
|
||||||
|
{# {% include 'chats_explorer/block_message.html' %}#}
|
||||||
|
{# {% endwith %}#}
|
||||||
|
{##}
|
||||||
|
{# {% endfor %}#}
|
||||||
|
{# <br>#}
|
||||||
|
{# {% endfor %}#}
|
||||||
|
{##}
|
||||||
|
{# </div>#}
|
||||||
|
{# </div>#}
|
||||||
|
{##}
|
||||||
|
{# {% endif %}#}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<script>
|
||||||
|
$(document).ready(function(){
|
||||||
|
$("#page-Decoded").addClass("active");
|
||||||
|
$("#nav_chat").addClass("active");
|
||||||
|
|
||||||
|
// unblur images
|
||||||
|
let images = document.getElementsByClassName('object_image');
|
||||||
|
for(i = 0; i < images.length; i++) {
|
||||||
|
images[i].style.filter = "blur(0px)";
|
||||||
|
}
|
||||||
|
sparkline("sparkline", {{ meta['sparkline'] }}, {});
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
{#<script>#}
|
||||||
|
{# d3.json("{{ url_for('chats_explorer.chats_explorer_messages_stats_week_all') }}?type=chat&subtype={{ chat['subtype'] }}&id={{ chat['id'] }}")#}
|
||||||
|
{# .then(function(data) {#}
|
||||||
|
{# create_heatmap_week_hour('#heatmapweekhourall', data);#}
|
||||||
|
{# })#}
|
||||||
|
{##}
|
||||||
|
{#{% if not chat['subchannels'] %}#}
|
||||||
|
{#d3.json("{{ url_for('chats_explorer.chats_explorer_messages_stats_week') }}?type=chat&subtype={{ chat['subtype'] }}&id={{ chat['id'] }}")#}
|
||||||
|
{#.then(function(data) {#}
|
||||||
|
{# create_heatmap_week_hour('#heatmapweekhour', data);#}
|
||||||
|
{#})#}
|
||||||
|
{#{% endif %}#}
|
||||||
|
{#</script>#}
|
||||||
|
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
Loading…
Reference in a new issue