class FeedOutOfDateTrigger(BaseTrigger):
    __trigger_name__ = 'stale_feed_data'
    __description__ = 'Triggers if the CVE data is older than the window specified by the parameter MAXAGE (unit is number of days).'
    max_age = IntegerStringParameter(name='max_days_since_sync', example_str='10', description='Fire the trigger if the last sync was more than this number of days ago.', is_required=True)

    def evaluate(self, image_obj, context):
        # Map to a namespace
        ns = DistroNamespace.for_obj(image_obj)

        oldest_update = None
        if ns:
            vulnerability_feed = DataFeeds.instance().vulnerabilities
            for namespace_name in ns.like_namespace_names:
                # Check feed names
                groups = vulnerability_feed.group_by_name(namespace_name)
                if groups:
                    # No records yet, but we have the feed, so may just not have any data yet
                    oldest_update = groups[0].last_sync
                    break

        if self.max_age.value() is not None:
            try:
                if oldest_update is not None:
                    oldest_update = calendar.timegm(oldest_update.timetuple())
                    mintime = time.time() - int(int(self.max_age.value()) * 86400)
                    if oldest_update < mintime:
                        self._fire(msg="The vulnerability feed for this image distro is older than MAXAGE ("+str(self.max_age.value())+") days")
                else:
                    self._fire(
                        msg="The vulnerability feed for this image distro is older than MAXAGE (" + str(self.max_age.value()) + ") days")
            except Exception as err:
                self._fire(msg="Cannot perform data feed up-to-date check - message from server: " + str(err))
Esempio n. 2
0
class FeedOutOfDateTrigger(BaseTrigger):
    __trigger_name__ = "stale_feed_data"
    __description__ = "Triggers if the CVE data for the image's distro is older than the window specified by the parameter MAXAGE (unit is number of days)."
    max_age = IntegerStringParameter(
        name="max_days_since_sync",
        example_str="10",
        description=
        "Fire the trigger if the last sync was more than this number of days ago.",
        is_required=True,
    )

    def evaluate(self, image_obj, context):
        # Map to a namespace
        ns = DistroNamespace.for_obj(image_obj)

        oldest_update = None
        if ns:
            for namespace_name in ns.like_namespace_names:
                # Check feed names
                for feed in feed_registry.registered_vulnerability_feed_names(
                ):
                    # First match, assume only one matches for the namespace
                    group = get_feed_group_detached(feed, namespace_name)
                    if group:
                        # No records yet, but we have the feed, so may just not have any data yet
                        oldest_update = group.last_sync
                        logger.debug(
                            "Found date for oldest update in feed %s group %s date = %s",
                            feed,
                            group.name,
                            oldest_update,
                        )
                        break

        if self.max_age.value() is not None:
            try:
                if oldest_update is not None:
                    oldest_update = calendar.timegm(oldest_update.timetuple())
                    mintime = time.time() - int(
                        int(self.max_age.value()) * 86400)
                    if oldest_update < mintime:
                        self._fire(
                            msg=
                            "The vulnerability feed for this image distro is older than MAXAGE ("
                            + str(self.max_age.value()) + ") days")
                else:
                    self._fire(
                        msg=
                        "The vulnerability feed for this image distro is older than MAXAGE ("
                        + str(self.max_age.value()) + ") days")
            except Exception as err:
                self._fire(
                    msg=
                    "Cannot perform data feed up-to-date check - message from server: "
                    + str(err))
class VulnerabilityMatchTrigger(BaseTrigger):
    __trigger_name__ = 'package'
    __description__ = 'Triggers if a found vulnerability in an image meets the comparison criteria.'

    SEVERITY_COMPARISONS = {
        '=': lambda x, y: x == y,
        '!=': lambda x, y: x != y,
        '<': lambda x, y: x < y,
        '>': lambda x, y: x > y,
        '<=': lambda x, y: x <= y,
        '>=': lambda x, y: x >= y
    }

    package_type = EnumStringParameter(
        name='package_type',
        example_str='all',
        enum_values=['all', 'os', 'non-os'],
        description='Only trigger for specific package type.',
        is_required=True,
        sort_order=1)

    severity_comparison = EnumStringParameter(
        name='severity_comparison',
        example_str='>',
        description=
        'The type of comparison to perform for severity evaluation.',
        enum_values=list(SEVERITY_COMPARISONS.keys()),
        is_required=False,
        sort_order=2)
    severity = EnumStringParameter(name='severity',
                                   example_str='high',
                                   description='Severity to compare against.',
                                   enum_values=SEVERITY_ORDERING,
                                   is_required=False,
                                   sort_order=3)

    cvss_baseScore_comparison = EnumStringParameter(
        name='cvssV3_baseScore_comparison',
        example_str='>',
        description=
        'The type of comparison to perform for CVSSV3 baseScore evaluation.',
        enum_values=list(SEVERITY_COMPARISONS.keys()),
        is_required=False,
        sort_order=4)
    cvss_baseScore = FloatStringParameter(
        name='cvssV3_baseScore',
        example_string='5.0',
        description='CVSSV3 baseScore to compare against.',
        is_required=False,
        sort_order=5)

    cvss_exploitabilityScore_comparison = EnumStringParameter(
        name='cvssV3_exploitabilityScore_comparison',
        example_str='>',
        description=
        'The type of comparison to perform for CVSSV3 exploitabilityScore evaluation.',
        enum_values=list(SEVERITY_COMPARISONS.keys()),
        is_required=False,
        sort_order=6)
    cvss_exploitabilityScore = FloatStringParameter(
        name='cvssV3_exploitabilityScore',
        example_string='5.0',
        description='CVSSV3 exploitabilityScore to compare against.',
        is_required=False,
        sort_order=7)

    cvss_impactScore_comparison = EnumStringParameter(
        name='cvssV3_impactScore_comparison',
        example_str='>',
        description=
        'The type of comparison to perform for CVSSV3 impactScore evaluation.',
        enum_values=list(SEVERITY_COMPARISONS.keys()),
        is_required=False,
        sort_order=8)
    cvss_impactScore = FloatStringParameter(
        name='cvssV3_impactScore',
        example_string='5.0',
        description='CVSSV3 impactScore to compare against.',
        is_required=False,
        sort_order=9)

    fix_available = BooleanStringParameter(
        name='fix_available',
        example_str='true',
        description=
        'If present, the fix availability for the vulnerability record must match the value of this parameter.',
        is_required=False,
        sort_order=10)
    vendor_only = BooleanStringParameter(
        name='vendor_only',
        example_str='true',
        description=
        'If True, an available fix for this CVE must not be explicitly marked as wont be addressed by the vendor',
        is_required=False,
        sort_order=11)
    max_days_since_creation = IntegerStringParameter(
        name='max_days_since_creation',
        example_str='7',
        description=
        'If provided, this CVE must be older than the days provided to trigger.',
        is_required=False,
        sort_order=12)
    max_days_since_fix = IntegerStringParameter(
        name='max_days_since_fix',
        example_str='30',
        description=
        'If provided (only evaluated when fix_available option is also set to true), the fix first observed time must be older than days provided, to trigger.',
        is_required=False,
        sort_order=13)

    def evaluate(self, image_obj, context):
        is_fix_available = self.fix_available.value()
        is_vendor_only = self.vendor_only.value(default_if_none=True)
        comparison_idx = SEVERITY_ORDERING.index(
            self.severity.value(default_if_none='unknown').lower())
        comparison_fn = self.SEVERITY_COMPARISONS.get(
            self.severity_comparison.value(default_if_none=">"))

        cvss_baseScore = self.cvss_baseScore.value()
        cvss_baseScore_comparison_fn = self.SEVERITY_COMPARISONS.get(
            self.cvss_baseScore_comparison.value(default_if_none=">="))

        cvss_exploitabilityScore = self.cvss_exploitabilityScore.value()
        cvss_exploitabilityScore_comparison_fn = self.SEVERITY_COMPARISONS.get(
            self.cvss_exploitabilityScore_comparison.value(
                default_if_none=">="))

        cvss_impactScore = self.cvss_impactScore.value()
        cvss_impactScore_comparison_fn = self.SEVERITY_COMPARISONS.get(
            self.cvss_impactScore_comparison.value(default_if_none=">="))

        now = time.time()

        timeallowed = None
        if self.max_days_since_creation.value() is not None:
            timeallowed = now - int(
                int(self.max_days_since_creation.value()) * 86400)

        fix_timeallowed = None
        if self.max_days_since_fix.value() is not None:
            fix_timeallowed = now - int(
                int(self.max_days_since_fix.value()) * 86400)

        if not comparison_fn:
            pass
            #raise KeyError(self.severity_comparison)

        if self.package_type.value() in ['all', 'non-os']:
            cpevulns = context.data.get('loaded_cpe_vulnerabilities')
            if cpevulns:
                try:
                    for sev in list(cpevulns.keys()):
                        found_severity_idx = SEVERITY_ORDERING.index(
                            sev.lower()) if sev else 0
                        if comparison_fn(found_severity_idx, comparison_idx):
                            for image_cpe, vulnerability_cpe in cpevulns[sev]:
                                parameter_data = OrderedDict()

                                parameter_data['severity'] = sev.upper()
                                parameter_data[
                                    'vulnerability_id'] = vulnerability_cpe.vulnerability_id
                                parameter_data['pkg_class'] = 'non-os'
                                parameter_data['pkg_type'] = image_cpe.pkg_type

                                # Check if the vulnerability is too recent for this policy
                                if timeallowed:
                                    if calendar.timegm(
                                            vulnerability_cpe.created_at.
                                            timetuple()) > timeallowed:
                                        continue
                                    parameter_data[
                                        'max_days_since_creation'] = vulnerability_cpe.created_at.date(
                                        )

                                if cvss_baseScore is not None:
                                    vuln_cvss_baseScore = vulnerability_cpe.parent.get_baseScore(
                                    )
                                    if not cvss_baseScore_comparison_fn(
                                            vuln_cvss_baseScore,
                                            cvss_baseScore):
                                        log.debug(
                                            "vulnerability cvss V3 baseScore {} is not {} than policy cvss V3 baseScore {}, skipping"
                                            .format(
                                                vuln_cvss_baseScore,
                                                self.cvss_baseScore_comparison.
                                                value(), cvss_baseScore))
                                        continue
                                    else:
                                        parameter_data[
                                            'cvssV3_baseScore'] = vuln_cvss_baseScore

                                if cvss_exploitabilityScore is not None:
                                    vuln_cvss_exploitabilityScore = vulnerability_cpe.parent.get_exploitabilityScore(
                                    )
                                    if not cvss_exploitabilityScore_comparison_fn(
                                            vuln_cvss_exploitabilityScore,
                                            cvss_exploitabilityScore):
                                        log.debug(
                                            "vulnerability cvss V3 exploitabilityScore {} is not {} than policy cvss V3 exploitabilityScore {}, skipping"
                                            .format(
                                                vuln_cvss_exploitabilityScore,
                                                self.
                                                cvss_exploitabilityScore_comparison
                                                .value(),
                                                cvss_exploitabilityScore))
                                        continue
                                    else:

                                        parameter_data[
                                            'cvssV3_exploitabilityScore'] = vuln_cvss_baseScore

                                if cvss_impactScore is not None:
                                    vuln_cvss_impactScore = vulnerability_cpe.parent.get_impactScore(
                                    )
                                    if not cvss_impactScore_comparison_fn(
                                            vuln_cvss_impactScore,
                                            cvss_impactScore):
                                        log.debug(
                                            "vulnerability cvss V3 impactScore {} is not {} than policy cvss V3 impactScore {}, skipping"
                                            .format(
                                                vuln_cvss_impactScore,
                                                self.
                                                cvss_impactScore_comparison.
                                                value(), cvss_impactScore))
                                        continue
                                    else:
                                        parameter_data[
                                            'cvssV3_impactScore'] = vuln_cvss_baseScore

                                trigger_fname = None
                                if image_cpe.pkg_type in ['java', 'gem']:
                                    try:
                                        trigger_fname = image_cpe.pkg_path.split(
                                            "/")[-1]
                                    except:
                                        trigger_fname = None
                                elif image_cpe.pkg_type in ['npm']:
                                    try:
                                        trigger_fname = image_cpe.pkg_path.split(
                                            "/")[-2]
                                    except:
                                        trigger_fname = None

                                if not trigger_fname:
                                    trigger_fname = "-".join(
                                        [image_cpe.name, image_cpe.version])

                                if is_fix_available is not None:
                                    # Must do a fix_available check
                                    fix_available_in = image_cpe.fixed_in()
                                    if is_fix_available == (fix_available_in
                                                            is not None):
                                        pass
                                    else:
                                        # fix_available is set, but no fix is availble so skip
                                        continue

                                parameter_data[
                                    'link'] = "https://nvd.nist.gov/vuln/detail/{}".format(
                                        parameter_data['vulnerability_id'])

                                fix_msg = ''

                                score_msg = ''
                                score_tuples = []
                                for s in [
                                        'cvssV3_baseScore',
                                        'cvssV3_exploitabilityScore',
                                        'cvssV3_impactScore'
                                ]:
                                    if parameter_data.get(s, None):
                                        score_tuples.append("{}={}".format(
                                            s, parameter_data.get(s)))
                                if score_tuples:
                                    score_msg = "({})".format(
                                        ' '.join(score_tuples))

                                time_msg = ''
                                time_tuples = []
                                for s in [
                                        'max_days_since_creation',
                                        'max_days_since_fix'
                                ]:
                                    if parameter_data.get(s, None):
                                        time_tuples.append("{}={}".format(
                                            s, parameter_data.get(s)))
                                if time_tuples:
                                    time_msg = "({})".format(
                                        ' '.join(time_tuples))

                                # new detail message approach
                                #pkgname = image_cpe.pkg_path
                                #msg = "Vulnerability found in package {} - matching parameters: ".format(pkgname)
                                #for i in parameter_data:
                                #    msg += "{}={} ".format(i, parameter_data[i])

                                pkgname = image_cpe.pkg_path
                                msg = "{} Vulnerability found in {} package type ({}) - {} {}{}{}({} - {})".format(
                                    parameter_data['severity'].upper(),
                                    parameter_data['pkg_class'],
                                    parameter_data['pkg_type'], pkgname,
                                    fix_msg, score_msg, time_msg,
                                    parameter_data['vulnerability_id'],
                                    parameter_data['link'])
                                self._fire(instance_id=vulnerability_cpe.
                                           vulnerability_id + '+' +
                                           trigger_fname,
                                           msg=msg)

                except Exception as err:
                    log.warn(
                        "problem during non-os vulnerability evaluation - exception: {}"
                        .format(err))

        if self.package_type.value() in ['all', 'os']:
            vulns = context.data.get('loaded_vulnerabilities')

            if vulns:
                for pkg_vuln in vulns:
                    parameter_data = OrderedDict()

                    parameter_data[
                        'severity'] = pkg_vuln.vulnerability.severity.upper()
                    parameter_data[
                        'vulnerability_id'] = pkg_vuln.vulnerability_id
                    parameter_data['pkg_class'] = 'os'
                    parameter_data['pkg_type'] = pkg_vuln.pkg_type

                    # Filter by level first
                    found_severity_idx = SEVERITY_ORDERING.index(
                        pkg_vuln.vulnerability.severity.lower(
                        )) if pkg_vuln.vulnerability.severity else 0
                    if comparison_fn(found_severity_idx, comparison_idx):
                        # Check vendor_only flag specified by the user in policy
                        if is_vendor_only:
                            if pkg_vuln.fix_has_no_advisory():
                                # skip this vulnerability
                                continue

                        # Check if the vulnerability is to recent for this policy
                        if timeallowed:
                            if calendar.timegm(
                                    pkg_vuln.vulnerability.created_at.
                                    timetuple()) > timeallowed:
                                continue
                            parameter_data[
                                'max_days_since_creation'] = pkg_vuln.vulnerability.created_at.date(
                                )

                        if is_fix_available and fix_timeallowed is not None:
                            fix = pkg_vuln.fixed_artifact()
                            if fix.version and fix.version != 'None':
                                if fix.fix_observed_at and calendar.timegm(
                                        fix.fix_observed_at.timetuple(
                                        )) > fix_timeallowed:
                                    continue
                                else:
                                    parameter_data[
                                        'max_days_since_fix'] = fix.fix_observed_at.date(
                                        )

                        vuln_cvss_baseScore = -1.0
                        vuln_cvss_exploitabilityScore = -1.0
                        vuln_cvss_impactScore = -1.0

                        for nvd_record in pkg_vuln.vulnerability.get_nvd_vulnerabilities(
                        ):
                            cvss_scores = nvd_record.get_cvssScores()
                            if cvss_scores.get('baseScore',
                                               -1.0) > vuln_cvss_baseScore:
                                vuln_cvss_baseScore = cvss_scores.get(
                                    'baseScore', -1.0)
                            if cvss_scores.get(
                                    'exploitabilityScore',
                                    -1.0) > vuln_cvss_exploitabilityScore:
                                vuln_cvss_exploitabilityScore = cvss_scores.get(
                                    'exploitabilityScore', -1.0)
                            if cvss_scores.get('impactScore',
                                               -1.0) > vuln_cvss_impactScore:
                                vuln_cvss_impactScore = cvss_scores.get(
                                    'impactScore', -1.0)

                        if cvss_baseScore is not None:
                            if not cvss_baseScore_comparison_fn(
                                    vuln_cvss_baseScore, cvss_baseScore):
                                log.debug(
                                    "OS vulnerability cvss V3 baseScore {} is not {} than policy cvss V3 baseScore {}, skipping"
                                    .format(
                                        vuln_cvss_baseScore,
                                        self.cvss_baseScore_comparison.value(),
                                        cvss_baseScore))
                                continue
                            else:
                                parameter_data[
                                    'cvssV3_baseScore'] = vuln_cvss_baseScore

                        if cvss_exploitabilityScore is not None:
                            if not cvss_exploitabilityScore_comparison_fn(
                                    vuln_cvss_exploitabilityScore,
                                    cvss_exploitabilityScore):
                                log.debug(
                                    "OS vulnerability cvss V3 exploitabilityScore {} is not {} than policy cvss V3 exploitabilityScore {}, skipping"
                                    .format(
                                        vuln_cvss_exploitabilityScore,
                                        self.
                                        cvss_exploitabilityScore_comparison.
                                        value(), cvss_exploitabilityScore))
                                continue
                            else:
                                parameter_data[
                                    'cvssV3_exploitabilityScore'] = vuln_cvss_exploitabilityScore

                        if cvss_impactScore is not None:
                            if not cvss_impactScore_comparison_fn(
                                    vuln_cvss_impactScore, cvss_impactScore):
                                log.debug(
                                    "OS vulnerability cvss V3 impactScore {} is not {} than policy cvss V3 impactScore {}, skipping"
                                    .format(
                                        vuln_cvss_impactScore,
                                        self.cvss_impactScore_comparison.value(
                                        ), cvss_impactScore))
                                continue
                            else:
                                parameter_data[
                                    'cvssV3_impactScore'] = vuln_cvss_impactScore

                        # Check fix_available status if specified by user in policy
                        if is_fix_available is not None:
                            # Must to a fix_available check
                            fix_available_in = pkg_vuln.fixed_in()

                            if is_fix_available == (fix_available_in
                                                    is not None):
                                # explicit fix state check matches fix availability
                                if is_fix_available:
                                    parameter_data[
                                        'fixed_version'] = fix_available_in

                        parameter_data['link'] = pkg_vuln.vulnerability.link

                        fix_msg = ''
                        if parameter_data.get('fixed_version', None):
                            fix_msg = "(fixed in: {})".format(
                                parameter_data.get('fixed_version'))

                        score_msg = ''
                        score_tuples = []
                        for s in [
                                'cvssV3_baseScore',
                                'cvssV3_exploitabilityScore',
                                'cvssV3_impactScore'
                        ]:
                            if parameter_data.get(s, None):
                                score_tuples.append("{}={}".format(
                                    s, parameter_data.get(s)))
                        if score_tuples:
                            score_msg = "({})".format(' '.join(score_tuples))

                        time_msg = ''
                        time_tuples = []
                        for s in [
                                'max_days_since_creation', 'max_days_since_fix'
                        ]:
                            if parameter_data.get(s, None):
                                time_tuples.append("{}={}".format(
                                    s, parameter_data.get(s)))
                        if time_tuples:
                            time_msg = "({})".format(' '.join(time_tuples))

                        # new detail message approach
                        #pkgname = pkg_vuln.pkg_name
                        #if pkg_vuln.pkg_version != 'None':
                        #    pkgname += "-{}".format(pkg_vuln.pkg_version)
                        #msg = "Vulnerability found in package {} - matching parameters: ".format(pkgname)
                        #for i in parameter_data:
                        #    msg += "{}={} ".format(i, parameter_data[i])

                        # original detail message approach
                        pkgname = pkg_vuln.pkg_name
                        msg = "{} Vulnerability found in {} package type ({}) - {} {}{}{}({} - {})".format(
                            parameter_data['severity'].upper(),
                            parameter_data['pkg_class'],
                            parameter_data['pkg_type'], pkgname, fix_msg,
                            score_msg, time_msg,
                            parameter_data['vulnerability_id'],
                            parameter_data['link'])

                        self._fire(instance_id=pkg_vuln.vulnerability_id +
                                   '+' + pkg_vuln.pkg_name,
                                   msg=msg)
