diff --git a/examples/scans.py b/examples/scans.py index 272f4f7..c755b7d 100644 --- a/examples/scans.py +++ b/examples/scans.py @@ -4,7 +4,8 @@ from time import time from tenable_io.api.models import Scan -from tenable_io.api.scans import ScanExportRequest +from tenable_io.api.credentials import CredentialPermission, CredentialRequest +from tenable_io.api.scans import ScanExportRequest, ScanSettings, ScanCreateRequest, ScanCredentials from tenable_io.client import TenableIOClient from tenable_io.exceptions import TenableIOApiException @@ -123,6 +124,43 @@ def example(test_targets): assert imported_scan.details().info.name == scan.details().info.name os.remove(test_nessus_file) + ''' + Create a new scan using managed credentials. + Note: First we must create a new managed credential + ''' + # Created managed credential for SSH + test_user = client.users_api.list().users[0] + test_permission = CredentialPermission(grantee_uuid=test_user.uuid, + type=CredentialPermission.USER_TYPE, + permissions=CredentialPermission.CAN_EDIT, + name=test_user.username, + isPending=True) + credential_request = CredentialRequest(name='Lab SSH', + description='SSH Credentials for Lab', + type_='SSH', + settings={ + 'auth_method': 'password', + 'elevate_privledges_with': 'Nothing', + 'username': 'username', + 'password': 'password' + }, + permissions=[test_permission]) + credential_uuid = client.credentials_api.create(credential_request) + credential_detail = client.credentials_api.details(credential_uuid) + + # Create scan settings + settings = ScanSettings(name='Credentialed Scan', + text_targets=test_targets) + credentials = ScanCredentials(add=[credential_detail]) + template_uuid = client.scan_helper.template('basic').uuid + + # Create Scan + scan_request = ScanCreateRequest(uuid=template_uuid, + settings=settings, + credentials=credentials) + scan_id = client.scans_api.create(scan_request) + assert scan_id + ''' Stop all scans. Note: Use with caution as this will stop all ongoing scans (including any automated test). diff --git a/tenable_io/api/credentials.py b/tenable_io/api/credentials.py index 4a90866..22600a9 100644 --- a/tenable_io/api/credentials.py +++ b/tenable_io/api/credentials.py @@ -49,7 +49,10 @@ def details(self, uuid): :return: An instance of :class:`tenable_io.api.models.CredentialDetails`. """ response = self._client.get('credentials/%(uuid)s', path_params={'uuid': uuid}) - return CredentialDetails.from_json(response.text) + # We manually add the uuid back to the response object to it can be referenced later more easily + credential_details = loads(response.text) + credential_details['uuid'] = uuid + return CredentialDetails.from_dict(credential_details) def update(self, uuid, credential_request): diff --git a/tenable_io/api/models.py b/tenable_io/api/models.py index 6b20987..7e531ed 100644 --- a/tenable_io/api/models.py +++ b/tenable_io/api/models.py @@ -1696,6 +1696,41 @@ def scans(self, scans): self._scans = scans +class ScanCredentials(BaseModel): + + def __init__( + self, + add=[], + edit=[], + delete=[], + ): + self.add = add + self.edit = edit + self.delete = delete + + def _parse_credential_list(self, credential_list): + _parsed_credentials = {} + for cd in credential_list: + if cd.category.id not in _parsed_credentials: + _parsed_credentials[cd.category.id] = {} + _parsed_credentials[cd.category.id][cd.type.id] = [{'id': cd.uuid}] + else: + if cd.type.id not in _parsed_credentials[cd.category.id]: + _parsed_credentials[cd.category.id][cd.type.id] = [{'id': cd.uuid}] + else: + _parsed_credentials[cd.category.id][cd.type.id].append({'id': cd.uuid}) + return _parsed_credentials + + def as_payload(self, filter_=None): + payload = {} + # All items for each attribute will be an instance of CredentialDetails + payload['add'] = self._parse_credential_list(self.add) + payload['edit'] = self._parse_credential_list(self.edit) + payload['delete'] = [] + + return payload + + class ScanSettings(BaseModel): def __init__( @@ -3541,6 +3576,7 @@ class CredentialDetails(BaseModel): def __init__( self, + uuid=None, name=None, description=None, category=None, @@ -3555,6 +3591,7 @@ def __init__( self._permissions = None self._settings = None + self.uuid = uuid self.name = name self.description = description self.category = category diff --git a/tenable_io/api/scans.py b/tenable_io/api/scans.py index 8082918..780b990 100644 --- a/tenable_io/api/scans.py +++ b/tenable_io/api/scans.py @@ -1,7 +1,8 @@ from json import loads from tenable_io.api.base import BaseApi -from tenable_io.api.models import Scan, ScanDetails, ScanHistory, ScanHostDetails, ScanList, ScanSettings +from tenable_io.api.models import Scan, ScanCredentials, ScanDetails, ScanHistory, \ + ScanHostDetails, ScanList, ScanSettings from tenable_io.api.base import BaseRequest @@ -266,10 +267,12 @@ def __init__( self, uuid, settings, + credentials ): assert isinstance(settings, ScanSettings) self.uuid = uuid self.settings = settings + self.credentials = credentials def as_payload(self, filter_=None): payload = super(ScanSaveRequest, self).as_payload(True) @@ -277,6 +280,10 @@ def as_payload(self, filter_=None): payload.__setitem__('settings', self.settings.as_payload()) else: payload.pop('settings', None) + if self.credentials is not None and isinstance(self.credentials, ScanCredentials): + payload.__setitem__('credentials', self.credentials.as_payload()) + else: + payload.pop('credentials', None) return payload @@ -286,8 +293,9 @@ def __init__( self, uuid, settings=None, + credentials=None, ): - super(ScanCreateRequest, self).__init__(uuid, settings) + super(ScanCreateRequest, self).__init__(uuid, settings, credentials) class ScanConfigureRequest(ScanSaveRequest): @@ -296,8 +304,9 @@ def __init__( self, uuid=None, settings=None, + credentials=None, ): - super(ScanConfigureRequest, self).__init__(uuid, settings) + super(ScanConfigureRequest, self).__init__(uuid, settings, credentials) class ScanExportRequest(BaseRequest):