Esempio n. 1
0
    def import_scan(self,
                    fobj: Optional[BytesIO] = None,
                    file_id: Optional[str] = None,
                    folder_id: Optional[int] = None,
                    password: Optional[str] = None) -> Dict:
        '''
        Import a scan report into the Nessus scanner.  Either a file object or
        a file_id must be specified.

        Args:
            fobj (BytesIO, optional):
                The file object to import.
            file_id (str, optional):
                The id of the already uploaded file object to import.
            folder_id (int, optional):
                The folder that the imported scan should reside within.
            password (str, optional):
                If the file object is encrypted, this password will be used
                to decrypt.

        Example:

            >>> with open('Example.nessus', 'rb') as reportfile:
            ...     nessus.scans.import_scan(reportfile)
        '''
        if not file_id:
            file_id = self._api.files.upload(fobj)
        return self._post('import',
                          json=dict_clean({
                              'file': file_id,
                              'folder_id': folder_id,
                              'password': password
                          }))
Esempio n. 2
0
 def edit(self,
          rule_id: int,
          plugin_id: Optional[int] = None,
          type: Optional[Literal['recast_critical', 'recast_high',
                                 'recast_medium', 'recast_low',
                                 'recast_info', 'exclude']] = None,
          host: Optional[str] = None,
          date: Optional[int] = None) -> None:
     '''
     Creates a new plugin rule
     
     Args:
         rule_id (int): The rule to modify
         plugin_id (int, optional): The plugin id to modify
         type: (str, optional): The type of modification to perform
         host (str, optional): The host to apply this rule to
         date (int, optional): The unix date for this rule to expire
     
     Example:
     
         >>> nessus.plugin_rules.edit(1, date=1645164000)
     '''
     rule = self.details(1)
     payload = dict_merge(
         rule,
         dict_clean({
             'plugin_id': str(plugin_id),
             'type': type,
             'host': host,
             'date': date
         }))
     return self._put(f'{rule_id}', json=payload)
Esempio n. 3
0
def test_dict_clean():
    dirty = {
        'a': 1,
        'b': {
            'c': 2,
            'd': None
        },
        'e': None,
        'f': [{
            'g': 1,
            'h': None
        }, {
            'i': None
        }],
        'j': [1, None, {}]
    }
    assert dict_clean(dirty) == {
        'a': 1,
        'b': {
            'c': 2
        },
        'f': [{
            'g': 1
        }],
        'j': [1, None]
    }
Esempio n. 4
0
 def settings(self,
              update: Literal['all', 'plugins', 'disabled'],
              custom_host: Optional[str] = None,
              auto_update_delay: Optional[int] = None) -> None:
     '''
     Update the software update settings
     
     Args:
         update (str): 
             What components should be updated?  Expected values are
             ``all``, ``plugins``, and ``disabled``.
         custom_host (str, optional):
             URL of the custom plugin feed host
         auto_update_delay (int, optional):
             How often should the plugin feed attempt to update (in hours)
     
     Example:
     
         >>> nessus.software_update.settings(update='all',
         ...                                 auto_update_delay=24
         ...                                 )
     '''
     self._put(json=dict_clean({
         'update': update,
         'custom_host': custom_host,
         'auto_update_delay': auto_update_delay
     }))
Esempio n. 5
0
 def edit(self,
          user_id: int,
          permissions: int,
          name: Optional[str] = None,
          email: Optional[str] = None) -> Dict:
     '''
     Updates the specified user object
     
     Args:
         user_id (int): The id of the user to update
         permissions (int): The permissions settings for the user
         name (str, optional): The user's friendly name
         email (str, optional): The user's email address
     
     Returns:
         Dict:
             The updates user object
     
     Example:
         
         >>> nessus.users.edit(1, 32, name='Updated User')
     '''
     return self._put(f'{user_id}',
                      json=dict_clean({
                          'permissions': permissions,
                          'name': name,
                          'email': email
                      }))