Esempio n. 4
0
class VulnerabilityMatchTrigger(BaseTrigger):
    __trigger_name__ = "package"
    __description__ = (
        "Triggers if a found vulnerability in an image meets the comparison criteria."
    )

    SEVERITY_COMPARISONS = {
        "=": lambda x, y: x == y,
        "!=": lambda x, y: x != y,
        "<": lambda x, y: x < y,
        ">": lambda x, y: x > y,
        "<=": lambda x, y: x <= y,
        ">=": lambda x, y: x >= y,
    }

    package_type = EnumStringParameter(
        name="package_type",
        example_str="all",
        enum_values=["all", "os", "non-os"],
        description="Only trigger for specific package type.",
        is_required=True,
        sort_order=1,
    )

    severity_comparison = EnumStringParameter(
        name="severity_comparison",
        example_str=">",
        description=
        "The type of comparison to perform for severity evaluation.",
        enum_values=list(SEVERITY_COMPARISONS.keys()),
        is_required=False,
        sort_order=2,
    )
    severity = EnumStringParameter(
        name="severity",
        example_str="high",
        description="Severity to compare against.",
        enum_values=SEVERITY_ORDERING,
        is_required=False,
        sort_order=3,
    )

    cvss_v3_base_score_comparison = EnumStringParameter(
        name="cvss_v3_base_score_comparison",
        example_str=">",
        description=
        "The type of comparison to perform for CVSS v3 base score evaluation.",
        enum_values=list(SEVERITY_COMPARISONS.keys()),
        is_required=False,
        sort_order=4,
    )
    cvss_v3_base_score = FloatStringParameter(
        name="cvss_v3_base_score",
        example_string="5.0",
        description="CVSS v3 base score to compare against.",
        is_required=False,
        sort_order=5,
    )

    cvss_v3_exploitability_score_comparison = EnumStringParameter(
        name="cvss_v3_exploitability_score_comparison",
        example_str=">",
        description=
        "The type of comparison to perform for CVSS v3 exploitability sub score evaluation.",
        enum_values=list(SEVERITY_COMPARISONS.keys()),
        is_required=False,
        sort_order=6,
    )
    cvss_v3_exploitability_score = FloatStringParameter(
        name="cvss_v3_exploitability_score",
        example_string="5.0",
        description="CVSS v3 exploitability sub score to compare against.",
        is_required=False,
        sort_order=7,
    )

    cvss_v3_impact_score_comparison = EnumStringParameter(
        name="cvss_v3_impact_score_comparison",
        example_str=">",
        description=
        "The type of comparison to perform for CVSS v3 impact sub score evaluation.",
        enum_values=list(SEVERITY_COMPARISONS.keys()),
        is_required=False,
        sort_order=8,
    )
    cvss_v3_impact_score = FloatStringParameter(
        name="cvss_v3_impact_score",
        example_string="5.0",
        description="CVSS v3 impact sub score to compare against.",
        is_required=False,
        sort_order=9,
    )

    fix_available = BooleanStringParameter(
        name="fix_available",
        example_str="true",
        description=
        "If present, the fix availability for the vulnerability record must match the value of this parameter.",
        is_required=False,
        sort_order=10,
    )
    vendor_only = BooleanStringParameter(
        name="vendor_only",
        example_str="true",
        description=
        "If True, an available fix for this CVE must not be explicitly marked as wont be addressed by the vendor",
        is_required=False,
        sort_order=11,
    )
    max_days_since_creation = IntegerStringParameter(
        name="max_days_since_creation",
        example_str="7",
        description=
        "If provided, this CVE must be older than the days provided to trigger.",
        is_required=False,
        sort_order=12,
    )
    max_days_since_fix = IntegerStringParameter(
        name="max_days_since_fix",
        example_str="30",
        description=
        "If provided (only evaluated when fix_available option is also set to true), the fix first observed time must be older than days provided, to trigger.",
        is_required=False,
        sort_order=13,
    )

    vendor_cvss_v3_base_score_comparison = EnumStringParameter(
        name="vendor_cvss_v3_base_score_comparison",
        example_str=">",
        description=
        "The type of comparison to perform for vendor specified CVSS v3 base score evaluation.",
        enum_values=list(SEVERITY_COMPARISONS.keys()),
        is_required=False,
        sort_order=14,
    )
    vendor_cvss_v3_base_score = FloatStringParameter(
        name="vendor_cvss_v3_base_score",
        example_string="5.0",
        description="Vendor CVSS v3 base score to compare against.",
        is_required=False,
        sort_order=15,
    )

    vendor_cvss_v3_exploitability_score_comparison = EnumStringParameter(
        name="vendor_cvss_v3_exploitability_score_comparison",
        example_str=">",
        description=
        "The type of comparison to perform for vendor specified CVSS v3 exploitability sub score evaluation.",
        enum_values=list(SEVERITY_COMPARISONS.keys()),
        is_required=False,
        sort_order=16,
    )
    vendor_cvss_v3_exploitability_score = FloatStringParameter(
        name="vendor_cvss_v3_exploitability_score",
        example_string="5.0",
        description=
        "Vendor CVSS v3 exploitability sub score to compare against.",
        is_required=False,
        sort_order=17,
    )

    vendor_cvss_v3_impact_score_comparison = EnumStringParameter(
        name="vendor_cvss_v3_impact_score_comparison",
        example_str=">",
        description=
        "The type of comparison to perform for vendor specified CVSS v3 impact sub score evaluation.",
        enum_values=list(SEVERITY_COMPARISONS.keys()),
        is_required=False,
        sort_order=18,
    )
    vendor_cvss_v3_impact_score = FloatStringParameter(
        name="vendor_cvss_v3_impact_score",
        example_string="5.0",
        description="Vendor CVSS v3 impact sub score to compare against.",
        is_required=False,
        sort_order=19,
    )

    package_path_exclude = SimpleStringParameter(
        name="package_path_exclude",
        example_str=".*test\.jar*",
        description=
        "The regex to evaluate against the package path to exclude vulnerabilities",
        is_required=False,
        sort_order=20,
    )

    def evaluate(self, image_obj, context):
        is_fix_available = self.fix_available.value()
        is_vendor_only = self.vendor_only.value(default_if_none=True)
        comparison_idx = SEVERITY_ORDERING.index(
            self.severity.value(default_if_none="unknown").lower())
        comparison_fn = self.SEVERITY_COMPARISONS.get(
            self.severity_comparison.value(default_if_none=">"))

        cvss_v3_base_score = self.cvss_v3_base_score.value()
        cvss_v3_base_score_comparison_fn = self.SEVERITY_COMPARISONS.get(
            self.cvss_v3_base_score_comparison.value(default_if_none=">="))

        cvss_v3_exploitability_score = self.cvss_v3_exploitability_score.value(
        )
        cvss_v3_exploitability_score_comparison_fn = self.SEVERITY_COMPARISONS.get(
            self.cvss_v3_exploitability_score_comparison.value(
                default_if_none=">="))

        cvss_v3_impact_score = self.cvss_v3_impact_score.value()
        cvss_v3_impact_score_comparison_fn = self.SEVERITY_COMPARISONS.get(
            self.cvss_v3_impact_score_comparison.value(default_if_none=">="))

        now = time.time()

        timeallowed = None
        if self.max_days_since_creation.value() is not None:
            timeallowed = now - int(
                int(self.max_days_since_creation.value()) * 86400)

        fix_timeallowed = None
        if self.max_days_since_fix.value() is not None:
            fix_timeallowed = now - int(
                int(self.max_days_since_fix.value()) * 86400)

        if not comparison_fn:
            pass
            # raise KeyError(self.severity_comparison)

        vendor_cvss_v3_base_score = self.vendor_cvss_v3_base_score.value()
        vendor_cvss_v3_base_score_comparison_fn = self.SEVERITY_COMPARISONS.get(
            self.vendor_cvss_v3_base_score_comparison.value(
                default_if_none=">="))

        vendor_cvss_v3_exploitability_score = (
            self.vendor_cvss_v3_exploitability_score.value())
        vendor_cvss_v3_exploitability_score_comparison_fn = (
            self.SEVERITY_COMPARISONS.get(
                self.vendor_cvss_v3_exploitability_score_comparison.value(
                    default_if_none=">=")))

        vendor_cvss_v3_impact_score = self.vendor_cvss_v3_impact_score.value()
        vendor_cvss_v3_impact_score_comparison_fn = self.SEVERITY_COMPARISONS.get(
            self.vendor_cvss_v3_impact_score_comparison.value(
                default_if_none=">="))

        package_path_re = None
        path_exclude_re_value = self.package_path_exclude.value()
        if path_exclude_re_value is not None:
            package_path_re = re.compile(path_exclude_re_value)

        vuln_matches = context.data.get("loaded_vulnerabilities")

        if vuln_matches:
            pkg_type_value = self.package_type.value()

            for vuln_match in vuln_matches:
                vulnerability_obj = vuln_match.vulnerability
                artifact_obj = vuln_match.artifact
                fix_obj = vuln_match.fix
                match_obj = vuln_match.match
                nvd_cvss_objects = vuln_match.get_cvss_scores_nvd()
                vendor_cvss_objects = vuln_match.get_cvss_scores_vendor()

                new_vuln_pkg_class = ("non-os"
                                      if artifact_obj.pkg_type.lower()
                                      in nonos_package_types else "os")

                # Filter first by package class, if rule has a filter
                if pkg_type_value != "all" and pkg_type_value != new_vuln_pkg_class:
                    logger.debug(
                        "Gate package type {} is not a match for artifact package type {}, skipping"
                        .format(pkg_type_value, artifact_obj.pkg_type))
                    # don't process the vulnerability if the affected package type is not a match for the gate
                    continue

                # Start constructing gate parameter data
                parameter_data = OrderedDict()

                parameter_data["severity"] = vulnerability_obj.severity.upper()
                parameter_data[
                    "vulnerability_id"] = vulnerability_obj.vulnerability_id
                parameter_data["pkg_type"] = artifact_obj.pkg_type
                parameter_data["pkg_class"] = new_vuln_pkg_class

                # setting fixed_version here regardless of gate parameter,
                fix_versions = [
                    version for version in fix_obj.versions if version
                ]
                fix_available_in = ", ".join(
                    fix_versions) if fix_versions else None

                if fix_available_in:
                    parameter_data["fixed_version"] = fix_available_in

                # Filter by level first
                found_severity_idx = (SEVERITY_ORDERING.index(
                    vulnerability_obj.severity.lower())
                                      if vulnerability_obj.severity else 0)
                if comparison_fn(found_severity_idx, comparison_idx):
                    # package path excludes logic for non-os packages only
                    if new_vuln_pkg_class == "non-os" and package_path_re:
                        match_found = package_path_re.match(
                            artifact_obj.location)
                        if match_found is not None:
                            logger.debug(
                                "Non-OS vulnerability {} package path {} matches package path exclude {}, skipping"
                                .format(
                                    artifact_obj.name,
                                    artifact_obj.location,
                                    path_exclude_re_value,
                                ))
                            continue

                    # Check vendor_only flag specified by the user in policy
                    if is_vendor_only:
                        if fix_obj.wont_fix:
                            logger.debug(
                                "{} vulnerability {} for package {} is marked by vendor as won't fix, skipping"
                                .format(
                                    new_vuln_pkg_class,
                                    vulnerability_obj.vulnerability_id,
                                    artifact_obj.name,
                                ))
                            continue

                    # Check if the vulnerability is to recent for this policy
                    if timeallowed:
                        if (calendar.timegm(match_obj.detected_at.timetuple())
                                > timeallowed):
                            continue
                        parameter_data[
                            "max_days_since_creation"] = match_obj.detected_at.date(
                            )

                    if is_fix_available and fix_timeallowed is not None:
                        fix_observed_at = (fix_obj.observed_at
                                           if fix_available_in else None)

                        if fix_observed_at:
                            if (calendar.timegm(fix_observed_at.timetuple()) >
                                    fix_timeallowed):
                                continue
                            else:
                                parameter_data[
                                    "max_days_since_fix"] = fix_observed_at.date(
                                    )

                    vuln_cvss_base_score = -1.0
                    vuln_cvss_exploitability_score = -1.0
                    vuln_cvss_impact_score = -1.0

                    # Gather cvss scores before operating with max
                    nvd_cvss_v3_scores = []
                    for cvss_obj in nvd_cvss_objects:
                        if cvss_obj.version.startswith("3"):
                            nvd_cvss_v3_scores.append(cvss_obj)

                    # Compute max score for each type
                    if nvd_cvss_v3_scores:
                        vuln_cvss_base_score = max(
                            item.base_score for item in nvd_cvss_v3_scores)
                        vuln_cvss_exploitability_score = max(
                            item.exploitability_score
                            for item in nvd_cvss_v3_scores)
                        vuln_cvss_impact_score = max(
                            item.impact_score for item in nvd_cvss_v3_scores)

                    if cvss_v3_base_score is not None:
                        if not cvss_v3_base_score_comparison_fn(
                                vuln_cvss_base_score, cvss_v3_base_score):
                            logger.debug(
                                "{} vulnerability {} cvss V3 base_score {} is not {} than policy cvss V3 base_score {}, skipping"
                                .format(
                                    new_vuln_pkg_class,
                                    vulnerability_obj.vulnerability_id,
                                    vuln_cvss_base_score,
                                    self.cvss_v3_base_score_comparison.value(),
                                    cvss_v3_base_score,
                                ))
                            continue
                        else:
                            parameter_data[
                                "cvss_v3_base_score"] = vuln_cvss_base_score

                    if cvss_v3_exploitability_score is not None:
                        if not cvss_v3_exploitability_score_comparison_fn(
                                vuln_cvss_exploitability_score,
                                cvss_v3_exploitability_score):
                            logger.debug(
                                "{} vulnerability {} cvss V3 exploitability_score {} is not {} than policy cvss V3 exploitability_score {}, skipping"
                                .format(
                                    new_vuln_pkg_class,
                                    vulnerability_obj.vulnerability_id,
                                    vuln_cvss_exploitability_score,
                                    self.
                                    cvss_v3_exploitability_score_comparison.
                                    value(),
                                    cvss_v3_exploitability_score,
                                ))
                            continue
                        else:
                            parameter_data[
                                "cvss_v3_exploitability_score"] = vuln_cvss_exploitability_score

                    if cvss_v3_impact_score is not None:
                        if not cvss_v3_impact_score_comparison_fn(
                                vuln_cvss_impact_score, cvss_v3_impact_score):
                            logger.debug(
                                "{} vulnerability {} cvss V3 impact_score {} is not {} than policy cvss V3 impact_score {}, skipping"
                                .format(
                                    new_vuln_pkg_class,
                                    vulnerability_obj.vulnerability_id,
                                    vuln_cvss_impact_score,
                                    self.cvss_v3_impact_score_comparison.value(
                                    ),
                                    cvss_v3_impact_score,
                                ))
                            continue
                        else:
                            parameter_data[
                                "cvss_v3_impact_score"] = vuln_cvss_impact_score

                    # Check fix_available status if specified by user in policy
                    if is_fix_available is not None:
                        # explicit fix state check matches fix availability
                        if is_fix_available != (fix_available_in is not None):
                            # if_fix_available is set but does not match is_fix_available check
                            continue

                    parameter_data["link"] = vulnerability_obj.link

                    fix_msg = ""
                    if parameter_data.get("fixed_version", None):
                        fix_msg = "(fixed in: {})".format(
                            parameter_data.get("fixed_version"))

                    score_msg = ""
                    score_tuples = []
                    for s in [
                            "cvss_v3_base_score",
                            "cvss_v3_exploitability_score",
                            "cvss_v3_impact_score",
                    ]:
                        if parameter_data.get(s, None):
                            score_tuples.append("{}={}".format(
                                s, parameter_data.get(s)))
                    if score_tuples:
                        score_msg = "({})".format(" ".join(score_tuples))

                    time_msg = ""
                    time_tuples = []
                    for s in ["max_days_since_creation", "max_days_since_fix"]:
                        if parameter_data.get(s, None):
                            time_tuples.append("{}={}".format(
                                s, parameter_data.get(s)))
                    if time_tuples:
                        time_msg = "({})".format(" ".join(time_tuples))

                    # process vendor CVSS scores
                    vuln_vendor_cvss_base_score = -1.0
                    vuln_vendor_cvss_exploitability_score = -1.0
                    vuln_vendor_cvss_impact_score = -1.0

                    # Gather cvss scores before operating with max
                    vendor_cvss_v3_scores = []
                    for score_obj in vendor_cvss_objects:
                        if score_obj.version.startswith("3"):
                            vendor_cvss_v3_scores.append(score_obj)

                    if vendor_cvss_v3_scores:
                        vuln_vendor_cvss_base_score = max(
                            item.base_score for item in vendor_cvss_v3_scores)
                        vuln_vendor_cvss_exploitability_score = max(
                            item.exploitability_score
                            for item in vendor_cvss_v3_scores)
                        vuln_vendor_cvss_impact_score = max(
                            item.impact_score
                            for item in vendor_cvss_v3_scores)

                    if vendor_cvss_v3_base_score is not None:
                        if not vendor_cvss_v3_base_score_comparison_fn(
                                vuln_vendor_cvss_base_score,
                                vendor_cvss_v3_base_score):
                            logger.debug(
                                "{} vulnerability {} vendor cvss V3 base score {} is not {} than policy vendor cvss V3 base score {}, skipping"
                                .format(
                                    new_vuln_pkg_class,
                                    vulnerability_obj.vulnerability_id,
                                    vuln_vendor_cvss_base_score,
                                    self.vendor_cvss_v3_base_score_comparison.
                                    value(),
                                    vendor_cvss_v3_base_score,
                                ))
                            continue
                        else:
                            parameter_data[
                                "vendor_cvss_v3_base_score"] = vuln_vendor_cvss_base_score

                    if vendor_cvss_v3_exploitability_score is not None:
                        if not vendor_cvss_v3_exploitability_score_comparison_fn(
                                vuln_vendor_cvss_exploitability_score,
                                vendor_cvss_v3_exploitability_score,
                        ):
                            logger.debug(
                                "{} vulnerability {} vendor cvss V3 exploitability sub score {} is not {} than policy vendor cvss V3 exploitability sub score {}, skipping"
                                .format(
                                    new_vuln_pkg_class,
                                    vulnerability_obj.vulnerability_id,
                                    vuln_vendor_cvss_exploitability_score,
                                    self.
                                    vendor_cvss_v3_exploitability_score_comparison
                                    .value(),
                                    vendor_cvss_v3_exploitability_score,
                                ))
                            continue
                        else:
                            parameter_data[
                                "vendor_cvss_v3_exploitability_score"] = vuln_vendor_cvss_exploitability_score

                    if vendor_cvss_v3_impact_score is not None:
                        if not vendor_cvss_v3_impact_score_comparison_fn(
                                vuln_vendor_cvss_impact_score,
                                vendor_cvss_v3_impact_score):
                            logger.debug(
                                "%s vulnerability %s vendor cvss V3 impact sub score %d is not %s than policy vendor cvss V3 impact score %d, skipping",
                                new_vuln_pkg_class,
                                vulnerability_obj.vulnerability_id,
                                vuln_vendor_cvss_impact_score,
                                self.vendor_cvss_v3_impact_score_comparison.
                                value(),
                                vendor_cvss_v3_impact_score,
                            )
                            continue
                        else:
                            parameter_data[
                                "vendor_cvss_v3_impact_score"] = vuln_vendor_cvss_impact_score

                    vendor_score_msg = ""
                    vendor_score_tuples = []
                    for s in [
                            "vendor_cvss_v3_base_score",
                            "vendor_cvss_v3_exploitability_score",
                            "vendor_cvss_v3_impact_score",
                    ]:
                        if parameter_data.get(s, None):
                            vendor_score_tuples.append("{}={}".format(
                                s, parameter_data.get(s)))
                    if vendor_score_tuples:
                        vendor_score_msg = "({})".format(
                            " ".join(vendor_score_tuples))

                    # new detail message approach
                    # pkgname = pkg_vuln.pkg_name
                    # if pkg_vuln.pkg_version != 'None':
                    #    pkgname += "-{}".format(pkg_vuln.pkg_version)
                    # msg = "Vulnerability found in package {} - matching parameters: ".format(pkgname)
                    # for i in parameter_data:
                    #    msg += "{}={} ".format(i, parameter_data[i])

                    if new_vuln_pkg_class == "non-os":
                        trigger_fname = None
                        if artifact_obj.pkg_type in ["java", "gem"]:
                            try:
                                trigger_fname = artifact_obj.location.split(
                                    "/")[-1]
                            except:
                                trigger_fname = None
                        elif artifact_obj.pkg_type in ["npm"]:
                            try:
                                trigger_fname = artifact_obj.location.split(
                                    "/")[-2]
                            except:
                                trigger_fname = None

                        if not trigger_fname:
                            trigger_fname = "-".join(
                                [artifact_obj.name, artifact_obj.version])

                        pkgname = artifact_obj.location
                    else:
                        trigger_fname = artifact_obj.name
                        pkgname = artifact_obj.name

                    # original detail message approach
                    msg = "{} Vulnerability found in {} package type ({}) - {} {}{}{}{}({} - {})".format(
                        parameter_data["severity"].upper(),
                        parameter_data["pkg_class"],
                        parameter_data["pkg_type"],
                        pkgname,
                        fix_msg,
                        score_msg,
                        time_msg,
                        vendor_score_msg,
                        parameter_data["vulnerability_id"],
                        parameter_data["link"],
                    )

                    self._fire(
                        instance_id=vulnerability_obj.vulnerability_id + "+" +
                        trigger_fname,
                        msg=msg,
                    )
