Ejemplo n.º 1
0
    def _gen_doc_format(self, vuln, fid, fdef):
        '''
        Converts the YAML field definition into the processed result.
        '''
        tconf = self.config.get('truncation', dict())
        # if the definition is a list, then we are dealing with a document
        # structure and should build the appropriate dict structure.
        if isinstance(fdef, list):
            content = list()
            for item in fdef:
                # The heading is derrived from the name attribute
                content.append({
                    'type':
                    'heading',
                    'attrs': {
                        'level': 1
                    },
                    'content': [{
                        'type': 'text',
                        'text': item['name']
                    }]
                })

                # The paragraph is derived from the appropriate
                # field parameter.  If the string formatting fails
                # from a KeyError, then replace the output with
                # an empty paragraph.
                try:
                    content.append({
                        'type':
                        'paragraph',
                        'content': [{
                            'type':
                            'text',
                            'text':
                            trunc(item[fid].format(vuln=vuln),
                                  tconf.get('limit', 10000),
                                  tconf.get('suffix', '...'))
                        }]
                    })
                except KeyError:
                    content.append({
                        'type':
                        'paragraph',
                        'content': [{
                            'type': 'text',
                            'text': 'No Output'
                        }]
                    })
            return {'version': 1, 'type': 'doc', 'content': content}

        # if the definition is a dictionary, then this is a simple single-field
        # response and we should simple return back the processed string.
        elif isinstance(fdef, dict):
            return trunc(fdef[fid].format(vuln=vuln), limit=255)
