'''
Assets
======
The following methods allow for interaction into the Tenable Vulnerability Management
:devportal:`assets <assets>` API endpoints.
Methods available on ``tio.assets``:
.. rst-class:: hide-signature
.. autoclass:: AssetsAPI
:members:
'''
from tenable.io.base import TIOEndpoint
[docs]class AssetsAPI(TIOEndpoint):
'''
This will contain all methods related to Assets
'''
[docs] def list(self):
'''
Returns a list of assets.
:devportal:`assets: list-assets <assets-list-assets>`
Returns:
:obj:`list`:
List of asset records.
Examples:
>>> for asset in tio.assets.list():
... pprint(asset)
'''
return self._api.get('assets').json()['assets']
[docs] def delete(self, uuid):
'''
Deletes the asset.
:devportal:`workbenches: asset-delete <workbenches-asset-delete>`
Args:
asset_uuid (str): The unique identifier for the asset.
Returns:
:obj:`None`:
Examples:
>>> asset_id = '00000000-0000-0000-0000-000000000000'
>>> tio.asset.delete(asset_id)
'''
self._api.delete('workbenches/assets/{}'.format(
self._check('uuid', uuid, 'uuid')))
[docs] def details(self, uuid):
'''
Retrieves the details about a specific asset.
:devportal:`assets: asset-info <assets-asset-info>`
Args:
uuid (str):
The UUID (unique identifier) for the asset.
Returns:
:obj:`dict`:
Asset resource definition.
Examples:
>>> asset = tio.assets.details(
... '00000000-0000-0000-0000-000000000000')
'''
return self._api.get(
'assets/{}'.format(
self._check('uuid', uuid, str)
)).json()
[docs] def asset_import(self, source, *assets):
'''
Imports asset information into Tenable Vulnerability Management from an external source.
:devportal:`assets: import <assets-import>`
Imports a list of asset definition dictionaries. Each asset record must
contain at least one of the following attributes: ``fqdn``, ``ipv4``,
``netbios_name``, ``mac_address``. Each record may also contain
additional properties.
Args:
*assets (dict):
One or more asset definition dictionaries
source (str):
An identifier to be used to upload the assets.
Returns:
:obj:`str`:
The job UUID.
Examples:
import single asset:
>>> tio.assets.asset_import('example_source', {
... 'fqdn': ['example.py.test'],
... 'ipv4': ['192.168.254.1'],
... 'netbios_name': 'example',
... 'mac_address': ['00:00:00:00:00:00']
... })
import multiple asset:
>>> tio.assets.asset_import('multiple_asset_example_source',
... {
... 'fqdn': ['example_one.py.test'],
... 'ipv4': ['192.168.1.1'],
... 'netbios_name': 'example_one',
... 'mac_address': ['00:00:00:00:00:00']
... },{
... 'fqdn': ['example_two.py.test'],
... 'ipv4': ['192.168.255.1'],
... 'netbios_name': 'example_two',
... 'mac_address': ['00:00:00:00:00:00']
... })
'''
# We will likely want to perform some more stringent checking of the
# asset resources that are being defined, however a simple type check
# should suffice for now.
return self._api.post(
'import/assets', json={
'assets': [self._check('asset', i, dict) for i in assets],
'source': self._check('source', source, str)
}).json()['asset_import_job_uuid']
[docs] def list_import_jobs(self):
'''
Returns a list of asset import jobs.
:devportal:`assets: list-import-jobs <assets-list-import-jobs>`
Returns:
:obj:`list`:
List of job records.
Examples:
>>> for job in tio.assets.list_import_jobs():
... pprint(job)
'''
return self._api.get('import/asset-jobs').json()['asset_import_jobs']
[docs] def import_job_details(self, uuid):
'''
Returns the details about a specific asset import job.
:devportal:`assets: import-job-info <assets-import-job-info>`
uuid (str):
The UUID (unique identifier) for the job.
Returns:
:obj:`dict`:
The job Resource record.
Examples:
>>> job = tio.assets.import_job_details(
... '00000000-0000-0000-0000-000000000000')
>>> pprint(job)
'''
return self._api.get(
'import/asset-jobs/{}'.format(
self._check('uuid', uuid, str)
)).json()
[docs] def move_assets(self, source, destination, targets):
'''
Moves assets from the specified network to another network.
:devportal:`assets: move-assets <assets-bulk-move>`
source (str):
The UUID of the network currently associated with the assets.
destination (str):
The UUID of the network to associate with the specified assets.
targets (list):
The IPv4 addresses of the assets to move.
Returns:
:obj:`int`:
Returns the number of moved assets.
Examples:
>>> asset = tio.assets.move_assets('00000000-0000-0000-0000-000000000000',
... '10000000-0000-0000-0000-000000000001', ["127.0.0.1"])
>>> pprint(asset)
'''
payload = {
'source': self._check('source', source, 'uuid'),
'destination': self._check('destination', destination, 'uuid'),
'targets': ','.join(self._check('targets', targets, list))
}
return self._api.post('api/v2/assets/bulk-jobs/move-to-network', json=payload).json()
[docs] def bulk_delete(self, *filters, hard_delete=None, filter_type=None):
'''
Deletes the specified assets.
:devportal:`assets: bulk_delete <assets-bulk-delete>`
Args:
*filters (tuple):
A defined filter tuple consisting of the name, operator, and
value. Example: ``('host.hostname', 'match', 'asset.com')``.
filter_type (str, optional):
If multiple filters are defined, the filter_type toggles the
behavior as to how these filters are used. Either all the
filters have to match (``AND``) or any of the filters have to
match (``OR``). If not specified, the default behavior is to
assume filter_type is ``AND``.
hard_delete (bool, optional):
Should the assets be completely removed with all related data?
Returns:
:obj:`dict`:
Returns the number of deleted assets.
Examples:
>>> asset = tio.assets.bulk_delete(
... ('host.hostname', 'match', 'asset.com'), filter_type='or')
>>> pprint(asset)
'''
payload = dict()
# run the rules through the filter parser...
filter_type = self._check('filter_type', filter_type, str,
choices=['and', 'or'], default='and', case='lower')
parsed = self._parse_filters(
filters, self._api.filters.workbench_asset_filters(), rtype='assets')['asset']
if hard_delete:
payload['hard_delete'] = self._check('hard_delete', hard_delete, bool)
payload['query'] = {filter_type: parsed}
return self._api.post('api/v2/assets/bulk-jobs/delete', json=payload).json()
[docs] def update_acr(self, assets_uuid_list, reason, value, note=""):
"""
Updates ACR for the provided asset UUID's with reason(s).
Args:
assets_uuid_list (list):
Asset UUID's which are being updated.
reason (list):
List of reason(s).
value (str):
New ACR value for assets, ranging from 1 - 10.
note (str):
Additional note if any.
Returns:
:obj:`int`:
Status code for the request.
Examples:
>>> tio.assets.update_acr(
... ['00000000-0000-0000-0000-000000000000', '00000000-0000-0000-0000-000000000001'],
... ['Other'], 1, 'some notes')
"""
asset_uuids = []
for asset_uuid in assets_uuid_list:
asset_uuids.append({"id": asset_uuid})
note = note + " - pyTenable"
payload = [{"acr_score": int(value), "reason": reason, "asset": asset_uuids, "note": note}]
return self._api.post('api/v2/assets/bulk-jobs/acr', json=payload).status_code