Esempio n. 5
0
class VulnerabilityMatchTrigger(BaseTrigger):
    __trigger_name__ = 'package'
    __description__ = 'Triggers if a found vulnerability in an image meets the comparison criteria.'

    SEVERITY_COMPARISONS = {
        '=': lambda x, y: x == y,
        '!=': lambda x, y: x != y,
        '<': lambda x, y: x < y,
        '>': lambda x, y: x > y,
        '<=': lambda x, y: x <= y,
        '>=': lambda x, y: x >= y
    }

    package_type = EnumStringParameter(
        name='package_type',
        example_str='all',
        enum_values=['all', 'os', 'non-os'],
        description='Only trigger for specific package type.',
        is_required=True,
        sort_order=1)
    severity_comparison = EnumStringParameter(
        name='severity_comparison',
        example_str='>',
        description=
        'The type of comparison to perform for severity evaluation.',
        enum_values=list(SEVERITY_COMPARISONS.keys()),
        is_required=True,
        sort_order=2)
    severity = EnumStringParameter(name='severity',
                                   example_str='high',
                                   description='Severity to compare against.',
                                   enum_values=SEVERITY_ORDERING,
                                   is_required=True,
                                   sort_order=3)
    fix_available = BooleanStringParameter(
        name='fix_available',
        example_str='true',
        description=
        'If present, the fix availability for the vulnerability record must match the value of this parameter.',
        is_required=False,
        sort_order=4)
    vendor_only = BooleanStringParameter(
        name='vendor_only',
        example_str='true',
        description=
        'If True, an available fix for this CVE must not be explicitly marked as wont be addressed by the vendor',
        is_required=False,
        sort_order=5)
    max_days_since_creation = IntegerStringParameter(
        name='max_days_since_creation',
        example_str='7',
        description=
        'If provided, this CVE must be older than the days provided to trigger.',
        is_required=False,
        sort_order=6)

    def evaluate(self, image_obj, context):
        is_fix_available = self.fix_available.value()
        is_vendor_only = self.vendor_only.value(default_if_none=True)
        comparison_idx = SEVERITY_ORDERING.index(self.severity.value().lower())
        comparison_fn = self.SEVERITY_COMPARISONS.get(
            self.severity_comparison.value())
        timeallowed = time.time()
        if self.max_days_since_creation.value() is not None:
            timeallowed -= int(
                int(self.max_days_since_creation.value()) * 86400)
        if not comparison_fn:
            raise KeyError(self.severity_comparison)

        if self.package_type.value() in ['all', 'non-os']:
            cpevulns = context.data.get('loaded_cpe_vulnerabilities')
            if cpevulns:
                try:
                    for sev in list(cpevulns.keys()):
                        found_severity_idx = SEVERITY_ORDERING.index(
                            sev.lower()) if sev else 0
                        if comparison_fn(found_severity_idx, comparison_idx):
                            for image_cpe, vulnerability_cpe in cpevulns[sev]:
                                # Check if the vulnerability is to recent for this policy
                                if calendar.timegm(vulnerability_cpe.created_at
                                                   .timetuple()) > timeallowed:
                                    continue
                                trigger_fname = None
                                if image_cpe.pkg_type in ['java', 'gem']:
                                    try:
                                        trigger_fname = image_cpe.pkg_path.split(
                                            "/")[-1]
                                    except:
                                        trigger_fname = None
                                elif image_cpe.pkg_type in ['npm']:
                                    try:
                                        trigger_fname = image_cpe.pkg_path.split(
                                            "/")[-2]
                                    except:
                                        trigger_fname = None

                                if not trigger_fname:
                                    trigger_fname = "-".join(
                                        [image_cpe.name, image_cpe.version])

                                if is_fix_available is not None:
                                    # Must to a fix_available check
                                    fix_available_in = image_cpe.fixed_in()
                                    if is_fix_available == (fix_available_in
                                                            is not None):
                                        message = sev.upper() + " Vulnerability found in non-os package type ("+image_cpe.pkg_type+") - " + \
                                                  image_cpe.pkg_path + " (" + vulnerability_cpe.vulnerability_id + " - https://nvd.nist.gov/vuln/detail/" + vulnerability_cpe.vulnerability_id + ")"
                                        self._fire(
                                            instance_id=vulnerability_cpe.
                                            vulnerability_id + '+' +
                                            trigger_fname,
                                            msg=message)
                                else:
                                    message = sev.upper() + " Vulnerability found in non-os package type ("+image_cpe.pkg_type+") - " + \
                                              image_cpe.pkg_path + " (" + vulnerability_cpe.vulnerability_id + " - https://nvd.nist.gov/vuln/detail/" + vulnerability_cpe.vulnerability_id + ")"
                                    self._fire(instance_id=vulnerability_cpe.
                                               vulnerability_id + '+' +
                                               trigger_fname,
                                               msg=message)

                except Exception as err:
                    log.warn(
                        "problem during non-os vulnerability evaluation - exception: {}"
                        .format(err))

        if self.package_type.value() in ['all', 'os']:
            vulns = context.data.get('loaded_vulnerabilities')
            if vulns:
                for pkg_vuln in vulns:
                    # Filter by level first
                    found_severity_idx = SEVERITY_ORDERING.index(
                        pkg_vuln.vulnerability.severity.lower(
                        )) if pkg_vuln.vulnerability.severity else 0

                    if comparison_fn(found_severity_idx, comparison_idx):
                        # Check vendor_only flag specified by the user in policy
                        if is_vendor_only and pkg_vuln.fix_has_no_advisory():
                            # skip this vulnerability
                            continue

                        # Check if the vulnerability is to recent for this policy
                        if calendar.timegm(pkg_vuln.vulnerability.created_at.
                                           timetuple()) > timeallowed:
                            continue

                        # Check fix_available status if specified by user in policy
                        if is_fix_available is not None:
                            # Must to a fix_available check
                            fix_available_in = pkg_vuln.fixed_in()

                            if is_fix_available == (fix_available_in
                                                    is not None):
                                # explicit fix state check matches fix availability
                                if is_fix_available:
                                    message = pkg_vuln.vulnerability.severity.upper() + " Vulnerability found in os package type ("+pkg_vuln.pkg_type+") - " + \
                                              pkg_vuln.pkg_name + " (fixed in: {}".format(fix_available_in) + ") - (" + pkg_vuln.vulnerability_id + " - " + pkg_vuln.vulnerability.link + ")"
                                else:
                                    message = pkg_vuln.vulnerability.severity.upper() + " Vulnerability found in os package type ("+pkg_vuln.pkg_type+") - " + \
                                              pkg_vuln.pkg_name + " (" + pkg_vuln.vulnerability_id + " - " + pkg_vuln.vulnerability.link + ")"

                                self._fire(
                                    instance_id=pkg_vuln.vulnerability_id +
                                    '+' + pkg_vuln.pkg_name,
                                    msg=message)
                        else:
                            # No fix status check since not specified by user
                            message = pkg_vuln.vulnerability.severity.upper() + " Vulnerability found in os package type ("+pkg_vuln.pkg_type+") - " + \
                                      pkg_vuln.pkg_name + " (" + pkg_vuln.vulnerability_id + " - " + pkg_vuln.vulnerability.link + ")"
                            self._fire(instance_id=pkg_vuln.vulnerability_id +
                                       '+' + pkg_vuln.pkg_name,
                                       msg=message)
