示例#1
0
class CveSeverityTrigger(BaseTrigger):
    __vuln_levels__ = None
    fix_available = BooleanStringParameter(
        name='fix_available',
        description=
        'If present, the fix availability for the CVE record must match the value of this parameter.',
        is_required=False)
    vendor_only = BooleanStringParameter(
        name='vendor_only',
        description=
        'If True, an available fix for this CVE must not be explicitly marked as wont be addressed by the vendor',
        is_required=False)

    def evaluate(self, image_obj, context):
        vulns = context.data.get('loaded_vulnerabilities')
        if not vulns:
            return

        is_fix_available = self.fix_available.value()
        is_vendor_only = self.vendor_only.value(default_if_none=True)

        for pkg_vuln in vulns:
            # Filter by level first
            if pkg_vuln.vulnerability.severity in self.__vuln_levels__:

                # 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 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
                        message = pkg_vuln.vulnerability.severity.upper() + " Vulnerability found in package - " + \
                                  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 package - " + \
                              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)
示例#2
0
class ParameterizedDockerfileModeBaseTrigger(BaseTrigger):
    """
    Base class for any trigger that lets the user decide if it applies to only actual dockerfiles or not
    """

    actual_dockerfile_only = BooleanStringParameter(
        "actual_dockerfile_only",
        example_str="true",
        description=
        "Only evaluate against a user-provided dockerfile, skip evaluation on inferred/guessed dockerfiles. Default is False.",
        is_required=False,
    )

    def evaluate(self, image_obj, context):
        if not hasattr(context,
                       "data") or not context.data.get("prepared_dockerfile"):
            return
        elif self.actual_dockerfile_only.value() and (
                image_obj.dockerfile_mode is None
                or image_obj.dockerfile_mode.lower() != "actual"):
            return
        else:
            return self._evaluate(image_obj, context)

    def _evaluate(self, image_obj, context):
        raise NotImplementedError()
class VulnerabilityBlacklistTrigger(BaseTrigger):
    __trigger_name__ = "blacklist"
    __description__ = "Triggers if any of a list of specified vulnerabilities has been detected in the image."

    vulnerability_ids = CommaDelimitedStringListParameter(
        name="vulnerability_ids",
        example_str="CVE-2019-1234",
        description=
        "List of vulnerability IDs, will cause the trigger to fire if any are detected.",
        is_required=True,
        sort_order=1,
    )
    vendor_only = BooleanStringParameter(
        name="vendor_only",
        example_str="true",
        description=
        "If set to True, discard matches against this vulnerability if vendor has marked as will not fix in the vulnerability record.",
        is_required=False,
        sort_order=2,
    )

    def evaluate(self, image_obj, context):
        vids = self.vulnerability_ids.value()
        is_vendor_only = self.vendor_only.value(default_if_none=True)

        found_vids = []

        for vid in vids:
            found = False

            # search for vid in OS vulns
            vulns = context.data.get("loaded_vulnerabilities")
            for vuln in vulns:
                if is_vendor_only:
                    if vuln.fix_has_no_advisory():
                        continue
                if vid == vuln.vulnerability_id:
                    found = True
                    break

            if not found:
                # search for vid in non-os vulns
                cpevulns = context.data.get("loaded_cpe_vulnerabilities")
                for sev in list(cpevulns.keys()):
                    for image_cpe, vulnerability_cpe in cpevulns[sev]:
                        if vid == vulnerability_cpe.vulnerability_id:
                            found = True
                            break
                    if found:
                        break
            if found:
                found_vids.append(vid)

        if found_vids:
            self._fire(msg="Blacklisted vulnerabilities detected: {}".format(
                found_vids))
class VulnerabilityBlacklistTrigger(BaseTrigger):
    __trigger_name__ = "blacklist"
    __description__ = "Triggers if any of a list of specified vulnerabilities has been detected in the image."

    vulnerability_ids = CommaDelimitedStringListParameter(
        name="vulnerability_ids",
        example_str="CVE-2019-1234",
        description=
        "List of vulnerability IDs, will cause the trigger to fire if any are detected.",
        is_required=True,
        sort_order=1,
    )
    vendor_only = BooleanStringParameter(
        name="vendor_only",
        example_str="true",
        description=
        "If set to True, discard matches against this vulnerability if vendor has marked as will not fix in the vulnerability record.",
        is_required=False,
        sort_order=2,
    )

    def evaluate(self, image_obj, context):
        vids = self.vulnerability_ids.value()
        is_vendor_only = self.vendor_only.value(default_if_none=True)

        found_vids = []

        for vid in vids:
            found = False

            matches = context.data.get("loaded_vulnerabilities_new")
            for match in matches:
                if is_vendor_only:
                    if match.fixes and any(item.wont_fix
                                           for item in match.fixes):
                        continue
                # search for vid in all vulns
                if vid == match.vulnerability.vulnerability_id:
                    found = True
                    break

            if found:
                found_vids.append(vid)

        if found_vids:
            self._fire(msg="Blacklisted vulnerabilities detected: {}".format(
                found_vids))