Ejemplo n.º 2
0
    def _process_vuln(self, vuln, fid):
        '''
        Processes a singular vulnerability and adds/updates the appropriate
        Jira issues.
        '''
        issue = self._gen_issue_skel()
        subissue = self._gen_subissue_skel()
        jql = [
            'project = "{}"'.format(self._project['key']),
            'issuetype = "{}"'.format(self.task['name']),
            'status not in (Closed, Done, Resolved)'
        ]
        sjql = [
            'project = "{}"'.format(self._project['key']),
            'issuetype = "{}"'.format(self.subtask['name']),
            'status not in (Closed, Done, Resolved)'
        ]
        sevprio = self.config['tenable'].get('severity_prioritization')

        for f in self._fields:
            # determine the JQL operator that we may need to use.
            if f['type'] == 'labels':
                oper = '='
            else:
                oper = '~'

            if f.get('static_value'):
                value = f.get('static_value')
            elif f.get('is_platform_id'):
                _, value = self._get_platform()
            elif f.get('is_tio_tags') and fid == 'tio_field':
                value = vuln.get('asset.tags')
            else:
                value = vuln.get(f.get(fid))

            # Here we will be setting the severity priority for the task and
            # subtask.
            if sevprio and f['jira_field'] == 'Finding Severity':
                subissue['priority'] = {
                    'id': str(sevprio.get(value.lower(), 4))
                }
                self._log.debug(f'Setting Finding Sev to {value.lower()}')
            if sevprio and f['jira_field'] == 'Vulnerability Severity':
                issue['priority'] = {'id': str(sevprio.get(value.lower(), 4))}
                self._log.debug(f'Setting Vuln Sev to {value.lower()}')

            processed = None

            if value:
                # for text-type fields, only sent the field if there is some
                # sort of data in it and recast the field as a string.
                if f['type'] in ['readonlyfield', 'textarea']:
                    processed = str(value)
                    if f['type'] in ['readonlyfield']:
                        processed = trunc(processed, 255)

                # for labels, just pass on the field as-is
                elif f['type'] in ['labels']:
                    if isinstance(value, str):
                        if fid == 'tsc_field':
                            processed = value.split(',')
                        else:
                            processed = [
                                value,
                            ]
                    else:
                        processed = value

                # For datetime fields, validate that the field actually had
                # a value and then convert it into the appropriate format.
                elif f['type'] in ['datetime']:

                    try:
                        processed = arrow.get(value).format(
                            'YYYY-MM-DDTHH:mm:ss.SSSZ')
                    except arrow.parser.ParserError:
                        processed = arrow.get(
                            int(value)).format('YYYY-MM-DDTHH:mm:ss.SSSZ')

                # For anything else, just pass through
                else:
                    processed = value

                if self.task['name'] in f['issue_type']:
                    issue[f['jira_id']] = processed
                if self.subtask['name'] in f['issue_type']:
                    subissue[f['jira_id']] = processed

            # Handle any JQL conversions that need to be done in order to make
            # the JQL statement valid.
            if isinstance(processed, list):
                if len(processed) > 1:
                    oper = "in"
                    svalue = '({})'.format(','.join(
                        ["{}".format(x) for x in processed]))
                else:
                    svalue = processed[0]
            elif not processed:
                oper = "is"
                svalue = "EMPTY"
            else:
                svalue = '"{}"'.format(processed)

            # construct the JQL statement
            jql_statement = '"{}" {} {}'.format(f['jira_field'], oper, svalue)

            # Add the JQL statement as necessary to the appropriate JQL queries.
            if f['jira_field'] in self.task['search']:
                jql.append(jql_statement)
            if f['jira_field'] in self.subtask['search']:
                sjql.append(jql_statement)

        # Now to process the default fields.
        for field in self.config['issue_default_fields']:
            fdef = self.config['issue_default_fields'][field]
            if self.task['name'] in fdef:
                issue[field] = self._gen_doc_format(vuln, fid,
                                                    fdef[self.task['name']])
            if self.subtask['name'] in fdef:
                subissue[field] = self._gen_doc_format(
                    vuln, fid, fdef[self.subtask['name']])
        return issue, subissue, jql, sjql
    def _transform_vuln(self, vuln):
        '''
        Transforms a Tenable.io exported vulnerability into the IBM Security
        Connect format.

        Args:
            vuln (dict): The Tenable.io vulnerability format
        '''

        plugin = vuln['plugin']
        asset = vuln['asset']
        port = vuln['port']
        statusmap = {
            'OPEN': 'ACTIVE',
            'NEW': 'ACTIVE',
            'REOPENED': 'ACTIVE',
            'FIXED': 'INACTIVE'
        }
        sevmap = {0: 0.0, 1: 3.0, 2: 5.0, 3: 7.0, 4: 10.0}

        self._add_to_cache(
            'vulnerability',
            {
                'external_id':
                str(plugin['id']),
                'external_reference':
                ''.join([
                    'https://cloud.tenable.com',
                    '/tio/app.html#/vulnerability-management',
                    '/vulnerabilities/by-plugins/vulnerability-details',
                    '/{}/overview'.format(plugin['id'])
                ]),
                'name':
                plugin['name'],
                'description':
                plugin['description'],
                'disclosed_on':
                self._ts(plugin.get('vuln_publication_date', '')),
                'published_on':
                self._ts(plugin.get('publication_date', '')),
                'updated_at':
                self._ts(plugin.get('modification_date', '')),
                'base_score':
                plugin.get('cvss_base_score', sevmap[vuln['severity_id']]),
                'source':
                'tenable.io',

                ### Extended Fields Specific to Tenable.io
                'links':
                plugin.get('see_also', list()),
                'cpes':
                plugin.get('cpe', list()),
                'solution':
                trunc(plugin.get('solution', ''), 1024),
                'synopsis':
                trunc(plugin.get('synopsis', ''), 1024),
            })

        self._add_to_cache(
            'asset_vulnerability',
            {
                '_from_external_id': '{}'.format(asset['uuid']),
                '_to_external_id': '{}'.format(plugin['id']),
                'timestamp': self._ts(arrow.utcnow()),
                'last_modified': self._ts(vuln['last_found']),
                'active': vuln['state'] != 'FIXED',
                'report': str(self._report_id),
                'source': 'tenable.io',
                'port': {
                    'port_number': int(port['port']),
                    'protocol': '',
                    'status': statusmap[vuln['state']],
                },

                ### Extended Fields Specific to Tenable.io
                'vuln_output': vuln.get('output', ''),
            })
Ejemplo n.º 4
0
def test_trunc():
    assert trunc('Hello There!', 128) == 'Hello There!'
    assert trunc('Too Small', 6) == 'Too...'
    assert trunc('Too Small', 3, suffix=None) == 'Too'