Exemplo n.º 1
0
    def handle_get(cls, **kwargs):
        # pylint: disable=singleton-comparison, too-many-branches, too-many-statements
        retval = {
            'system_count': 0,
            'cves_total': 0,
            'cves_by_severity': {
                '0to3.9': {
                    'percentage': 0,
                    'count': 0,
                    'known_exploit_count': 0
                },
                '4to7.9': {
                    'percentage': 0,
                    'count': 0,
                    'known_exploit_count': 0
                },
                '8to10': {
                    'percentage': 0,
                    'count': 0,
                    'known_exploit_count': 0
                },
                'na': {
                    'percentage': 0,
                    'count': 0,
                    'known_exploit_count': 0
                }
            },
            'recent_cves': {
                'last7days': 0,
                'last30days': 0,
                'last90days': 0
            },
            'rules_total': 0,
            'rules_by_severity': {
                1: {
                    'rule_count': 0,
                    'systems_affected': 0
                },
                2: {
                    'rule_count': 0,
                    'systems_affected': 0
                },
                3: {
                    'rule_count': 0,
                    'systems_affected': 0
                },
                4: {
                    'rule_count': 0,
                    'systems_affected': 0
                },
            },
            'top_cves': [],
            'top_rules': [],
        }
        rh_account, cve_cache_from, cve_cache_keepalive = get_account_data(connexion.context['user'])
        if rh_account is None:
            return retval
        retval['system_count'] = get_system_count(rh_account)
        if retval['system_count'] == 0:
            return retval

        # API using cache, set keepalive for account to enable maintaining cache
        update_cve_cache_keepalive(rh_account, cve_cache_keepalive)

        # Use cache if not disabled + cache exists
        if not DISABLE_ACCOUNT_CACHE and cve_cache_from:
            count_query = (CveAccountCache
                           .select(CveAccountCache.cve_id.alias("cve_id_"),
                                   CveAccountCache.systems_affected.alias("systems_affected_"))
                           .where(CveAccountCache.rh_account_id == rh_account))
        else:
            count_query = (SystemVulnerabilities
                           .select(SystemVulnerabilities.cve_id.alias("cve_id_"),
                                   fn.Count(SystemVulnerabilities.id).alias("systems_affected_"))
                           .join(SystemPlatform, on=(SystemVulnerabilities.system_id == SystemPlatform.id) &
                                                    (SystemPlatform.rh_account_id == rh_account) &
                                                    (SystemPlatform.opt_out == False) &
                                                    (SystemPlatform.stale == False) &
                                                    (SystemPlatform.when_deleted.is_null(True)) &
                                                    (fn.COALESCE(SystemPlatform.host_type, 'null') != HostType.EDGE))
                           .where(SystemVulnerabilities.rh_account_id == rh_account)
                           .where((SystemVulnerabilities.mitigation_reason.is_null(True)) |
                                  (SystemVulnerabilities.rule_id << InsightsRule.select(InsightsRule.id).where(
                                   (InsightsRule.active == False) & (~InsightsRule.rule_only))))
                           .where((SystemVulnerabilities.when_mitigated.is_null(True)) |
                                  (SystemVulnerabilities.rule_id << InsightsRule.select(InsightsRule.id).where(
                                   (InsightsRule.active == True) & (~InsightsRule.rule_only))))
                           .group_by(SystemVulnerabilities.cve_id))
            count_query = cyndi_join(count_query)

        cve_query = (CveMetadata
                     .select(CveMetadata.id.alias("cve_id"),
                             fn.COALESCE(CveMetadata.cvss3_score, CveMetadata.cvss2_score).alias('cvss_score'),
                             CveMetadata.public_date, CveMetadata.exploits)
                     .join(count_query, JOIN.INNER, on=(CveMetadata.id == count_query.c.cve_id_))
                     .dicts())

        cve_data = [(cve["cvss_score"], cve["public_date"], cve["exploits"]) for cve in cve_query]

        cves_total = len(cve_data)
        if cves_total == 0:
            return retval

        retval['cves_total'] = cves_total

        today = datetime.now(timezone.utc).replace(hour=0, minute=0, second=0, microsecond=0)  # offset-aware
        last7 = today - timedelta(days=7)
        last30 = today - timedelta(days=30)
        last90 = today - timedelta(days=90)

        for cvss_score, public_date, exploits in cve_data:
            if cvss_score is None:
                retval["cves_by_severity"]["na"]["count"] += 1
                if exploits:
                    retval["cves_by_severity"]["na"]["known_exploit_count"] += 1
            elif cvss_score < 4:
                retval["cves_by_severity"]["0to3.9"]["count"] += 1
                if exploits:
                    retval["cves_by_severity"]["0to3.9"]["known_exploit_count"] += 1
            elif 4 <= cvss_score < 8:
                retval["cves_by_severity"]["4to7.9"]["count"] += 1
                if exploits:
                    retval["cves_by_severity"]["4to7.9"]["known_exploit_count"] += 1
            elif cvss_score >= 8:
                retval["cves_by_severity"]["8to10"]["count"] += 1
                if exploits:
                    retval["cves_by_severity"]["8to10"]["known_exploit_count"] += 1

            if public_date is not None:
                if public_date >= last7:
                    retval["recent_cves"]["last7days"] += 1
                if public_date >= last30:
                    retval["recent_cves"]["last30days"] += 1
                if public_date >= last90:
                    retval["recent_cves"]["last90days"] += 1

        cve_count_by_severity = [v['count'] for v in retval['cves_by_severity'].values()]
        rounded_percentage = round_to_100_percent(cve_count_by_severity)

        for indx, keys in enumerate(retval['cves_by_severity']):
            retval['cves_by_severity'][keys]['percentage'] = rounded_percentage[indx]

        # The algorithm searches for CVEs with cvss score between 8 and 10, and then sort by a number of affected
        # systems if there are not 3 CVE in the 8 to 10 range, then it looks for CVEs in 4 to 8 range, sorted by a
        # number of systems affected. The high-end range check is exclusive that is why 11 here.
        cves_limit = 3
        top_cves = cls._get_top_cves_by_cvss(8.0, 11, count_query, limit=cves_limit)
        cls._build_top_cves(top_cves, retval)
        cves_count = top_cves.count()
        if cves_count < cves_limit:
            next_tier_top = cls._get_top_cves_by_cvss(4.0, 8.0, count_query, limit=cves_limit - cves_count)
            cls._build_top_cves(next_tier_top, retval)
            next_cves_count = next_tier_top.count()
            if next_cves_count < (cves_limit - cves_count):
                last_tier_top = cls._get_top_cves_by_cvss(0.0, 4.0, count_query, limit=cves_limit - (cves_count + next_cves_count))
                cls._build_top_cves(last_tier_top, retval)

        rules_breakdown = (SystemVulnerabilities.select(fn.COUNT(fn.Distinct(InsightsRule.id)).alias('rule_count'), InsightsRule.rule_impact.alias('severity'),
                                                        fn.COUNT(fn.Distinct(SystemVulnerabilities.system_id)).alias('systems_affected'))
                           .join(InsightsRule, on=(SystemVulnerabilities.rule_id == InsightsRule.id))
                           .join(SystemPlatform, on=(SystemVulnerabilities.system_id == SystemPlatform.id) &
                                                    (SystemPlatform.rh_account_id == rh_account) &
                                                    (SystemPlatform.opt_out == False) &
                                                    (SystemPlatform.stale == False) &
                                                    (SystemPlatform.when_deleted.is_null(True)) &
                                                    (SystemPlatform.last_evaluation.is_null(False) |
                                                     SystemPlatform.advisor_evaluated.is_null(False)) &
                                                    (fn.COALESCE(SystemPlatform.host_type, 'null') != HostType.EDGE))
                           .where(SystemVulnerabilities.rh_account_id == rh_account)
                           .where((SystemVulnerabilities.mitigation_reason.is_null(True)) &
                                  (SystemVulnerabilities.rule_id << InsightsRule.select(InsightsRule.id).where((InsightsRule.active == True)
                                                                                                               & (~InsightsRule.rule_only))))
                           .group_by(InsightsRule.rule_impact)
                           .dicts())

        rules_breakdown = cyndi_join(rules_breakdown)

        for section in rules_breakdown:
            retval['rules_by_severity'][section['severity']]['rule_count'] = section['rule_count']
            retval['rules_by_severity'][section['severity']]['systems_affected'] = section['systems_affected']

        retval['rules_total'] = sum([item['rule_count'] for item in rules_breakdown])

        if not DISABLE_ACCOUNT_CACHE and cve_cache_from:
            top_rules = (RuleAccountCache.select(InsightsRule.name.alias('rule_id'), InsightsRule.description_text.alias('name'),
                                                 InsightsRule.rule_impact.alias('severity'), InsightsRule.summary_text.alias('description'),
                                                 RuleAccountCache.systems_affected, fn.ARRAY_AGG(fn.Distinct(CveMetadata.cve)).alias('associated_cves'))
                         .join(InsightsRule, on=(RuleAccountCache.rule_id == InsightsRule.id))
                         .join(CveRuleMapping, on=(InsightsRule.id == CveRuleMapping.rule_id))
                         .join(CveMetadata, on=(CveRuleMapping.cve_id == CveMetadata.id))
                         .where(RuleAccountCache.rh_account_id == rh_account)
                         .group_by(InsightsRule.name, InsightsRule.description_text, InsightsRule.rule_impact, InsightsRule.summary_text,
                                   RuleAccountCache.systems_affected)
                         .order_by(InsightsRule.rule_impact.desc(), RuleAccountCache.systems_affected.desc(), InsightsRule.description_text, InsightsRule.name)
                         .limit(3)
                         .dicts())
        else:
            top_rules = (SystemVulnerabilities.select(InsightsRule.name.alias('rule_id'), InsightsRule.description_text.alias('name'),
                                                      InsightsRule.rule_impact.alias('severity'), InsightsRule.summary_text.alias('description'),
                                                      fn.COUNT(fn.Distinct(SystemVulnerabilities.system_id)).alias('systems_affected'),
                                                      fn.ARRAY_AGG(fn.Distinct(CveMetadata.cve)).alias('associated_cves'))
                         .join(InsightsRule, on=(SystemVulnerabilities.rule_id == InsightsRule.id))
                         .join(CveRuleMapping, on=(InsightsRule.id == CveRuleMapping.rule_id))
                         .join(CveMetadata, on=(CveRuleMapping.cve_id == CveMetadata.id))
                         .join(SystemPlatform, on=(SystemVulnerabilities.system_id == SystemPlatform.id) &
                                                  (SystemPlatform.rh_account_id == rh_account) &
                                                  (SystemPlatform.opt_out == False) &
                                                  (SystemPlatform.stale == False) &
                                                  (SystemPlatform.when_deleted.is_null(True)) &
                                                  (SystemPlatform.last_evaluation.is_null(False) |
                                                   SystemPlatform.advisor_evaluated.is_null(False)) &
                                                  (fn.COALESCE(SystemPlatform.host_type, 'null') != HostType.EDGE))
                         .where(SystemVulnerabilities.rh_account_id == rh_account)
                         .where((SystemVulnerabilities.mitigation_reason.is_null(True)) &
                                (SystemVulnerabilities.rule_id << InsightsRule.select(InsightsRule.id).where((InsightsRule.active == True)
                                                                                                             & (~InsightsRule.rule_only))))
                         .group_by(InsightsRule.name, InsightsRule.description_text, InsightsRule.rule_impact, InsightsRule.summary_text)
                         .order_by(InsightsRule.rule_impact.desc(), SQL('systems_affected desc'), InsightsRule.description_text, InsightsRule.name)
                         .limit(3)
                         .dicts())
            top_rules = cyndi_join(top_rules)

        for top_rule in top_rules:
            retval['top_rules'].append(top_rule)

        return retval
