mirror of
https://github.com/ail-project/ail-framework.git
synced 2024-11-10 00:28:22 +00:00
chg: [trackers] refactor trackers
This commit is contained in:
parent
6b60041db2
commit
4473086f89
14 changed files with 871 additions and 875 deletions
|
@ -710,6 +710,13 @@ def delete_object_tag(tag, obj_type, id, subtype=''):
|
||||||
r_tags.srem(f'tag:{obj_type}:{subtype}:{id}', tag)
|
r_tags.srem(f'tag:{obj_type}:{subtype}:{id}', tag)
|
||||||
update_tag_global_by_obj_type(tag, obj_type, subtype=subtype)
|
update_tag_global_by_obj_type(tag, obj_type, subtype=subtype)
|
||||||
|
|
||||||
|
def delete_object_tags(obj_type, subtype, obj_id):
|
||||||
|
if not subtype:
|
||||||
|
subtype = ''
|
||||||
|
for tag in get_object_tags(obj_type, obj_id, subtype=subtype):
|
||||||
|
delete_object_tag(tag, obj_type, obj_id, subtype=subtype)
|
||||||
|
|
||||||
|
|
||||||
################################################################################################################
|
################################################################################################################
|
||||||
|
|
||||||
# TODO: REWRITE OLD
|
# TODO: REWRITE OLD
|
||||||
|
@ -960,7 +967,10 @@ def is_galaxy_tag(tag, namespace=None):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def is_custom_tag(tag):
|
def is_custom_tag(tag):
|
||||||
return r_tags.sismember('tags:custom', tag)
|
try:
|
||||||
|
return r_tags.sismember('tags:custom', tag)
|
||||||
|
except:
|
||||||
|
return False
|
||||||
|
|
||||||
# # TODO:
|
# # TODO:
|
||||||
# def is_valid_tag(tag):
|
# def is_valid_tag(tag):
|
||||||
|
@ -1018,6 +1028,20 @@ def sort_tags_taxonomies_galaxies(tags):
|
||||||
galaxies_tags.append(tag)
|
galaxies_tags.append(tag)
|
||||||
return taxonomies_tags, galaxies_tags
|
return taxonomies_tags, galaxies_tags
|
||||||
|
|
||||||
|
def sort_tags_taxonomies_galaxies_customs(tags):
|
||||||
|
taxonomies_tags = []
|
||||||
|
galaxies_tags = []
|
||||||
|
customs_tags = []
|
||||||
|
for tag in tags:
|
||||||
|
if is_taxonomie_tag(tag):
|
||||||
|
taxonomies_tags.append(tag)
|
||||||
|
elif is_custom_tag(tag):
|
||||||
|
print()
|
||||||
|
customs_tags.append(tag)
|
||||||
|
else:
|
||||||
|
galaxies_tags.append(tag)
|
||||||
|
return taxonomies_tags, galaxies_tags, customs_tags
|
||||||
|
|
||||||
##-- Taxonomies - Galaxies --##
|
##-- Taxonomies - Galaxies --##
|
||||||
|
|
||||||
def is_tag_in_all_tag(tag):
|
def is_tag_in_all_tag(tag):
|
||||||
|
@ -1089,8 +1113,9 @@ def get_modal_add_tags(object_id, object_type='item', object_subtype=''):
|
||||||
|
|
||||||
######## NEW VERSION ########
|
######## NEW VERSION ########
|
||||||
def create_custom_tag(tag):
|
def create_custom_tag(tag):
|
||||||
r_tags.sadd('tags:custom', tag)
|
if not is_taxonomie_tag(tag) and not is_galaxy_tag(tag):
|
||||||
r_tags.sadd('tags:custom:enabled_tags', tag)
|
r_tags.sadd('tags:custom', tag)
|
||||||
|
r_tags.sadd('tags:custom:enabled_tags', tag)
|
||||||
|
|
||||||
# # TODO: ADD color
|
# # TODO: ADD color
|
||||||
def get_tag_metadata(tag, r_int=False):
|
def get_tag_metadata(tag, r_int=False):
|
||||||
|
|
|
@ -97,23 +97,26 @@ class Tracker:
|
||||||
def exists(self):
|
def exists(self):
|
||||||
return r_tracker.exists(f'tracker:{self.uuid}')
|
return r_tracker.exists(f'tracker:{self.uuid}')
|
||||||
|
|
||||||
|
def _get_field(self, field):
|
||||||
|
return r_tracker.hget(f'tracker:{self.uuid}', field)
|
||||||
|
|
||||||
def _set_field(self, field, value):
|
def _set_field(self, field, value):
|
||||||
r_tracker.hset(f'tracker:{self.uuid}', field, value)
|
r_tracker.hset(f'tracker:{self.uuid}', field, value)
|
||||||
|
|
||||||
def get_date(self):
|
def get_date(self):
|
||||||
return r_tracker.hget(f'tracker:{self.uuid}', 'date')
|
return self._get_field('date')
|
||||||
|
|
||||||
def get_last_change(self, r_str=False):
|
def get_last_change(self, r_str=False):
|
||||||
last_change = r_tracker.hget(f'tracker:{self.uuid}', 'last_change')
|
last_change = self._get_field('last_change')
|
||||||
if r_str and last_change:
|
if r_str and last_change:
|
||||||
last_change = datetime.datetime.fromtimestamp(float(last_change)).strftime('%Y-%m-%d %H:%M:%S')
|
last_change = datetime.datetime.fromtimestamp(float(last_change)).strftime('%Y-%m-%d %H:%M:%S')
|
||||||
return last_change
|
return last_change
|
||||||
|
|
||||||
def get_first_seen(self):
|
def get_first_seen(self):
|
||||||
return r_tracker.hget(f'tracker:{self.uuid}', 'first_seen')
|
return self._get_field('first_seen')
|
||||||
|
|
||||||
def get_last_seen(self):
|
def get_last_seen(self):
|
||||||
return r_tracker.hget(f'tracker:{self.uuid}', 'last_seen')
|
return self._get_field('last_seen')
|
||||||
|
|
||||||
def _set_first_seen(self, date):
|
def _set_first_seen(self, date):
|
||||||
self._set_field('first_seen', date)
|
self._set_field('first_seen', date)
|
||||||
|
@ -166,10 +169,10 @@ class Tracker:
|
||||||
r_tracker.hdel(f'tracker:{self.uuid}', 'last_seen')
|
r_tracker.hdel(f'tracker:{self.uuid}', 'last_seen')
|
||||||
|
|
||||||
def get_description(self):
|
def get_description(self):
|
||||||
return r_tracker.hget(f'tracker:{self.uuid}', 'description')
|
return self._get_field('description')
|
||||||
|
|
||||||
def get_level(self):
|
def get_level(self):
|
||||||
level = r_tracker.hget(f'tracker:{self.uuid}', 'level')
|
level = self._get_field('level')
|
||||||
if not level:
|
if not level:
|
||||||
level = 0
|
level = 0
|
||||||
return int(level)
|
return int(level)
|
||||||
|
@ -194,7 +197,7 @@ class Tracker:
|
||||||
self._set_field('level', level)
|
self._set_field('level', level)
|
||||||
|
|
||||||
def get_filters(self):
|
def get_filters(self):
|
||||||
filters = r_tracker.hget(f'tracker:{self.uuid}', 'filters')
|
filters = self._get_field('filters')
|
||||||
if not filters:
|
if not filters:
|
||||||
return {}
|
return {}
|
||||||
else:
|
else:
|
||||||
|
@ -205,20 +208,22 @@ class Tracker:
|
||||||
self._set_field('filters', json.dumps(filters))
|
self._set_field('filters', json.dumps(filters))
|
||||||
|
|
||||||
def get_tracked(self):
|
def get_tracked(self):
|
||||||
return r_tracker.hget(f'tracker:{self.uuid}', 'tracked')
|
return self._get_field('tracked')
|
||||||
|
|
||||||
def get_type(self):
|
def get_type(self):
|
||||||
return r_tracker.hget(f'tracker:{self.uuid}', 'type')
|
return self._get_field('type')
|
||||||
|
|
||||||
def get_tags(self):
|
def get_tags(self):
|
||||||
return r_tracker.smembers(f'tracker:tags:{self.uuid}')
|
return r_tracker.smembers(f'tracker:tags:{self.uuid}')
|
||||||
|
|
||||||
def _set_tags(self, tags):
|
def _set_tags(self, tags):
|
||||||
for tag in tags:
|
for tag in tags:
|
||||||
tag = escape(tag)
|
|
||||||
r_tracker.sadd(f'tracker:tags:{self.uuid}', tag)
|
r_tracker.sadd(f'tracker:tags:{self.uuid}', tag)
|
||||||
Tag.create_custom_tag(tag) # TODO CUSTOM TAGS
|
Tag.create_custom_tag(tag) # TODO CUSTOM TAGS
|
||||||
|
|
||||||
|
def _del_tags(self):
|
||||||
|
return r_tracker.delete(f'tracker:tags:{self.uuid}')
|
||||||
|
|
||||||
def mail_export(self):
|
def mail_export(self):
|
||||||
return r_tracker.exists(f'tracker:mail:{self.uuid}')
|
return r_tracker.exists(f'tracker:mail:{self.uuid}')
|
||||||
|
|
||||||
|
@ -229,8 +234,11 @@ class Tracker:
|
||||||
for mail in mails:
|
for mail in mails:
|
||||||
r_tracker.sadd(f'tracker:mail:{self.uuid}', escape(mail))
|
r_tracker.sadd(f'tracker:mail:{self.uuid}', escape(mail))
|
||||||
|
|
||||||
|
def _del_mails(self):
|
||||||
|
r_tracker.delete(f'tracker:mail:{self.uuid}')
|
||||||
|
|
||||||
def get_user(self):
|
def get_user(self):
|
||||||
return r_tracker.hget(f'tracker:{self.uuid}', 'user_id')
|
return self._get_field('user_id')
|
||||||
|
|
||||||
def webhook_export(self):
|
def webhook_export(self):
|
||||||
return r_tracker.hexists(f'tracker:mail:{self.uuid}', 'webhook')
|
return r_tracker.hexists(f'tracker:mail:{self.uuid}', 'webhook')
|
||||||
|
@ -267,6 +275,8 @@ class Tracker:
|
||||||
meta['level'] = self.get_level()
|
meta['level'] = self.get_level()
|
||||||
if 'description' in options:
|
if 'description' in options:
|
||||||
meta['description'] = self.get_description()
|
meta['description'] = self.get_description()
|
||||||
|
if 'nb_objs' in options:
|
||||||
|
meta['nb_objs'] = self.get_nb_objs()
|
||||||
if 'tags' in options:
|
if 'tags' in options:
|
||||||
meta['tags'] = self.get_tags()
|
meta['tags'] = self.get_tags()
|
||||||
if 'filters' in options:
|
if 'filters' in options:
|
||||||
|
@ -289,39 +299,28 @@ class Tracker:
|
||||||
r_tracker.lpush('trackers:dashboard', mess)
|
r_tracker.lpush('trackers:dashboard', mess)
|
||||||
r_tracker.ltrim(f'trackers:dashboard', 0, 9)
|
r_tracker.ltrim(f'trackers:dashboard', 0, 9)
|
||||||
|
|
||||||
# - TODO Data Retention TO Implement - #
|
def get_nb_objs_by_type(self, obj_type):
|
||||||
# Or Daily/Monthly Global DB Cleanup:
|
return r_tracker.scard(f'tracker:objs:{self.uuid}:{obj_type}')
|
||||||
# Iterate on each tracker:
|
|
||||||
# Iterate on each Obj:
|
|
||||||
# Iterate on each date:
|
|
||||||
# Delete from tracker range if date limit exceeded
|
|
||||||
# - TODO
|
|
||||||
def add(self, obj_type, subtype, obj_id, date=None):
|
|
||||||
if not subtype:
|
|
||||||
subtype = ''
|
|
||||||
if not date:
|
|
||||||
date = Date.get_today_date_str()
|
|
||||||
|
|
||||||
new_obj_date = r_tracker.sadd(f'tracker:objs:{self.uuid}:{date}', f'{obj_type}:{subtype}:{obj_id}')
|
|
||||||
new_obj = r_tracker.sadd(f'obj:trackers:{obj_type}:{subtype}:{obj_id}', self.uuid)
|
|
||||||
# MATCHES
|
|
||||||
if new_obj:
|
|
||||||
r_tracker.zincrby(f'tracker:match:{self.uuid}', 1, 'total')
|
|
||||||
r_tracker.zincrby(f'tracker:match:{self.uuid}', 1, obj_type)
|
|
||||||
|
|
||||||
# Only save date for daterange objects - Needed for the DB Cleaner
|
|
||||||
if obj_type != 'item': # not obj_date:
|
|
||||||
r_tracker.sadd(f'obj:tracker:{obj_type}:{subtype}:{obj_id}:{self.uuid}', date)
|
|
||||||
r_tracker.sadd(f'tracker:objs:{self.uuid}:{obj_type}', f'{subtype}:{obj_id}')
|
|
||||||
|
|
||||||
if new_obj_date:
|
|
||||||
self.update_daterange(date)
|
|
||||||
|
|
||||||
self._add_to_dashboard(obj_type, subtype, obj_id)
|
|
||||||
|
|
||||||
def get_objs_by_type(self, obj_type):
|
def get_objs_by_type(self, obj_type):
|
||||||
return r_tracker.smembers(f'tracker:objs:{self.uuid}:{obj_type}')
|
return r_tracker.smembers(f'tracker:objs:{self.uuid}:{obj_type}')
|
||||||
|
|
||||||
|
def get_nb_objs(self):
|
||||||
|
objs = {}
|
||||||
|
for obj_type in get_objects_tracked():
|
||||||
|
nb = self.get_nb_objs_by_type(obj_type)
|
||||||
|
if nb:
|
||||||
|
objs[obj_type] = nb
|
||||||
|
return objs
|
||||||
|
|
||||||
|
def get_objs(self):
|
||||||
|
objs = []
|
||||||
|
for obj_type in get_objects_tracked():
|
||||||
|
for obj in self.get_objs_by_type(obj_type):
|
||||||
|
subtype, obj_id = obj.split(':', 1)
|
||||||
|
objs.append((obj_type, subtype, obj_id))
|
||||||
|
return objs
|
||||||
|
|
||||||
def get_nb_objs_by_date(self, date):
|
def get_nb_objs_by_date(self, date):
|
||||||
return r_tracker.scard(f'tracker:objs:{self.uuid}:{date}')
|
return r_tracker.scard(f'tracker:objs:{self.uuid}:{date}')
|
||||||
|
|
||||||
|
@ -335,10 +334,32 @@ class Tracker:
|
||||||
return objs
|
return objs
|
||||||
|
|
||||||
def get_obj_dates(self, obj_type, subtype, obj_id):
|
def get_obj_dates(self, obj_type, subtype, obj_id):
|
||||||
if obj_type == 'item':
|
return r_tracker.smembers(f'obj:tracker:{obj_type}:{subtype}:{obj_id}:{self.uuid}')
|
||||||
return [item_basic.get_item_date(obj_id)]
|
|
||||||
else:
|
# - TODO Data Retention TO Implement - #
|
||||||
return r_tracker.smembers(f'obj:tracker:{obj_type}:{subtype}:{obj_id}:{self.uuid}')
|
# Or Daily/Monthly Global DB Cleanup:
|
||||||
|
# Iterate on each tracker:
|
||||||
|
# Iterate on each Obj:
|
||||||
|
# Iterate on each date:
|
||||||
|
# Delete from tracker range if date limit exceeded
|
||||||
|
# - TODO
|
||||||
|
def add(self, obj_type, subtype, obj_id, date=None):
|
||||||
|
if not subtype:
|
||||||
|
subtype = ''
|
||||||
|
if not date:
|
||||||
|
date = Date.get_today_date_str()
|
||||||
|
|
||||||
|
new_obj_date = r_tracker.sadd(f'tracker:objs:{self.uuid}:{date}', f'{obj_type}:{subtype}:{obj_id}')
|
||||||
|
r_tracker.sadd(f'obj:trackers:{obj_type}:{subtype}:{obj_id}', self.uuid)
|
||||||
|
|
||||||
|
# Only save object match date - Needed for the DB Cleaner
|
||||||
|
r_tracker.sadd(f'obj:tracker:{obj_type}:{subtype}:{obj_id}:{self.uuid}', date)
|
||||||
|
r_tracker.sadd(f'tracker:objs:{self.uuid}:{obj_type}', f'{subtype}:{obj_id}')
|
||||||
|
|
||||||
|
if new_obj_date:
|
||||||
|
self.update_daterange(date)
|
||||||
|
|
||||||
|
self._add_to_dashboard(obj_type, subtype, obj_id)
|
||||||
|
|
||||||
def remove(self, obj_type, subtype, obj_id):
|
def remove(self, obj_type, subtype, obj_id):
|
||||||
if not subtype:
|
if not subtype:
|
||||||
|
@ -350,9 +371,6 @@ class Tracker:
|
||||||
|
|
||||||
r_tracker.srem(f'obj:trackers:{obj_type}:{subtype}:{obj_id}', self.uuid)
|
r_tracker.srem(f'obj:trackers:{obj_type}:{subtype}:{obj_id}', self.uuid)
|
||||||
r_tracker.srem(f'tracker:objs:{self.uuid}', f'{obj_type}:{subtype}:{obj_id}')
|
r_tracker.srem(f'tracker:objs:{self.uuid}', f'{obj_type}:{subtype}:{obj_id}')
|
||||||
# MATCHES
|
|
||||||
r_tracker.zincrby(f'tracker:match:{self.uuid}', -1, 'total')
|
|
||||||
r_tracker.zincrby(f'tracker:match:{self.uuid}', -1, obj_type)
|
|
||||||
self.update_daterange()
|
self.update_daterange()
|
||||||
|
|
||||||
# TODO escape custom tags
|
# TODO escape custom tags
|
||||||
|
@ -483,19 +501,28 @@ class Tracker:
|
||||||
# Tags
|
# Tags
|
||||||
nb_old_tags = r_tracker.scard(f'tracker:tags:{self.uuid}')
|
nb_old_tags = r_tracker.scard(f'tracker:tags:{self.uuid}')
|
||||||
if nb_old_tags > 0 or tags:
|
if nb_old_tags > 0 or tags:
|
||||||
r_tracker.delete(f'tracker:tags:{self.uuid}')
|
self._del_tags()
|
||||||
self._set_tags(tags)
|
self._set_tags(tags)
|
||||||
|
|
||||||
# Mails
|
# Mails
|
||||||
nb_old_mails = r_tracker.scard(f'tracker:mail:{self.uuid}')
|
nb_old_mails = r_tracker.scard(f'tracker:mail:{self.uuid}')
|
||||||
if nb_old_mails > 0 or mails:
|
if nb_old_mails > 0 or mails:
|
||||||
r_tracker.delete(f'tracker:mail:{self.uuid}')
|
self._del_mails()
|
||||||
self._set_mails(mails)
|
self._set_mails(mails)
|
||||||
|
|
||||||
nb_old_sources = r_tracker.scard(f'tracker:sources:{self.uuid}') # TODO FILTERS
|
# Filters
|
||||||
if nb_old_sources > 0 or sources:
|
if not filters:
|
||||||
r_tracker.delete(f'tracker:sources:{self.uuid}')
|
filters = {}
|
||||||
self._set_sources(sources)
|
for obj_type in get_objects_tracked():
|
||||||
|
filters[obj_type] = {}
|
||||||
|
else:
|
||||||
|
self.set_filters(filters)
|
||||||
|
for obj_type in filters:
|
||||||
|
r_tracker.sadd(f'trackers:objs:{tracker_type}:{obj_type}', to_track)
|
||||||
|
r_tracker.sadd(f'trackers:uuid:{tracker_type}:{to_track}', f'{self.uuid}:{obj_type}')
|
||||||
|
if tracker_type != old_type:
|
||||||
|
r_tracker.srem(f'trackers:objs:{old_type}:{obj_type}', old_to_track)
|
||||||
|
r_tracker.srem(f'trackers:uuid:{old_type}:{old_to_track}', f'{self.uuid}:{obj_type}')
|
||||||
|
|
||||||
# Refresh Trackers
|
# Refresh Trackers
|
||||||
trigger_trackers_refresh(tracker_type)
|
trigger_trackers_refresh(tracker_type)
|
||||||
|
@ -506,7 +533,37 @@ class Tracker:
|
||||||
return self.uuid
|
return self.uuid
|
||||||
|
|
||||||
def delete(self):
|
def delete(self):
|
||||||
pass
|
for obj in self.get_objs():
|
||||||
|
self.remove(obj[0], obj[1], obj[2])
|
||||||
|
|
||||||
|
tracker_type = self.get_type()
|
||||||
|
tracked = self.get_tracked()
|
||||||
|
r_tracker.srem(f'all:tracker:{tracker_type}', tracked)
|
||||||
|
# tracker - uuid map
|
||||||
|
r_tracker.srem(f'all:tracker_uuid:{tracker_type}:{tracked}', self.uuid)
|
||||||
|
r_tracker.srem('trackers:all', self.uuid)
|
||||||
|
r_tracker.srem(f'trackers:all:{tracker_type}', self.uuid)
|
||||||
|
|
||||||
|
if tracker_type == 'typosquatting':
|
||||||
|
r_tracker.delete(f'tracker:typosquatting:{tracked}')
|
||||||
|
elif tracker_type == 'yara':
|
||||||
|
if not is_default_yara_rule(tracked):
|
||||||
|
filepath = get_yara_rule_file_by_tracker_name(tracked)
|
||||||
|
if filepath:
|
||||||
|
os.remove(filepath)
|
||||||
|
|
||||||
|
# Filters
|
||||||
|
filters = self.get_filters()
|
||||||
|
if not filters:
|
||||||
|
filters = get_objects_tracked()
|
||||||
|
for obj_type in filters:
|
||||||
|
r_tracker.srem(f'trackers:objs:{tracker_type}:{obj_type}', tracked)
|
||||||
|
r_tracker.srem(f'trackers:uuid:{tracker_type}:{tracked}', f'{self.uuid}:{obj_type}')
|
||||||
|
|
||||||
|
self._del_mails()
|
||||||
|
self._del_tags()
|
||||||
|
# meta
|
||||||
|
r_tracker.delete(f'tracker:{self.uuid}')
|
||||||
|
|
||||||
|
|
||||||
def create_tracker(tracker_type, to_track, user_id, level, description=None, filters={}, tags=[], mails=[], webhook=None, tracker_uuid=None):
|
def create_tracker(tracker_type, to_track, user_id, level, description=None, filters={}, tags=[], mails=[], webhook=None, tracker_uuid=None):
|
||||||
|
@ -612,12 +669,14 @@ def get_trackers_dashboard():
|
||||||
tracker_uuid, timestamp, obj_type, subtype, obj_id = raw.split(':', 4)
|
tracker_uuid, timestamp, obj_type, subtype, obj_id = raw.split(':', 4)
|
||||||
tracker = Tracker(tracker_uuid)
|
tracker = Tracker(tracker_uuid)
|
||||||
meta = tracker.get_meta(options={'tags'})
|
meta = tracker.get_meta(options={'tags'})
|
||||||
|
if not meta.get('type'):
|
||||||
|
meta['type'] = 'Tracker DELETED'
|
||||||
timestamp = datetime.datetime.fromtimestamp(float(timestamp)).strftime('%Y-%m-%d %H:%M:%S')
|
timestamp = datetime.datetime.fromtimestamp(float(timestamp)).strftime('%Y-%m-%d %H:%M:%S')
|
||||||
meta['timestamp'] = timestamp
|
meta['timestamp'] = timestamp
|
||||||
trackers.append(meta)
|
trackers.append(meta)
|
||||||
return trackers
|
return trackers
|
||||||
|
|
||||||
def get_user_dashboard(user_id): # TODO SORT + REMOVE OLDER ROWS
|
def get_user_dashboard(user_id): # TODO SORT + REMOVE OLDER ROWS (trim)
|
||||||
trackers = []
|
trackers = []
|
||||||
for raw in r_tracker.lrange(f'trackers:user:{user_id}', 0, -1):
|
for raw in r_tracker.lrange(f'trackers:user:{user_id}', 0, -1):
|
||||||
tracker_uuid, timestamp, obj_type, subtype, obj_id = raw.split(':', 4)
|
tracker_uuid, timestamp, obj_type, subtype, obj_id = raw.split(':', 4)
|
||||||
|
@ -822,8 +881,8 @@ def api_add_tracker(dict_input, user_id):
|
||||||
description = escape(description)
|
description = escape(description)
|
||||||
webhook = dict_input.get('webhook', '')
|
webhook = dict_input.get('webhook', '')
|
||||||
webhook = escape(webhook)
|
webhook = escape(webhook)
|
||||||
res = api_validate_tracker_to_add(to_track , tracker_type, nb_words=nb_words)
|
res = api_validate_tracker_to_add(to_track, tracker_type, nb_words=nb_words)
|
||||||
if res[1]!=200:
|
if res[1] != 200:
|
||||||
return res
|
return res
|
||||||
to_track = res[0]['tracked']
|
to_track = res[0]['tracked']
|
||||||
tracker_type = res[0]['type']
|
tracker_type = res[0]['type']
|
||||||
|
@ -878,23 +937,81 @@ def api_add_tracker(dict_input, user_id):
|
||||||
|
|
||||||
return {'tracked': to_track, 'type': tracker_type, 'uuid': tracker_uuid}, 200
|
return {'tracked': to_track, 'type': tracker_type, 'uuid': tracker_uuid}, 200
|
||||||
|
|
||||||
# TODO
|
|
||||||
def api_edit_tracker(dict_input, user_id):
|
def api_edit_tracker(dict_input, user_id):
|
||||||
pass
|
tracker_uuid = dict_input.get('uuid')
|
||||||
# tracker_uuid = dict_input.get('uuid', None)
|
res = api_check_tracker_acl(tracker_uuid, user_id)
|
||||||
# # check edit ACL
|
if res:
|
||||||
# if tracker_uuid:
|
return res
|
||||||
# res = api_is_allowed_to_edit_tracker(tracker_uuid, user_id)
|
|
||||||
# if res[1] != 200:
|
tracker = Tracker(tracker_uuid)
|
||||||
# return res
|
|
||||||
# else:
|
to_track = dict_input.get('tracked', None)
|
||||||
# # check if tracker already tracked in global
|
if not to_track:
|
||||||
# if level==1:
|
return {"status": "error", "reason": "Tracker not provided"}, 400
|
||||||
# if is_tracked_in_global_level(to_track, tracker_type) and not tracker_uuid:
|
tracker_type = dict_input.get('type', None)
|
||||||
# return {"status": "error", "reason": "Tracker already exist"}, 409
|
if not tracker_type:
|
||||||
# else:
|
return {"status": "error", "reason": "Tracker type not provided"}, 400
|
||||||
# if is_tracked_in_user_level(to_track, tracker_type, user_id) and not tracker_uuid:
|
nb_words = dict_input.get('nb_words', 1)
|
||||||
# return {"status": "error", "reason": "Tracker already exist"}, 409
|
description = dict_input.get('description', '')
|
||||||
|
description = escape(description)
|
||||||
|
webhook = dict_input.get('webhook', '')
|
||||||
|
webhook = escape(webhook)
|
||||||
|
res = api_validate_tracker_to_add(to_track, tracker_type, nb_words=nb_words)
|
||||||
|
if res[1] != 200:
|
||||||
|
return res
|
||||||
|
to_track = res[0]['tracked']
|
||||||
|
tracker_type = res[0]['type']
|
||||||
|
|
||||||
|
tags = dict_input.get('tags', [])
|
||||||
|
mails = dict_input.get('mails', [])
|
||||||
|
res = verify_mail_list(mails)
|
||||||
|
if res:
|
||||||
|
return res
|
||||||
|
|
||||||
|
# Filters # TODO MOVE ME
|
||||||
|
filters = dict_input.get('filters', {})
|
||||||
|
if filters:
|
||||||
|
if filters.keys() == {'decoded', 'item', 'pgp'} and set(filters['pgp'].get('subtypes', [])) == {'mail', 'name'}:
|
||||||
|
if not filters['decoded'] and not filters['item']:
|
||||||
|
filters = {}
|
||||||
|
for obj_type in filters:
|
||||||
|
if obj_type not in get_objects_tracked():
|
||||||
|
return {"status": "error", "reason": "Invalid Tracker Object type"}, 400
|
||||||
|
|
||||||
|
if obj_type == 'pgp':
|
||||||
|
if set(filters['pgp'].get('subtypes', [])) == {'mail', 'name'}:
|
||||||
|
filters['pgp'].pop('subtypes')
|
||||||
|
|
||||||
|
for filter_name in filters[obj_type]:
|
||||||
|
if filter_name not in {'mimetypes', 'sources', 'subtypes'}:
|
||||||
|
return {"status": "error", "reason": "Invalid Filter"}, 400
|
||||||
|
elif filter_name == 'mimetypes': # TODO
|
||||||
|
pass
|
||||||
|
elif filter_name == 'sources':
|
||||||
|
if obj_type == 'item':
|
||||||
|
res = item_basic.verify_sources_list(filters['item']['sources'])
|
||||||
|
if res:
|
||||||
|
return res
|
||||||
|
else:
|
||||||
|
return {"status": "error", "reason": "Invalid Filter sources"}, 400
|
||||||
|
elif filter_name == 'subtypes':
|
||||||
|
obj_subtypes = set(get_object_all_subtypes(obj_type))
|
||||||
|
for subtype in filters[obj_type]['subtypes']:
|
||||||
|
if subtype not in obj_subtypes:
|
||||||
|
return {"status": "error", "reason": "Invalid Tracker Object subtype"}, 400
|
||||||
|
|
||||||
|
level = dict_input.get('level', 1)
|
||||||
|
try:
|
||||||
|
level = int(level)
|
||||||
|
except TypeError:
|
||||||
|
level = 1
|
||||||
|
if level not in range(0, 1):
|
||||||
|
level = 1
|
||||||
|
|
||||||
|
tracker.edit(tracker_type, to_track, level, description=description, filters=filters,
|
||||||
|
tags=tags, mails=mails, webhook=webhook)
|
||||||
|
return {'tracked': to_track, 'type': tracker_type, 'uuid': tracker_uuid}, 200
|
||||||
|
|
||||||
|
|
||||||
def api_delete_tracker(data, user_id):
|
def api_delete_tracker(data, user_id):
|
||||||
tracker_uuid = data.get('uuid')
|
tracker_uuid = data.get('uuid')
|
||||||
|
@ -1073,7 +1190,7 @@ def save_yara_rule(yara_rule_type, yara_rule, tracker_uuid=None):
|
||||||
filename = os.path.join('custom-rules', tracker_uuid + '.yar')
|
filename = os.path.join('custom-rules', tracker_uuid + '.yar')
|
||||||
with open(os.path.join(get_yara_rules_dir(), filename), 'w') as f:
|
with open(os.path.join(get_yara_rules_dir(), filename), 'w') as f:
|
||||||
f.write(str(yara_rule))
|
f.write(str(yara_rule))
|
||||||
if yara_rule_type == 'yara_default':
|
elif yara_rule_type == 'yara_default':
|
||||||
filename = os.path.join('ail-yara-rules', 'rules', yara_rule)
|
filename = os.path.join('ail-yara-rules', 'rules', yara_rule)
|
||||||
return filename
|
return filename
|
||||||
|
|
||||||
|
@ -1316,7 +1433,9 @@ class RetroHunt:
|
||||||
def get_nb_objs(self):
|
def get_nb_objs(self):
|
||||||
objs = {}
|
objs = {}
|
||||||
for obj_type in get_objects_retro_hunted():
|
for obj_type in get_objects_retro_hunted():
|
||||||
objs[obj_type] = self.get_nb_objs_by_type(obj_type)
|
nb = self.get_nb_objs_by_type(obj_type)
|
||||||
|
if nb:
|
||||||
|
objs[obj_type] = nb
|
||||||
return objs
|
return objs
|
||||||
|
|
||||||
def get_objs(self):
|
def get_objs(self):
|
||||||
|
@ -1363,12 +1482,6 @@ class RetroHunt:
|
||||||
for mail in mails:
|
for mail in mails:
|
||||||
r_tracker.sadd(f'retro_hunt:mails:{self.uuid}', escape(mail))
|
r_tracker.sadd(f'retro_hunt:mails:{self.uuid}', escape(mail))
|
||||||
|
|
||||||
# TODO Delete filters - SAVE DEFAULT OBJECTS ???
|
|
||||||
# Filters
|
|
||||||
# if not filters:
|
|
||||||
# filters = {}
|
|
||||||
# for obj_type in get_objects_tracked():
|
|
||||||
# filters[obj_type] = {}
|
|
||||||
if filters:
|
if filters:
|
||||||
self.set_filters(filters)
|
self.set_filters(filters)
|
||||||
|
|
||||||
|
@ -1379,11 +1492,17 @@ class RetroHunt:
|
||||||
state = 'pending'
|
state = 'pending'
|
||||||
self._set_state(state)
|
self._set_state(state)
|
||||||
|
|
||||||
# TODO Delete Rule custom
|
|
||||||
def delete(self):
|
def delete(self):
|
||||||
if self.is_running():
|
if self.is_running() and self.get_state() != 'completed':
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
# Delete custom rule
|
||||||
|
rule = self.get_rule()
|
||||||
|
if not is_default_yara_rule(rule):
|
||||||
|
filepath = get_yara_rule_file_by_tracker_name(rule)
|
||||||
|
if filepath:
|
||||||
|
os.remove(filepath)
|
||||||
|
|
||||||
r_tracker.srem('retro_hunts:pending', self.uuid)
|
r_tracker.srem('retro_hunts:pending', self.uuid)
|
||||||
r_tracker.delete(f'retro_hunts:{self.uuid}')
|
r_tracker.delete(f'retro_hunts:{self.uuid}')
|
||||||
r_tracker.delete(f'retro_hunt:tags:{self.uuid}')
|
r_tracker.delete(f'retro_hunt:tags:{self.uuid}')
|
||||||
|
@ -1580,7 +1699,7 @@ def api_delete_retro_hunt_task(task_uuid):
|
||||||
if res:
|
if res:
|
||||||
return res
|
return res
|
||||||
retro_hunt = RetroHunt(task_uuid)
|
retro_hunt = RetroHunt(task_uuid)
|
||||||
if retro_hunt.is_running():
|
if retro_hunt.is_running() and retro_hunt.get_state() != 'completed':
|
||||||
return {"status": "error", "reason": "You can't delete a running task"}, 400
|
return {"status": "error", "reason": "You can't delete a running task"}, 400
|
||||||
else:
|
else:
|
||||||
return retro_hunt.delete(), 200
|
return retro_hunt.delete(), 200
|
||||||
|
|
|
@ -83,12 +83,7 @@ class Tracker_Regex(AbstractModule):
|
||||||
print(f'new tracked regex found: {tracker_name} in {obj_id}')
|
print(f'new tracked regex found: {tracker_name} in {obj_id}')
|
||||||
self.redis_logger.warning(f'new tracked regex found: {tracker_name} in {obj_id}')
|
self.redis_logger.warning(f'new tracked regex found: {tracker_name} in {obj_id}')
|
||||||
|
|
||||||
if obj.get_type() == 'item':
|
tracker.add(obj.get_type(), obj.get_subtype(r_str=True), obj_id)
|
||||||
date = obj.get_date()
|
|
||||||
else:
|
|
||||||
date = None
|
|
||||||
|
|
||||||
tracker.add(obj.get_type(), obj.get_subtype(r_str=True), obj_id, date=date)
|
|
||||||
|
|
||||||
for tag in tracker.get_tags():
|
for tag in tracker.get_tags():
|
||||||
if obj.get_type() == 'item':
|
if obj.get_type() == 'item':
|
||||||
|
|
|
@ -124,14 +124,10 @@ class Tracker_Term(AbstractModule):
|
||||||
if ail_objects.is_filtered(obj, filters):
|
if ail_objects.is_filtered(obj, filters):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
print(f'new tracked term found: {tracker_name} in {obj_id}')
|
print(f'new tracked term {tracker_uuid} found: {tracker_name} in {obj_id}')
|
||||||
self.redis_logger.warning(f'new tracked term found: {tracker_name} in {obj_id}')
|
self.redis_logger.warning(f'new tracked term found: {tracker_name} in {obj_id}')
|
||||||
|
|
||||||
if obj.get_type() == 'item':
|
tracker.add(obj.get_type(), obj.get_subtype(), obj_id)
|
||||||
date = obj.get_date()
|
|
||||||
else:
|
|
||||||
date = None
|
|
||||||
tracker.add(obj.get_type(), obj.get_subtype(), obj_id, date=date)
|
|
||||||
|
|
||||||
# Tags
|
# Tags
|
||||||
for tag in tracker.get_tags():
|
for tag in tracker.get_tags():
|
||||||
|
|
|
@ -78,12 +78,7 @@ class Tracker_Typo_Squatting(AbstractModule):
|
||||||
print(f'new tracked typosquatting found: {tracked} in {obj_id}')
|
print(f'new tracked typosquatting found: {tracked} in {obj_id}')
|
||||||
self.redis_logger.warning(f'tracker typosquatting: {tracked} in {obj_id}')
|
self.redis_logger.warning(f'tracker typosquatting: {tracked} in {obj_id}')
|
||||||
|
|
||||||
if obj.get_type() == 'item':
|
tracker.add(obj.get_type(), obj.get_subtype(r_str=True), obj_id)
|
||||||
date = obj.get_date()
|
|
||||||
else:
|
|
||||||
date = None
|
|
||||||
|
|
||||||
tracker.add(obj.get_type(), obj.get_subtype(r_str=True), obj_id, date=date)
|
|
||||||
|
|
||||||
# Tags
|
# Tags
|
||||||
for tag in tracker.get_tags():
|
for tag in tracker.get_tags():
|
||||||
|
|
|
@ -84,12 +84,7 @@ class Tracker_Yara(AbstractModule):
|
||||||
if ail_objects.is_filtered(self.obj, filters):
|
if ail_objects.is_filtered(self.obj, filters):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if self.obj.get_type() == 'item':
|
tracker.add(self.obj.get_type(), self.obj.get_subtype(r_str=True), obj_id)
|
||||||
date = self.obj.get_date()
|
|
||||||
else:
|
|
||||||
date = None
|
|
||||||
|
|
||||||
tracker.add(self.obj.get_type(), self.obj.get_subtype(r_str=True), obj_id, date=date)
|
|
||||||
|
|
||||||
# Tags
|
# Tags
|
||||||
for tag in tracker.get_tags():
|
for tag in tracker.get_tags():
|
||||||
|
|
|
@ -9,7 +9,7 @@ import os
|
||||||
import sys
|
import sys
|
||||||
import json
|
import json
|
||||||
|
|
||||||
from flask import render_template, jsonify, request, Blueprint, redirect, url_for, Response, escape
|
from flask import render_template, jsonify, request, Blueprint, redirect, url_for, Response, escape, abort
|
||||||
from flask_login import login_required, current_user, login_user, logout_user
|
from flask_login import login_required, current_user, login_user, logout_user
|
||||||
|
|
||||||
sys.path.append('modules')
|
sys.path.append('modules')
|
||||||
|
@ -27,6 +27,7 @@ from lib.objects import ail_objects
|
||||||
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
|
||||||
|
from packages import Date
|
||||||
|
|
||||||
|
|
||||||
bootstrap_label = Flask_config.bootstrap_label
|
bootstrap_label = Flask_config.bootstrap_label
|
||||||
|
@ -47,6 +48,14 @@ def create_json_response(data, status_code):
|
||||||
|
|
||||||
# ============= ROUTES ==============
|
# ============= ROUTES ==============
|
||||||
|
|
||||||
|
@hunters.route("/yara/rule/default/content", methods=['GET'])
|
||||||
|
@login_required
|
||||||
|
@login_read_only
|
||||||
|
def get_default_yara_rule_content():
|
||||||
|
default_yara_rule = request.args.get('rule')
|
||||||
|
res = Tracker.api_get_default_rule_content(default_yara_rule)
|
||||||
|
return Response(json.dumps(res[0], indent=2, sort_keys=True), mimetype='application/json'), res[1]
|
||||||
|
|
||||||
##################
|
##################
|
||||||
# TRACKERS #
|
# TRACKERS #
|
||||||
##################
|
##################
|
||||||
|
@ -120,98 +129,160 @@ def tracked_menu_typosquatting():
|
||||||
return render_template("trackersManagement.html", user_trackers=user_trackers, global_trackers=global_trackers,
|
return render_template("trackersManagement.html", user_trackers=user_trackers, global_trackers=global_trackers,
|
||||||
bootstrap_label=bootstrap_label, tracker_type=tracker_type)
|
bootstrap_label=bootstrap_label, tracker_type=tracker_type)
|
||||||
|
|
||||||
|
|
||||||
|
@hunters.route("/tracker/show")
|
||||||
|
@login_required
|
||||||
|
@login_read_only
|
||||||
|
def show_tracker():
|
||||||
|
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]
|
||||||
|
|
||||||
|
date_from = request.args.get('date_from')
|
||||||
|
date_to = request.args.get('date_to')
|
||||||
|
|
||||||
|
if date_from:
|
||||||
|
date_from = date_from.replace('-', '')
|
||||||
|
if date_to:
|
||||||
|
date_to = date_to.replace('-', '')
|
||||||
|
|
||||||
|
tracker = Tracker.Tracker(tracker_uuid)
|
||||||
|
meta = tracker.get_meta(options={'description', 'level', 'mails', 'filters', 'sparkline', 'tags',
|
||||||
|
'user', 'webhook', 'nb_objs'})
|
||||||
|
|
||||||
|
if meta['type'] == 'yara':
|
||||||
|
yara_rule_content = Tracker.get_yara_rule_content(meta['tracked'])
|
||||||
|
else:
|
||||||
|
yara_rule_content = None
|
||||||
|
|
||||||
|
if meta['type'] == 'typosquatting':
|
||||||
|
typo_squatting = Tracker.get_tracked_typosquatting_domains(meta['tracked'])
|
||||||
|
sorted(typo_squatting)
|
||||||
|
else:
|
||||||
|
typo_squatting = set()
|
||||||
|
|
||||||
|
if date_from:
|
||||||
|
date_from, date_to = Date.sanitise_daterange(meta['first_seen'], meta['last_seen'])
|
||||||
|
objs = tracker.get_objs_by_daterange(date_from, date_to)
|
||||||
|
meta['objs'] = ail_objects.get_objects_meta(objs, flask_context=True)
|
||||||
|
else:
|
||||||
|
date_from = ''
|
||||||
|
date_to = ''
|
||||||
|
meta['objs'] = []
|
||||||
|
|
||||||
|
meta['date_from'] = date_from
|
||||||
|
meta['date_to'] = date_to
|
||||||
|
meta['item_sources'] = sorted(meta['filters'].get('item', {}).get('sources', []))
|
||||||
|
if meta['filters']:
|
||||||
|
meta['filters'] = json.dumps(meta['filters'], indent=4)
|
||||||
|
|
||||||
|
return render_template("tracker_show.html", meta=meta,
|
||||||
|
rule_content=yara_rule_content,
|
||||||
|
typo_squatting=typo_squatting,
|
||||||
|
bootstrap_label=bootstrap_label)
|
||||||
|
|
||||||
|
def parse_add_edit_request(request_form):
|
||||||
|
to_track = request_form.get("tracker")
|
||||||
|
tracker_uuid = request_form.get("tracker_uuid")
|
||||||
|
tracker_type = request_form.get("tracker_type")
|
||||||
|
nb_words = request_form.get("nb_word", 1)
|
||||||
|
description = request.form.get("description", '')
|
||||||
|
webhook = request_form.get("webhook", '')
|
||||||
|
level = request_form.get("level", 0)
|
||||||
|
mails = request_form.get("mails", [])
|
||||||
|
|
||||||
|
# TAGS
|
||||||
|
tags = request_form.get("tags", [])
|
||||||
|
taxonomies_tags = request_form.get('taxonomies_tags')
|
||||||
|
if taxonomies_tags:
|
||||||
|
try:
|
||||||
|
taxonomies_tags = json.loads(taxonomies_tags)
|
||||||
|
except:
|
||||||
|
taxonomies_tags = []
|
||||||
|
else:
|
||||||
|
taxonomies_tags = []
|
||||||
|
galaxies_tags = request_form.get('galaxies_tags')
|
||||||
|
if galaxies_tags:
|
||||||
|
try:
|
||||||
|
galaxies_tags = json.loads(galaxies_tags)
|
||||||
|
except:
|
||||||
|
galaxies_tags = []
|
||||||
|
else:
|
||||||
|
galaxies_tags = []
|
||||||
|
# custom tags
|
||||||
|
if tags:
|
||||||
|
tags = tags.split()
|
||||||
|
else:
|
||||||
|
tags = []
|
||||||
|
escaped = []
|
||||||
|
for tag in tags:
|
||||||
|
escaped.append(tag)
|
||||||
|
tags = escaped + taxonomies_tags + galaxies_tags
|
||||||
|
|
||||||
|
# YARA #
|
||||||
|
if tracker_type == 'yara':
|
||||||
|
yara_default_rule = request_form.get("yara_default_rule")
|
||||||
|
yara_custom_rule = request_form.get("yara_custom_rule")
|
||||||
|
if yara_custom_rule:
|
||||||
|
to_track = yara_custom_rule
|
||||||
|
tracker_type = 'yara_custom'
|
||||||
|
else:
|
||||||
|
to_track = yara_default_rule
|
||||||
|
tracker_type = 'yara_default'
|
||||||
|
|
||||||
|
if level == 'on':
|
||||||
|
level = 1
|
||||||
|
else:
|
||||||
|
level = 0
|
||||||
|
if mails:
|
||||||
|
mails = mails.split()
|
||||||
|
else:
|
||||||
|
mails = []
|
||||||
|
|
||||||
|
# FILTERS
|
||||||
|
filters = {}
|
||||||
|
for obj_type in Tracker.get_objects_tracked():
|
||||||
|
new_filter = request_form.get(f'{obj_type}_obj')
|
||||||
|
if new_filter == 'on':
|
||||||
|
filters[obj_type] = {}
|
||||||
|
# Mimetypes
|
||||||
|
mimetypes = request_form.get(f'mimetypes_{obj_type}', [])
|
||||||
|
if mimetypes:
|
||||||
|
mimetypes = json.loads(mimetypes)
|
||||||
|
filters[obj_type]['mimetypes'] = mimetypes
|
||||||
|
# Sources
|
||||||
|
sources = request_form.get(f'sources_{obj_type}', [])
|
||||||
|
if sources:
|
||||||
|
sources = json.loads(sources)
|
||||||
|
filters[obj_type]['sources'] = sources
|
||||||
|
# Subtypes
|
||||||
|
for obj_subtype in ail_core.get_object_all_subtypes(obj_type):
|
||||||
|
subtype = request_form.get(f'filter_{obj_type}_{obj_subtype}')
|
||||||
|
if subtype == 'on':
|
||||||
|
if 'subtypes' not in filters[obj_type]:
|
||||||
|
filters[obj_type]['subtypes'] = []
|
||||||
|
filters[obj_type]['subtypes'].append(obj_subtype)
|
||||||
|
|
||||||
|
input_dict = {"tracked": to_track, "type": tracker_type,
|
||||||
|
"tags": tags, "mails": mails, "filters": filters,
|
||||||
|
"level": level, "description": description, "webhook": webhook}
|
||||||
|
if tracker_uuid:
|
||||||
|
input_dict['uuid'] = tracker_uuid
|
||||||
|
if tracker_type == 'set':
|
||||||
|
try:
|
||||||
|
input_dict['nb_words'] = int(nb_words)
|
||||||
|
except (ValueError, TypeError):
|
||||||
|
input_dict['nb_words'] = 1
|
||||||
|
return input_dict
|
||||||
|
|
||||||
@hunters.route("/tracker/add", methods=['GET', 'POST'])
|
@hunters.route("/tracker/add", methods=['GET', 'POST'])
|
||||||
@login_required
|
@login_required
|
||||||
@login_analyst
|
@login_analyst
|
||||||
def add_tracked_menu():
|
def add_tracked_menu():
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
to_track = request.form.get("tracker")
|
input_dict = parse_add_edit_request(request.form)
|
||||||
tracker_uuid = request.form.get("tracker_uuid")
|
|
||||||
tracker_type = request.form.get("tracker_type")
|
|
||||||
nb_words = request.form.get("nb_word", 1)
|
|
||||||
description = request.form.get("description", '')
|
|
||||||
webhook = request.form.get("webhook", '')
|
|
||||||
level = request.form.get("level", 0)
|
|
||||||
mails = request.form.get("mails", [])
|
|
||||||
|
|
||||||
# TAGS
|
|
||||||
tags = request.form.get("tags", [])
|
|
||||||
taxonomies_tags = request.form.get('taxonomies_tags')
|
|
||||||
if taxonomies_tags:
|
|
||||||
try:
|
|
||||||
taxonomies_tags = json.loads(taxonomies_tags)
|
|
||||||
except:
|
|
||||||
taxonomies_tags = []
|
|
||||||
else:
|
|
||||||
taxonomies_tags = []
|
|
||||||
galaxies_tags = request.form.get('galaxies_tags')
|
|
||||||
if galaxies_tags:
|
|
||||||
try:
|
|
||||||
galaxies_tags = json.loads(galaxies_tags)
|
|
||||||
except:
|
|
||||||
galaxies_tags = []
|
|
||||||
else:
|
|
||||||
galaxies_tags = []
|
|
||||||
# custom tags
|
|
||||||
if tags:
|
|
||||||
tags = tags.split()
|
|
||||||
else:
|
|
||||||
tags = []
|
|
||||||
tags = tags + taxonomies_tags + galaxies_tags
|
|
||||||
|
|
||||||
# YARA #
|
|
||||||
if tracker_type == 'yara':
|
|
||||||
yara_default_rule = request.form.get("yara_default_rule")
|
|
||||||
yara_custom_rule = request.form.get("yara_custom_rule")
|
|
||||||
if yara_custom_rule:
|
|
||||||
to_track = yara_custom_rule
|
|
||||||
tracker_type = 'yara_custom'
|
|
||||||
else:
|
|
||||||
to_track = yara_default_rule
|
|
||||||
tracker_type = 'yara_default'
|
|
||||||
|
|
||||||
if level == 'on':
|
|
||||||
level = 1
|
|
||||||
else:
|
|
||||||
level = 0
|
|
||||||
if mails:
|
|
||||||
mails = mails.split()
|
|
||||||
else:
|
|
||||||
tags = []
|
|
||||||
|
|
||||||
# FILTERS
|
|
||||||
filters = {}
|
|
||||||
for obj_type in Tracker.get_objects_tracked():
|
|
||||||
new_filter = request.form.get(f'{obj_type}_obj')
|
|
||||||
if new_filter == 'on':
|
|
||||||
filters[obj_type] = {}
|
|
||||||
# Mimetypes
|
|
||||||
mimetypes = request.form.get(f'mimetypes_{obj_type}', [])
|
|
||||||
if mimetypes:
|
|
||||||
mimetypes = json.loads(mimetypes)
|
|
||||||
filters[obj_type]['mimetypes'] = mimetypes
|
|
||||||
# Sources
|
|
||||||
sources = request.form.get(f'sources_{obj_type}', [])
|
|
||||||
if sources:
|
|
||||||
sources = json.loads(sources)
|
|
||||||
filters[obj_type]['sources'] = sources
|
|
||||||
# Subtypes
|
|
||||||
for obj_subtype in ail_core.get_object_all_subtypes(obj_type):
|
|
||||||
subtype = request.form.get(f'filter_{obj_type}_{obj_subtype}')
|
|
||||||
if subtype == 'on':
|
|
||||||
if 'subtypes' not in filters[obj_type]:
|
|
||||||
filters[obj_type]['subtypes'] = []
|
|
||||||
filters[obj_type]['subtypes'].append(obj_subtype)
|
|
||||||
|
|
||||||
input_dict = {"tracked": to_track, "type": tracker_type,
|
|
||||||
"tags": tags, "mails": mails, "filters": filters,
|
|
||||||
"level": level, "description": description, "webhook": webhook}
|
|
||||||
if tracker_type == 'set':
|
|
||||||
try:
|
|
||||||
input_dict['nb_words'] = int(nb_words)
|
|
||||||
except TypeError:
|
|
||||||
input_dict['nb_words'] = 1
|
|
||||||
|
|
||||||
user_id = current_user.get_id()
|
user_id = current_user.get_id()
|
||||||
res = Tracker.api_add_tracker(input_dict, user_id)
|
res = Tracker.api_add_tracker(input_dict, user_id)
|
||||||
if res[1] == 200:
|
if res[1] == 200:
|
||||||
|
@ -220,9 +291,42 @@ def add_tracked_menu():
|
||||||
return create_json_response(res[0], res[1])
|
return create_json_response(res[0], res[1])
|
||||||
else:
|
else:
|
||||||
return render_template("tracker_add.html",
|
return render_template("tracker_add.html",
|
||||||
all_sources=item_basic.get_all_items_sources(r_list=True),
|
all_sources=item_basic.get_all_items_sources(r_list=True),
|
||||||
tags_selector_data=Tag.get_tags_selector_data(),
|
tags_selector_data=Tag.get_tags_selector_data(),
|
||||||
all_yara_files=Tracker.get_all_default_yara_files())
|
all_yara_files=Tracker.get_all_default_yara_files())
|
||||||
|
|
||||||
|
@hunters.route("/tracker/edit", methods=['GET', 'POST'])
|
||||||
|
@login_required
|
||||||
|
@login_analyst
|
||||||
|
def tracker_edit():
|
||||||
|
if request.method == 'POST':
|
||||||
|
input_dict = parse_add_edit_request(request.form)
|
||||||
|
user_id = current_user.get_id()
|
||||||
|
res = Tracker.api_edit_tracker(input_dict, user_id)
|
||||||
|
if res[1] == 200:
|
||||||
|
return redirect(url_for('hunters.show_tracker', uuid=res[0].get('uuid')))
|
||||||
|
else:
|
||||||
|
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)
|
||||||
|
dict_tracker = tracker.get_meta(options={'description', 'level', 'mails', 'filters', 'tags', 'webhook'})
|
||||||
|
if dict_tracker['type'] == 'yara':
|
||||||
|
if not Tracker.is_default_yara_rule(dict_tracker['tracked']):
|
||||||
|
dict_tracker['content'] = Tracker.get_yara_rule_content(dict_tracker['tracked'])
|
||||||
|
taxonomies_tags, galaxies_tags, custom_tags = Tag.sort_tags_taxonomies_galaxies_customs(dict_tracker['tags'])
|
||||||
|
tags_selector_data = Tag.get_tags_selector_data()
|
||||||
|
tags_selector_data['taxonomies_tags'] = taxonomies_tags
|
||||||
|
tags_selector_data['galaxies_tags'] = galaxies_tags
|
||||||
|
dict_tracker['tags'] = custom_tags
|
||||||
|
return render_template("tracker_add.html",
|
||||||
|
dict_tracker=dict_tracker,
|
||||||
|
all_sources=item_basic.get_all_items_sources(r_list=True),
|
||||||
|
tags_selector_data=tags_selector_data,
|
||||||
|
all_yara_files=Tracker.get_all_default_yara_files())
|
||||||
|
|
||||||
@hunters.route('/tracker/delete', methods=['GET'])
|
@hunters.route('/tracker/delete', methods=['GET'])
|
||||||
@login_required
|
@login_required
|
||||||
|
@ -234,7 +338,31 @@ def tracker_delete():
|
||||||
if res[1] != 200:
|
if res[1] != 200:
|
||||||
return create_json_response(res[0], res[1])
|
return create_json_response(res[0], res[1])
|
||||||
else:
|
else:
|
||||||
return redirect(url_for('hunter.tracked_menu'))
|
return redirect(url_for('hunters.trackers_dashboard'))
|
||||||
|
|
||||||
|
|
||||||
|
@hunters.route("/tracker/graph/json", methods=['GET'])
|
||||||
|
@login_required
|
||||||
|
@login_read_only
|
||||||
|
def get_json_tracker_graph():
|
||||||
|
user_id = current_user.get_id()
|
||||||
|
tracker_uuid = request.args.get('uuid')
|
||||||
|
res = Tracker.api_check_tracker_acl(tracker_uuid, user_id)
|
||||||
|
if res:
|
||||||
|
return create_json_response(res[0], res[1])
|
||||||
|
|
||||||
|
date_from = request.args.get('date_from')
|
||||||
|
date_to = request.args.get('date_to')
|
||||||
|
|
||||||
|
if date_from:
|
||||||
|
date_from = date_from.replace('-', '')
|
||||||
|
if date_to:
|
||||||
|
date_to = date_to.replace('-', '')
|
||||||
|
if date_from and date_to:
|
||||||
|
res = Tracker.get_trackers_graph_by_day([tracker_uuid], date_from=date_from, date_to=date_to)
|
||||||
|
else:
|
||||||
|
res = Tracker.get_trackers_graph_by_day([tracker_uuid])
|
||||||
|
return jsonify(res)
|
||||||
|
|
||||||
|
|
||||||
####################
|
####################
|
||||||
|
|
|
@ -1,219 +0,0 @@
|
||||||
#!/usr/bin/env python3
|
|
||||||
# -*-coding:UTF-8 -*
|
|
||||||
|
|
||||||
'''
|
|
||||||
Flask functions and routes for tracked items
|
|
||||||
'''
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
import json
|
|
||||||
import flask
|
|
||||||
from flask import Flask, render_template, jsonify, request, Blueprint, url_for, redirect, Response, escape
|
|
||||||
|
|
||||||
from Role_Manager import login_admin, login_analyst, login_read_only
|
|
||||||
from flask_login import login_required, current_user
|
|
||||||
|
|
||||||
sys.path.append(os.environ['AIL_BIN'])
|
|
||||||
##################################
|
|
||||||
# Import Project packages
|
|
||||||
##################################
|
|
||||||
from lib.objects import ail_objects
|
|
||||||
from lib import item_basic
|
|
||||||
from lib import Tracker
|
|
||||||
from lib import Tag
|
|
||||||
from packages import Date
|
|
||||||
|
|
||||||
|
|
||||||
# ============ VARIABLES ============
|
|
||||||
import Flask_config
|
|
||||||
|
|
||||||
app = Flask_config.app
|
|
||||||
baseUrl = Flask_config.baseUrl
|
|
||||||
bootstrap_label = Flask_config.bootstrap_label
|
|
||||||
|
|
||||||
hunter = Blueprint('hunter', __name__, template_folder='templates')
|
|
||||||
|
|
||||||
# ============ FUNCTIONS ============
|
|
||||||
|
|
||||||
def create_json_response(data, status_code):
|
|
||||||
return Response(json.dumps(data, indent=2, sort_keys=True), mimetype='application/json'), status_code
|
|
||||||
|
|
||||||
# ============ ROUTES ============
|
|
||||||
|
|
||||||
@hunter.route("/tracker/edit", methods=['GET', 'POST'])
|
|
||||||
@login_required
|
|
||||||
@login_analyst
|
|
||||||
def edit_tracked_menu():
|
|
||||||
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) # check if is author or admin
|
|
||||||
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)
|
|
||||||
dict_tracker = tracker.get_meta(options={'description', 'level', 'mails', 'sources', 'tags', 'user', 'webhook'})
|
|
||||||
dict_tracker['tags'] = ' '.join(dict_tracker['tags'])
|
|
||||||
dict_tracker['mails'] = ' '.join(dict_tracker['mails'])
|
|
||||||
|
|
||||||
if dict_tracker['type'] == 'set':
|
|
||||||
dict_tracker['tracker'], dict_tracker['nb_words'] = dict_tracker['tracker'].split(';')
|
|
||||||
dict_tracker['tracker'] = dict_tracker['tracker'].replace(',', ' ')
|
|
||||||
elif dict_tracker['type'] == 'yara': #is_valid_default_yara_rule
|
|
||||||
if Tracker.is_default_yara_rule(dict_tracker['tracker']):
|
|
||||||
dict_tracker['yara_file'] = dict_tracker['tracker'].split('/')
|
|
||||||
dict_tracker['yara_file'] = dict_tracker['yara_file'][-2] + '/' + dict_tracker['yara_file'][-1]
|
|
||||||
dict_tracker['content'] = None
|
|
||||||
else:
|
|
||||||
dict_tracker['yara_file'] = None
|
|
||||||
dict_tracker['content'] = Tracker.get_yara_rule_content(dict_tracker['tracker'])
|
|
||||||
|
|
||||||
return render_template("edit_tracker.html", dict_tracker=dict_tracker,
|
|
||||||
all_sources=item_basic.get_all_items_sources(r_list=True),
|
|
||||||
all_yara_files=Tracker.get_all_default_yara_files())
|
|
||||||
|
|
||||||
## TO EDIT
|
|
||||||
# word
|
|
||||||
# set of word + nb words
|
|
||||||
# regex
|
|
||||||
# yara custom
|
|
||||||
# yara default ???? => allow edit ?
|
|
||||||
|
|
||||||
#### EDIT SHow Trackers ??????????????????????????????????????????????????
|
|
||||||
|
|
||||||
@hunter.route("/tracker/show_tracker")
|
|
||||||
@login_required
|
|
||||||
@login_read_only
|
|
||||||
def show_tracker():
|
|
||||||
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]
|
|
||||||
|
|
||||||
date_from = request.args.get('date_from')
|
|
||||||
date_to = request.args.get('date_to')
|
|
||||||
|
|
||||||
if date_from:
|
|
||||||
date_from = date_from.replace('-', '')
|
|
||||||
if date_to:
|
|
||||||
date_to = date_to.replace('-', '')
|
|
||||||
|
|
||||||
tracker = Tracker.Tracker(tracker_uuid)
|
|
||||||
meta = tracker.get_meta(options={'description', 'level', 'mails', 'filters', 'sparkline', 'tags',
|
|
||||||
'user', 'webhook'})
|
|
||||||
|
|
||||||
if meta['type'] == 'yara':
|
|
||||||
yara_rule_content = Tracker.get_yara_rule_content(meta['tracked'])
|
|
||||||
else:
|
|
||||||
yara_rule_content = None
|
|
||||||
|
|
||||||
if meta['type'] == 'typosquatting':
|
|
||||||
typo_squatting = Tracker.get_tracked_typosquatting_domains(meta['tracked'])
|
|
||||||
sorted(typo_squatting)
|
|
||||||
else:
|
|
||||||
typo_squatting = set()
|
|
||||||
|
|
||||||
if date_from:
|
|
||||||
date_from, date_to = Date.sanitise_daterange(meta['first_seen'], meta['last_seen'])
|
|
||||||
objs = tracker.get_objs_by_daterange(date_from, date_to)
|
|
||||||
meta['objs'] = ail_objects.get_objects_meta(objs, flask_context=True)
|
|
||||||
else:
|
|
||||||
date_from = ''
|
|
||||||
date_to = ''
|
|
||||||
meta['objs'] = []
|
|
||||||
|
|
||||||
meta['date_from'] = date_from
|
|
||||||
meta['date_to'] = date_to
|
|
||||||
print(meta['filters'])
|
|
||||||
meta['item_sources'] = sorted(meta['filters'].get('item', {}).get('sources', []))
|
|
||||||
# meta['filters'] = json.dumps(meta['filters'], indent=4)
|
|
||||||
|
|
||||||
return render_template("showTracker.html", tracker_metadata=meta,
|
|
||||||
yara_rule_content=yara_rule_content,
|
|
||||||
typo_squatting=typo_squatting,
|
|
||||||
bootstrap_label=bootstrap_label)
|
|
||||||
|
|
||||||
@hunter.route("/tracker/update_tracker_description", methods=['POST'])
|
|
||||||
@login_required
|
|
||||||
@login_analyst
|
|
||||||
def update_tracker_description():
|
|
||||||
user_id = current_user.get_id()
|
|
||||||
term_uuid = request.form.get('uuid')
|
|
||||||
res = Tracker.api_is_allowed_to_edit_tracker(term_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]
|
|
||||||
description = escape( str(request.form.get('description', '')) )
|
|
||||||
Term.replace_tracker_description(term_uuid, description)
|
|
||||||
return redirect(url_for('hunter.show_tracker', uuid=term_uuid))
|
|
||||||
|
|
||||||
@hunter.route("/tracker/update_tracker_tags", methods=['POST'])
|
|
||||||
@login_required
|
|
||||||
@login_analyst
|
|
||||||
def update_tracker_tags():
|
|
||||||
user_id = current_user.get_id()
|
|
||||||
term_uuid = request.form.get('uuid')
|
|
||||||
res = Tracker.api_is_allowed_to_edit_tracker(term_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]
|
|
||||||
tags = request.form.get('tags')
|
|
||||||
if tags:
|
|
||||||
tags = tags.split()
|
|
||||||
else:
|
|
||||||
tags = []
|
|
||||||
Term.replace_tracked_term_tags(term_uuid, tags)
|
|
||||||
return redirect(url_for('hunter.show_tracker', uuid=term_uuid))
|
|
||||||
|
|
||||||
@hunter.route("/tracker/update_tracker_mails", methods=['POST'])
|
|
||||||
@login_required
|
|
||||||
@login_analyst
|
|
||||||
def update_tracker_mails():
|
|
||||||
user_id = current_user.get_id()
|
|
||||||
term_uuid = request.form.get('uuid')
|
|
||||||
res = Tracker.api_is_allowed_to_edit_tracker(term_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]
|
|
||||||
mails = request.form.get('mails')
|
|
||||||
if mails:
|
|
||||||
mails = mails.split()
|
|
||||||
else:
|
|
||||||
mails = []
|
|
||||||
res = Term.replace_tracked_term_mails(term_uuid, mails)
|
|
||||||
if res: # invalid mail
|
|
||||||
return Response(json.dumps(res[0], indent=2, sort_keys=True), mimetype='application/json'), res[1]
|
|
||||||
return redirect(url_for('hunter.show_tracker', uuid=term_uuid))
|
|
||||||
|
|
||||||
@hunter.route("/tracker/get_json_tracker_stats", methods=['GET'])
|
|
||||||
@login_required
|
|
||||||
@login_read_only
|
|
||||||
def get_json_tracker_stats():
|
|
||||||
user_id = current_user.get_id()
|
|
||||||
tracker_uuid = request.args.get('uuid')
|
|
||||||
res = Tracker.api_check_tracker_acl(tracker_uuid, user_id)
|
|
||||||
if res:
|
|
||||||
return create_json_response(res[0], res[1])
|
|
||||||
|
|
||||||
date_from = request.args.get('date_from')
|
|
||||||
date_to = request.args.get('date_to')
|
|
||||||
|
|
||||||
if date_from:
|
|
||||||
date_from = date_from.replace('-', '')
|
|
||||||
if date_to:
|
|
||||||
date_to = date_to.replace('-', '')
|
|
||||||
if date_from and date_to:
|
|
||||||
res = Tracker.get_trackers_graph_by_day([tracker_uuid], date_from=date_from, date_to=date_to)
|
|
||||||
else:
|
|
||||||
res = Tracker.get_trackers_graph_by_day([tracker_uuid])
|
|
||||||
return jsonify(res)
|
|
||||||
|
|
||||||
@hunter.route("/tracker/yara/default_rule/content", methods=['GET'])
|
|
||||||
@login_required
|
|
||||||
@login_read_only
|
|
||||||
def get_default_yara_rule_content():
|
|
||||||
default_yara_rule = request.args.get('rule_name')
|
|
||||||
res = Tracker.api_get_default_rule_content(default_yara_rule)
|
|
||||||
return Response(json.dumps(res[0], indent=2, sort_keys=True), mimetype='application/json'), res[1]
|
|
||||||
|
|
||||||
|
|
||||||
# ========= REGISTRATION =========
|
|
||||||
app.register_blueprint(hunter, url_prefix=baseUrl)
|
|
|
@ -1,440 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
|
|
||||||
<title>AIL Framework - 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/daterangepicker.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 language="javascript" src="{{ url_for('static', filename='js/d3.min.js') }}"></script>
|
|
||||||
<script src="{{ url_for('static', filename='js/d3/sparklines.js') }}"></script>
|
|
||||||
<script src="{{ url_for('static', filename='js/d3/graphlinesgroup.js') }}"></script>
|
|
||||||
<script language="javascript" src="{{ url_for('static', filename='js/moment.min.js') }}"></script>
|
|
||||||
<script language="javascript" src="{{ url_for('static', filename='js/jquery.daterangepicker.min.js') }}"></script>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.btn-link {
|
|
||||||
color: #17a2b8
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-link:hover {
|
|
||||||
color: blue;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mouse_pointer {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
|
|
||||||
{% include 'nav_bar.html' %}
|
|
||||||
|
|
||||||
<div class="container-fluid">
|
|
||||||
<div class="row">
|
|
||||||
|
|
||||||
{% include 'hunter/menu_sidebar.html' %}
|
|
||||||
|
|
||||||
<div class="col-12 col-lg-10" id="core_content">
|
|
||||||
|
|
||||||
<div class="card my-3">
|
|
||||||
<div class="card-header" style="background-color:#d9edf7;font-size: 15px">
|
|
||||||
<h4 class="text-secondary">
|
|
||||||
{% if tracker_metadata['description'] %}
|
|
||||||
{{ tracker_metadata['description'] }}
|
|
||||||
{% endif %}
|
|
||||||
<span class="btn-interaction btn-link h6 mouse_pointer" title="Edit Tracker description"
|
|
||||||
onclick="edit_description();"><i class="fas fa-pencil-alt"></i></span>
|
|
||||||
</h4>
|
|
||||||
<div class="text-info">
|
|
||||||
{{ tracker_metadata['uuid'] }}
|
|
||||||
</div>
|
|
||||||
<ul class="list-group mb-2">
|
|
||||||
<li class="list-group-item py-0">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-10">
|
|
||||||
<table class="table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Type</th>
|
|
||||||
<th>Tracker</th>
|
|
||||||
<th>Created</th>
|
|
||||||
<th>Access Level</th>
|
|
||||||
<th>Created by</th>
|
|
||||||
<th>First seen</th>
|
|
||||||
<th>Last seen</th>
|
|
||||||
{% if tracker_metadata['webhook'] %}
|
|
||||||
<th>Webhook</th>
|
|
||||||
{% endif %}
|
|
||||||
<th>Tags <span class="btn-link btn-interaction mouse_pointer"
|
|
||||||
title="Edit Tags List" onclick="edit_tags();"><i
|
|
||||||
class="fas fa-pencil-alt" style="color:Red;"></i></span></th>
|
|
||||||
<th>Email <span class="btn-link btn-interaction mouse_pointer"
|
|
||||||
title="Edit Email List" onclick="edit_mails();"><i
|
|
||||||
class="fas fa-pencil-alt" style="color:Red;"></i></span></th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td>{{ tracker_metadata['type'] }}</td>
|
|
||||||
{% if tracker_metadata['type'] == 'typosquatting' %}
|
|
||||||
<td>
|
|
||||||
<a class="btn btn-primary" data-toggle="collapse" href="#collapseTypo" role="button" aria-expanded="false" aria-controls="collapseTypo">
|
|
||||||
{{ tracker_metadata['tracked'].split(",")[0] }}
|
|
||||||
</a>
|
|
||||||
<div class="collapse" id="collapseTypo">
|
|
||||||
<div class="card card-body">
|
|
||||||
{% if typo_squatting %}
|
|
||||||
{% for typo in typo_squatting %}
|
|
||||||
{{typo}}
|
|
||||||
<br/>
|
|
||||||
{% endfor %}
|
|
||||||
{%endif%}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
{% else %}
|
|
||||||
<td>{{ tracker_metadata['tracked'] }}</td>
|
|
||||||
{% endif %}
|
|
||||||
<td>{{ tracker_metadata['date'][0:4] }}/{{ tracker_metadata['date'][4:6] }}/{{ tracker_metadata['date'][6:8] }}</td>
|
|
||||||
<td>
|
|
||||||
{% if tracker_metadata['level'] == 0 %}
|
|
||||||
Private
|
|
||||||
{% else %}
|
|
||||||
Global
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
|
||||||
<td>{{ tracker_metadata['user'] }}</td>
|
|
||||||
<td>
|
|
||||||
{% if tracker_metadata['first_seen'] %}
|
|
||||||
{{ tracker_metadata['first_seen'][0:4] }}/{{ tracker_metadata['first_seen'][4:6] }}/{{ tracker_metadata['first_seen'][6:8] }}
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{% if tracker_metadata['last_seen'] %}
|
|
||||||
{{ tracker_metadata['last_seen'][0:4] }}/{{ tracker_metadata['last_seen'][4:6] }}/{{ tracker_metadata['last_seen'][6:8] }}
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
|
||||||
{% if tracker_metadata['webhook'] %}
|
|
||||||
<td>
|
|
||||||
Turned ON
|
|
||||||
</td>
|
|
||||||
{% endif %}
|
|
||||||
<td>
|
|
||||||
{% for tag in tracker_metadata['tags'] %}
|
|
||||||
<a href="{{ url_for('tags_ui.get_obj_by_tags') }}?object_type=item<ags={{ tag }}">
|
|
||||||
<span class="badge badge-{{ bootstrap_label[loop.index0 % 5] }}">{{ tag }}</span>
|
|
||||||
</a>
|
|
||||||
{% endfor %}
|
|
||||||
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{% for mail in tracker_metadata['mails'] %}
|
|
||||||
{{ mail }}<br>
|
|
||||||
{% endfor %}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-1">
|
|
||||||
<div id="sparkline"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h6>Filters:</h6>
|
|
||||||
{% if tracker_metadata['filters'] %}
|
|
||||||
<pre>{{ tracker_metadata['filters'] }}</pre>
|
|
||||||
{% else %}
|
|
||||||
<span class="badge badge-secondary">No Filters</span><br>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{# <h6>Sources:</h6>#}
|
|
||||||
{# {% if tracker_metadata['sources'] %}#}
|
|
||||||
{# {% for sources in tracker_metadata['sources'] %}#}
|
|
||||||
{# <span class="badge badge-secondary">{{ sources }}</span><br>#}
|
|
||||||
{# {% endfor %}#}
|
|
||||||
{# {% else %}#}
|
|
||||||
{# <span class="badge badge-secondary">All Sources</span><br>#}
|
|
||||||
{# {% endif %}#}
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<div id="div_edit_description">
|
|
||||||
<form action="{{ url_for('hunter.update_tracker_description') }}" method='post'>
|
|
||||||
<input name="uuid" type="text" value="{{ tracker_metadata['uuid'] }}" hidden>
|
|
||||||
<div>Update this tracker description:</div>
|
|
||||||
<div class="input-group mb-2 mr-sm-2">
|
|
||||||
<div class="input-group-prepend">
|
|
||||||
<div class="input-group-text"><i class="fas fa-pencil-alt"></i></div>
|
|
||||||
</div>
|
|
||||||
<input id="description" name="description" class="form-control"
|
|
||||||
placeholder="Tracker Description" type="text"
|
|
||||||
value="
|
|
||||||
{% if tracker_metadata['description'] %}{{ tracker_metadata['description'] }}{% endif %}">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button class="btn btn-info">
|
|
||||||
<i class="fas fa-pencil-alt"></i> Edit Description
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="div_edit_tags">
|
|
||||||
<form action="{{ url_for('hunter.update_tracker_tags') }}" method='post'>
|
|
||||||
<input name="uuid" type="text" value="{{ tracker_metadata['uuid'] }}" hidden>
|
|
||||||
<div>All Tags added for this tracker, space separated:</div>
|
|
||||||
<div class="input-group mb-2 mr-sm-2">
|
|
||||||
<div class="input-group-prepend">
|
|
||||||
<div class="input-group-text"><i class="fas fa-tag"></i></div>
|
|
||||||
</div>
|
|
||||||
<input id="tags" name="tags" class="form-control"
|
|
||||||
placeholder="Tags (optional, space separated)" type="text"
|
|
||||||
value="{% for tag in tracker_metadata['tags'] %}{{ tag }} {% endfor %}">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button class="btn btn-info">
|
|
||||||
<i class="fas fa-pencil-alt"></i> Edit Tags
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="div_edit_mails">
|
|
||||||
<form action="{{ url_for('hunter.update_tracker_mails') }}" method='post'>
|
|
||||||
<input name="uuid" type="text" value="{{ tracker_metadata['uuid'] }}" hidden>
|
|
||||||
<div>All E-Mails to Notify for this tracker, space separated:</div>
|
|
||||||
<div class="input-group mb-2 mr-sm-2">
|
|
||||||
<div class="input-group-prepend">
|
|
||||||
<div class="input-group-text"><i class="fas fa-at"></i></div>
|
|
||||||
</div>
|
|
||||||
<input id="mails" name="mails" class="form-control"
|
|
||||||
placeholder="E-Mails Notification (optional, space separated)" type="text"
|
|
||||||
value="{% for mail in tracker_metadata['mails'] %}{{ mail }} {% endfor %}">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button class="btn btn-info">
|
|
||||||
<i class="fas fa-pencil-alt"></i> Edit Email Notification
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="d-flex flex-row-reverse">
|
|
||||||
<a href="{{ url_for('hunters.tracker_delete') }}?uuid={{ tracker_metadata['uuid'] }}"
|
|
||||||
style="font-size: 15px">
|
|
||||||
<button class='btn btn-danger'><i class="fas fa-trash-alt"></i></button>
|
|
||||||
</a>
|
|
||||||
<a href="{{ url_for('hunter.edit_tracked_menu') }}?uuid={{ tracker_metadata['uuid'] }}"
|
|
||||||
class="mx-2" style="font-size: 15px">
|
|
||||||
<button class='btn btn-info'>Edit Tracker <i class="fas fa-pencil-alt"></i></button>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{% if yara_rule_content %}
|
|
||||||
<p class="my-0"></br></br>
|
|
||||||
<pre class="border bg-light">{{ yara_rule_content }}</pre></p>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="graphline" class="text-center"></div>
|
|
||||||
|
|
||||||
<div class="card mb-5 mt-1">
|
|
||||||
<div class="card-body">
|
|
||||||
|
|
||||||
<div class="row mb-3">
|
|
||||||
<div class="col-md-6">
|
|
||||||
<div class="input-group" id="date-range-from">
|
|
||||||
<div class="input-group-prepend"><span class="input-group-text"><i
|
|
||||||
class="far fa-calendar-alt" aria-hidden="true"></i></span></div>
|
|
||||||
<input class="form-control" id="date-range-from-input" placeholder="yyyy-mm-dd"
|
|
||||||
name="date_from" autocomplete="off"
|
|
||||||
{% if tracker_metadata['date_from'] %}value="{{ tracker_metadata['date_from'][0:4] }}-{{ tracker_metadata['date_from'][4:6] }}-{{ tracker_metadata['date_from'][6:8] }}"
|
|
||||||
{% elif tracker_metadata['first_seen'] %}value="{{ tracker_metadata['first_seen'][0:4] }}-{{ tracker_metadata['first_seen'][4:6] }}-{{ tracker_metadata['first_seen'][6:8] }}"
|
|
||||||
{% endif %}>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-6">
|
|
||||||
<div class="input-group" id="date-range-to">
|
|
||||||
<div class="input-group-prepend"><span class="input-group-text"><i
|
|
||||||
class="far fa-calendar-alt" aria-hidden="true"></i></span></div>
|
|
||||||
<input class="form-control" id="date-range-to-input" placeholder="yyyy-mm-dd"
|
|
||||||
name="date_to" autocomplete="off"
|
|
||||||
{% if tracker_metadata['date_to'] %}value="{{ tracker_metadata['date_to'][0:4] }}-{{ tracker_metadata['date_to'][4:6] }}-{{ tracker_metadata['date_to'][6:8] }}"
|
|
||||||
{% elif tracker_metadata['last_seen'] %}value="{{ tracker_metadata['last_seen'][0:4] }}-{{ tracker_metadata['last_seen'][4:6] }}-{{ tracker_metadata['last_seen'][6:8] }}"
|
|
||||||
{% endif %}>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button class="btn btn-info" type="button" id="button-search-tags" onclick="getItems();">
|
|
||||||
<i class="fas fa-search"></i> Search Tracked Items
|
|
||||||
</button>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{% if tracker_metadata['objs'] %}
|
|
||||||
<div class="mt-4">
|
|
||||||
<table id="myTable_" class="table table-striped border-primary">
|
|
||||||
<thead class="bg-dark text-white">
|
|
||||||
<tr>
|
|
||||||
<th>Type</th>
|
|
||||||
<th></th>
|
|
||||||
<th>Id</th>
|
|
||||||
<th>Tags</th>
|
|
||||||
<th></th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody style="font-size: 15px;">
|
|
||||||
{% for object in tracker_metadata['objs'] %}
|
|
||||||
<tr class="border-color: blue;">
|
|
||||||
<td>
|
|
||||||
{% with style=object['icon']['style'], icon=object['icon']['icon'] , color=object['icon']['color'] %}
|
|
||||||
{% include 'objects/obj_svg_block.html' %}
|
|
||||||
{% endwith %}
|
|
||||||
{{ object['type']}}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{% if object['subtype'] %}
|
|
||||||
{{ object['subtype']}}
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<a href="{{ object['link'] }}">
|
|
||||||
{{ object['id']}}
|
|
||||||
</a>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{% for tag in object['tags'] %}
|
|
||||||
<span class="badge badge-{{ bootstrap_label[loop.index0 % 5] }} pull-left">{{ tag }}</span>
|
|
||||||
{% endfor %}
|
|
||||||
</td>
|
|
||||||
<td class="text-right">
|
|
||||||
{# <a href="{{ url_for('investigations_b.unregister_investigation') }}?uuid={{ tracker_metadata['uuid']}}&type={{ object['type'] }}&subtype={{ object['subtype']}}&id={{ object['id']}}">#}
|
|
||||||
{# <button type="button" class="btn btn-danger"><i class="fas fa-trash-alt"></i></button>#}
|
|
||||||
{# </a>#}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
$(document).ready(function () {
|
|
||||||
$('#div_edit_mails').hide();
|
|
||||||
$('#div_edit_tags').hide();
|
|
||||||
$('#div_edit_description').hide();
|
|
||||||
$("#page-Tracker").addClass("active");
|
|
||||||
|
|
||||||
$('#date-range-from').dateRangePicker({
|
|
||||||
separator: ' to ',
|
|
||||||
getValue: function () {
|
|
||||||
if ($('#date-range-from-input').val() && $('#date-range-to-input').val())
|
|
||||||
return $('#date-range-from-input').val() + ' to ' + $('#date-range-to-input').val();
|
|
||||||
else
|
|
||||||
return '';
|
|
||||||
},
|
|
||||||
setValue: function (s, s1, s2) {
|
|
||||||
$('#date-range-from-input').val(s1);
|
|
||||||
$('#date-range-to-input').val(s2);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
$('#date-range-to').dateRangePicker({
|
|
||||||
separator: ' to ',
|
|
||||||
getValue: function () {
|
|
||||||
if ($('#date-range-from-input').val() && $('#date-range-to-input').val())
|
|
||||||
return $('#date-range-from-input').val() + ' to ' + $('#date-range-to-input').val();
|
|
||||||
else
|
|
||||||
return '';
|
|
||||||
},
|
|
||||||
setValue: function (s, s1, s2) {
|
|
||||||
$('#date-range-from-input').val(s1);
|
|
||||||
$('#date-range-to-input').val(s2);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$('#myTable_').DataTable({
|
|
||||||
"aLengthMenu": [[5, 10, 15, -1], [5, 10, 15, "All"]],
|
|
||||||
"iDisplayLength": 10,
|
|
||||||
"order": [[0, "asc"]]
|
|
||||||
});
|
|
||||||
|
|
||||||
sparkline("sparkline", {{ tracker_metadata['sparkline'] }}, {});
|
|
||||||
let div_width = $("#graphline").width();
|
|
||||||
$.getJSON("{{ url_for('hunter.get_json_tracker_stats') }}?uuid={{ tracker_metadata['uuid'] }}{%if tracker_metadata['date_from']%}&date_from={{ tracker_metadata['date_from'] }}{%endif%}{%if tracker_metadata['date_to']%}&date_to={{ tracker_metadata['date_to'] }}{%endif%}",
|
|
||||||
function (data) {
|
|
||||||
multilines_group("graphline", data, {"width": div_width});
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
function toggle_sidebar() {
|
|
||||||
if ($('#nav_menu').is(':visible')) {
|
|
||||||
$('#nav_menu').hide();
|
|
||||||
$('#side_menu').removeClass('border-right')
|
|
||||||
$('#side_menu').removeClass('col-lg-2')
|
|
||||||
$('#core_content').removeClass('col-lg-10')
|
|
||||||
} else {
|
|
||||||
$('#nav_menu').show();
|
|
||||||
$('#side_menu').addClass('border-right')
|
|
||||||
$('#side_menu').addClass('col-lg-2')
|
|
||||||
$('#core_content').addClass('col-lg-10')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function edit_tags() {
|
|
||||||
$('#div_edit_mails').hide();
|
|
||||||
$('#div_edit_description').hide();
|
|
||||||
$('#div_edit_tags').show();
|
|
||||||
}
|
|
||||||
|
|
||||||
function edit_mails() {
|
|
||||||
$('#div_edit_tags').hide();
|
|
||||||
$('#div_edit_description').hide();
|
|
||||||
$('#div_edit_mails').show();
|
|
||||||
}
|
|
||||||
|
|
||||||
function edit_description() {
|
|
||||||
$('#div_edit_tags').hide();
|
|
||||||
$('#div_edit_mails').hide();
|
|
||||||
$('#div_edit_description').show();
|
|
||||||
}
|
|
||||||
|
|
||||||
function getItems() {
|
|
||||||
var date_from = $('#date-range-from-input').val();
|
|
||||||
var date_to = $('#date-range-to-input').val();
|
|
||||||
window.location.replace("{{ url_for('hunter.show_tracker') }}?uuid={{ tracker_metadata['uuid'] }}&date_from=" + date_from + "&date_to=" + date_to);
|
|
||||||
}
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|
||||||
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
|
@ -307,7 +307,7 @@ function get_default_rule_content(selector){
|
||||||
if (yara_name === "Select a default rule") {
|
if (yara_name === "Select a default rule") {
|
||||||
jQuery("#default_yara_rule_content").text("")
|
jQuery("#default_yara_rule_content").text("")
|
||||||
} else {
|
} else {
|
||||||
$.getJSON("{{ url_for('hunter.get_default_yara_rule_content') }}?rule_name=" + yara_name,
|
$.getJSON("{{ url_for('hunters.get_default_yara_rule_content') }}?rule=" + yara_name,
|
||||||
function(data) {
|
function(data) {
|
||||||
jQuery("#default_yara_rule_content").text(data['content'])
|
jQuery("#default_yara_rule_content").text(data['content'])
|
||||||
});
|
});
|
||||||
|
|
|
@ -35,7 +35,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
|
|
||||||
<form action="{{ url_for('hunters.add_tracked_menu') }}" method='post'>
|
<form action="{%if dict_tracker%}{{ url_for('hunters.tracker_edit') }}{%else%}{{ url_for('hunters.add_tracked_menu') }}{%endif%}" method='post'>
|
||||||
{%if dict_tracker%}
|
{%if dict_tracker%}
|
||||||
<input id="tracker_uuid" name="tracker_uuid" class="form-control" type="text" value="{{dict_tracker['uuid']}}" hidden>
|
<input id="tracker_uuid" name="tracker_uuid" class="form-control" type="text" value="{{dict_tracker['uuid']}}" hidden>
|
||||||
{%endif%}
|
{%endif%}
|
||||||
|
@ -46,7 +46,7 @@
|
||||||
<div class="input-group-prepend">
|
<div class="input-group-prepend">
|
||||||
<div class="input-group-text bg-secondary text-white"><i class="fas fa-at"></i></div>
|
<div class="input-group-text bg-secondary text-white"><i class="fas fa-at"></i></div>
|
||||||
</div>
|
</div>
|
||||||
<input id="mails" name="mails" class="form-control" placeholder="E-Mails Notification (optional, space separated)" type="text" {%if dict_tracker%}{%if dict_tracker['mails']%}value="{{dict_tracker['mails']}}"{%endif%}{%endif%}>
|
<input id="mails" name="mails" class="form-control" placeholder="E-Mails Notification (optional, space separated)" type="text" {%if dict_tracker%}{%if dict_tracker['mails']%}value="{%for mail in dict_tracker['mails'] %}{{mail}} {%endfor%}"{%endif%}{%endif%}>
|
||||||
</div>
|
</div>
|
||||||
<div class="input-group mb-2 mr-sm-2">
|
<div class="input-group mb-2 mr-sm-2">
|
||||||
<div class="input-group-prepend">
|
<div class="input-group-prepend">
|
||||||
|
@ -191,7 +191,7 @@
|
||||||
<div class="input-group-prepend">
|
<div class="input-group-prepend">
|
||||||
<div class="input-group-text bg-danger text-white"><i class="fas fa-tag"></i></div>
|
<div class="input-group-text bg-danger text-white"><i class="fas fa-tag"></i></div>
|
||||||
</div>
|
</div>
|
||||||
<input id="tags" name="tags" class="form-control" placeholder="Custom Tags (optional, space separated)" type="text" {%if dict_tracker%}{%if dict_tracker['tags']%}value="{{dict_tracker['tags']}}"{%endif%}{%endif%}>
|
<input id="tags" name="tags" class="form-control" placeholder="Custom Tags (optional, space separated)" type="text" {%if dict_tracker%}{%if dict_tracker['tags']%}value="{%for tag in dict_tracker['tags']%}{{tag}} {%endfor%}"{%endif%}{%endif%}>
|
||||||
</div>
|
</div>
|
||||||
{% include 'tags/block_tags_selector.html' %}
|
{% include 'tags/block_tags_selector.html' %}
|
||||||
</div>
|
</div>
|
||||||
|
@ -224,7 +224,7 @@
|
||||||
|
|
||||||
<div class="row" id="simple_input">
|
<div class="row" id="simple_input">
|
||||||
<div class="col-12 col-lg-10">
|
<div class="col-12 col-lg-10">
|
||||||
<input id="tracker" name="tracker" class="form-control" placeholder="Terms to track (space separated)" type="text" {%if dict_tracker%}{%if dict_tracker['tracker']!='yara'%}value="{{dict_tracker['tracker']}}"{%endif%}{%endif%}>
|
<input id="tracker" name="tracker" class="form-control" placeholder="Terms to track (space separated)" type="text" {%if dict_tracker%}{%if dict_tracker['type'] != 'yara' and dict_tracker['type'] != 'typosquatting'%}value="{{dict_tracker['tracked']}}"{%endif%}{%endif%}>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 col-lg-2">
|
<div class="col-12 col-lg-2">
|
||||||
<input type="number" id="nb_word" name="nb_word" name="quantity" min="1" placeholder="Nb of keywords" {%if dict_tracker%}{%if dict_tracker['nb_words']%}value="{{dict_tracker['nb_words']}}"{%endif%}{%endif%}>
|
<input type="number" id="nb_word" name="nb_word" name="quantity" min="1" placeholder="Nb of keywords" {%if dict_tracker%}{%if dict_tracker['nb_words']%}value="{{dict_tracker['nb_words']}}"{%endif%}{%endif%}>
|
||||||
|
@ -291,7 +291,7 @@ $(document).ready(function(){
|
||||||
|
|
||||||
sources_item = $('#sources_item').tagSuggest({
|
sources_item = $('#sources_item').tagSuggest({
|
||||||
data: {{all_sources|safe}},
|
data: {{all_sources|safe}},
|
||||||
{%if dict_tracker%}{%if dict_tracker['item_sources']%}value: {{dict_tracker['item_sources']|safe}},{%endif%}{%endif%}
|
{%if dict_tracker%}{%if dict_tracker['filters']%}{%if dict_tracker['filters']['item']%}{%if dict_tracker['filters']['item']['sources']%}value: {{dict_tracker['filters']['item']['sources']|safe}},{%endif%}{%endif%}{%endif%}{%endif%}
|
||||||
sortOrder: 'name',
|
sortOrder: 'name',
|
||||||
maxDropHeight: 200,
|
maxDropHeight: 200,
|
||||||
name: 'sources_item',
|
name: 'sources_item',
|
||||||
|
@ -335,9 +335,10 @@ $(document).ready(function(){
|
||||||
|
|
||||||
{%if dict_tracker%}
|
{%if dict_tracker%}
|
||||||
$('#tracker_type').val('{{dict_tracker['type']}}').change();
|
$('#tracker_type').val('{{dict_tracker['type']}}').change();
|
||||||
|
{%if dict_tracker['type']=='yara'%}
|
||||||
{%if dict_tracker['type']=='yara' and dict_tracker['yara_file']%}
|
{% if dict_tracker['tracked'][0:21] == 'ail-yara-rules/rules/' %}
|
||||||
$('#yara_default_rule').val('{{dict_tracker['yara_file']}}').change();
|
$('#yara_default_rule').val('{{dict_tracker['tracked'][21:]}}').change();
|
||||||
|
{%endif%}
|
||||||
{%endif%}
|
{%endif%}
|
||||||
{%endif%}
|
{%endif%}
|
||||||
|
|
||||||
|
@ -386,7 +387,7 @@ function get_default_rule_content(selector){
|
||||||
if (yara_name === "Select a default rule") {
|
if (yara_name === "Select a default rule") {
|
||||||
jQuery("#default_yara_rule_content").text("")
|
jQuery("#default_yara_rule_content").text("")
|
||||||
} else {
|
} else {
|
||||||
$.getJSON("{{ url_for('hunter.get_default_yara_rule_content') }}?rule_name=" + yara_name,
|
$.getJSON("{{ url_for('hunters.get_default_yara_rule_content') }}?rule=" + yara_name,
|
||||||
function(data) {
|
function(data) {
|
||||||
jQuery("#default_yara_rule_content").text(data['content'])
|
jQuery("#default_yara_rule_content").text(data['content'])
|
||||||
});
|
});
|
||||||
|
|
401
var/www/templates/hunter/tracker_show.html
Normal file
401
var/www/templates/hunter/tracker_show.html
Normal file
|
@ -0,0 +1,401 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
|
||||||
|
<title>AIL Framework - 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/daterangepicker.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 language="javascript" src="{{ url_for('static', filename='js/d3.min.js') }}"></script>
|
||||||
|
<script src="{{ url_for('static', filename='js/d3/sparklines.js') }}"></script>
|
||||||
|
<script src="{{ url_for('static', filename='js/d3/graphlinesgroup.js') }}"></script>
|
||||||
|
<script language="javascript" src="{{ url_for('static', filename='js/moment.min.js') }}"></script>
|
||||||
|
<script language="javascript" src="{{ url_for('static', filename='js/jquery.daterangepicker.min.js') }}"></script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.btn-link {
|
||||||
|
color: #17a2b8
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-link:hover {
|
||||||
|
color: blue;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mouse_pointer {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
{% include 'nav_bar.html' %}
|
||||||
|
|
||||||
|
<div class="container-fluid">
|
||||||
|
<div class="row">
|
||||||
|
{% include 'hunter/menu_sidebar.html' %}
|
||||||
|
<div class="col-12 col-lg-10" id="core_content">
|
||||||
|
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-4">
|
||||||
|
<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'] }}
|
||||||
|
{% else %}
|
||||||
|
{{ meta['uuid'] }}
|
||||||
|
{% 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>Tracked</b></td>
|
||||||
|
<td>
|
||||||
|
{% if meta['type'] == 'typosquatting' %}
|
||||||
|
<a class="btn btn-primary" data-toggle="collapse" href="#collapseTypo" role="button" aria-expanded="false" aria-controls="collapseTypo">
|
||||||
|
{{ meta['tracked'] }}
|
||||||
|
</a>
|
||||||
|
<div class="collapse" id="collapseTypo">
|
||||||
|
<div class="card card-body">
|
||||||
|
{% if typo_squatting %}
|
||||||
|
{% for typo in typo_squatting %}
|
||||||
|
{{typo}}
|
||||||
|
<br/>
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
{{ meta['tracked'] }}
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="text-right"><b>Date</b></td>
|
||||||
|
<td>
|
||||||
|
{{meta['date'][0:4]}}/{{meta['date'][4:6]}}/{{meta['date'][6:8]}}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="text-right"><b>Level</b></td>
|
||||||
|
<td>
|
||||||
|
{% if meta['level'] == 0 %}
|
||||||
|
Private
|
||||||
|
{% else %}
|
||||||
|
Global
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="text-right"><b>Creator</b></td>
|
||||||
|
<td>{{meta['user']}}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="text-right"><b>First Seen</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</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>Mails</b></td>
|
||||||
|
<td>
|
||||||
|
{% for mail in meta['mails'] %}
|
||||||
|
<div>{{ mail }}</div>
|
||||||
|
{% endfor %}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="text-right"><b>Webhook</b></td>
|
||||||
|
<td>{{meta['webhook']}}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="text-right"><b>Filters</b></td>
|
||||||
|
<td>
|
||||||
|
<div class="">
|
||||||
|
{% if meta['filters'] %}
|
||||||
|
<pre>{{ meta['filters'] }}</pre>
|
||||||
|
{% else %}
|
||||||
|
<span class="badge badge-secondary">No Filters</span><br>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</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>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<div class="d-flex flex-row-reverse">
|
||||||
|
<a href="{{ url_for('hunters.tracker_delete') }}?uuid={{ meta['uuid'] }}" style="font-size: 15px">
|
||||||
|
<button class='btn btn-danger'><i class="fas fa-trash-alt"></i></button>
|
||||||
|
</a>
|
||||||
|
<a href="{{ url_for('hunters.tracker_edit') }}?uuid={{ meta['uuid'] }}" class="mx-2" style="font-size: 15px">
|
||||||
|
<button class='btn btn-info'>Edit Tracker <i class="fas fa-pencil-alt"></i></button>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-lg-8 mt-1">
|
||||||
|
|
||||||
|
{% if rule_content %}
|
||||||
|
<h5 class="mb-0">Yara Rule:</h5>
|
||||||
|
<p class="my-0">
|
||||||
|
<pre class="border bg-light">{{ rule_content }}</pre>
|
||||||
|
</p>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<div class="card mb-5 mt-1">
|
||||||
|
<div class="card-body">
|
||||||
|
|
||||||
|
<div class="row mb-3">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="input-group" id="date-range-from">
|
||||||
|
<div class="input-group-prepend"><span class="input-group-text"><i
|
||||||
|
class="far fa-calendar-alt" aria-hidden="true"></i></span></div>
|
||||||
|
<input class="form-control" id="date-range-from-input" placeholder="yyyy-mm-dd"
|
||||||
|
name="date_from" autocomplete="off"
|
||||||
|
{% if meta['date_from'] %}value="{{ meta['date_from'][0:4] }}-{{ meta['date_from'][4:6] }}-{{ meta['date_from'][6:8] }}"
|
||||||
|
{% elif meta['first_seen'] %}value="{{ meta['first_seen'][0:4] }}-{{ meta['first_seen'][4:6] }}-{{ meta['first_seen'][6:8] }}"
|
||||||
|
{% endif %}>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="input-group" id="date-range-to">
|
||||||
|
<div class="input-group-prepend"><span class="input-group-text"><i
|
||||||
|
class="far fa-calendar-alt" aria-hidden="true"></i></span></div>
|
||||||
|
<input class="form-control" id="date-range-to-input" placeholder="yyyy-mm-dd"
|
||||||
|
name="date_to" autocomplete="off"
|
||||||
|
{% if meta['date_to'] %}value="{{ meta['date_to'][0:4] }}-{{ meta['date_to'][4:6] }}-{{ meta['date_to'][6:8] }}"
|
||||||
|
{% elif meta['last_seen'] %}value="{{ meta['last_seen'][0:4] }}-{{ meta['last_seen'][4:6] }}-{{ meta['last_seen'][6:8] }}"
|
||||||
|
{% endif %}>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button class="btn btn-info" type="button" id="button-search-tags" onclick="getItems();">
|
||||||
|
<i class="fas fa-search"></i> Tracked Objects
|
||||||
|
</button>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% if meta['objs'] %}
|
||||||
|
<hr>
|
||||||
|
<div class="mt-4">
|
||||||
|
<table id="myTable_" class="table table-striped border-primary">
|
||||||
|
<thead class="bg-dark text-white">
|
||||||
|
<tr>
|
||||||
|
<th>Type</th>
|
||||||
|
<th></th>
|
||||||
|
<th>Id</th>
|
||||||
|
<th>Tags</th>
|
||||||
|
<th></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody style="font-size: 15px;">
|
||||||
|
{% for object in meta['objs'] %}
|
||||||
|
<tr class="border-color: blue;">
|
||||||
|
<td>
|
||||||
|
{% with style=object['icon']['style'], icon=object['icon']['icon'] , color=object['icon']['color'] %}
|
||||||
|
{% include 'objects/obj_svg_block.html' %}
|
||||||
|
{% endwith %}
|
||||||
|
{{ object['type']}}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{% if object['subtype'] %}
|
||||||
|
{{ object['subtype']}}
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<a href="{{ object['link'] }}">
|
||||||
|
{{ object['id']}}
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{% for tag in object['tags'] %}
|
||||||
|
<span class="badge badge-{{ bootstrap_label[loop.index0 % 5] }} pull-left">{{ tag }}</span>
|
||||||
|
{% endfor %}
|
||||||
|
</td>
|
||||||
|
<td class="text-right">
|
||||||
|
{# <a href="{{ url_for('investigations_b.unregister_investigation') }}?uuid={{ meta['uuid']}}&type={{ object['type'] }}&subtype={{ object['subtype']}}&id={{ object['id']}}">#}
|
||||||
|
{# <button type="button" class="btn btn-danger"><i class="fas fa-trash-alt"></i></button>#}
|
||||||
|
{# </a>#}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<div id="graphline" class="text-center mb-4"></div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
$(document).ready(function () {
|
||||||
|
$('#div_edit_mails').hide();
|
||||||
|
$('#div_edit_tags').hide();
|
||||||
|
$('#div_edit_description').hide();
|
||||||
|
$("#page-Tracker").addClass("active");
|
||||||
|
|
||||||
|
$('#date-range-from').dateRangePicker({
|
||||||
|
separator: ' to ',
|
||||||
|
getValue: function () {
|
||||||
|
if ($('#date-range-from-input').val() && $('#date-range-to-input').val())
|
||||||
|
return $('#date-range-from-input').val() + ' to ' + $('#date-range-to-input').val();
|
||||||
|
else
|
||||||
|
return '';
|
||||||
|
},
|
||||||
|
setValue: function (s, s1, s2) {
|
||||||
|
$('#date-range-from-input').val(s1);
|
||||||
|
$('#date-range-to-input').val(s2);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
$('#date-range-to').dateRangePicker({
|
||||||
|
separator: ' to ',
|
||||||
|
getValue: function () {
|
||||||
|
if ($('#date-range-from-input').val() && $('#date-range-to-input').val())
|
||||||
|
return $('#date-range-from-input').val() + ' to ' + $('#date-range-to-input').val();
|
||||||
|
else
|
||||||
|
return '';
|
||||||
|
},
|
||||||
|
setValue: function (s, s1, s2) {
|
||||||
|
$('#date-range-from-input').val(s1);
|
||||||
|
$('#date-range-to-input').val(s2);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#myTable_').DataTable({
|
||||||
|
"aLengthMenu": [[5, 10, 15, -1], [5, 10, 15, "All"]],
|
||||||
|
"iDisplayLength": 10,
|
||||||
|
"order": [[0, "asc"]]
|
||||||
|
});
|
||||||
|
|
||||||
|
sparkline("sparkline", {{ meta['sparkline'] }}, {});
|
||||||
|
let div_width = $("#graphline").width();
|
||||||
|
$.getJSON("{{ url_for('hunters.get_json_tracker_graph') }}?uuid={{ meta['uuid'] }}{%if meta['date_from']%}&date_from={{ meta['date_from'] }}{%endif%}{%if meta['date_to']%}&date_to={{ meta['date_to'] }}{%endif%}",
|
||||||
|
function (data) {
|
||||||
|
multilines_group("graphline", data, {"width": div_width});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
function toggle_sidebar() {
|
||||||
|
if ($('#nav_menu').is(':visible')) {
|
||||||
|
$('#nav_menu').hide();
|
||||||
|
$('#side_menu').removeClass('border-right')
|
||||||
|
$('#side_menu').removeClass('col-lg-2')
|
||||||
|
$('#core_content').removeClass('col-lg-10')
|
||||||
|
} else {
|
||||||
|
$('#nav_menu').show();
|
||||||
|
$('#side_menu').addClass('border-right')
|
||||||
|
$('#side_menu').addClass('col-lg-2')
|
||||||
|
$('#core_content').addClass('col-lg-10')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function edit_tags() {
|
||||||
|
$('#div_edit_mails').hide();
|
||||||
|
$('#div_edit_description').hide();
|
||||||
|
$('#div_edit_tags').show();
|
||||||
|
}
|
||||||
|
|
||||||
|
function edit_mails() {
|
||||||
|
$('#div_edit_tags').hide();
|
||||||
|
$('#div_edit_description').hide();
|
||||||
|
$('#div_edit_mails').show();
|
||||||
|
}
|
||||||
|
|
||||||
|
function edit_description() {
|
||||||
|
$('#div_edit_tags').hide();
|
||||||
|
$('#div_edit_mails').hide();
|
||||||
|
$('#div_edit_description').show();
|
||||||
|
}
|
||||||
|
|
||||||
|
function getItems() {
|
||||||
|
var date_from = $('#date-range-from-input').val();
|
||||||
|
var date_to = $('#date-range-to-input').val();
|
||||||
|
window.location.replace("{{ url_for('hunters.show_tracker') }}?uuid={{ meta['uuid'] }}&date_from=" + date_from + "&date_to=" + date_to);
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
|
@ -70,7 +70,7 @@
|
||||||
<td>{{ dict_uuid['type'] }}</td>
|
<td>{{ dict_uuid['type'] }}</td>
|
||||||
<td>
|
<td>
|
||||||
<span>
|
<span>
|
||||||
<a target="_blank" href="{{ url_for('hunter.show_tracker') }}?uuid={{ dict_uuid['uuid'] }}">
|
<a target="_blank" href="{{ url_for('hunters.show_tracker') }}?uuid={{ dict_uuid['uuid'] }}">
|
||||||
{% if dict_uuid['tracked'] %}
|
{% if dict_uuid['tracked'] %}
|
||||||
{% if dict_uuid['tracked']|length > 256 %}
|
{% if dict_uuid['tracked']|length > 256 %}
|
||||||
{{ dict_uuid['tracked'][0:256] }}...
|
{{ dict_uuid['tracked'][0:256] }}...
|
||||||
|
@ -135,7 +135,7 @@
|
||||||
<td>{{ dict_uuid['type'] }}</td>
|
<td>{{ dict_uuid['type'] }}</td>
|
||||||
<td>
|
<td>
|
||||||
<span>
|
<span>
|
||||||
<a target="_blank" href="{{ url_for('hunter.show_tracker') }}?uuid={{ dict_uuid['uuid'] }}">
|
<a target="_blank" href="{{ url_for('hunters.show_tracker') }}?uuid={{ dict_uuid['uuid'] }}">
|
||||||
{% if dict_uuid['tracked'] %}
|
{% if dict_uuid['tracked'] %}
|
||||||
{% if dict_uuid['tracked']|length > 256 %}
|
{% if dict_uuid['tracked']|length > 256 %}
|
||||||
{{ dict_uuid['tracked'][0:256] }}...
|
{{ dict_uuid['tracked'][0:256] }}...
|
||||||
|
|
|
@ -100,7 +100,7 @@
|
||||||
<td>{{ meta['type'] }}</td>
|
<td>{{ meta['type'] }}</td>
|
||||||
<td>
|
<td>
|
||||||
<span>
|
<span>
|
||||||
<a target="_blank" href="{{ url_for('hunter.show_tracker') }}?uuid={{ meta['uuid'] }}">
|
<a target="_blank" href="{{ url_for('hunters.show_tracker') }}?uuid={{ meta['uuid'] }}">
|
||||||
{% if meta['tracked'] %}
|
{% if meta['tracked'] %}
|
||||||
{% if meta['tracked']|length > 256 %}
|
{% if meta['tracked']|length > 256 %}
|
||||||
{{ meta['tracked'][0:256] }}...
|
{{ meta['tracked'][0:256] }}...
|
||||||
|
|
Loading…
Reference in a new issue