#!/usr/bin/env python
# coding: utf-8
# # Title: msticpy - nbwidgets
# ## Description:
# This contains a few aggregated widgets using IPyWidgets that help speed things up during an investigation.
#
# You must have msticpy installed to run this notebook:
# ```
# %pip install --upgrade msticpy
# ```
#
# MSTICpy versions >= 0.8.5
#
# ## Table of Contents
# - [Setting query start/end times](#QueryTime)
# - [Simple time range](#Lookback)
# - [Selecting and Displaying Alerts](#AlertSelector)
# - [Selecting from list or dict](#SelectString)
# - [Getting a value from environment](#GetEnvironmentKey)
#
# In[5]:
# Imports
import sys
MIN_REQ_PYTHON = (3,6)
if sys.version_info < MIN_REQ_PYTHON:
print('Check the Kernel->Change Kernel menu and ensure that Python 3.6')
print('or later is selected as the active kernel.')
sys.exit("Python %s.%s or later is required.\n" % MIN_REQ_PYTHON)
from IPython.display import display, Markdown
import pandas as pd
from msticpy import nbwidgets
from msticpy.nbtools.security_alert import SecurityAlert
# [Contents](#contents)
# ## QueryTime
#
# This widget is used to specify time boundaries - designed to be used with the built-in msticpy queries and custom queries.
# The `start` and `end` times are exposed as datetime properties.
#
# ```
# QueryTime.
#
# Composite widget to capture date and time origin
# and set start and end times for queries.
#
# Parameters
# ----------
# QueryParamProvider : QueryParamProvider
# Abstract base class
#
# Parameters
# ----------
# origin_time : datetime, optional
# The origin time (the default is `datetime.utcnow()`)
# label : str, optional
# The description to display
# (the default is 'Select time ({units}) to look back')
# before : int, optional
# The default number of `units` before the `origin_time`
# (the default is 60)
# after : int, optional
# The default number of `units` after the `origin_time`
# (the default is 10)
# max_before : int, optional
# The largest value for `before` (the default is 600)
# max_after : int, optional
# The largest value for `after` (the default is 100)
# units : str, optional
# Time unit (the default is 'min')
# Permissable values are 'day', 'hour', 'minute', 'second'
# These can all be abbreviated down to initial characters
# ('d', 'm', etc.)
# auto_display : bool, optional
# Whether to display on instantiation (the default is False)
# ```
# In[6]:
q_times = nbwidgets.QueryTime(units='day', max_before=20, before=5, max_after=1)
q_times.display()
# In[7]:
print(q_times.start, '....', q_times.end)
# Keep multiple query boundaries aligged by having QueryTime instances reference the time of the same alert or event, or have them chained from one another by referencing the origin_time of an earlier QueryTimes object
# In[8]:
from datetime import datetime, timedelta
class MyAlert:
pass
alert = MyAlert()
alert.TimeGenerated = datetime.utcnow() - timedelta(15)
alert.TimeGenerated
q_times1 = nbwidgets.QueryTime(units='hour', max_before=20, before=1, max_after=1,
origin_time=alert.TimeGenerated, auto_display=True)
q_times2 = nbwidgets.QueryTime(units='hour', max_before=20, before=4, max_after=2,
origin_time=alert.TimeGenerated, auto_display=True)
# In[9]:
alert.TimeGenerated = datetime.utcnow()
q_times1 = nbwidgets.QueryTime(units='hour', max_before=20, before=1, max_after=1,
origin_time=alert.TimeGenerated, auto_display=True)
q_times2 = nbwidgets.QueryTime(units='hour', max_before=20, before=4, max_after=2,
origin_time=q_times2.origin_time, auto_display=True)
# In[10]:
# Use the values in a query
my_kql = f'''
SecurityAlert
| where TimeGenerated >= datetime({q_times1.start})
| where TimeGenerated <= datetime({q_times1.end})'''
print(my_kql)
# [Contents](#contents)
# ## Lookback
# Simpler version with single slider value
#
# Docstring:
# `nbwidgets.Lookback?`
# In[11]:
alert.TimeGenerated = datetime.utcnow() - timedelta(5)
lb = nbwidgets.Lookback(origin_time=alert.TimeGenerated, auto_display=True, max_value=48)
# In[12]:
print(lb.start, '....', lb.end)
# [Contents](#contents)
# ## Alert Browser
#
# ```
# SelectAlert.
#
# View list of alerts and select one for investigation.
# Optionally provide and action to call with the selected alert as a parameter
# (typically used to display the alert.)
#
# Attributes:
# selected_alert: the selected alert
# alert_id: the ID of the selected alert
# alerts: the current alert list (DataFrame)
# Init docstring:
# Create a new instance of AlertSelector.
#
# Parameters
# ----------
# alerts : pd.DataFrame
# DataFrame of alerts.
# action : Callable[..., None], optional
# Optional function to execute for each selected alert.
# (the default is None)
# columns : list, optional
# Override the default column names to use from `alerts`
# (the default is ['StartTimeUtc', 'AlertName',
# 'CompromisedEntity', 'SystemAlertId'])
# auto_display : bool, optional
# Whether to display on instantiation (the default is False)
# ```
# ### Simple alert selector
# Selected alert is available as `select_alert_widget.selected_alert` property
# In[13]:
# Load test data
alerts = pd.read_csv('data/alertlist.csv')
display(Markdown('### Simple alert selector'))
display(Markdown('Selected alert is available as `select_alert_widget.selected_alert`'))
alert_select = nbwidgets.SelectAlert(alerts=alerts)
alert_select.display()
# ### Alert selector with action=SecurityAlert'
# You can pass a function that returns one or more displayable objects.
# You can also pass a class (in this case we're passing `SecurityAlert`) that produces an IPython displayable object.
#
# The `action` class/function is passed the raw alert row as a parameter, as it is selected from the list
# In[14]:
alert_select = nbwidgets.SelectAlert(alerts=alerts, action=SecurityAlert)
alert_select.display()
# In[15]:
from IPython.display import HTML
security_alert = None
# create a function to get the displayable object
def alert_with_entities(alert):
return HTML(SecurityAlert(alert).to_html(show_entities=True))
alert_select = nbwidgets.SelectAlert(alerts=alerts.query('CompromisedEntity == "MSTICALERTSWIN1"'),
action=alert_with_entities)
display(Markdown('### Or a more detailed display with extracted entities'))
alert_select
# [Contents](#contents)
# ## SelectItem
#
# Similar to AlertSelector but simpler and allows you to use any list or dictionary of items.
#
# ```
# Selection list from list or dict.
#
# Attributes:
# value : The selected value.
# Init docstring:
# Select an item from a list or dict.
#
# Parameters
# ----------
# description : str, optional
# The widget label to display (the default is None)
# item_list : List[str], optional
# A `list` of items to select from (the default is None)
# item_dict : Mapping[str, str], optional
# A `dict` of items to select from. When using `item_dict`
# the keys are displayed as the selectable items and value
# corresponding to the selected key is set as the `value`
# property.
# (the default is None)
# action : Callable[..., None], optional
# function to call when item selected (passed a single
# parameter - the value of the currently selected item)
# (the default is None)
# auto_display : bool, optional
# Whether to display on instantiation (the default is False)
# height : str, optional
# Selection list height (the default is '100px')
# width : str, optional
# Selection list width (the default is '50%')
# ```
# In[16]:
# extract the entities from the previously selected alert
security_alert = SecurityAlert(alert_select.selected_alert)
if security_alert is None:
security_alert = SecurityAlert(alerts.iloc[1])
ent_dict = {ent['Type']:ent for ent in security_alert.entities}
# from IPython.display import HTML
# # create a display function for the entities
# def entity_to_html(entity):
# e_text = str(entity)
# e_type = entity.Type
# e_text = e_text.replace("\n", "
").replace(" ", " ")
# return HTML(f"