chg: [tracker] add experimental report generator

This commit is contained in:
terrtia 2024-04-03 17:39:45 +02:00
parent a282354fce
commit dbde04caa3
No known key found for this signature in database
GPG key ID: 1E1B1F50D84613D0
11 changed files with 496 additions and 59 deletions

View file

@ -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)

View file

@ -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():

View file

@ -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):

View file

@ -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:

View file

@ -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:

View file

@ -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)

View file

@ -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"])

View file

@ -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 #
#################### ####################

View file

@ -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,32 +30,56 @@
<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"]}}&nbsp;&nbsp; {{meta["nb_subchannels"]}}&nbsp;&nbsp;
<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>
@ -61,10 +87,13 @@
</span> </span>
{{ meta["nb_messages"] }}&nbsp;&nbsp; {{ meta["nb_messages"] }}&nbsp;&nbsp;
</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>

View file

@ -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=&quot;list-group&quot;>{% for r in row[3] %}<li class=&quot;list-group-item&quot;><div><svg height=&quot;26&quot; width=&quot;26&quot;><g class=&quot;nodes&quot;><circle cx=&quot;13&quot; cy=&quot;13&quot; r=&quot;13&quot; fill=&quot;{{ message['extracted_matches'][r[0]]['icon']['color'] }}&quot;></circle><text x=&quot;13&quot; y=&quot;13&quot; text-anchor=&quot;middle&quot; dominant-baseline=&quot;central&quot; class=&quot;{{ message['extracted_matches'][r[0]]['icon']['style'] }}&quot; font-size=&quot;16px&quot;>{{ 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=&quot;list-group&quot;>{% for r in row[3] %}<li class=&quot;list-group-item&quot;><div><svg height=&quot;26&quot; width=&quot;26&quot;><g class=&quot;nodes&quot;><circle cx=&quot;13&quot; cy=&quot;13&quot; r=&quot;13&quot; fill=&quot;{{ message['extracted_matches'][r[0]]['icon']['color'] }}&quot;></circle><text x=&quot;13&quot; y=&quot;13&quot; text-anchor=&quot;middle&quot; dominant-baseline=&quot;central&quot; class=&quot;{{ message['extracted_matches'][r[0]]['icon']['style'] }}&quot; font-size=&quot;16px&quot;>{{ 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'] %}

View 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>&nbsp;
{% elif meta['type'] == 'set' %}
<i class="fas fa-layer-group"></i>&nbsp;
{% elif meta['type'] == 'regex' %}
<i class="fas fa-compass"></i>&nbsp;
{% elif meta['type'] == 'typosquatting' %}
<i class="fas fa-clone"></i>&nbsp;
{% elif meta['type'] == 'yara' %}
<span class="bg-danger text-white font-weight-bold" style="font-size: 120%">&nbsp;{ </span>&nbsp;
{% 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 &nbsp;<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 &nbsp;<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>