Esempio n. 6
0
    def export_scan(self,
                    scan_id: int,
                    history_id: Optional[int] = None,
                    fobj: Optional[BytesIO] = None,
                    **kwargs) -> BytesIO:
        '''
        Generate a scan export or report and download it.

        Args:
            scan_id (int): The id of the scan to export.
            history_id (int, optional):
                The history id of the specific point in time to export.
            fobj (BytexIO, optional):
                The file object to write the exported file to.  If none is
                specified then a BytesIO object is written to in memory.
            filters (list[tuple], optional):
                The filters to apply to the exported data.
            format (str, optional):
                The exported scan format.  Supported values are ``nessus``,
                ``html``, ``csv``, and ``db``.  If unspecified, the default is
                ``nessus``.
            password (str, optional):
                The password to apply to the exported data (required for db).
            template_id (int, optional):
                When exporting in HTML or PDF, what report definition should
                the exported data be represented within.
            chunk_size (int, optional):
                The chunk sizing for the download itself.
            stream_hook (callable, optional):
                Overload the default downloading behavior with a custom
                stream hook.
            hook_kwargs (dict, optional):
                keyword arguments to pass to the stream_hook callable in
                addition to the default passed params.
        '''
        dlopts = {
            'fobj': fobj,
            'chunk_size': kwargs.pop('chunk_size', None),
            'stream_hook': kwargs.pop('stream_hook', None),
            'hook_kwargs': kwargs.pop('hook_kwargs', None)
        }
        schema = ScanExportSchema()
        payload = dict_clean(schema.dump(schema.load(kwargs)))
        token = self._post(f'{scan_id}/export',
                           params=dict_clean({'history_id': history_id}),
                           json=payload)['token']
        return self._api.tokens._fetch(token, **dlopts)  # noqa PLW0212
Esempio n. 7
0
 def edit(self, 
          smtp_host: Optional[str] = None,
          smtp_port: Optional[int] = None,
          smtp_enc: Optional[Literal['No Encryption', 
                                     'Use TLS if available',
                                     'Force SSL'
                                     'Force TLS'
                                     ]] = None,
          smtp_from: Optional[str] = None,
          smtp_www_host: Optional[str] = None,
          smtp_user: Optional[str] = None,
          smtp_pass: Optional[str] = None,
          smtp_auth: Optional[Literal['NONE', 
                                      'PLAIN',
                                      'LOGIN',
                                      'NTLM',
                                      'CRAM-MD5'
                                      ]] = None
          ) -> None:
     '''
     Updates the Nessus daemon's mail settings
     
     Args:
         smtp_host (str, optional): 
             DNS/IP Address of the SMTP server
         smtp_port (int, optional): 
             Port number for the SMTP service
         smtp_enc (str, optional):
             The connection encryption for the SMTP server
         smtp_from (str, optional): 
             Reply email address for email sent by the Nessus daemon
         smtp_www_host (str, optional):
             The host to use in email links
         smtp_user (str, optional):
             The username to use when authenticating to the SMTP service
         smtp_pass (str, optional):
             The password to use when authenticating to the SMTP service
         smtp_auth (str, optional): 
             The authentication type for the SMTP server
     
     Example:
     
         >>> nessus.mail.edit(smtp_user='******',
         ...                  smtp_pass='******',
         ...                  smtp_auth='LOGIN',
         ...                  )
     '''
     current = self.details()
     updated = dict_merge(current, dict_clean({
         'smtp_host': smtp_host,
         'smtp_port': smtp_port,
         'smtp_enc': smtp_enc,
         'smtp_from': smtp_from,
         'smtp_www_host': smtp_www_host,
         'smtp_user': smtp_user,
         'smtp_pass': smtp_pass,
         'smtp_auth': smtp_auth
     }))
     self._put(json=updated)