示例#5
0
class CveSeverityTrigger(BaseTrigger):
    __vuln_levels__ = None
    fix_available = BooleanStringParameter(
        name='fix_available',
        description=
        'If present, the fix availability for the CVE record must match the value of this parameter.',
        is_required=False)

    def evaluate(self, image_obj, context):
        vulns = context.data.get('loaded_vulnerabilities')
        if not vulns:
            return

        is_fix_available = self.fix_available.value()

        for pkg_vuln in vulns:
            # Filter by level first
            if pkg_vuln.vulnerability.severity in self.__vuln_levels__:

                # 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
                        message = pkg_vuln.vulnerability.severity.upper() + " Vulnerability found in package - " + \
                                  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 package - " + \
                              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)
示例#6
0
class FileAttributeMatchTrigger(BaseTrigger):
    __trigger_name__ = 'attribute_match'
    __description__ = 'Triggers if a filename exists in the container that has attributes that match those which are provided . This check has a performance impact on policy evaluation.'

    filename = TriggerParameter(
        validator=TypeValidator('string'),
        name='filename',
        example_str='/etc/passwd',
        description='Filename to check against provided checksum.',
        is_required=True,
        sort_order=1)

    checksum_algo = EnumStringParameter(name='checksum_algorithm',
                                        enum_values=['sha256'],
                                        example_str='sha256',
                                        description='Checksum algorithm',
                                        is_required=False,
                                        sort_order=2)
    checksum = TriggerParameter(
        validator=TypeValidator('string'),
        name='checksum',
        example_str=
        '832cd0f75b227d13aac82b1f70b7f90191a4186c151f9db50851d209c45ede11',
        description='Checksum of file.',
        is_required=False,
        sort_order=3)

    checksum_op = EnumStringParameter(
        name='checksum_match',
        enum_values=['equals', 'not_equals'],
        example_str='equals',
        description='Checksum operation to perform.',
        is_required=False,
        sort_order=4)

    mode = TriggerParameter(validator=TypeValidator('string'),
                            name='mode',
                            example_str='00644',
                            description='File mode of file.',
                            is_required=False,
                            sort_order=5)
    mode_op = EnumStringParameter(
        name='mode_op',
        enum_values=['equals', 'not_equals'],
        example_str='equals',
        description='File mode operation to perform.',
        is_required=False,
        sort_order=6)

    skip_if_file_missing = BooleanStringParameter(
        name='skip_missing',
        example_str='true',
        description=
        'If set to true, do not fire this trigger if the file is not present.  If set to false, fire this trigger ignoring the other parameter settings.',
        is_required=False,
        sort_order=7)

    def evaluate(self, image_obj, context):
        filename = self.filename.value()

        checksum_algo = self.checksum_algo.value(default_if_none='sha256')
        checksum = self.checksum.value()
        checksum_op = self.checksum_op.value(default_if_none='equals')

        mode = self.mode.value()
        mode_op = self.mode_op.value(default_if_none='equals')

        skip_if_file_missing = self.skip_if_file_missing.value(
            default_if_none=True)

        filedetails = {}
        if hasattr(context, 'data'):
            filedetails = context.data.get('filedetail')

        fire_params = {}
        filedetail = filedetails.get(filename, None)

        if filedetail:

            # checksum checks

            if checksum and checksum_op and checksum_algo:
                file_checksum = None
                if checksum_algo == 'sha256':
                    file_checksum = filedetail.get('sha256_checksum', "")

                if checksum_op == 'equals' and file_checksum == checksum:
                    fire_params[
                        'checksum'] = "checksum={} op={} specified_checksum={}".format(
                            file_checksum, checksum_op, checksum)
                elif checksum_op == 'not_equals' and file_checksum != checksum:
                    fire_params[
                        'checksum'] = "checksum={} op={} specified_checksum={}".format(
                            file_checksum, checksum_op, checksum)
                else:
                    return

            # mode checks

            if mode and mode_op:
                file_mode = filedetail.get('mode', 0)

                file_mode_cmp = oct(stat.S_IMODE(file_mode))
                input_mode_cmp = oct(int(mode, 8))

                if mode_op == 'equals' and file_mode_cmp == input_mode_cmp:
                    fire_params[
                        'mode'] = "mode={} op={} specified_mode={}".format(
                            file_mode_cmp, mode_op, input_mode_cmp)
                elif mode_op == 'not_equals' and file_mode_cmp != input_mode_cmp:
                    fire_params[
                        'mode'] = "mode={} op={} specified_mode={}".format(
                            file_mode_cmp, mode_op, input_mode_cmp)
                else:
                    return
        else:
            # case where file doesn't exist
            if skip_if_file_missing:
                return
            fire_params['skip'] = "skip_missing=False"

        if fire_params:
            msg = "filename={}".format(filename)
            for k in fire_params.keys():
                msg += " and {}".format(fire_params[k])

            self._fire(msg=msg)