class VulnerabilityMatchTrigger(BaseTrigger):
    __trigger_name__ = 'package'
    __description__ = 'Triggers if a found vulnerability in an image meets the comparison criteria.'

    SEVERITY_COMPARISONS = {
        '=': lambda x, y: x == y,
        '!=': lambda x, y: x != y,
        '<': lambda x, y: x < y,
        '>': lambda x, y: x > y,
        '<=': lambda x, y: x <= y,
        '>=': lambda x, y: x >= y
    }

    package_type = EnumStringParameter(
        name='package_type',
        example_str='all',
        enum_values=['all', 'os', 'non-os'],
        description='Only trigger for specific package type.',
        is_required=True,
        sort_order=1)

    severity_comparison = EnumStringParameter(
        name='severity_comparison',
        example_str='>',
        description=
        'The type of comparison to perform for severity evaluation.',
        enum_values=list(SEVERITY_COMPARISONS.keys()),
        is_required=False,
        sort_order=2)
    severity = EnumStringParameter(name='severity',
                                   example_str='high',
                                   description='Severity to compare against.',
                                   enum_values=SEVERITY_ORDERING,
                                   is_required=False,
                                   sort_order=3)

    cvss_v3_base_score_comparison = EnumStringParameter(
        name='cvss_v3_base_score_comparison',
        example_str='>',
        description=
        'The type of comparison to perform for CVSS v3 base score evaluation.',
        enum_values=list(SEVERITY_COMPARISONS.keys()),
        is_required=False,
        sort_order=4)
    cvss_v3_base_score = FloatStringParameter(
        name='cvss_v3_base_score',
        example_string='5.0',
        description='CVSS v3 base score to compare against.',
        is_required=False,
        sort_order=5)

    cvss_v3_exploitability_score_comparison = EnumStringParameter(
        name='cvss_v3_exploitability_score_comparison',
        example_str='>',
        description=
        'The type of comparison to perform for CVSS v3 exploitability sub score evaluation.',
        enum_values=list(SEVERITY_COMPARISONS.keys()),
        is_required=False,
        sort_order=6)
    cvss_v3_exploitability_score = FloatStringParameter(
        name='cvss_v3_exploitability_score',
        example_string='5.0',
        description='CVSS v3 exploitability sub score to compare against.',
        is_required=False,
        sort_order=7)

    cvss_v3_impact_score_comparison = EnumStringParameter(
        name='cvss_v3_impact_score_comparison',
        example_str='>',
        description=
        'The type of comparison to perform for CVSS v3 impact sub score evaluation.',
        enum_values=list(SEVERITY_COMPARISONS.keys()),
        is_required=False,
        sort_order=8)
    cvss_v3_impact_score = FloatStringParameter(
        name='cvss_v3_impact_score',
        example_string='5.0',
        description='CVSS v3 impact sub score to compare against.',
        is_required=False,
        sort_order=9)

    fix_available = BooleanStringParameter(
        name='fix_available',
        example_str='true',
        description=
        'If present, the fix availability for the vulnerability record must match the value of this parameter.',
        is_required=False,
        sort_order=10)
    vendor_only = BooleanStringParameter(
        name='vendor_only',
        example_str='true',
        description=
        'If True, an available fix for this CVE must not be explicitly marked as wont be addressed by the vendor',
        is_required=False,
        sort_order=11)
    max_days_since_creation = IntegerStringParameter(
        name='max_days_since_creation',
        example_str='7',
        description=
        'If provided, this CVE must be older than the days provided to trigger.',
        is_required=False,
        sort_order=12)
    max_days_since_fix = IntegerStringParameter(
        name='max_days_since_fix',
        example_str='30',
        description=
        'If provided (only evaluated when fix_available option is also set to true), the fix first observed time must be older than days provided, to trigger.',
        is_required=False,
        sort_order=13)

    vendor_cvss_v3_base_score_comparison = EnumStringParameter(
        name='vendor_cvss_v3_base_score_comparison',
        example_str='>',
        description=
        'The type of comparison to perform for vendor specified CVSS v3 base score evaluation.',
        enum_values=list(SEVERITY_COMPARISONS.keys()),
        is_required=False,
        sort_order=14)
    vendor_cvss_v3_base_score = FloatStringParameter(
        name='vendor_cvss_v3_base_score',
        example_string='5.0',
        description='Vendor CVSS v3 base score to compare against.',
        is_required=False,
        sort_order=15)

    vendor_cvss_v3_exploitability_score_comparison = EnumStringParameter(
        name='vendor_cvss_v3_exploitability_score_comparison',
        example_str='>',
        description=
        'The type of comparison to perform for vendor specified CVSS v3 exploitability sub score evaluation.',
        enum_values=list(SEVERITY_COMPARISONS.keys()),
        is_required=False,
        sort_order=16)
    vendor_cvss_v3_exploitability_score = FloatStringParameter(
        name='vendor_cvss_v3_exploitability_score',
        example_string='5.0',
        description=
        'Vendor CVSS v3 exploitability sub score to compare against.',
        is_required=False,
        sort_order=17)

    vendor_cvss_v3_impact_score_comparison = EnumStringParameter(
        name='vendor_cvss_v3_impact_score_comparison',
        example_str='>',
        description=
        'The type of comparison to perform for vendor specified CVSS v3 impact sub score evaluation.',
        enum_values=list(SEVERITY_COMPARISONS.keys()),
        is_required=False,
        sort_order=18)
    vendor_cvss_v3_impact_score = FloatStringParameter(
        name='vendor_cvss_v3_impact_score',
        example_string='5.0',
        description='Vendor CVSS v3 impact sub score to compare against.',
        is_required=False,
        sort_order=19)

    def evaluate(self, image_obj, context):
        is_fix_available = self.fix_available.value()
        is_vendor_only = self.vendor_only.value(default_if_none=True)
        comparison_idx = SEVERITY_ORDERING.index(
            self.severity.value(default_if_none='unknown').lower())
        comparison_fn = self.SEVERITY_COMPARISONS.get(
            self.severity_comparison.value(default_if_none=">"))

        cvss_v3_base_score = self.cvss_v3_base_score.value()
        cvss_v3_base_score_comparison_fn = self.SEVERITY_COMPARISONS.get(
            self.cvss_v3_base_score_comparison.value(default_if_none=">="))

        cvss_v3_exploitability_score = self.cvss_v3_exploitability_score.value(
        )
        cvss_v3_exploitability_score_comparison_fn = self.SEVERITY_COMPARISONS.get(
            self.cvss_v3_exploitability_score_comparison.value(
                default_if_none=">="))

        cvss_v3_impact_score = self.cvss_v3_impact_score.value()
        cvss_v3_impact_score_comparison_fn = self.SEVERITY_COMPARISONS.get(
            self.cvss_v3_impact_score_comparison.value(default_if_none=">="))

        now = time.time()

        timeallowed = None
        if self.max_days_since_creation.value() is not None:
            timeallowed = now - int(
                int(self.max_days_since_creation.value()) * 86400)

        fix_timeallowed = None
        if self.max_days_since_fix.value() is not None:
            fix_timeallowed = now - int(
                int(self.max_days_since_fix.value()) * 86400)

        if not comparison_fn:
            pass
            #raise KeyError(self.severity_comparison)

        vendor_cvss_v3_base_score = self.vendor_cvss_v3_base_score.value()
        vendor_cvss_v3_base_score_comparison_fn = self.SEVERITY_COMPARISONS.get(
            self.vendor_cvss_v3_base_score_comparison.value(
                default_if_none=">="))

        vendor_cvss_v3_exploitability_score = self.vendor_cvss_v3_exploitability_score.value(
        )
        vendor_cvss_v3_exploitability_score_comparison_fn = self.SEVERITY_COMPARISONS.get(
            self.vendor_cvss_v3_exploitability_score_comparison.value(
                default_if_none=">="))

        vendor_cvss_v3_impact_score = self.vendor_cvss_v3_impact_score.value()
        vendor_cvss_v3_impact_score_comparison_fn = self.SEVERITY_COMPARISONS.get(
            self.vendor_cvss_v3_impact_score_comparison.value(
                default_if_none=">="))

        api_endpoint = None  # for populating vulnerability links that point back to engine

        if self.package_type.value() in ['all', 'non-os']:
            cpevulns = context.data.get('loaded_cpe_vulnerabilities')
            if cpevulns:
                try:
                    for sev in list(cpevulns.keys()):
                        found_severity_idx = SEVERITY_ORDERING.index(
                            sev.lower()) if sev else 0
                        if comparison_fn(found_severity_idx, comparison_idx):
                            for image_cpe, vulnerability_cpe in cpevulns[sev]:
                                parameter_data = OrderedDict()

                                parameter_data['severity'] = sev.upper()
                                parameter_data[
                                    'vulnerability_id'] = vulnerability_cpe.vulnerability_id
                                parameter_data['pkg_class'] = 'non-os'
                                parameter_data['pkg_type'] = image_cpe.pkg_type

                                # Check if the vulnerability is too recent for this policy
                                if timeallowed:
                                    if calendar.timegm(
                                            vulnerability_cpe.created_at.
                                            timetuple()) > timeallowed:
                                        continue
                                    parameter_data[
                                        'max_days_since_creation'] = vulnerability_cpe.created_at.date(
                                        )

                                if cvss_v3_base_score is not None:
                                    vuln_cvss_base_score = vulnerability_cpe.parent.get_max_base_score_nvd(
                                    )
                                    if not cvss_v3_base_score_comparison_fn(
                                            vuln_cvss_base_score,
                                            cvss_v3_base_score):
                                        logger.debug(
                                            "Non-OS vulnerability {} cvss V3 base score {} is not {} than policy cvss V3 base score {}, skipping"
                                            .format(
                                                vulnerability_cpe.parent.name,
                                                vuln_cvss_base_score,
                                                self.
                                                cvss_v3_base_score_comparison.
                                                value(), cvss_v3_base_score))
                                        continue
                                    else:
                                        parameter_data[
                                            'cvss_v3_base_score'] = vuln_cvss_base_score

                                if cvss_v3_exploitability_score is not None:
                                    vuln_cvss_exploitability_score = vulnerability_cpe.parent.get_max_exploitability_score_nvd(
                                    )
                                    if not cvss_v3_exploitability_score_comparison_fn(
                                            vuln_cvss_exploitability_score,
                                            cvss_v3_exploitability_score):
                                        logger.debug(
                                            "Non-OS vulnerability {} cvss V3 exploitability sub score {} is not {} than policy cvss V3 exploitability sub score {}, skipping"
                                            .format(
                                                vulnerability_cpe.parent.name,
                                                vuln_cvss_exploitability_score,
                                                self.
                                                cvss_v3_exploitability_score_comparison
                                                .value(),
                                                cvss_v3_exploitability_score))
                                        continue
                                    else:
                                        parameter_data[
                                            'cvss_v3_exploitability_score'] = vuln_cvss_exploitability_score

                                if cvss_v3_impact_score is not None:
                                    vuln_cvss_impact_score = vulnerability_cpe.parent.get_max_impact_score_nvd(
                                    )
                                    if not cvss_v3_impact_score_comparison_fn(
                                            vuln_cvss_impact_score,
                                            cvss_v3_impact_score):
                                        logger.debug(
                                            "Non-OS vulnerability {} cvss V3 impact sub score {} is not {} than policy cvss V3 impact score {}, skipping"
                                            .format(
                                                vulnerability_cpe.parent.name,
                                                vuln_cvss_impact_score,
                                                self.
                                                cvss_v3_impact_score_comparison
                                                .value(),
                                                cvss_v3_impact_score))
                                        continue
                                    else:
                                        parameter_data[
                                            'cvss_v3_impact_score'] = vuln_cvss_impact_score

                                trigger_fname = None
                                if image_cpe.pkg_type in ['java', 'gem']:
                                    try:
                                        trigger_fname = image_cpe.pkg_path.split(
                                            "/")[-1]
                                    except:
                                        trigger_fname = None
                                elif image_cpe.pkg_type in ['npm']:
                                    try:
                                        trigger_fname = image_cpe.pkg_path.split(
                                            "/")[-2]
                                    except:
                                        trigger_fname = None

                                if not trigger_fname:
                                    trigger_fname = "-".join(
                                        [image_cpe.name, image_cpe.version])

                                if is_fix_available is not None:
                                    # Must do a fix_available check
                                    fix_available_in = vulnerability_cpe.get_fixed_in(
                                    )

                                    # explicit fix state check matches fix availability
                                    if is_fix_available == (
                                            fix_available_in is not None
                                            and len(fix_available_in) > 0):
                                        if is_fix_available:
                                            parameter_data[
                                                'fixed_version'] = ', '.join(
                                                    fix_available_in)
                                    else:
                                        # if_fix_available is set but does not match is_fix_available check
                                        continue

                                    if is_fix_available and fix_timeallowed is not None:
                                        if fix_available_in:
                                            if calendar.timegm(
                                                    vulnerability_cpe.
                                                    created_at.timetuple(
                                                    )) > fix_timeallowed:
                                                continue
                                            else:
                                                parameter_data[
                                                    'max_days_since_fix'] = vulnerability_cpe.created_at.date(
                                                    )

                                if not vulnerability_cpe.parent.link:
                                    if not api_endpoint:
                                        api_endpoint = get_service_endpoint(
                                            'apiext').strip('/')
                                    parameter_data[
                                        'link'] = '{}/query/vulnerabilities?id={}'.format(
                                            api_endpoint,
                                            vulnerability_cpe.vulnerability_id)
                                else:
                                    parameter_data[
                                        'link'] = vulnerability_cpe.parent.link

                                fix_msg = ''
                                if parameter_data.get('fixed_version', None):
                                    fix_msg = "(fixed in: {})".format(
                                        parameter_data.get('fixed_version'))

                                score_msg = ''
                                score_tuples = []
                                for s in [
                                        'cvss_v3_base_score',
                                        'cvss_v3_exploitability_score',
                                        'cvss_v3_impact_score'
                                ]:
                                    if parameter_data.get(s, None):
                                        score_tuples.append("{}={}".format(
                                            s, parameter_data.get(s)))
                                if score_tuples:
                                    score_msg = "({})".format(
                                        ' '.join(score_tuples))

                                time_msg = ''
                                time_tuples = []
                                for s in [
                                        'max_days_since_creation',
                                        'max_days_since_fix'
                                ]:
                                    if parameter_data.get(s, None):
                                        time_tuples.append("{}={}".format(
                                            s, parameter_data.get(s)))
                                if time_tuples:
                                    time_msg = "({})".format(
                                        ' '.join(time_tuples))

                                if vendor_cvss_v3_base_score is not None:
                                    vuln_vendor_cvss_base_score = vulnerability_cpe.parent.get_max_base_score_vendor(
                                    )
                                    if not vendor_cvss_v3_base_score_comparison_fn(
                                            vuln_vendor_cvss_base_score,
                                            vendor_cvss_v3_base_score):
                                        logger.debug(
                                            "Non-OS vulnerability {} vendor cvss V3 base score {} is not {} than policy vendor cvss V3 base score {}, skipping"
                                            .format(
                                                vulnerability_cpe.parent.name,
                                                vuln_vendor_cvss_base_score,
                                                self.
                                                vendor_cvss_v3_base_score_comparison
                                                .value(),
                                                vendor_cvss_v3_base_score))
                                        continue
                                    else:
                                        parameter_data[
                                            'vendor_cvss_v3_base_score'] = vuln_vendor_cvss_base_score

                                if vendor_cvss_v3_exploitability_score is not None:
                                    vuln_vendor_cvss_exploitability_score = vulnerability_cpe.parent.get_max_exploitability_score_vendor(
                                    )
                                    if not vendor_cvss_v3_exploitability_score_comparison_fn(
                                            vuln_vendor_cvss_exploitability_score,
                                            vendor_cvss_v3_exploitability_score
                                    ):
                                        logger.debug(
                                            "Non-OS vulnerability {} vendor cvss V3 exploitability sub score {} is not {} than policy vendor cvss V3 exploitability sub score {}, skipping"
                                            .format(
                                                vulnerability_cpe.parent.name,
                                                vuln_vendor_cvss_exploitability_score,
                                                self.
                                                vendor_cvss_v3_exploitability_score_comparison
                                                .value(),
                                                vendor_cvss_v3_exploitability_score
                                            ))
                                        continue
                                    else:
                                        parameter_data[
                                            'vendor_cvss_v3_exploitability_score'] = vuln_vendor_cvss_exploitability_score

                                if vendor_cvss_v3_impact_score is not None:
                                    vuln_vendor_cvss_impact_score = vulnerability_cpe.parent.get_max_impact_score_vendor(
                                    )
                                    if not vendor_cvss_v3_impact_score_comparison_fn(
                                            vuln_vendor_cvss_impact_score,
                                            vendor_cvss_v3_impact_score):
                                        logger.debug(
                                            "Non-OS vulnerability {} vendor cvss V3 impact sub score {} is not {} than policy vendor cvss V3 impact score {}, skipping"
                                            .format(
                                                vulnerability_cpe.parent.name,
                                                vuln_vendor_cvss_impact_score,
                                                self.
                                                vendor_cvss_v3_impact_score_comparison
                                                .value(),
                                                vendor_cvss_v3_impact_score))
                                        continue
                                    else:
                                        parameter_data[
                                            'vendor_cvss_v3_impact_score'] = vuln_vendor_cvss_impact_score

                                vendor_score_msg = ''
                                vendor_score_tuples = []
                                for s in [
                                        'vendor_cvss_v3_base_score',
                                        'vendor_cvss_v3_exploitability_score',
                                        'vendor_cvss_v3_impact_score'
                                ]:
                                    if parameter_data.get(s, None):
                                        vendor_score_tuples.append(
                                            "{}={}".format(
                                                s, parameter_data.get(s)))
                                if vendor_score_tuples:
                                    vendor_score_msg = "({})".format(
                                        ' '.join(vendor_score_tuples))

                                # new detail message approach
                                #pkgname = image_cpe.pkg_path
                                #msg = "Vulnerability found in package {} - matching parameters: ".format(pkgname)
                                #for i in parameter_data:
                                #    msg += "{}={} ".format(i, parameter_data[i])

                                pkgname = image_cpe.pkg_path
                                msg = "{} Vulnerability found in {} package type ({}) - {} {}{}{}{}({} - {})".format(
                                    parameter_data['severity'].upper(),
                                    parameter_data['pkg_class'],
                                    parameter_data['pkg_type'], pkgname,
                                    fix_msg, score_msg, time_msg,
                                    vendor_score_msg,
                                    parameter_data['vulnerability_id'],
                                    parameter_data['link'])
                                self._fire(instance_id=vulnerability_cpe.
                                           vulnerability_id + '+' +
                                           trigger_fname,
                                           msg=msg)

                except Exception as err:
                    logger.warn(
                        "problem during non-os vulnerability evaluation - exception: {}"
                        .format(err))

        # Process vulnerabilities that match using ImagePackageVulnerability objects, which can include all types as well
        vulns = context.data.get('loaded_vulnerabilities')

        if vulns:
            # get nvd classes from context or select them once and be done
            _nvd_cls, _cpe_cls = context.data.get('nvd_cpe_cls', (None, None))
            if not _nvd_cls or not _cpe_cls:
                _nvd_cls, _cpe_cls = select_nvd_classes()

            pkg_type_value = self.package_type.value()
            for pkg_vuln in vulns:
                parameter_data = OrderedDict()

                parameter_data[
                    'severity'] = pkg_vuln.vulnerability.severity.upper()
                parameter_data['vulnerability_id'] = pkg_vuln.vulnerability_id
                parameter_data['pkg_type'] = pkg_vuln.pkg_type
                parameter_data[
                    'pkg_class'] = 'non-os' if pkg_vuln.pkg_type in nonos_package_types else 'os'

                # Filter first by package class, if rule has a filter
                if pkg_type_value != 'all' and pkg_type_value != parameter_data[
                        'pkg_class']:
                    continue

                # Filter by level first
                found_severity_idx = SEVERITY_ORDERING.index(
                    pkg_vuln.vulnerability.severity.lower(
                    )) if pkg_vuln.vulnerability.severity else 0
                if comparison_fn(found_severity_idx, comparison_idx):
                    # Check vendor_only flag specified by the user in policy
                    if is_vendor_only:
                        if pkg_vuln.fix_has_no_advisory():
                            # skip this vulnerability
                            continue

                    # Check if the vulnerability is to recent for this policy
                    if timeallowed:
                        if calendar.timegm(pkg_vuln.vulnerability.created_at.
                                           timetuple()) > timeallowed:
                            continue
                        parameter_data[
                            'max_days_since_creation'] = pkg_vuln.vulnerability.created_at.date(
                            )

                    if is_fix_available and fix_timeallowed is not None:
                        fix = pkg_vuln.fixed_artifact()
                        if fix.version and fix.version != 'None':
                            if fix.fix_observed_at and calendar.timegm(
                                    fix.fix_observed_at.timetuple(
                                    )) > fix_timeallowed:
                                continue
                            else:
                                parameter_data[
                                    'max_days_since_fix'] = fix.fix_observed_at.date(
                                    )

                    vuln_cvss_base_score = -1.0
                    vuln_cvss_exploitability_score = -1.0
                    vuln_cvss_impact_score = -1.0

                    for nvd_record in pkg_vuln.vulnerability.get_nvd_vulnerabilities(
                            _nvd_cls=_nvd_cls, _cpe_cls=_cpe_cls):
                        cvss_score = nvd_record.get_max_cvss_score_nvd()
                        if cvss_score.get('base_score',
                                          -1.0) > vuln_cvss_base_score:
                            vuln_cvss_base_score = cvss_score.get(
                                'base_score', -1.0)
                        if cvss_score.get(
                                'exploitability_score',
                                -1.0) > vuln_cvss_exploitability_score:
                            vuln_cvss_exploitability_score = cvss_score.get(
                                'exploitability_score', -1.0)
                        if cvss_score.get('impact_score',
                                          -1.0) > vuln_cvss_impact_score:
                            vuln_cvss_impact_score = cvss_score.get(
                                'impact_score', -1.0)

                    if cvss_v3_base_score is not None:
                        if not cvss_v3_base_score_comparison_fn(
                                vuln_cvss_base_score, cvss_v3_base_score):
                            logger.debug(
                                "OS vulnerability {} cvss V3 base_score {} is not {} than policy cvss V3 base_score {}, skipping"
                                .format(
                                    pkg_vuln.vulnerability_id,
                                    vuln_cvss_base_score,
                                    self.cvss_v3_base_score_comparison.value(),
                                    cvss_v3_base_score))
                            continue
                        else:
                            parameter_data[
                                'cvss_v3_base_score'] = vuln_cvss_base_score

                    if cvss_v3_exploitability_score is not None:
                        if not cvss_v3_exploitability_score_comparison_fn(
                                vuln_cvss_exploitability_score,
                                cvss_v3_exploitability_score):
                            logger.debug(
                                "OS vulnerability {} cvss V3 exploitability_score {} is not {} than policy cvss V3 exploitability_score {}, skipping"
                                .format(
                                    pkg_vuln.vulnerability_id,
                                    vuln_cvss_exploitability_score,
                                    self.
                                    cvss_v3_exploitability_score_comparison.
                                    value(), cvss_v3_exploitability_score))
                            continue
                        else:
                            parameter_data[
                                'cvss_v3_exploitability_score'] = vuln_cvss_exploitability_score

                    if cvss_v3_impact_score is not None:
                        if not cvss_v3_impact_score_comparison_fn(
                                vuln_cvss_impact_score, cvss_v3_impact_score):
                            logger.debug(
                                "OS vulnerability {} cvss V3 impact_score {} is not {} than policy cvss V3 impact_score {}, skipping"
                                .format(
                                    pkg_vuln.vulnerability_id,
                                    vuln_cvss_impact_score,
                                    self.cvss_v3_impact_score_comparison.value(
                                    ), cvss_v3_impact_score))
                            continue
                        else:
                            parameter_data[
                                'cvss_v3_impact_score'] = vuln_cvss_impact_score

                    # Check fix_available status if specified by user in policy
                    if is_fix_available is not None:
                        # Must to a fix_available check
                        fix_available_in = pkg_vuln.fixed_in()

                        if is_fix_available == (fix_available_in is not None):
                            # explicit fix state check matches fix availability
                            if is_fix_available:
                                parameter_data[
                                    'fixed_version'] = fix_available_in
                        else:
                            continue

                    parameter_data['link'] = pkg_vuln.vulnerability.link

                    fix_msg = ''
                    if parameter_data.get('fixed_version', None):
                        fix_msg = "(fixed in: {})".format(
                            parameter_data.get('fixed_version'))

                    score_msg = ''
                    score_tuples = []
                    for s in [
                            'cvss_v3_base_score',
                            'cvss_v3_exploitability_score',
                            'cvss_v3_impact_score'
                    ]:
                        if parameter_data.get(s, None):
                            score_tuples.append("{}={}".format(
                                s, parameter_data.get(s)))
                    if score_tuples:
                        score_msg = "({})".format(' '.join(score_tuples))

                    time_msg = ''
                    time_tuples = []
                    for s in ['max_days_since_creation', 'max_days_since_fix']:
                        if parameter_data.get(s, None):
                            time_tuples.append("{}={}".format(
                                s, parameter_data.get(s)))
                    if time_tuples:
                        time_msg = "({})".format(' '.join(time_tuples))

                    # vendor vulnerability scores not currently available for os packages, this is a pass through
                    vuln_vendor_cvss_base_score = -1.0
                    vuln_vendor_cvss_exploitability_score = -1.0
                    vuln_vendor_cvss_impact_score = -1.0

                    if vendor_cvss_v3_base_score is not None:
                        if not vendor_cvss_v3_base_score_comparison_fn(
                                vuln_vendor_cvss_base_score,
                                vendor_cvss_v3_base_score):
                            logger.debug(
                                "OS vulnerability {} vendor cvss V3 base score {} is not {} than policy vendor cvss V3 base score {}, skipping"
                                .format(
                                    pkg_vuln.vulnerability_id,
                                    vuln_vendor_cvss_base_score,
                                    self.vendor_cvss_v3_base_score_comparison.
                                    value(), vendor_cvss_v3_base_score))
                            continue
                        else:
                            parameter_data[
                                'vendor_cvss_v3_base_score'] = vuln_vendor_cvss_base_score

                    if vendor_cvss_v3_exploitability_score is not None:
                        if not vendor_cvss_v3_exploitability_score_comparison_fn(
                                vuln_vendor_cvss_exploitability_score,
                                vendor_cvss_v3_exploitability_score):
                            logger.debug(
                                "OS vulnerability {} vendor cvss V3 exploitability sub score {} is not {} than policy vendor cvss V3 exploitability sub score {}, skipping"
                                .format(
                                    pkg_vuln.vulnerability_id,
                                    vuln_vendor_cvss_exploitability_score,
                                    self.
                                    vendor_cvss_v3_exploitability_score_comparison
                                    .value(),
                                    vendor_cvss_v3_exploitability_score))
                            continue
                        else:
                            parameter_data[
                                'vendor_cvss_v3_exploitability_score'] = vuln_vendor_cvss_exploitability_score

                    if vendor_cvss_v3_impact_score is not None:
                        if not vendor_cvss_v3_impact_score_comparison_fn(
                                vuln_vendor_cvss_impact_score,
                                vendor_cvss_v3_impact_score):
                            logger.debug(
                                "OS vulnerability {} vendor cvss V3 impact sub score {} is not {} than policy vendor cvss V3 impact score {}, skipping"
                                .format(
                                    pkg_vuln.vulnerability_id,
                                    vuln_vendor_cvss_impact_score,
                                    self.vendor_cvss_v3_impact_score_comparison
                                    .value(), vendor_cvss_v3_impact_score))
                            continue
                        else:
                            parameter_data[
                                'vendor_cvss_v3_impact_score'] = vuln_vendor_cvss_impact_score

                    vendor_score_msg = ''
                    vendor_score_tuples = []
                    for s in [
                            'vendor_cvss_v3_base_score',
                            'vendor_cvss_v3_exploitability_score',
                            'vendor_cvss_v3_impact_score'
                    ]:
                        if parameter_data.get(s, None):
                            vendor_score_tuples.append("{}={}".format(
                                s, parameter_data.get(s)))
                    if vendor_score_tuples:
                        vendor_score_msg = "({})".format(
                            ' '.join(vendor_score_tuples))

                    # new detail message approach
                    #pkgname = pkg_vuln.pkg_name
                    #if pkg_vuln.pkg_version != 'None':
                    #    pkgname += "-{}".format(pkg_vuln.pkg_version)
                    #msg = "Vulnerability found in package {} - matching parameters: ".format(pkgname)
                    #for i in parameter_data:
                    #    msg += "{}={} ".format(i, parameter_data[i])

                    # original detail message approach
                    pkgname = pkg_vuln.pkg_name
                    msg = "{} Vulnerability found in {} package type ({}) - {} {}{}{}{}({} - {})".format(
                        parameter_data['severity'].upper(),
                        parameter_data['pkg_class'],
                        parameter_data['pkg_type'], pkgname, fix_msg,
                        score_msg, time_msg, vendor_score_msg,
                        parameter_data['vulnerability_id'],
                        parameter_data['link'])

                    self._fire(instance_id=pkg_vuln.vulnerability_id + '+' +
                               pkg_vuln.pkg_name,
                               msg=msg)