Esempio n. 8
0
    def list(self,  # noqa: PLC0103,PLR0913
             name: Optional[str] = None,
             contains: Optional[str] = None,
             offset: int = 0,
             limit: int = 1000,
             return_json: bool = False
             ) -> Union[Dict, CSIterator]:
        '''
        Returns the list of images stored within Container Security.

        :devportal:`API Documentation <container-security-v2-list-repositories>`  # noqa: E501

        Args:
            name (str, optional):
                Image name to filter on.  Filter is case-sensitive
                and enforces an exact match.
            contains (str, optional):
                Partial name to filter on.  Filter is case-sensitive.
            offset (int, optional):
                The number of records to skip before starting to return data.
            limit (int, optional):
                The number of records to return for each page of data.
            return_json (bool, optional):
                If set, then the response will instead be a Dict object instead
                of an iterable.

        Examples:

            Using the default iterable:

            >>> for repo in tio.cs.repositories.list():
            ...     print(repo)

            Getting the raw JSON response:

            >>> resp = tio.cs.repositories.list(return_json=True)
            >>> for item in resp['items']:
            ...     print(item)
        '''
        params = dict_clean({
            'offset': offset,
            'limit': limit,
            'name': name,
            'contains': contains,
        })
        if return_json:
            return self._get(params=params)
        return CSIterator(self._api,
                          _path=self._path,
                          _params=params,
                          _limit=limit,
                          _offset=offset
                          )
Esempio n. 9
0
 def reformat_filters(self, data, **kwargs) -> Dict:  # noqa PLW0613 PLR0201
     '''
     Reformats the response to match what the API expects to see
     '''
     filters = data.pop('filters', None)
     if data.get('search_type'):
         data['filter.search_type'] = data.pop('search_type')
     if filters:
         for f in filters:  # noqa PLC0103
             idx = filters.index(f)
             data[f'filter.{idx}.filter'] = f['filter']
             data[f'filter.{idx}.quality'] = f['quality']
             data[f'filter.{idx}.value'] = f['value']
     return dict_clean(data)
Esempio n. 10
0
    def edit(self,
             name: Optional[str] = None,
             email: Optional[str] = None) -> None:
        '''
        Updates the current user's settings.

        Args:
            name (str, optional): Updated name for the user
            email (str, optional): Updated email for the user

        Example:

            >>> nessus.session.edit(email='*****@*****.**')
        '''
        self._put(json=dict_clean({'name': name, 'email': email}))
Esempio n. 11
0
 def update(self,
            scanner_id: int,
            force_plugin_update: Optional[bool] = None,
            force_ui_update: Optional[bool] = None,
            finish_update: Optional[bool] = None,
            registration_code: Optional[str] = None,
            aws_update_interval: Optional[int] = None) -> None:
     '''
     Update the scanner
     
     Args:
         scanner_id (int): 
             Id of the scanner to update
         force_plugin_update (bool, optional):
             Should the scanner plugins be forcibly updated?
         force_ui_update (bool, optional):
             Should the scanner UI be forcibly updated?
         finish_update (bool, optional):
             Should the scanner service be restarted to run the latest
             software update?  This is only valid if automatic updates on
             the scanner are disabled.
         registration_code (str, optional):
             Sets the registration code for the scanner.
         aws_update_interval (int, optional):
             Informs the scanner how often to check into the controlling
             Nessus service.  This is only valid for AWS scanners.
     
     Example:
         
         >>> nessus.scanners.update(1,
         ...                        force_plugin_update=True,
         ...                        force_ui_update=True
         ...                        )
     '''
     if force_plugin_update is not None:
         force_plugin_update = int(force_plugin_update)
     if force_ui_update is not None:
         force_ui_update = int(force_ui_update)
     if finish_update is not None:
         finish_update = int(finish_update)
     self._put(f'{scanner_id}',
               json=dict_clean({
                   'force_plugin_update': force_plugin_update,
                   'force_ui_update': force_ui_update,
                   'finish_update': finish_update,
                   'registration_code': registration_code,
                   'aws_update_interval': aws_update_interval
               }))
