'''
Plugins
=======
The following methods allow for interaction into the Tenable Vulnerability Management
:devportal:`plugins <plugins>` API endpoints.
Methods available on ``tio.plugins``:
.. rst-class:: hide-signature
.. autoclass:: PluginsAPI
:members:
'''
from datetime import date
from tenable.io.base import TIOEndpoint, TIOIterator
class PluginIterator(TIOIterator):
'''
The plugins iterator provides a scalable way to work through plugin result
sets of any size. The iterator will walk through each page of data,
returning one record at a time. If it reaches the end of a page of
records, then it will request the next page of information and then continue
to return records from the next page (and the next, and the next) until the
counter reaches the total number of records that the API has reported.
Attributes:
count (int): The current number of records that have been returned
page (list):
The current page of data being walked through. pages will be
cycled through as the iterator requests more information from the
API.
page_count (int): The number of record returned from the current page.
total (int):
The total number of records that exist for the current request.
populate_maptable (bool):
Informs the iterator whether to construct the plugin to family maps
for injecting the plugin family data into each item.
'''
_maptable = None
populate_maptable = False
def _populate_family_cache(self):
'''
Generates the maptable to use to graft on the plugin family information
to the plugins. Effectively what we doing is generating a dictionary of
2 subdictionaries. Each one of these is a simple hash table allowing
the iterator to resolve the name of the family by ID and the family ID
by the plugin membership. This information is currently lacking in the
plugin listing output and was requested by a customer.
.. note::
This currently seems to add about 7-10 seconds before the first item
is returned, as it seems to take this long to generate the data. We
can focus on reducing this time later on with the introduction of
multi-threaded iterators && async API calls.
'''
self._maptable = {
'plugins': dict(),
'families': dict()
}
for family in self._api.plugins.families():
self._maptable['families'][family['id']] = family['name']
for fam_id in self._maptable['families'].keys():
for plugin in self._api.plugins.family_details(fam_id)['plugins']:
self._maptable['plugins'][plugin['id']] = fam_id
def next(self):
item = super(PluginIterator, self).next()
# If the populate_maptable flag is set, then we will build the mappings.
if not self._maptable and self.populate_maptable:
self._populate_family_cache()
# If the maptable exists, then graft on the plugin family information
# on to to the item.
if self._maptable:
try:
fid = self._maptable['plugins'][item['id']]
item['family_id'] = fid
item['family_name'] = self._maptable['families'][fid]
except KeyError:
self._log.warning("plugin id {} not found in plugin family".format(item['id']))
item['family_id'] = None
item['family_name'] = None
return item
[docs]class PluginsAPI(TIOEndpoint):
'''
This will contain all methods related to plugins
'''
[docs] def families(self):
'''
List the available plugin families.
:devportal:`plugins: families <plugins-families>`
Returns:
:obj:`list`:
List of plugin family resource records.
Examples:
>>> for family in tio.plugins.families():
... pprint(family)
'''
return self._api.get('plugins/families').json()['families']
[docs] def family_details(self, family_id):
'''
Retrieve the details for a specific plugin family.
:devportal:`plugins: family-details plugins-family-details>`
Args:
family_id (int): The plugin family unique identifier.
Returns:
:obj:`dict`:
Returns a dictionary stating the id, name, and plugins that are
housed within the plugin family.
Examples:
>>> family = tio.plugins.family_details(1)
'''
return self._api.get('plugins/families/{}'.format(
self._check('family_id', family_id, int)
)).json()
[docs] def plugin_details(self, plugin_id):
'''
Retrieve the details for a specific plugin.
:devportal:`plugins: plugin-details <plugins-plugin-details>`
Args:
plugin_id (int): The plugin id for the requested plugin.
Returns:
:obj:`dict`:
A dictionary stating the id, name, family, and any other
relevant attributes associated to the plugin.
Examples:
>>> plugin = tio.plugins.plugin_details(19506)
>>> pprint(plugin)
'''
return self._api.get('plugins/plugin/{}'.format(
self._check('plugin_id', plugin_id, int))).json()
[docs] def list(self, page=None, size=None, last_updated=None, num_pages=None):
'''
Get the listing of plugin details from Tenable Vulnerability Management.
:devportal:`plugins: list <io-plugins-list>`
Args:
size (int, optional):
The number of records to retrieve. Default is 1000
page (int, optional):
The starting page to retrieve. Default is 0.
last_updated (date, optional):
A datetime.date object stating when the threshold for the last
updated field can be for a plugin.
num_pages (int, optional):
The total number of pages to request before stopping the
iterator.
Returns:
:obj:`PluginsIterator`:
An iterator that handles the page management of the requested
records.
Examples:
Getting the listing of all plugins:
>>> for plugin in tio.plugins.list():
... pprint(plugin)
Retrieving all of the plugins updated since 2019-01-01:
>>> for plugin in tio.plugins.list(last_updated=date(2019, 1, 1)):
... pprint(plugin)
Informing the iterator to cache the plugin family data for injection
into each item:
>>> plugins = tio.plugins.list(last_updated=date(2019, 1, 1))
>>> plugins.populate_maptable = True
>>> for plugin in plugins:
... pprint(plugin)
'''
return PluginIterator(self._api,
_api_version=2,
_size=self._check('size', size, int, default=1000),
_page_num=self._check('page', page, int, default=1),
_query={
'last_updated': self._check('last_updated', last_updated, date,
default=date(1970, 1, 1)).strftime('%Y-%m-%d')
},
_pages_total=self._check('num_pages', num_pages, int),
_path='plugins/plugin',
_resource='plugin_details')