Exemplo n.º 2
0
    def _full_query(rh_account_id, synopsis):
        # pylint: disable=singleton-comparison
        selectables = [
            fn.COALESCE(CveAccountData.status_id, 0).alias('cve_status_id'),
            SystemPlatform.inventory_id,
            SystemPlatform.display_name,
            SystemPlatform.last_evaluation,
            SystemPlatform.advisor_evaluated.alias('rules_evaluation'),
            SystemPlatform.last_upload,
            SystemPlatform.stale_timestamp,
            SystemPlatform.stale_warning_timestamp,
            SystemPlatform.culled_timestamp,
            Status.id.alias('status_id'),
            Status.name.alias('status_name'),
            SystemVulnerabilities.status_text.alias('status_text'),
            SystemVulnerabilities.rule_hit_details,
            SystemVulnerabilities.when_mitigated,
            SystemVulnerabilities.first_reported,
            InsightsRule.name.alias('rule_id'),
            InsightsRule.active.alias('rule_active'),
            InsightsRule.description_text,
            InsightsRule.reason_text,
            InsightsRule.resolution_text,
            InsightsRule.kbase_node_id,
            InsightsRule.more_info_text,
            InventoryHosts.tags,
            InventoryHosts.updated,
            InventoryHosts.insights_id,
            OS_INFO_QUERY.alias('os'),
            fn.COALESCE(SystemVulnerabilities.advisory_available,
                        True).alias('advisory_available'),
            fn.COALESCE(
                SystemVulnerabilities.remediation_type_id,
                remediation.PLAYBOOK.value).alias('remediation_type_id'),
        ]

        return (SystemVulnerabilities.select(*selectables).join(
            SystemPlatform,
            on=(SystemVulnerabilities.system_id == SystemPlatform.id)).join(
                Status,
                on=(SystemVulnerabilities.status_id == Status.id)).join(
                    CveMetadata,
                    on=(SystemVulnerabilities.cve_id == CveMetadata.id)).join(
                        CveAccountData,
                        JOIN.LEFT_OUTER,
                        on=((CveAccountData.rh_account_id == rh_account_id)
                            & (CveMetadata.id == CveAccountData.cve_id))).join(
                                InsightsRule,
                                JOIN.LEFT_OUTER,
                                on=(InsightsRule.id ==
                                    SystemVulnerabilities.rule_id)).
                where(CveMetadata.cve == synopsis).where(
                    SystemVulnerabilities.rh_account_id == rh_account_id).
                where((SystemVulnerabilities.mitigation_reason.is_null(True))
                      | (InsightsRule.active == False)
                      & (~InsightsRule.rule_only)).where(
                          (SystemVulnerabilities.when_mitigated.is_null(True))
                          | (InsightsRule.active == True)
                          & (~InsightsRule.rule_only)).where(
                              (SystemPlatform.opt_out == False)
                              & (SystemPlatform.stale == False)
                              & (SystemPlatform.when_deleted.is_null(True))))