Esempio n. 12
0
 def create(self,
            username: str,
            password: str,
            permissions: int,
            type: Literal['local', 'ldap'] = 'local',
            name: Optional[str] = None,
            email: Optional[str] = None) -> Dict:
     '''
     Creates a new user
     
     Args:
         username (str): The unique username
         password (str): The user's password
         permissions (int): 
             The permission level for the user.  Basic users are ``16``,
             regular Users are ``32``, and administrators are ``64``.
         type (str, optional): 
             The type of user account to create.  The default is ``local``
         name (str, optional): A friendly name for the user
         email (str, optional): The user's email address
     
     Returns:
         Dict:
             The created user object
     
     Example:
     
         >>> nessus.users.create(username='******', 
         ...                     password='******',
         ...                     permissions=32,
         ...                     name='Example User',
         ...                     email='*****@*****.**'
         ...                     )
     '''
     return self._post(json=dict_clean({
         'username': username,
         'password': password,
         'permissions': permissions,
         'type': type,
         'name': name,
         'email': email
     }))
Esempio n. 13
0
    def export_formats(self,
                       scan_id: int,
                       schedule_id: Optional[int] = None) -> Dict:
        '''
        Returns the available export formats and report options.

        Args:
            scan_id (int): The scan to export
            schedule_id (int, optional):
                The schedule id associated with the scan

        Returns:
            Dict:
                The available export and report options

        Example:

            >>> nessus.scans.export_formats(1)
        '''
        return self._get(f'{scan_id}/export/formats',
                         params=dict_clean({'schedule_id': schedule_id}))
Esempio n. 14
0
 def restart(self,
             reason: Optional[str] = None,
             soft: Optional[bool] = None,
             unlink: Optional[bool] = None,
             when_idle: Optional[bool] = None) -> None:
     '''
     Initiates a restart of this Nessus service
     
     Args:
         reason (str, optional):
             What is the reason for the restart to occur?
         soft (bool, optional):
             Should we only restart the web service (soft restart) or
             restart the whole Nessus service?
         unlink (bool, optional):
             Should the scanner be unlinked from it's upstream controller
             before restarting?
         when_idle (bool, optional):
             Should the scanner restart once there are no running scans?
     
     Example:
         
         >>> nessus.server.restart(reason='Time to restart',
         ...                       when_idle=True,
         ...                       soft=True
         ...                       )
     '''
     if soft is not None:
         soft = str(soft).lower()
     if unlink is not None:
         unlink = str(unlink).lower()
     if when_idle is not None:
         when_idle = str(when_idle).lower()
     return self._get('restart',
                      params=dict_clean({
                          'reason': reason,
                          'soft': soft,
                          'unlink': unlink,
                          'when_idle': when_idle
                      }))
