'''
Alerts
======
The following methods allow for interaction into the Tenable Security Center
`Alert <https://docs.tenable.com/security-center/api/Alert.htm>`_ API.
Methods available on ``sc.alerts``:
.. rst-class:: hide-signature
.. autoclass:: AlertAPI
:members:
.. _iCal Date-Time:
https://tools.ietf.org/html/rfc5545#section-3.3.5
.. _iCal Recurrence Rule:
https://tools.ietf.org/html/rfc5545#section-3.3.10
'''
from .base import SCEndpoint
from tenable.utils import dict_merge
[docs]class AlertAPI(SCEndpoint):
def _constructor(self, *filters, **kw):
'''
Handles building an alert document.
'''
# call the analysis query constructor to assemble a query.
if len(filters) > 0:
# checking to see if data_type was passed. If it wasn't then we
# will set the value to the default of 'vuln'.
if 'data_type' not in kw:
kw['data_type'] = 'vuln'
kw['type'] = kw['data_type']
kw['tool'] = ''
kw = self._query_constructor(*filters, **kw)
del(kw['data_type'])
del(kw['query']['tool'])
del(kw['tool'])
elif 'query_id' in kw:
kw = self._query_constructor(*filters, **kw)
if 'name' in kw:
kw['name'] = self._check('name', kw['name'], str)
if 'description' in kw:
kw['description'] = self._check(
'description', kw['description'], str)
if 'query' in kw:
kw['query'] = self._check('query', kw['query'], dict)
if 'always_exec_on_trigger' in kw:
# executeOnEveryTrigger expected a boolean response as a lower-case
# string. We will accept a boolean and then transform it into a
# string value.
kw['executeOnEveryTrigger'] = str(self._check(
'always_exec_on_trigger', kw['always_exec_on_trigger'], bool)).lower()
del(kw['always_exec_on_trigger'])
if 'trigger' in kw:
# here we will be expanding the trigger from the common format of
# tuples that we are using within pytenable into the native
# supported format that SecurityCenter expects.
self._check('trigger', kw['trigger'], tuple)
kw['triggerName'] = self._check(
'triggerName', kw['trigger'][0], str)
kw['triggerOperator'] = self._check(
'triggerOperator', kw['trigger'][1], str,
choices=['>=', '<=', '=', '!='])
kw['triggerValue'] = self._check(
'triggerValue', kw['trigger'][2], str)
del(kw['trigger'])
# hand off the building the schedule sub-document to the schedule
# document builder.
if 'schedule' in kw:
kw['schedule'] = self._schedule_constructor(kw['schedule'])
# FR: at some point we should start looking into checking and
# normalizing the action document.
return kw
[docs] def list(self, fields=None):
'''
Retrieves the list of alerts.
:sc-api:`alert: list <Alert.htm#AlertRESTReference-/alert>`
Args:
fields (list, optional):
A list of attributes to return for each alert.
Returns:
:obj:`dict`:
A list of alert resources.
Examples:
>>> for alert in sc.alerts.list()['manageable']:
... pprint(alert)
'''
params = dict()
if fields:
params['fields'] = ','.join([self._check('field', f, str)
for f in fields])
return self._api.get('alert', params=params).json()['response']
[docs] def details(self, id, fields=None):
'''
Returns the details for a specific alert.
:sc-api:`alert: details <Alert.htm#AlertRESTReference-/alert/{id}>`
Args:
id (int): The identifier for the alert.
fields (list, optional): A list of attributes to return.
Returns:
:obj:`dict`:
The alert resource record.
Examples:
>>> alert = sc.alerts.detail(1)
>>> pprint(alert)
'''
params = dict()
if fields:
params['fields'] = ','.join([self._check('field', f, str) for f in fields])
return self._api.get('alert/{}'.format(self._check('id', id, int)),
params=params).json()['response']
[docs] def create(self, *filters, **kw):
'''
Creates a new alert. The fields below are explicitly checked, however
any additional parameters mentioned in the API docs can be passed to the
document constructor.
:sc-api:`alert: create <Alert.htm#alert_POST>`
Args:
*filters (tuple):
A filter expression. Refer to the detailed description within
the analysis endpoint documentation for more details on how to
formulate filter expressions.
data_type (str):
The type of filters being used. Must be of type ``lce``,
``ticket``, ``user``, or ``vuln``. If no data-type is
specified, then the default of ``vuln`` will be set.
name (str): The name of the alert.
description (str, optional): A description for the alert.
trigger (tuple):
A tuple in the filter-tuple format detailing what would
constitute a trigger. For example: ``('sumip', '=', '1000')``.
always_exec_on_trigger (bool, optional):
Should the trigger always execute when the trigger fires, or
only execute when the returned data changes?
Default is ``False``.
schedule (dict, optional):
This is the schedule dictionary that will inform Tenable Security Center how
often to run the alert. If left unspecified then we will
default to ``{'type': 'never'}``.
action (list):
The action(s) that will be performed when the alert trigger
fires. Each action is a dictionary detailing what type of
action to take, and the details surrounding that action. The
supported type of actions are ``email``, ``notifications``,
``report``, ``scan``, ``syslog``, and ``ticket``. The following
examples lay out each type of action as an example:
* Email action type:
.. code-block:: python
{'type': 'email',
'subject': 'Example Email Subject',
'message': 'Example Email Body'
'addresses': 'user1@company.com\\nuser2@company.com',
'users': [{'id': 1}, {'id': 2}],
'includeResults': 'true'}
* Notification action type:
.. code-block:: python
{'type': 'notification',
'message': 'Example notification',
'users': [{'id': 1}, {'id': 2}]}
* Report action type:
.. code-block:: python
{'type': 'report',
'report': {'id': 1}}
* Scan action type:
.. code-block:: python
{'type': 'scan',
'scan': {'id': 1}}
* Syslog action type:
.. code-block:: python
{'type': 'syslog',
'host': '127.0.0.1',
'port': '514',
'message': 'Example Syslog Message',
'severity': 'Critical'}
* Ticket action type:
.. code-block:: python
{'type': 'ticket',
'assignee': {'id': 1},
'name': 'Example Ticket Name',
'description': 'Example Ticket Description',
'notes': 'Example Ticket Notes'}
Returns:
:obj:`dict`:
The alert resource created.
Examples:
>>> sc.alerts.create(
... ('severity', '=', '3,4'),
... ('exploitAvailable', '=', 'true'),
... trigger=('sumip', '>=', '100'),
... name='Too many High or Critical and Exploitable',
... action=[{
... 'type': 'notification',
... 'message': 'Too many High or Crit Exploitable Vulns',
... 'users': [{'id': 1}]
... }])
'''
payload = self._constructor(*filters, **kw)
return self._api.post('alert', json=payload).json()['response']
[docs] def edit(self, id, *filters, **kw):
'''
Updates an existing alert. All fields are optional and will overwrite
the existing value.
:sc-api:`alert: update <Alert.htm#alert_id_PATCH>`
Args:
if (int): The alert identifier.
*filters (tuple):
A filter expression. Refer to the detailed description within
the analysis endpoint documentation for more details on how to
formulate filter expressions.
data_type (str):
The type of filters being used. Must be of type ``lce``,
``ticket``, ``user``, or ``vuln``. If no data-type is
specified, then the default of ``vuln`` will be set.
name (str, optional): The name of the alert.
description (str, optional): A description for the alert.
trigger (tuple, optional):
A tuple in the filter-tuple format detailing what would
constitute a trigger. For example: ``('sumip', '=', '1000')``.
always_exec_on_trigger (bool, optional):
Should the trigger always execute when the trigger fires, or
only execute when the returned data changes?
Default is ``False``.
schedule (dict, optional):
This is the schedule dictionary that will inform Tenable Security Center how
often to run the alert. If left unspecified then we will
default to ``{'type': 'never'}``.
action (list):
The action(s) that will be performed when the alert trigger
fires. Each action is a dictionary detailing what type of
action to take, and the details surrounding that action.
Returns:
:obj:`dict`:
The modified alert resource.
Examples:
>>> sc.alerts.update(1, name='New Alert Name')
'''
payload = self._constructor(*filters, **kw)
return self._api.patch('alert/{}'.format(
self._check('id', id, int)), json=payload).json()['response']
[docs] def delete(self, id):
'''
Deletes the specified alert.
:sc-api:`alert: delete <Alert.htm#alert_id_DELETE>`
Args:
id (int): The alert identifier.
Returns:
:obj:`str`:
The response code of the action.
Examples:
>>> sc.alerts.delete(1)
'''
return self._api.delete('alert/{}'.format(
self._check('id', id, int))).json()['response']
[docs] def execute(self, id):
'''
Executes the specified alert.
:sc-api:`alert: execute <Alert.htm#AlertRESTReference-/alert/{id}/execute>`
Args:
id (int): The alert identifier.
Returns:
:obj:`dict`:
The alert resource.
'''
return self._api.post('alert/{}/execute'.format(
self._check('id', id, int))).json()['response']