class VulnerabilityMatchTrigger(BaseTrigger):
    __trigger_name__ = "package"
    __description__ = (
        "Triggers if a found vulnerability in an image meets the comparison criteria."
    )

    SEVERITY_COMPARISONS = {
        "=": lambda x, y: x == y,
        "!=": lambda x, y: x != y,
        "<": lambda x, y: x < y,
        ">": lambda x, y: x > y,
        "<=": lambda x, y: x <= y,
        ">=": lambda x, y: x >= y,
    }

    package_type = EnumStringParameter(
        name="package_type",
        example_str="all",
        enum_values=["all", "os", "non-os"],
        description="Only trigger for specific package type.",
        is_required=True,
        sort_order=1,
    )

    severity_comparison = EnumStringParameter(
        name="severity_comparison",
        example_str=">",
        description=
        "The type of comparison to perform for severity evaluation.",
        enum_values=list(SEVERITY_COMPARISONS.keys()),
        is_required=False,
        sort_order=2,
    )
    severity = EnumStringParameter(
        name="severity",
        example_str="high",
        description="Severity to compare against.",
        enum_values=SEVERITY_ORDERING,
        is_required=False,
        sort_order=3,
    )

    cvss_v3_base_score_comparison = EnumStringParameter(
        name="cvss_v3_base_score_comparison",
        example_str=">",
        description=
        "The type of comparison to perform for CVSS v3 base score evaluation.",
        enum_values=list(SEVERITY_COMPARISONS.keys()),
        is_required=False,
        sort_order=4,
    )
    cvss_v3_base_score = FloatStringParameter(
        name="cvss_v3_base_score",
        example_string="5.0",
        description="CVSS v3 base score to compare against.",
        is_required=False,
        sort_order=5,
    )

    cvss_v3_exploitability_score_comparison = EnumStringParameter(
        name="cvss_v3_exploitability_score_comparison",
        example_str=">",
        description=
        "The type of comparison to perform for CVSS v3 exploitability sub score evaluation.",
        enum_values=list(SEVERITY_COMPARISONS.keys()),
        is_required=False,
        sort_order=6,
    )
    cvss_v3_exploitability_score = FloatStringParameter(
        name="cvss_v3_exploitability_score",
        example_string="5.0",
        description="CVSS v3 exploitability sub score to compare against.",
        is_required=False,
        sort_order=7,
    )

    cvss_v3_impact_score_comparison = EnumStringParameter(
        name="cvss_v3_impact_score_comparison",
        example_str=">",
        description=
        "The type of comparison to perform for CVSS v3 impact sub score evaluation.",
        enum_values=list(SEVERITY_COMPARISONS.keys()),
        is_required=False,
        sort_order=8,
    )
    cvss_v3_impact_score = FloatStringParameter(
        name="cvss_v3_impact_score",
        example_string="5.0",
        description="CVSS v3 impact sub score to compare against.",
        is_required=False,
        sort_order=9,
    )

    fix_available = BooleanStringParameter(
        name="fix_available",
        example_str="true",
        description=
        "If present, the fix availability for the vulnerability record must match the value of this parameter.",
        is_required=False,
        sort_order=10,
    )
    vendor_only = BooleanStringParameter(
        name="vendor_only",
        example_str="true",
        description=
        "If True, an available fix for this CVE must not be explicitly marked as wont be addressed by the vendor",
        is_required=False,
        sort_order=11,
    )
    max_days_since_creation = IntegerStringParameter(
        name="max_days_since_creation",
        example_str="7",
        description=
        "If provided, this CVE must be older than the days provided to trigger.",
        is_required=False,
        sort_order=12,
    )
    max_days_since_fix = IntegerStringParameter(
        name="max_days_since_fix",
        example_str="30",
        description=
        "If provided (only evaluated when fix_available option is also set to true), the fix first observed time must be older than days provided, to trigger.",
        is_required=False,
        sort_order=13,
    )

    vendor_cvss_v3_base_score_comparison = EnumStringParameter(
        name="vendor_cvss_v3_base_score_comparison",
        example_str=">",
        description=
        "The type of comparison to perform for vendor specified CVSS v3 base score evaluation.",
        enum_values=list(SEVERITY_COMPARISONS.keys()),
        is_required=False,
        sort_order=14,
    )
    vendor_cvss_v3_base_score = FloatStringParameter(
        name="vendor_cvss_v3_base_score",
        example_string="5.0",
        description="Vendor CVSS v3 base score to compare against.",
        is_required=False,
        sort_order=15,
    )

    vendor_cvss_v3_exploitability_score_comparison = EnumStringParameter(
        name="vendor_cvss_v3_exploitability_score_comparison",
        example_str=">",
        description=
        "The type of comparison to perform for vendor specified CVSS v3 exploitability sub score evaluation.",
        enum_values=list(SEVERITY_COMPARISONS.keys()),
        is_required=False,
        sort_order=16,
    )
    vendor_cvss_v3_exploitability_score = FloatStringParameter(
        name="vendor_cvss_v3_exploitability_score",
        example_string="5.0",
        description=
        "Vendor CVSS v3 exploitability sub score to compare against.",
        is_required=False,
        sort_order=17,
    )

    vendor_cvss_v3_impact_score_comparison = EnumStringParameter(
        name="vendor_cvss_v3_impact_score_comparison",
        example_str=">",
        description=
        "The type of comparison to perform for vendor specified CVSS v3 impact sub score evaluation.",
        enum_values=list(SEVERITY_COMPARISONS.keys()),
        is_required=False,
        sort_order=18,
    )
    vendor_cvss_v3_impact_score = FloatStringParameter(
        name="vendor_cvss_v3_impact_score",
        example_string="5.0",
        description="Vendor CVSS v3 impact sub score to compare against.",
        is_required=False,
        sort_order=19,
    )

    def evaluate(self, image_obj, context):
        is_fix_available = self.fix_available.value()
        is_vendor_only = self.vendor_only.value(default_if_none=True)
        comparison_idx = SEVERITY_ORDERING.index(
            self.severity.value(default_if_none="unknown").lower())
        comparison_fn = self.SEVERITY_COMPARISONS.get(
            self.severity_comparison.value(default_if_none=">"))

        cvss_v3_base_score = self.cvss_v3_base_score.value()
        cvss_v3_base_score_comparison_fn = self.SEVERITY_COMPARISONS.get(
            self.cvss_v3_base_score_comparison.value(default_if_none=">="))

        cvss_v3_exploitability_score = self.cvss_v3_exploitability_score.value(
        )
        cvss_v3_exploitability_score_comparison_fn = self.SEVERITY_COMPARISONS.get(
            self.cvss_v3_exploitability_score_comparison.value(
                default_if_none=">="))

        cvss_v3_impact_score = self.cvss_v3_impact_score.value()
        cvss_v3_impact_score_comparison_fn = self.SEVERITY_COMPARISONS.get(
            self.cvss_v3_impact_score_comparison.value(default_if_none=">="))

        now = time.time()

        timeallowed = None
        if self.max_days_since_creation.value() is not None:
            timeallowed = now - int(
                int(self.max_days_since_creation.value()) * 86400)

        fix_timeallowed = None
        if self.max_days_since_fix.value() is not None:
            fix_timeallowed = now - int(
                int(self.max_days_since_fix.value()) * 86400)

        if not comparison_fn:
            pass
            # raise KeyError(self.severity_comparison)

        vendor_cvss_v3_base_score = self.vendor_cvss_v3_base_score.value()
        vendor_cvss_v3_base_score_comparison_fn = self.SEVERITY_COMPARISONS.get(
            self.vendor_cvss_v3_base_score_comparison.value(
                default_if_none=">="))

        vendor_cvss_v3_exploitability_score = (
            self.vendor_cvss_v3_exploitability_score.value())
        vendor_cvss_v3_exploitability_score_comparison_fn = (
            self.SEVERITY_COMPARISONS.get(
                self.vendor_cvss_v3_exploitability_score_comparison.value(
                    default_if_none=">=")))

        vendor_cvss_v3_impact_score = self.vendor_cvss_v3_impact_score.value()
        vendor_cvss_v3_impact_score_comparison_fn = self.SEVERITY_COMPARISONS.get(
            self.vendor_cvss_v3_impact_score_comparison.value(
                default_if_none=">="))

        api_endpoint = (
            None  # for populating vulnerability links that point back to engine
        )

        if self.package_type.value() in ["all", "non-os"]:
            cpevulns = context.data.get("loaded_cpe_vulnerabilities")
            if cpevulns:
                try:
                    for sev in list(cpevulns.keys()):
                        found_severity_idx = (SEVERITY_ORDERING.index(
                            sev.lower()) if sev else 0)
                        if comparison_fn(found_severity_idx, comparison_idx):
                            for image_cpe, vulnerability_cpe in cpevulns[sev]:
                                parameter_data = OrderedDict()

                                parameter_data["severity"] = sev.upper()
                                parameter_data[
                                    "vulnerability_id"] = vulnerability_cpe.vulnerability_id
                                parameter_data["pkg_class"] = "non-os"
                                parameter_data["pkg_type"] = image_cpe.pkg_type

                                # setting fixed_version here regardless of gate parameter,
                                fix_available_in = vulnerability_cpe.get_fixed_in(
                                )
                                if fix_available_in:
                                    parameter_data[
                                        "fixed_version"] = ", ".join(
                                            fix_available_in)

                                # Check if the vulnerability is too recent for this policy
                                if timeallowed:
                                    if (calendar.timegm(vulnerability_cpe.
                                                        created_at.timetuple())
                                            > timeallowed):
                                        continue
                                    parameter_data[
                                        "max_days_since_creation"] = vulnerability_cpe.created_at.date(
                                        )

                                if cvss_v3_base_score is not None:
                                    vuln_cvss_base_score = (
                                        vulnerability_cpe.parent.
                                        get_max_base_score_nvd())
                                    if not cvss_v3_base_score_comparison_fn(
                                            vuln_cvss_base_score,
                                            cvss_v3_base_score):
                                        logger.debug(
                                            "Non-OS vulnerability {} cvss V3 base score {} is not {} than policy cvss V3 base score {}, skipping"
                                            .format(
                                                vulnerability_cpe.parent.name,
                                                vuln_cvss_base_score,
                                                self.
                                                cvss_v3_base_score_comparison.
                                                value(),
                                                cvss_v3_base_score,
                                            ))
                                        continue
                                    else:
                                        parameter_data[
                                            "cvss_v3_base_score"] = vuln_cvss_base_score

                                if cvss_v3_exploitability_score is not None:
                                    vuln_cvss_exploitability_score = (
                                        vulnerability_cpe.parent.
                                        get_max_exploitability_score_nvd())
                                    if not cvss_v3_exploitability_score_comparison_fn(
                                            vuln_cvss_exploitability_score,
                                            cvss_v3_exploitability_score,
                                    ):
                                        logger.debug(
                                            "Non-OS vulnerability {} cvss V3 exploitability sub score {} is not {} than policy cvss V3 exploitability sub score {}, skipping"
                                            .format(
                                                vulnerability_cpe.parent.name,
                                                vuln_cvss_exploitability_score,
                                                self.
                                                cvss_v3_exploitability_score_comparison
                                                .value(),
                                                cvss_v3_exploitability_score,
                                            ))
                                        continue
                                    else:
                                        parameter_data[
                                            "cvss_v3_exploitability_score"] = vuln_cvss_exploitability_score

                                if cvss_v3_impact_score is not None:
                                    vuln_cvss_impact_score = (
                                        vulnerability_cpe.parent.
                                        get_max_impact_score_nvd())
                                    if not cvss_v3_impact_score_comparison_fn(
                                            vuln_cvss_impact_score,
                                            cvss_v3_impact_score):
                                        logger.debug(
                                            "Non-OS vulnerability {} cvss V3 impact sub score {} is not {} than policy cvss V3 impact score {}, skipping"
                                            .format(
                                                vulnerability_cpe.parent.name,
                                                vuln_cvss_impact_score,
                                                self.
                                                cvss_v3_impact_score_comparison
                                                .value(),
                                                cvss_v3_impact_score,
                                            ))
                                        continue
                                    else:
                                        parameter_data[
                                            "cvss_v3_impact_score"] = vuln_cvss_impact_score

                                trigger_fname = None
                                if image_cpe.pkg_type in ["java", "gem"]:
                                    try:
                                        trigger_fname = image_cpe.pkg_path.split(
                                            "/")[-1]
                                    except:
                                        trigger_fname = None
                                elif image_cpe.pkg_type in ["npm"]:
                                    try:
                                        trigger_fname = image_cpe.pkg_path.split(
                                            "/")[-2]
                                    except:
                                        trigger_fname = None

                                if not trigger_fname:
                                    trigger_fname = "-".join(
                                        [image_cpe.name, image_cpe.version])

                                # Check fix_available status if specified by user in policy
                                if is_fix_available is not None:
                                    # explicit fix state check matches fix availability
                                    if is_fix_available != (
                                            fix_available_in is not None
                                            and len(fix_available_in) > 0):
                                        # if_fix_available is set but does not match is_fix_available check
                                        continue

                                    if is_fix_available and fix_timeallowed is not None:
                                        if fix_available_in:
                                            if (calendar.timegm(
                                                    vulnerability_cpe.
                                                    created_at.timetuple()) >
                                                    fix_timeallowed):
                                                continue
                                            else:
                                                parameter_data[
                                                    "max_days_since_fix"] = vulnerability_cpe.created_at.date(
                                                    )

                                if not vulnerability_cpe.parent.link:
                                    if not api_endpoint:
                                        api_endpoint = get_service_endpoint(
                                            "apiext").strip("/")
                                    parameter_data[
                                        "link"] = "{}/query/vulnerabilities?id={}".format(
                                            api_endpoint,
                                            vulnerability_cpe.vulnerability_id)
                                else:
                                    parameter_data[
                                        "link"] = vulnerability_cpe.parent.link

                                fix_msg = ""
                                if parameter_data.get("fixed_version", None):
                                    fix_msg = "(fixed in: {})".format(
                                        parameter_data.get("fixed_version"))

                                score_msg = ""
                                score_tuples = []
                                for s in [
                                        "cvss_v3_base_score",
                                        "cvss_v3_exploitability_score",
                                        "cvss_v3_impact_score",
                                ]:
                                    if parameter_data.get(s, None):
                                        score_tuples.append("{}={}".format(
                                            s, parameter_data.get(s)))
                                if score_tuples:
                                    score_msg = "({})".format(
                                        " ".join(score_tuples))

                                time_msg = ""
                                time_tuples = []
                                for s in [
                                        "max_days_since_creation",
                                        "max_days_since_fix",
                                ]:
                                    if parameter_data.get(s, None):
                                        time_tuples.append("{}={}".format(
                                            s, parameter_data.get(s)))
                                if time_tuples:
                                    time_msg = "({})".format(
                                        " ".join(time_tuples))

                                if vendor_cvss_v3_base_score is not None:
                                    vuln_vendor_cvss_base_score = (
                                        vulnerability_cpe.parent.
                                        get_max_base_score_vendor())
                                    if not vendor_cvss_v3_base_score_comparison_fn(
                                            vuln_vendor_cvss_base_score,
                                            vendor_cvss_v3_base_score,
                                    ):
                                        logger.debug(
                                            "Non-OS vulnerability {} vendor cvss V3 base score {} is not {} than policy vendor cvss V3 base score {}, skipping"
                                            .format(
                                                vulnerability_cpe.parent.name,
                                                vuln_vendor_cvss_base_score,
                                                self.
                                                vendor_cvss_v3_base_score_comparison
                                                .value(),
                                                vendor_cvss_v3_base_score,
                                            ))
                                        continue
                                    else:
                                        parameter_data[
                                            "vendor_cvss_v3_base_score"] = vuln_vendor_cvss_base_score

                                if vendor_cvss_v3_exploitability_score is not None:
                                    vuln_vendor_cvss_exploitability_score = (
                                        vulnerability_cpe.parent.
                                        get_max_exploitability_score_vendor())
                                    if not vendor_cvss_v3_exploitability_score_comparison_fn(
                                            vuln_vendor_cvss_exploitability_score,
                                            vendor_cvss_v3_exploitability_score,
                                    ):
                                        logger.debug(
                                            "Non-OS vulnerability {} vendor cvss V3 exploitability sub score {} is not {} than policy vendor cvss V3 exploitability sub score {}, skipping"
                                            .format(
                                                vulnerability_cpe.parent.name,
                                                vuln_vendor_cvss_exploitability_score,
                                                self.
                                                vendor_cvss_v3_exploitability_score_comparison
                                                .value(),
                                                vendor_cvss_v3_exploitability_score,
                                            ))
                                        continue
                                    else:
                                        parameter_data[
                                            "vendor_cvss_v3_exploitability_score"] = vuln_vendor_cvss_exploitability_score

                                if vendor_cvss_v3_impact_score is not None:
                                    vuln_vendor_cvss_impact_score = (
                                        vulnerability_cpe.parent.
                                        get_max_impact_score_vendor())
                                    if not vendor_cvss_v3_impact_score_comparison_fn(
                                            vuln_vendor_cvss_impact_score,
                                            vendor_cvss_v3_impact_score,
                                    ):
                                        logger.debug(
                                            "Non-OS vulnerability {} vendor cvss V3 impact sub score {} is not {} than policy vendor cvss V3 impact score {}, skipping"
                                            .format(
                                                vulnerability_cpe.parent.name,
                                                vuln_vendor_cvss_impact_score,
                                                self.
                                                vendor_cvss_v3_impact_score_comparison
                                                .value(),
                                                vendor_cvss_v3_impact_score,
                                            ))
                                        continue
                                    else:
                                        parameter_data[
                                            "vendor_cvss_v3_impact_score"] = vuln_vendor_cvss_impact_score

                                vendor_score_msg = ""
                                vendor_score_tuples = []
                                for s in [
                                        "vendor_cvss_v3_base_score",
                                        "vendor_cvss_v3_exploitability_score",
                                        "vendor_cvss_v3_impact_score",
                                ]:
                                    if parameter_data.get(s, None):
                                        vendor_score_tuples.append(
                                            "{}={}".format(
                                                s, parameter_data.get(s)))
                                if vendor_score_tuples:
                                    vendor_score_msg = "({})".format(
                                        " ".join(vendor_score_tuples))

                                # new detail message approach
                                # pkgname = image_cpe.pkg_path
                                # msg = "Vulnerability found in package {} - matching parameters: ".format(pkgname)
                                # for i in parameter_data:
                                #    msg += "{}={} ".format(i, parameter_data[i])

                                pkgname = image_cpe.pkg_path
                                msg = "{} Vulnerability found in {} package type ({}) - {} {}{}{}{}({} - {})".format(
                                    parameter_data["severity"].upper(),
                                    parameter_data["pkg_class"],
                                    parameter_data["pkg_type"],
                                    pkgname,
                                    fix_msg,
                                    score_msg,
                                    time_msg,
                                    vendor_score_msg,
                                    parameter_data["vulnerability_id"],
                                    parameter_data["link"],
                                )
                                self._fire(
                                    instance_id=vulnerability_cpe.
                                    vulnerability_id + "+" + trigger_fname,
                                    msg=msg,
                                )

                except Exception as err:
                    logger.warn(
                        "problem during non-os vulnerability evaluation - exception: {}"
                        .format(err))

        # Process vulnerabilities that match using ImagePackageVulnerability objects, which can include all types as well
        vulns = context.data.get("loaded_vulnerabilities")

        if vulns:
            # get nvd classes from context or select them once and be done
            _nvd_cls, _cpe_cls = context.data.get("nvd_cpe_cls", (None, None))
            if not _nvd_cls or not _cpe_cls:
                _nvd_cls, _cpe_cls = select_nvd_classes()

            pkg_type_value = self.package_type.value()
            for pkg_vuln in vulns:
                parameter_data = OrderedDict()

                parameter_data[
                    "severity"] = pkg_vuln.vulnerability.severity.upper()
                parameter_data["vulnerability_id"] = pkg_vuln.vulnerability_id
                parameter_data["pkg_type"] = pkg_vuln.pkg_type
                parameter_data["pkg_class"] = ("non-os" if pkg_vuln.pkg_type
                                               in nonos_package_types else
                                               "os")

                # setting fixed_version here regardless of gate parameter,
                fix_available_in = pkg_vuln.fixed_in()
                if fix_available_in:
                    parameter_data["fixed_version"] = fix_available_in

                # Filter first by package class, if rule has a filter
                if (pkg_type_value != "all"
                        and pkg_type_value != parameter_data["pkg_class"]):
                    continue

                # Filter by level first
                found_severity_idx = (SEVERITY_ORDERING.index(
                    pkg_vuln.vulnerability.severity.lower()) if
                                      pkg_vuln.vulnerability.severity else 0)
                if comparison_fn(found_severity_idx, comparison_idx):
                    # Check vendor_only flag specified by the user in policy
                    if is_vendor_only:
                        if pkg_vuln.fix_has_no_advisory():
                            # skip this vulnerability
                            continue

                    # Check if the vulnerability is to recent for this policy
                    if timeallowed:
                        if (calendar.timegm(
                                pkg_vuln.vulnerability.created_at.timetuple())
                                > timeallowed):
                            continue
                        parameter_data[
                            "max_days_since_creation"] = pkg_vuln.vulnerability.created_at.date(
                            )

                    if is_fix_available and fix_timeallowed is not None:
                        fix = pkg_vuln.fixed_artifact()
                        if fix.version and fix.version != "None":
                            if (fix.fix_observed_at and calendar.timegm(
                                    fix.fix_observed_at.timetuple()) >
                                    fix_timeallowed):
                                continue
                            else:
                                parameter_data[
                                    "max_days_since_fix"] = fix.fix_observed_at.date(
                                    )

                    vuln_cvss_base_score = -1.0
                    vuln_cvss_exploitability_score = -1.0
                    vuln_cvss_impact_score = -1.0

                    for nvd_record in pkg_vuln.vulnerability.get_nvd_vulnerabilities(
                            _nvd_cls=_nvd_cls, _cpe_cls=_cpe_cls):
                        cvss_score = nvd_record.get_max_cvss_score_nvd()
                        if cvss_score.get("base_score",
                                          -1.0) > vuln_cvss_base_score:
                            vuln_cvss_base_score = cvss_score.get(
                                "base_score", -1.0)
                        if (cvss_score.get("exploitability_score", -1.0) >
                                vuln_cvss_exploitability_score):
                            vuln_cvss_exploitability_score = cvss_score.get(
                                "exploitability_score", -1.0)
                        if (cvss_score.get("impact_score", -1.0) >
                                vuln_cvss_impact_score):
                            vuln_cvss_impact_score = cvss_score.get(
                                "impact_score", -1.0)

                    if cvss_v3_base_score is not None:
                        if not cvss_v3_base_score_comparison_fn(
                                vuln_cvss_base_score, cvss_v3_base_score):
                            logger.debug(
                                "OS vulnerability {} cvss V3 base_score {} is not {} than policy cvss V3 base_score {}, skipping"
                                .format(
                                    pkg_vuln.vulnerability_id,
                                    vuln_cvss_base_score,
                                    self.cvss_v3_base_score_comparison.value(),
                                    cvss_v3_base_score,
                                ))
                            continue
                        else:
                            parameter_data[
                                "cvss_v3_base_score"] = vuln_cvss_base_score

                    if cvss_v3_exploitability_score is not None:
                        if not cvss_v3_exploitability_score_comparison_fn(
                                vuln_cvss_exploitability_score,
                                cvss_v3_exploitability_score):
                            logger.debug(
                                "OS vulnerability {} cvss V3 exploitability_score {} is not {} than policy cvss V3 exploitability_score {}, skipping"
                                .format(
                                    pkg_vuln.vulnerability_id,
                                    vuln_cvss_exploitability_score,
                                    self.
                                    cvss_v3_exploitability_score_comparison.
                                    value(),
                                    cvss_v3_exploitability_score,
                                ))
                            continue
                        else:
                            parameter_data[
                                "cvss_v3_exploitability_score"] = vuln_cvss_exploitability_score

                    if cvss_v3_impact_score is not None:
                        if not cvss_v3_impact_score_comparison_fn(
                                vuln_cvss_impact_score, cvss_v3_impact_score):
                            logger.debug(
                                "OS vulnerability {} cvss V3 impact_score {} is not {} than policy cvss V3 impact_score {}, skipping"
                                .format(
                                    pkg_vuln.vulnerability_id,
                                    vuln_cvss_impact_score,
                                    self.cvss_v3_impact_score_comparison.value(
                                    ),
                                    cvss_v3_impact_score,
                                ))
                            continue
                        else:
                            parameter_data[
                                "cvss_v3_impact_score"] = vuln_cvss_impact_score

                    # Check fix_available status if specified by user in policy
                    if is_fix_available is not None:
                        # explicit fix state check matches fix availability
                        if is_fix_available != (fix_available_in is not None):
                            # if_fix_available is set but does not match is_fix_available check
                            continue

                    parameter_data["link"] = pkg_vuln.vulnerability.link

                    fix_msg = ""
                    if parameter_data.get("fixed_version", None):
                        fix_msg = "(fixed in: {})".format(
                            parameter_data.get("fixed_version"))

                    score_msg = ""
                    score_tuples = []
                    for s in [
                            "cvss_v3_base_score",
                            "cvss_v3_exploitability_score",
                            "cvss_v3_impact_score",
                    ]:
                        if parameter_data.get(s, None):
                            score_tuples.append("{}={}".format(
                                s, parameter_data.get(s)))
                    if score_tuples:
                        score_msg = "({})".format(" ".join(score_tuples))

                    time_msg = ""
                    time_tuples = []
                    for s in ["max_days_since_creation", "max_days_since_fix"]:
                        if parameter_data.get(s, None):
                            time_tuples.append("{}={}".format(
                                s, parameter_data.get(s)))
                    if time_tuples:
                        time_msg = "({})".format(" ".join(time_tuples))

                    # vendor vulnerability scores not currently available for os packages, this is a pass through
                    vuln_vendor_cvss_base_score = -1.0
                    vuln_vendor_cvss_exploitability_score = -1.0
                    vuln_vendor_cvss_impact_score = -1.0

                    if vendor_cvss_v3_base_score is not None:
                        if not vendor_cvss_v3_base_score_comparison_fn(
                                vuln_vendor_cvss_base_score,
                                vendor_cvss_v3_base_score):
                            logger.debug(
                                "OS vulnerability {} vendor cvss V3 base score {} is not {} than policy vendor cvss V3 base score {}, skipping"
                                .format(
                                    pkg_vuln.vulnerability_id,
                                    vuln_vendor_cvss_base_score,
                                    self.vendor_cvss_v3_base_score_comparison.
                                    value(),
                                    vendor_cvss_v3_base_score,
                                ))
                            continue
                        else:
                            parameter_data[
                                "vendor_cvss_v3_base_score"] = vuln_vendor_cvss_base_score

                    if vendor_cvss_v3_exploitability_score is not None:
                        if not vendor_cvss_v3_exploitability_score_comparison_fn(
                                vuln_vendor_cvss_exploitability_score,
                                vendor_cvss_v3_exploitability_score,
                        ):
                            logger.debug(
                                "OS vulnerability {} vendor cvss V3 exploitability sub score {} is not {} than policy vendor cvss V3 exploitability sub score {}, skipping"
                                .format(
                                    pkg_vuln.vulnerability_id,
                                    vuln_vendor_cvss_exploitability_score,
                                    self.
                                    vendor_cvss_v3_exploitability_score_comparison
                                    .value(),
                                    vendor_cvss_v3_exploitability_score,
                                ))
                            continue
                        else:
                            parameter_data[
                                "vendor_cvss_v3_exploitability_score"] = vuln_vendor_cvss_exploitability_score

                    if vendor_cvss_v3_impact_score is not None:
                        if not vendor_cvss_v3_impact_score_comparison_fn(
                                vuln_vendor_cvss_impact_score,
                                vendor_cvss_v3_impact_score):
                            logger.debug(
                                "OS vulnerability {} vendor cvss V3 impact sub score {} is not {} than policy vendor cvss V3 impact score {}, skipping"
                                .format(
                                    pkg_vuln.vulnerability_id,
                                    vuln_vendor_cvss_impact_score,
                                    self.vendor_cvss_v3_impact_score_comparison
                                    .value(),
                                    vendor_cvss_v3_impact_score,
                                ))
                            continue
                        else:
                            parameter_data[
                                "vendor_cvss_v3_impact_score"] = vuln_vendor_cvss_impact_score

                    vendor_score_msg = ""
                    vendor_score_tuples = []
                    for s in [
                            "vendor_cvss_v3_base_score",
                            "vendor_cvss_v3_exploitability_score",
                            "vendor_cvss_v3_impact_score",
                    ]:
                        if parameter_data.get(s, None):
                            vendor_score_tuples.append("{}={}".format(
                                s, parameter_data.get(s)))
                    if vendor_score_tuples:
                        vendor_score_msg = "({})".format(
                            " ".join(vendor_score_tuples))

                    # new detail message approach
                    # pkgname = pkg_vuln.pkg_name
                    # if pkg_vuln.pkg_version != 'None':
                    #    pkgname += "-{}".format(pkg_vuln.pkg_version)
                    # msg = "Vulnerability found in package {} - matching parameters: ".format(pkgname)
                    # for i in parameter_data:
                    #    msg += "{}={} ".format(i, parameter_data[i])

                    # original detail message approach
                    pkgname = pkg_vuln.pkg_name
                    msg = "{} Vulnerability found in {} package type ({}) - {} {}{}{}{}({} - {})".format(
                        parameter_data["severity"].upper(),
                        parameter_data["pkg_class"],
                        parameter_data["pkg_type"],
                        pkgname,
                        fix_msg,
                        score_msg,
                        time_msg,
                        vendor_score_msg,
                        parameter_data["vulnerability_id"],
                        parameter_data["link"],
                    )

                    self._fire(
                        instance_id=pkg_vuln.vulnerability_id + "+" +
                        pkg_vuln.pkg_name,
                        msg=msg,
                    )