Esempio n. 15
0
    def list(
            self,  # noqa: PLC0103,PLR0913
            name: Optional[str] = None,
            repo: Optional[str] = None,
            tag: Optional[str] = None,
            has_malware: Optional[bool] = None,
            score: Optional[int] = None,
            score_operator: Optional[Literal['EQ', 'GT', 'LT']] = None,
            os: Optional[str] = None,
            offset: int = 0,
            limit: int = 1000,
            return_json: bool = False) -> Union[Dict, CSIterator]:
        '''
        Returns the list of images stored within Container Security.

        :devportal:`API Documentation <container-security-v2-list-images>`

        Args:
            name (str, optional):
                Image name to filter on.  Filter is case-sensitive
                and enforces an exact match.
            repo (str, optional):
                Repository name to filter on.  Filter is case-sensitive
                and enforces an exact match.
            tag (str, optional):
                Tag to filter on.  Filter is case-sensitive and enforces
                an exact match.
            has_malware (bool, optional):
                Specifies whether to return only images with malware
                associated to them.
            score (int, optional):
                The score value to filter on.
            score_operator (str, optional):
                The score operator to use with the score value.  Supported
                operations are ``EQ`` (equal), ``GT`` (greater-than), and
                ``LT`` (less-than).
            os (str, optional):
                The operating system to filter on.  Filter is case-sensitive
                and enforces an exact match.
            offset (int, optional):
                The number of records to skip before starting to return data.
            limit (int, optional):
                The number of records to return for each page of data.
            return_json (bool, optional):
                If set, then the response will instead be a Dict object instead
                of an iterable.

        Examples:

            Using the default iterable:

            >>> for image in tio.cs.images.list():
            ...     print(image)

            Getting the raw JSON response:

            >>> resp = tio.cs.images.list(return_json=True)
            >>> for item in resp['items']:
            ...     print(item)
        '''
        params = dict_clean({
            'offset': offset,
            'limit': limit,
            'name': name,
            'repo': repo,
            'tag': tag,
            'hasMalware': has_malware,
            'score': score,
            'scoreOperator': score_operator,
            'os': os
        })
        if return_json:
            return self._get(params=params)
        return CSIterator(self._api,
                          _path=self._path,
                          _params=params,
                          _limit=limit,
                          _offset=offset)
Esempio n. 16
0
    def edit(self,
             exclusion_id,
             scanner_id=1,
             name=None,
             start_time=None,
             end_time=None,
             timezone=None,
             description=None,
             frequency=None,
             interval=None,
             weekdays=None,
             day_of_month=None,
             enabled=None):
        '''
        Edit an existing agent exclusion.

        :devportal:`agent-exclusions: edit <agent-exclusions-edit>`

        The edit function will first gather the details of the exclusion that
        will be edited and will overlay the changes on top.  The result will
        then be pushed back to the API to modify the exclusion.

        Args:
            exclusion_id (int): The id of the exclusion object in Tenable.io
            scanner_id (int, optional): The scanner id.
            name (str, optional): The name of the exclusion to create.
            description (str, optional):
                Some further detail about the exclusion.
            start_time (datetime, optional): When the exclusion should start.
            end_time (datetime, optional): When the exclusion should end.
            timezone (str, optional):
                The timezone to use for the exclusion.  The default if none is
                specified is to use UTC.
            frequency (str, optional):
                The frequency of the rule. The string inputted will be up-cased.
                Valid values are: *ONETIME, DAILY, WEEKLY, MONTHLY, YEARLY*.
            interval (int, optional): The interval of the rule.
            weekdays (list, optional):
                List of 2-character representations of the days of the week to
                repeat the frequency rule on.  Valid values are:
                *SU, MO, TU, WE, TH, FR, SA*
                Default values: ``['SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA']``
            day_of_month (int, optional):
                The day of the month to repeat a **MONTHLY** frequency rule on.
            enabled (bool, optional):
                enable/disable exclusion.

        Returns:
            dict: Dictionary of the newly minted exclusion.

        Examples:
            >>> exclusion = tio.agent_exclusions.edit(1, name='New Name')
        '''

        # Lets start constructing the payload to be sent to the API...
        payload = self.details(exclusion_id, scanner_id=scanner_id)

        if name:
            payload['name'] = self._check('name', name, str)

        if description:
            payload['description'] = self._check('description', description,
                                                 str)

        if enabled is not None:
            payload['schedule']['enabled'] = self._check(
                'enabled', enabled, bool)

        if payload['schedule']['enabled']:
            frequency = self._check(
                'frequency',
                frequency,
                str,
                choices=['ONETIME', 'DAILY', 'WEEKLY', 'MONTHLY', 'YEARLY'],
                default=payload['schedule']['rrules']['freq'],
                case='upper')

            rrules = {
                'freq': frequency,
                'interval': payload['schedule']['rrules']['interval'],
                'byweekday': None,
                'bymonthday': None,
            }

            # frequency default value is designed for weekly and monthly based on below conditions
            # - if schedule rrules is not None and not defined in edit params,
            #   and byweekday/bymonthday key already exist, assign old values
            # - if schedule rrules is not None and not defined in edit params
            #   and byweekday/bymonthday key not already exist, assign default values
            # - if schedule rrules is not None and defined in edit params, assign new values
            if frequency == 'WEEKLY':
                rrules['byweekday'] = ','.join(
                    self._check(
                        'weekdays',
                        weekdays,
                        list,
                        choices=['SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA'],
                        default=payload['schedule']['rrules'].get(
                            'byweekday', '').split()
                        or ['SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA'],
                        case='upper'))
                # In the same vein as the frequency check, we're accepting
                # case-insensitive input, comparing it to our known list of
                # acceptable responses, then joining them all together into a
                # comma-separated string.

            if frequency == 'MONTHLY':
                rrules['bymonthday'] = self._check(
                    'day_of_month',
                    day_of_month,
                    int,
                    choices=list(range(1, 32)),
                    default=payload['schedule']['rrules'].get(
                        'bymonthday',
                        datetime.today().day))

            # update new rrules in existing payload
            dict_merge(payload['schedule']['rrules'], rrules)
            # remove null values from payload
            payload = dict_clean(payload)

            if start_time:
                payload['schedule']['starttime'] = self._check(
                    'start_time', start_time,
                    datetime).strftime('%Y-%m-%d %H:%M:%S')

            if end_time:
                payload['schedule']['endtime'] = self._check(
                    'end_time', end_time,
                    datetime).strftime('%Y-%m-%d %H:%M:%S')

            if interval:
                payload['schedule']['rrules']['interval'] = self._check(
                    'interval', interval, int)

            if timezone:
                payload['schedule']['timezone'] = self._check(
                    'timezone', timezone, str, choices=self._api._tz)

        # Lets check to make sure that the scanner_id  and exclusion_id are
        # integers as the API documentation requests and if we don't raise an
        # error, then lets make the call.
        return self._api.put('scanners/{}/agents/exclusions/{}'.format(
            self._check('scanner_id', scanner_id, int),
            self._check('exclusion_id', exclusion_id, int)),
                             json=payload).json()