示例#7
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)

    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())
        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]:
                                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 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_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)
示例#9
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,
                    )
示例#10
0
class FileAttributeMatchTrigger(BaseTrigger):
    __trigger_name__ = "attribute_match"
    __description__ = "Triggers if a filename exists in the container that has attributes that match those which are provided . This check has a performance impact on policy evaluation."

    filename = TriggerParameter(
        validator=TypeValidator("string"),
        name="filename",
        example_str="/etc/passwd",
        description="Filename to check against provided checksum.",
        is_required=True,
        sort_order=1,
    )

    checksum_algo = EnumStringParameter(
        name="checksum_algorithm",
        enum_values=["sha256"],
        example_str="sha256",
        description="Checksum algorithm",
        is_required=False,
        sort_order=2,
    )
    checksum = TriggerParameter(
        validator=TypeValidator("string"),
        name="checksum",
        example_str="832cd0f75b227d13aac82b1f70b7f90191a4186c151f9db50851d209c45ede11",
        description="Checksum of file.",
        is_required=False,
        sort_order=3,
    )

    checksum_op = EnumStringParameter(
        name="checksum_match",
        enum_values=["equals", "not_equals"],
        example_str="equals",
        description="Checksum operation to perform.",
        is_required=False,
        sort_order=4,
    )

    mode = TriggerParameter(
        validator=TypeValidator("string"),
        name="mode",
        example_str="00644",
        description="File mode of file.",
        is_required=False,
        sort_order=5,
    )
    mode_op = EnumStringParameter(
        name="mode_op",
        enum_values=["equals", "not_equals"],
        example_str="equals",
        description="File mode operation to perform.",
        is_required=False,
        sort_order=6,
    )

    skip_if_file_missing = BooleanStringParameter(
        name="skip_missing",
        example_str="true",
        description="If set to true, do not fire this trigger if the file is not present.  If set to false, fire this trigger ignoring the other parameter settings.",
        is_required=False,
        sort_order=7,
    )

    def evaluate(self, image_obj, context):
        filename = self.filename.value()

        checksum_algo = self.checksum_algo.value(default_if_none="sha256")
        checksum = self.checksum.value()
        checksum_op = self.checksum_op.value(default_if_none="equals")

        mode = self.mode.value()
        mode_op = self.mode_op.value(default_if_none="equals")

        skip_if_file_missing = self.skip_if_file_missing.value(default_if_none=True)

        filedetails = {}
        if hasattr(context, "data"):
            filedetails = context.data.get("filedetail")

        fire_params = {}
        filedetail = filedetails.get(filename, None)

        if filedetail:

            # checksum checks

            if checksum and checksum_op and checksum_algo:
                file_checksum = None
                if checksum_algo == "sha256":
                    file_checksum = filedetail.get("sha256_checksum", "")

                if checksum_op == "equals" and file_checksum == checksum:
                    fire_params[
                        "checksum"
                    ] = "checksum={} op={} specified_checksum={}".format(
                        file_checksum, checksum_op, checksum
                    )
                elif checksum_op == "not_equals" and file_checksum != checksum:
                    fire_params[
                        "checksum"
                    ] = "checksum={} op={} specified_checksum={}".format(
                        file_checksum, checksum_op, checksum
                    )
                else:
                    return

            # mode checks

            if mode and mode_op:
                file_mode = filedetail.get("mode", 0)

                file_mode_cmp = oct(stat.S_IMODE(file_mode))
                input_mode_cmp = oct(int(mode, 8))

                if mode_op == "equals" and file_mode_cmp == input_mode_cmp:
                    fire_params["mode"] = "mode={} op={} specified_mode={}".format(
                        file_mode_cmp, mode_op, input_mode_cmp
                    )
                elif mode_op == "not_equals" and file_mode_cmp != input_mode_cmp:
                    fire_params["mode"] = "mode={} op={} specified_mode={}".format(
                        file_mode_cmp, mode_op, input_mode_cmp
                    )
                else:
                    return
        else:
            # case where file doesn't exist
            if skip_if_file_missing:
                return
            fire_params["skip"] = "skip_missing=False"

        if fire_params:
            msg = "filename={}".format(filename)
            for k in fire_params.keys():
                msg += " and {}".format(fire_params[k])

            self._fire(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

                                # 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,
                    )