def bot_scan_backend(community, redis, consumer_name, api_key):
    session = requests.Session()
    api = PolyswarmAPI(api_key, community=community)
    consumer = EventConsumer(['scan-requests'], 'bot-scan-backend',
                             consumer_name, get_walrus_db(redis), ScanRequest)
    producer = EventProducer('scan-results', get_walrus_db(redis))

    waiter = ResultWaiter(api, producer)

    with ThreadPoolExecutor() as pool:
        for event in consumer.iter_events():
            try:
                event = ScanRequest()
                if event.artifact_type == 'FILE':
                    stream = session.get(event.uri, stream=True).raw
                    result = api.submit(stream)
                elif event.artifact_type == 'URL':
                    result = api.submit(
                        event.uri, artifact_type=resources.ArtifactType.URL)
                else:
                    logger.warning(
                        'Unsupported artifact type %s, maybe update this backend',
                        event.artifact_type)
                    continue
                future = pool.submit(waiter.wait_for_result, result.id,
                                     event.context)

            except Exception as e:
                logger.exception('Exception occurred processing event %s: %s',
                                 event, e)
Example #2
0
class PolyswarmConnector():
    def __init__(self):
        self.config = {}  # type: Dict[str,str]
        self.config['polyswarm_api_key'] = demisto.params().get('api_key')
        self.config['base_url'] = demisto.params().get('base_url')
        self.config['polyswarm_community'] = demisto.params().get('polyswarm_community')

        self.polyswarm_api = PolyswarmAPI(key=self.config['polyswarm_api_key'],
                                          uri=self.config['base_url'])

    def _get_results(self,
                     object_name: str,
                     title: str,
                     total_scans: int,
                     positives: int,
                     permalink: str,
                     artifact: str,
                     indicator: object) -> object:

        results = {'Scan_UUID': artifact,
                   'Total': str(total_scans),
                   'Positives': str(positives),
                   'Permalink': permalink,
                   'Artifact': artifact}

        readable_output = tableToMarkdown(title,
                                          results)
        return CommandResults(
            readable_output=readable_output,
            outputs_prefix=f'PolySwarm.{object_name}',
            outputs_key_field='Scan_UUID',
            outputs=results,
            indicator=indicator,  # type: ignore
            ignore_auto_extract=True)

    def test_connectivity(self) -> bool:
        EICAR_HASH = '131f95c51cc819465fa1797f6ccacf9d494aaaff46fa3eac73ae63ffbdfd8267'  # guardrails-disable-line

        try:
            results = self.polyswarm_api.search(EICAR_HASH)
            for result in results:
                if result.failed:
                    return False
        except Exception:
            return False

        return True

    def get_score(self,
                  polyscore) -> int:

        if float(polyscore) < 0.2:
            return Common.DBotScore.GOOD
        elif 0.2 <= float(polyscore) < 0.7:
            return Common.DBotScore.SUSPICIOUS
        else:  # polyscore is >= 0.7
            return Common.DBotScore.BAD

    def return_hash_results(self,
                            results: list,
                            title: str,
                            error_msg: str) -> object:

        # default values
        total_scans: int = 0
        positives: int = 0
        md5: str = ''
        sha256: str = ''
        sha1: str = ''
        polyscore: int = 0

        for result in results:
            if result.failed:
                return_error(error_msg)

            if not result.assertions:
                return_error('Run Rescan for this hash')

            # iterate for getting positives and total_scan number
            for assertion in result.assertions:
                if assertion.verdict:
                    positives += 1
                total_scans += 1

            demisto.debug('Positives: {positives} - Total Scans: {total_scans}'.
                          format(positives=positives, total_scans=total_scans))

            md5 = result.md5
            sha256 = result.sha256
            sha1 = result.sha1

            polyscore = result.polyscore

        dbot_score = Common.DBotScore(indicator=md5,
                                      indicator_type=DBotScoreType.FILE,
                                      integration_name='PolySwarm',
                                      score=self.get_score(polyscore))

        indicator = Common.File(md5=md5,
                                sha1=sha1,
                                sha256=sha256,
                                dbot_score=dbot_score)

        return self._get_results('File', title, total_scans,
                                 positives, result.permalink,
                                 sha256, indicator)

    def file_reputation(self,
                        hashes: list) -> object:
        command_results = []

        artifacts = argToList(hashes)

        for artifact in artifacts:
            title = 'PolySwarm File Reputation for Hash: %s' % artifact

            demisto.debug(f'[file_reputation] {title}')

            try:
                results = self.polyswarm_api.search(artifact)

            except Exception as err:
                return_error('{ERROR_ENDPOINT}{err}'.
                             format(ERROR_ENDPOINT=ERROR_ENDPOINT,
                                    err=err))

            error_msg = 'Error fetching results. Please try again.'

            command_results.append(self.return_hash_results(results,
                                                            title,
                                                            error_msg))
        return command_results

    def detonate_file(self, entry_id: dict) -> object:
        title = 'PolySwarm File Detonation for Entry ID: %s' % entry_id

        demisto.debug(f'[detonate_file] {title}')

        try:
            file_info = demisto.getFilePath(entry_id)
        except Exception:
            return_error(f'File not found - EntryID: {entry_id}')

        try:
            demisto.debug(f'Submit file: {file_info}')
            instance = self.polyswarm_api.submit(file_info['path'],
                                                 artifact_name=file_info['name'])
            result = self.polyswarm_api.wait_for(instance)

        except Exception as err:
            return_error('{ERROR_ENDPOINT}{err}'.
                         format(ERROR_ENDPOINT=ERROR_ENDPOINT,
                                err=err))

        error_msg = 'Error submitting File.'

        return self.return_hash_results([result],
                                        title,
                                        error_msg)

    def rescan_file(self,
                    hashes: list) -> object:
        command_results = []

        artifacts = argToList(hashes)

        for artifact in artifacts:
            title = 'PolySwarm Rescan for Hash: %s' % artifact

            demisto.debug(f'[rescan_file] {title}')

            try:
                instance = self.polyswarm_api.rescan(artifact)
                result = self.polyswarm_api.wait_for(instance)

            except Exception as err:
                return_error('{ERROR_ENDPOINT}{err}'.
                             format(ERROR_ENDPOINT=ERROR_ENDPOINT,
                                    err=err))

            error_msg = 'Error rescaning File.'

            command_results.append(self.return_hash_results([result],
                                                            title,
                                                            error_msg))
        return command_results

    def get_file(self, hash_file: str):
        demisto.debug(f'[get_file] Hash: {hash_file}')

        handle_file = io.BytesIO()

        try:
            self.polyswarm_api.download_to_handle(hash_file,
                                                  handle_file)
            return fileResult(hash_file, handle_file.getvalue())
        except Exception as err:
            return_error('{ERROR_ENDPOINT}{err}'.
                         format(ERROR_ENDPOINT=ERROR_ENDPOINT,
                                err=err))

    def url_reputation(self,
                       param: dict,
                       artifact_type: str) -> list:

        command_results = []

        artifacts = argToList(param[artifact_type])

        for artifact in artifacts:
            title = 'PolySwarm %s Reputation for: %s' % (artifact_type.upper(),
                                                         artifact)

            demisto.debug(f'[url_reputation] {title}')

            # default values
            total_scans = 0
            positives = 0
            polyscore = 0

            # IP validation
            if artifact_type == 'ip':
                try:
                    socket.inet_aton(artifact)
                except socket.error:
                    return_error('Invalid IP Address: {ip}'.
                                 format(ip=artifact))

            try:
                # PolySwarm API: URL, IP and Domain are artifact_type='url'
                instance = self.polyswarm_api.submit(artifact,
                                                     artifact_type='url')
                result = self.polyswarm_api.wait_for(instance)

                if result.failed:
                    return demisto.results('Error submitting URL.')

                # iterate for getting positives and total_scan number
                for assertion in result.assertions:
                    if assertion.verdict:
                        positives += 1
                    total_scans += 1

                polyscore = result.polyscore

            except Exception as err:
                return_error('{ERROR_ENDPOINT}{err}'.
                             format(ERROR_ENDPOINT=ERROR_ENDPOINT,
                                    err=err))

            if artifact_type == 'ip':
                object_name = 'IP'
                dbot_score_type = DBotScoreType.IP
            elif artifact_type == 'url':
                object_name = 'URL'
                dbot_score_type = DBotScoreType.URL
            elif artifact_type == 'domain':
                object_name = 'Domain'
                dbot_score_type = DBotScoreType.DOMAIN

            dbot_score = Common.DBotScore(indicator=artifact,
                                          indicator_type=dbot_score_type,
                                          integration_name='PolySwarm',
                                          score=self.get_score(polyscore))

            indicator = None
            if artifact_type == 'ip':
                indicator = Common.IP(ip=artifact,
                                      dbot_score=dbot_score)
            elif artifact_type == 'url':
                indicator = Common.URL(url=artifact,
                                       dbot_score=dbot_score)  # type: ignore
            elif artifact_type == 'domain':
                indicator = Common.Domain(domain=artifact,
                                          dbot_score=dbot_score)  # type: ignore

            results = self._get_results(object_name, title, total_scans,
                                        positives, result.permalink,
                                        artifact, indicator)
            command_results.append(results)

        return command_results

    def get_report(self,
                   hashes: list) -> object:
        """
            UUID is equal to Hash.
        """
        title = 'PolySwarm Report for UUID: %s' % hashes

        demisto.debug(f'[get_report] {title}')

        return self.file_reputation(hashes)