mirror of https://github.com/MISP/misp-dashboard
207 lines
8.8 KiB
Python
207 lines
8.8 KiB
Python
import math, random
|
|
import os
|
|
import json
|
|
import copy
|
|
import datetime, time
|
|
import logging
|
|
from collections import OrderedDict
|
|
|
|
import util
|
|
|
|
class Trendings_helper:
|
|
def __init__(self, serv_redis_db, cfg):
|
|
self.serv_redis_db = serv_redis_db
|
|
self.cfg = cfg
|
|
|
|
# REDIS keys
|
|
self.keyEvent = "TRENDINGS_EVENTS"
|
|
self.keyCateg = "TRENDINGS_CATEGS"
|
|
self.keyTag = "TRENDINGS_TAGS"
|
|
self.keyDisc = "TRENDINGS_DISC"
|
|
self.keySigh = "TRENDINGS_SIGHT_sightings"
|
|
self.keyFalse = "TRENDINGS_SIGHT_false_positive"
|
|
|
|
#logger
|
|
logDir = cfg.get('Log', 'directory')
|
|
logfilename = cfg.get('Log', 'filename')
|
|
logPath = os.path.join(logDir, logfilename)
|
|
if not os.path.exists(logDir):
|
|
os.makedirs(logDir)
|
|
logging.basicConfig(filename=logPath, filemode='a', level=logging.INFO)
|
|
self.logger = logging.getLogger(__name__)
|
|
|
|
''' SETTER '''
|
|
|
|
def addGenericTrending(self, trendingType, data, timestamp):
|
|
timestampDate = datetime.datetime.fromtimestamp(float(timestamp))
|
|
timestampDate_str = util.getDateStrFormat(timestampDate)
|
|
keyname = "{}:{}".format(trendingType, timestampDate_str)
|
|
if isinstance(data, OrderedDict):
|
|
to_save = json.dumps(data)
|
|
else:
|
|
to_save = data
|
|
self.serv_redis_db.zincrby(keyname, to_save, 1)
|
|
self.logger.debug('Added to redis: keyname={}, content={}'.format(keyname, to_save))
|
|
|
|
def addTrendingEvent(self, eventName, timestamp):
|
|
self.addGenericTrending(self.keyEvent, eventName, timestamp)
|
|
|
|
def addTrendingCateg(self, categName, timestamp):
|
|
self.addGenericTrending(self.keyCateg, categName, timestamp)
|
|
|
|
def addTrendingDisc(self, eventName, timestamp):
|
|
self.addGenericTrending(self.keyDisc, eventName, timestamp)
|
|
|
|
def addTrendingTags(self, tags, timestamp):
|
|
for tag in tags:
|
|
ordDic = OrderedDict() #keep fields with the same layout in redis
|
|
ordDic['id'] = tag['id']
|
|
ordDic['name'] = tag['name']
|
|
ordDic['colour'] = tag['colour']
|
|
self.addGenericTrending(self.keyTag, ordDic, timestamp)
|
|
|
|
def addSightings(self, timestamp):
|
|
timestampDate = datetime.datetime.fromtimestamp(float(timestamp))
|
|
timestampDate_str = util.getDateStrFormat(timestampDate)
|
|
keyname = "{}:{}".format(self.keySigh, timestampDate_str)
|
|
self.serv_redis_db.incrby(keyname, 1)
|
|
self.logger.debug('Incrby: keyname={}'.format(keyname))
|
|
|
|
def addFalsePositive(self, timestamp):
|
|
timestampDate = datetime.datetime.fromtimestamp(float(timestamp))
|
|
timestampDate_str = util.getDateStrFormat(timestampDate)
|
|
keyname = "{}:{}".format(self.keyFalse, timestampDate_str)
|
|
self.serv_redis_db.incrby(keyname, 1)
|
|
self.logger.debug('Incrby: keyname={}'.format(keyname))
|
|
|
|
''' GETTER '''
|
|
|
|
def getGenericTrending(self, trendingType, dateS, dateE, topNum=0):
|
|
to_ret = []
|
|
prev_days = (dateE - dateS).days
|
|
for curDate in util.getXPrevDaysSpan(dateE, prev_days):
|
|
keyname = "{}:{}".format(trendingType, util.getDateStrFormat(curDate))
|
|
data = self.serv_redis_db.zrange(keyname, 0, topNum-1, desc=True, withscores=True)
|
|
data = [ [record[0].decode('utf8'), record[1]] for record in data ]
|
|
data = data if data is not None else []
|
|
to_ret.append([util.getTimestamp(curDate), data])
|
|
return to_ret
|
|
|
|
def getSpecificTrending(self, trendingType, dateS, dateE, specificLabel=''):
|
|
to_ret = []
|
|
prev_days = (dateE - dateS).days
|
|
for curDate in util.getXPrevDaysSpan(dateE, prev_days):
|
|
keyname = "{}:{}".format(trendingType, util.getDateStrFormat(curDate))
|
|
data = self.serv_redis_db.zscore(keyname, specificLabel)
|
|
data = [[specificLabel, data]] if data is not None else []
|
|
to_ret.append([util.getTimestamp(curDate), data])
|
|
return to_ret
|
|
|
|
def getTrendingEvents(self, dateS, dateE, specificLabel=None):
|
|
if specificLabel is None:
|
|
return self.getGenericTrending(self.keyEvent, dateS, dateE)
|
|
else:
|
|
specificLabel = specificLabel.replace('\\n', '\n'); # reset correctly label with their \n (CR) instead of their char value
|
|
return self.getSpecificTrending(self.keyEvent, dateS, dateE, specificLabel)
|
|
|
|
def getTrendingCategs(self, dateS, dateE):
|
|
return self.getGenericTrending(self.keyCateg, dateS, dateE)
|
|
|
|
# FIXME: Construct this when getting data
|
|
def getTrendingTags(self, dateS, dateE, topNum=12):
|
|
to_ret = []
|
|
prev_days = (dateE - dateS).days
|
|
for curDate in util.getXPrevDaysSpan(dateE, prev_days):
|
|
keyname = "{}:{}".format(self.keyTag, util.getDateStrFormat(curDate))
|
|
data = self.serv_redis_db.zrange(keyname, 0, topNum-1, desc=True, withscores=True)
|
|
data = [ [record[0].decode('utf8'), record[1]] for record in data ]
|
|
data = data if data is not None else []
|
|
temp = []
|
|
for jText, score in data:
|
|
temp.append([json.loads(jText), score])
|
|
data = temp
|
|
to_ret.append([util.getTimestamp(curDate), data])
|
|
return to_ret
|
|
|
|
def getTrendingSightings(self, dateS, dateE):
|
|
to_ret = []
|
|
prev_days = (dateE - dateS).days
|
|
for curDate in util.getXPrevDaysSpan(dateE, prev_days):
|
|
keyname = "{}:{}".format(self.keySigh, util.getDateStrFormat(curDate))
|
|
sight = self.serv_redis_db.get(keyname)
|
|
sight = 0 if sight is None else int(sight.decode('utf8'))
|
|
keyname = "{}:{}".format(self.keyFalse, util.getDateStrFormat(curDate))
|
|
fp = self.serv_redis_db.get(keyname)
|
|
fp = 0 if fp is None else int(fp.decode('utf8'))
|
|
to_ret.append([util.getTimestamp(curDate), { 'sightings': sight, 'false_positive': fp}])
|
|
return to_ret
|
|
|
|
def getTrendingDisc(self, dateS, dateE):
|
|
return self.getGenericTrending(self.keyDisc, dateS, dateE)
|
|
|
|
def getTypeaheadData(self, dateS, dateE):
|
|
to_ret = {}
|
|
for trendingType in [self.keyEvent, self.keyCateg]:
|
|
allSet = set()
|
|
prev_days = (dateE - dateS).days
|
|
for curDate in util.getXPrevDaysSpan(dateE, prev_days):
|
|
keyname = "{}:{}".format(trendingType, util.getDateStrFormat(curDate))
|
|
data = self.serv_redis_db.zrange(keyname, 0, -1, desc=True)
|
|
for elem in data:
|
|
allSet.add(elem.decode('utf8'))
|
|
to_ret[trendingType] = list(allSet)
|
|
tags = self.getTrendingTags(dateS, dateE)
|
|
tagSet = set()
|
|
for item in tags:
|
|
theDate, tagList = item
|
|
for tag in tagList:
|
|
tag = tag[0]
|
|
tagSet.add(tag['name'])
|
|
to_ret[self.keyTag] = list(tagSet)
|
|
return to_ret
|
|
|
|
# In contrary of getGenericTrending, it regroups items in the format: {item, start: timestamp1, end: timestamp2}
|
|
# so that it can be displayed easily on the timeline.
|
|
def getGenericTrendingOvertime(self, dateS, dateE, choice=None, topNum=0):
|
|
if choice == 'categs':
|
|
trendingType = self.keyCateg
|
|
elif choice == 'tags':
|
|
trendingType = self.keyTag
|
|
else:
|
|
trendingType = self.keyEvent
|
|
|
|
dico_items = {}
|
|
to_format = []
|
|
prev_days = (dateE - dateS).days
|
|
# get data
|
|
for curDate in util.getXPrevDaysSpan(dateE, prev_days):
|
|
keyname = "{}:{}".format(trendingType, util.getDateStrFormat(curDate))
|
|
data = self.serv_redis_db.zrange(keyname, 0, topNum-1, desc=True, withscores=True)
|
|
data = [ [record[0].decode('utf8'), record[1]] for record in data ]
|
|
data = data if data is not None else []
|
|
to_format.append([util.getTimestamp(curDate), data])
|
|
|
|
for timestamp, array in to_format:
|
|
for item, _ in array:
|
|
if item not in dico_items:
|
|
dico_items[item] = []
|
|
dico_items[item].append(timestamp)
|
|
|
|
# sort timestamps in correct order
|
|
for item in dico_items.keys():
|
|
dico_items[item].sort()
|
|
# dico_items have the form: {item: [t1,t2,t4], ...}
|
|
to_ret = []
|
|
ONEDAY = 60*60*24
|
|
for item, timestamps in dico_items.items():
|
|
obj = {'name': item, 'start': timestamps[0], 'end': timestamps[0]+ONEDAY}
|
|
for t in timestamps:
|
|
if t-obj['end'] > ONEDAY: #new entry
|
|
to_ret.append(copy.deepcopy(obj))
|
|
obj['start'] = t
|
|
obj['end'] = t+ONEDAY
|
|
else: # contrinue entry
|
|
obj['end'] = t+ONEDAY
|
|
to_ret.append(obj)
|
|
return to_ret
|