Exemplo n.º 3
0
def get_rules_for_cves(cves: list, rh_account=None) -> dict:
    """Return associated rules for a CVE"""
    # pylint: disable=singleton-comparison, unsubscriptable-object
    rules_map = {}
    rule_cve_mapping = {}
    rh_account_id = None

    if rh_account is not None:
        try:
            rh_account_id = RHAccount.select(
                RHAccount.id).where(RHAccount.name == rh_account)[0]
        except IndexError:
            pass

    if rh_account_id is not None:
        subquery = (
            SystemVulnerabilities.select(
                SystemVulnerabilities.id, SystemVulnerabilities.rule_id,
                SystemVulnerabilities.cve_id).join(
                    SystemPlatform,
                    on=((SystemVulnerabilities.system_id == SystemPlatform.id)
                        & (SystemPlatform.when_deleted.is_null(True)) &
                        (SystemPlatform.stale == False) &
                        (SystemPlatform.opt_out == False) &
                        (fn.COALESCE(SystemPlatform.host_type, 'null') !=
                         HostType.EDGE)))  # noqa: E712
            .where(SystemVulnerabilities.cve_id.in_(cves)).where(
                (SystemVulnerabilities.rh_account_id == rh_account_id)
                & (SystemVulnerabilities.mitigation_reason.is_null(True))))
        subquery = cyndi_join(subquery)
    mapping = (CveRuleMapping.select(CveRuleMapping.cve_id, InsightsRule.name, InsightsRule.description_text, InsightsRule.summary_text,
                                     InsightsRule.reboot_required, InsightsRule.playbook_count, InsightsRule.change_risk,
                                     InsightsRule.kbase_node_id, InsightsRule.active, CveMetadata.cve, InsightsRule.rule_impact,
                                     InsightsRule.publish_date)
               .join(InsightsRule, on=(CveRuleMapping.rule_id == InsightsRule.id))
               .join(CveMetadata, on=(CveRuleMapping.cve_id == CveMetadata.id))
               .where((InsightsRule.active == True) & (~InsightsRule.rule_only))  # noqa: E712
               .where(CveRuleMapping.rule_id.in_(CveRuleMapping.select(CveRuleMapping.rule_id).where(CveRuleMapping.cve_id.in_(cves)))).dicts()) \
        if rh_account_id is None else \
        (CveRuleMapping.select(CveRuleMapping.cve_id, InsightsRule.name, InsightsRule.description_text, InsightsRule.summary_text,
                               InsightsRule.reboot_required, InsightsRule.playbook_count, InsightsRule.change_risk,
                               InsightsRule.kbase_node_id, InsightsRule.active, CveMetadata.cve,
                               InsightsRule.rule_impact, InsightsRule.publish_date, fn.COUNT(subquery.c.id).alias('systems_affected'))
         .join(InsightsRule, on=(CveRuleMapping.rule_id == InsightsRule.id))
         .join(CveMetadata, on=(CveRuleMapping.cve_id == CveMetadata.id))
         .join(subquery, JOIN.LEFT_OUTER,
               on=((subquery.c.rule_id == InsightsRule.id) & (subquery.c.cve_id == CveRuleMapping.cve_id)))
         .where((InsightsRule.active == True) & (~InsightsRule.rule_only))  # noqa: E712
         .where(CveRuleMapping.rule_id.in_(CveRuleMapping.select(CveRuleMapping.rule_id).where(CveRuleMapping.cve_id.in_(cves))))
         .group_by(CveRuleMapping.cve_id, InsightsRule.name, InsightsRule.description_text, InsightsRule.summary_text,
                   InsightsRule.reboot_required, InsightsRule.playbook_count, InsightsRule.change_risk, InsightsRule.kbase_node_id, InsightsRule.active,
                   CveMetadata.cve, InsightsRule.rule_impact, InsightsRule.publish_date)
         .dicts())
    for row in mapping:
        rule_cve_mapping.setdefault(row['name'], []).append(row['cve'])
    for row in mapping:
        rule_detail = {
            'rule_id': row['name'],
            'description': row['description_text'],
            'summary': row['summary_text'],
            'reboot_required': row['reboot_required'],
            'playbook_count': row['playbook_count'],
            'change_risk': row['change_risk'],
            'kbase_node_id': row['kbase_node_id'],
            'associated_cves': rule_cve_mapping[row['name']],
            'rule_impact': row['rule_impact'],
            'publish_date': format_datetime(row['publish_date'])
        }
        if rh_account_id is not None:
            rule_detail['systems_affected'] = row['systems_affected']
        rules_map.setdefault(row['cve_id'], []).append(rule_detail)
    return rules_map