Esempio n. 17
0
    def create(self, infrastructure_id: int, name: str, ip: str, dns: str,
               **kwargs) -> List[Dict]:
        '''
        Creates a new directory instance.

        Args:
            infrastructure_id (int):
                The infrastructure object to bind this directory to.
            name (str):
                Name of the directory instance.
            ip (str):
                The IP Address of the directory server.
            dns (str):
                The DNS domain that this directory is tied to.
            directory_type (optional, str):
                The directory's type.
            ldap_port (optional, str):
                The port number associated to the LDAP service on the
                directory server.
            global_catalog_port (optional, str):
                The port number associated to the Global Catalog service
                running on the directory server.
            smb_port (optional, str):
                The port number associated to the Server Messaging
                Block (SMB) service running on the directory server.

        Returns:
            dict:
                The created directory instance.

        Examples:
            >>> tad.directories.create(
            ...     infrastructure_id=1,
            ...     name='ExampleServer',
            ...     ip='172.16.0.1',
            ...     directory_type='????',
            ...     dns='company.tld',
            ...     )
        '''
        schema = DirectorySchema(unknown=INCLUDE)
        payload = [
            schema.dump(
                schema.load(
                    dict_clean({
                        'infrastructureId':
                        infrastructure_id,
                        'name':
                        name,
                        'ip':
                        ip,
                        'type':
                        kwargs.get('directory_type'),
                        'dns':
                        dns,
                        'ldapPort':
                        kwargs.get('ldap_port'),
                        'globalCatalogPort':
                        kwargs.get('global_catalog_port'),
                        'smbPort':
                        kwargs.get('smb_port')
                    })))
        ]
        return schema.load(self._post(json=payload), many=True)