Exemplo n.º 4
0
    def handle_patch(cls, **kwargs):
        """Update the 'status' field for a system/cve combination"""
        # pylint: disable=singleton-comparison
        data = kwargs['data']
        in_inventory_id_list, in_cve_list, status_to_cves_map, status_text_to_cves_map = cls._prepare_data(
            data)

        try:
            rh_account = RHAccount.select(RHAccount.id).where(
                RHAccount.name == connexion.context['user'])
            systems = (SystemPlatform.select(SystemPlatform.id).where(
                SystemPlatform.rh_account_id == rh_account[0].id)
                       )  # noqa: E712
            if in_inventory_id_list is not None:
                systems = systems.where(
                    SystemPlatform.inventory_id << in_inventory_id_list)
            rows_modified = set()
            # set statuses and their CVE lists
            for status_id, status_cve_list in status_to_cves_map.items():
                status_id_update = (SystemVulnerabilities.update(
                    status_id=status_id).where(
                        (SystemVulnerabilities.system_id << systems) &
                        (SystemVulnerabilities.cve_id <<
                         (CveMetadata.select(CveMetadata.id).where(
                             CveMetadata.cve << status_cve_list)))).returning(
                                 SystemVulnerabilities.id))
                rows_modified.update([row.id for row in status_id_update])

            cls._update_divergent_status_count(in_cve_list, rh_account[0].id)

            for status_text, status_cve_list in status_text_to_cves_map.items(
            ):
                status_text_update = (SystemVulnerabilities.update(
                    status_text=status_text).where(
                        (SystemVulnerabilities.system_id << systems) &
                        (SystemVulnerabilities.cve_id <<
                         (CveMetadata.select(CveMetadata.id).where(
                             CveMetadata.cve << status_cve_list)))).returning(
                                 SystemVulnerabilities.id))
                rows_modified.update([row.id for row in status_text_update])

            updated_details = (SystemVulnerabilities.select(
                SystemPlatform.inventory_id, CveMetadata.cve).join(
                    CveMetadata,
                    on=(SystemVulnerabilities.cve_id == CveMetadata.id)).join(
                        SystemPlatform,
                        on=(SystemVulnerabilities.system_id == SystemPlatform.
                            id)).where(SystemVulnerabilities.id << list(
                                rows_modified)).dicts())
            updated = []
            for updated_row in updated_details:
                updated.append({
                    "inventory_id": updated_row["inventory_id"],
                    "cve": updated_row["cve"]
                })
            if not updated:
                # sysid/cve/acct combination does not exist
                return cls.format_exception(
                    'inventory_id/cve must exist and inventory_id must be visible to user',
                    404)
        except (IntegrityError, psycopg2IntegrityError,
                DataError) as value_error:
            # usually means bad-status-id
            LOGGER.error(str(value_error))
            DB.rollback()
            return cls.format_exception(
                f'status_id={list(status_to_cves_map.keys())} is invalid', 400)
        except ValueError as value_error:
            LOGGER.exception('Error during setting status (ValueError):')
            DB.rollback()
            return cls.format_exception(
                f'status_text or other key value is invalid ({value_error})',
                400)
        return {"updated": updated}
    def handle_patch(cls, **kwargs):
        """Set status for a CVE"""
        data = kwargs['data']
        cve_list = data['cve']
        if isinstance(cve_list, str):
            cve_list = [cve_list]

        values = {}
        updated = []
        if 'status_id' in data:
            values['status_id'] = data['status_id']
        if 'status_text' in data:
            try:
                values['status_text'] = data['status_text'].strip() \
                                        if data['status_text'].strip() else None
            except AttributeError:
                values['status_text'] = None
        if not values:
            return cls.format_exception(
                'At least one of the "status_id" or "status_text" parameters is required.',
                400)
        try:
            to_insert = []
            cves = CveMetadata.select(
                CveMetadata.id,
                CveMetadata.cve).where(CveMetadata.cve << cve_list)
            rh_account = get_or_create_account()
            for cve in cves:
                updated.append(cve.cve)
                if 'status_id' in data:
                    # refresh number of divergent statuses in CVE-system pairs
                    # pylint: disable=singleton-comparison
                    query = (
                        SystemVulnerabilities.select(
                            fn.Count(SystemVulnerabilities.id).alias(
                                'systems')).join(
                                    SystemPlatform,
                                    on=(SystemVulnerabilities.system_id ==
                                        SystemPlatform.id)).where(
                                            SystemPlatform.rh_account_id ==
                                            rh_account[0].id).where(
                                                SystemPlatform.opt_out ==
                                                False)  # noqa: E712
                        .where(SystemVulnerabilities.cve_id == cve.id).where(
                            SystemVulnerabilities.when_mitigated.is_null(True)
                        ).where(SystemVulnerabilities.status_id != values.get(
                            'status_id', 0)).dicts())
                    values['systems_status_divergent'] = query[0]['systems']

                to_insert.append(
                    (cve.id, rh_account[0].id, values.get('status_id', 0),
                     values.get('status_text',
                                None), values.get('systems_status_divergent',
                                                  0)))
            if not to_insert:
                return cls.format_exception(
                    'At least one given CVE must exist', 404)
            (CveAccountData.insert_many(
                to_insert, fields=cls._fields).on_conflict(
                    conflict_target=cls._conflict_target,
                    preserve=[],
                    update=values).execute())
        except (IntegrityError, psycopg2IntegrityError, DataError):
            # usually means bad status
            LOGGER.exception('Error during setting status (IntegrityError):')
            DB.rollback()
            return cls.format_exception(
                f'status_id=%s is invalid' % data['status_id'], 400)
        except ValueError as value_error:
            LOGGER.exception('Error during setting status (ValueError):')
            DB.rollback()
            return cls.format_exception(
                f'status_text or other key value is invalid ({value_error})',
                400)
        return {'updated': updated}
    def handle_get(cls, **kwargs):
        """Gets systems affected by a CVE"""
        synopsis = kwargs['cve_id']
        cls._cve_exists(synopsis)
        args_desc = [
            {
                'arg_name': 'status_id',
                'convert_func': parse_int_list
            },
            {
                'arg_name': 'uuid',
                'convert_func': None
            },
            {
                'arg_name': 'rule_presence',
                'convert_func': unique_bool_list
            },
            {
                'arg_name': 'rule_key',
                'convert_func': None
            },
            {
                'arg_name': 'tags',
                'convert_func': parse_tags
            },
            {
                'arg_name': 'sap_system',
                'convert_func': None
            },
            {
                'arg_name': 'show_advisories',
                'convert_func': None
            },
            {
                'arg_name': 'advisory',
                'convert_func': None
            },
            {
                'arg_name': 'sap_sids',
                'convert_func': None
            },
            {
                'arg_name': 'rhel_version',
                'convert_func': parse_str_list
            },
            {
                'arg_name': 'first_reported_from',
                'convert_func': dateutil.parser.parse
            },
            {
                'arg_name': 'first_reported_to',
                'convert_func': dateutil.parser.parse
            },
            {
                'arg_name': 'rule',
                'convert_func': None
            },
            {
                'arg_name': 'advisory_available',
                'convert_func': unique_bool_list
            },
            {
                'arg_name': 'remediation',
                'convert_func': parse_int_list
            },
        ]
        args = cls._parse_arguments(kwargs, args_desc)
        list_arguments = cls._parse_list_arguments(kwargs)

        # check if advisory parameter comes with show_advisories parameter
        if 'advisory' in args and args['advisory'] and (
                'show_advisories' not in args or not args['show_advisories']):
            raise ApplicationException(
                "Advisory parameter shouldn't be used without show_advisories parameter",
                400)
        rh_account_id, _, _ = get_account_data(connexion.context['user'])

        system_advisories = {}
        patch_access = None
        if 'show_advisories' in args and args[
                'show_advisories'] and 'advisory' in args and args['advisory']:
            # pylint: disable=singleton-comparison
            raw_inv_ids = (
                SystemVulnerabilities.select(SystemPlatform.inventory_id).join(
                    SystemPlatform,
                    on=(SystemVulnerabilities.system_id == SystemPlatform.id)).
                join(CveMetadata,
                     on=(SystemVulnerabilities.cve_id == CveMetadata.id)
                     ).where(CveMetadata.cve == synopsis).where(
                         SystemVulnerabilities.rh_account_id == rh_account_id).
                where((SystemPlatform.opt_out == False)  # noqa: E712
                      & (SystemPlatform.stale == False)  # noqa: E712
                      & (SystemPlatform.when_deleted.is_null(True))
                      & (fn.COALESCE(SystemPlatform.host_type, 'null') !=
                         HostType.EDGE))).dicts()
            raw_inv_ids = [inv_id['inventory_id'] for inv_id in raw_inv_ids]
            # Pass advisory argument for filtering advisories list by advisory to prevent redundant patch requests
            advisories_list = get_advisories_per_cve_from_db(synopsis)
            if not has_advisory(args['advisory'], advisories_list):
                advisories_list = []

            # get advisory data from PATCH
            system_advisories, patch_access = cls._get_advisories_per_system(
                advisories_list, raw_inv_ids)
            # Filter systems by advisory
            system_advisories = filter_by_advisory(args['advisory'],
                                                   system_advisories)

            asys_view = cls._DB_View(rh_account_id, synopsis, list_arguments,
                                     args, args, connexion.request.path,
                                     cls._ids_only, system_advisories)
            systems = list(asys_view)  # execute query
        elif 'show_advisories' in args and args['show_advisories'] and (
                'advisory' not in args or not args['advisory']):
            asys_view = cls._DB_View(rh_account_id, synopsis, list_arguments,
                                     args, args, connexion.request.path,
                                     cls._ids_only)
            systems = list(asys_view)  # execute query
            inv_ids = [sys['inventory_id'] for sys in systems]
            # get advisory data from PATCH
            advisories_list = get_advisories_per_cve_from_db(synopsis)
            system_advisories, patch_access = cls._get_advisories_per_system(
                advisories_list, inv_ids)
        else:
            asys_view = cls._DB_View(rh_account_id, synopsis, list_arguments,
                                     args, args, connexion.request.path,
                                     cls._ids_only)
            systems = list(asys_view)  # execute query

        response = {}
        result = []
        if cls._ids_only:
            result = systems
        else:
            for sys in systems:
                if 'show_advisories' in args and args['show_advisories']:
                    record = cls._build_attributes(
                        sys, system_advisories.get(sys['inventory_id'], []))
                    result.append({
                        'type': 'system',
                        'id': sys['inventory_id'],
                        'attributes': record
                    })
                else:
                    record = cls._build_attributes(sys)
                    result.append({
                        'type': 'system',
                        'id': sys['inventory_id'],
                        'attributes': record
                    })

        response['meta'] = asys_view.get_metadata()
        response['meta'][
            'patch_access'] = patch_access.status if patch_access else None
        response['links'] = asys_view.get_pagination_links()
        response['data'] = cls._format_data(list_arguments["data_format"],
                                            result,
                                            ids_only=cls._ids_only)
        return response
    def _cve_details(cls, synopsis, args):
        retval = cls._get_cve_details(synopsis, args)
        cve_details = (CveAccountData.select(
            BusinessRisk.name.alias('risk'),
            CveAccountData.business_risk_id.alias('risk_id'),
            CveAccountData.business_risk_text.alias('risk_text'),
            Status.name.alias("status"),
            CveAccountData.status_id.alias("status_id"),
            CveAccountData.status_text.alias("status_text")).join(
                BusinessRisk,
                on=(CveAccountData.business_risk_id == BusinessRisk.id)).join(
                    Status, on=(CveAccountData.status_id == Status.id)).join(
                        CveMetadata,
                        on=(CveAccountData.cve_id == CveMetadata.id)).where(
                            CveAccountData.rh_account_id == (
                                RHAccount.select(RHAccount.id).where(
                                    RHAccount.name ==
                                    connexion.context['user']))).where(
                                        CveMetadata.cve == synopsis)).dicts()
        if cve_details.count():
            retval['business_risk'] = cve_details[0]['risk']
            retval['business_risk_id'] = cve_details[0]['risk_id']
            retval['business_risk_text'] = cve_details[0]['risk_text']
            retval['status'] = cve_details[0]['status']
            retval['status_id'] = cve_details[0]['status_id']
            retval['status_text'] = cve_details[0]['status_text']
        else:
            retval['business_risk'] = DEFAULT_BUSINESS_RISK
            retval['business_risk_id'] = 0
            retval['business_risk_text'] = None
            retval['status'] = DEFAULT_STATUS
            retval['status_id'] = 0
            retval['status_text'] = None

        # add counts of systems with all statuses
        retval['systems_status_detail'] = {}
        # pylint: disable=singleton-comparison
        rh_account_id, _, _ = get_account_data(connexion.context['user'])
        status_detail = (SystemVulnerabilities.select(
            SystemVulnerabilities.status_id,
            fn.Count(SystemVulnerabilities.status_id).alias('systems')
        ).join(SystemPlatform,
               on=(SystemVulnerabilities.system_id == SystemPlatform.id)).join(
                   CveMetadata,
                   on=(SystemVulnerabilities.cve_id == CveMetadata.id)).join(
                       InsightsRule,
                       JOIN.LEFT_OUTER,
                       on=(InsightsRule.id == SystemVulnerabilities.rule_id)
                   ).where(CveMetadata.cve == synopsis).where(
                       SystemVulnerabilities.rh_account_id == rh_account_id
                   ).where(
                       (SystemVulnerabilities.mitigation_reason.is_null(True))
                       | (InsightsRule.active == False)
                       & (~InsightsRule.rule_only)).where(
                           (SystemVulnerabilities.when_mitigated.is_null(True))
                           | (InsightsRule.active == True)
                           & (~InsightsRule.rule_only)).where(
                               (SystemPlatform.opt_out == False)
                               & (SystemPlatform.stale == False)
                               & (SystemPlatform.when_deleted.is_null(True))
                               & (fn.COALESCE(SystemPlatform.host_type, 'null')
                                  != HostType.EDGE)).group_by(
                                      SystemVulnerabilities.status_id).dicts())

        divergent_systems = 0
        status_detail = cyndi_join(status_detail)
        for row in status_detail:
            retval['systems_status_detail'][row['status_id']] = row['systems']
            if row['status_id'] != retval['status_id']:
                divergent_systems += row['systems']
        retval['systems_status_divergent'] = divergent_systems
        return retval