Esempio n. 1
0
    def prepare_context(self, image_obj, context):
        """

        :rtype:
        """
        # Load the package vulnerability info up front
        pkg_vulns = image_obj.vulnerabilities()
        context.data['loaded_vulnerabilities'] = pkg_vulns

        # select the nvd classes once and be done, load them into context for eval
        _nvd_cls, _cpe_cls = select_nvd_classes()
        context.data['nvd_cpe_cls'] = (_nvd_cls, _cpe_cls)

        # Load the non-package (CPE) vulnerability info up front
        all_cpe_matches = image_obj.cpe_vulnerabilities(_nvd_cls=_nvd_cls,
                                                        _cpe_cls=_cpe_cls)
        if not all_cpe_matches:
            all_cpe_matches = []

        dedup_hash = {}
        severity_matches = {}
        for image_cpe, vulnerability_cpe in all_cpe_matches:
            sev = vulnerability_cpe.parent.severity
            if sev not in severity_matches:
                severity_matches[sev] = []

            if image_cpe.pkg_path:
                if image_cpe.pkg_path not in dedup_hash:
                    dedup_hash[image_cpe.pkg_path] = []

                if vulnerability_cpe.vulnerability_id not in dedup_hash[
                        image_cpe.pkg_path]:
                    dedup_hash[image_cpe.pkg_path].append(
                        vulnerability_cpe.vulnerability_id)
                    severity_matches[sev].append(
                        (image_cpe, vulnerability_cpe))
            else:
                severity_matches[sev].append((image_cpe, vulnerability_cpe))

        context.data['loaded_cpe_vulnerabilities'] = severity_matches

        return context
Esempio n. 2
0
    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):
                                        log.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):
                                        log.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):
                                        log.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):
                                        log.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
                                    ):
                                        log.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):
                                        log.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:
                    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:
                # 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()

                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_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):
                                log.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):
                                log.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):
                                log.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

                        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):
                                log.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):
                                log.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):
                                log.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)
    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,
                    )
Esempio n. 4
0
    def get_images_by_vulnerability(
        self,
        user_id,
        id,
        severity_filter,
        namespace_filter,
        affected_package_filter,
        vendor_only,
        db_session,
    ):
        """
        Return image with nested package records that match the filter criteria

        Copy pasted query_images_by_vulnerability() from synchronous_operations.py

        TODO use "with timer" for timing blocks
        TODO define message models use concretely objects instead of dictionaries
        """

        ret_hash = {}
        pkg_hash = {}
        advisory_cache = {}

        start = time.time()
        image_package_matches = []
        image_cpe_matches = []
        image_cpe_vlndb_matches = []

        (_nvd_cls, _cpe_cls) = select_nvd_classes(db_session)

        ipm_query = (db_session.query(ImagePackageVulnerability).filter(
            ImagePackageVulnerability.vulnerability_id == id).filter(
                ImagePackageVulnerability.pkg_user_id == user_id))
        icm_query = (db_session.query(
            ImageCpe, _cpe_cls).filter(_cpe_cls.vulnerability_id == id).filter(
                func.lower(ImageCpe.name) == _cpe_cls.name).filter(
                    ImageCpe.image_user_id == user_id).filter(
                        ImageCpe.version == _cpe_cls.version))
        icm_vulndb_query = db_session.query(ImageCpe, VulnDBCpe).filter(
            VulnDBCpe.vulnerability_id == id,
            func.lower(ImageCpe.name) == VulnDBCpe.name,
            ImageCpe.image_user_id == user_id,
            ImageCpe.version == VulnDBCpe.version,
            VulnDBCpe.is_affected.is_(True),
        )

        if severity_filter:
            ipm_query = ipm_query.filter(
                ImagePackageVulnerability.vulnerability.has(
                    severity=severity_filter))
            icm_query = icm_query.filter(
                _cpe_cls.parent.has(
                    severity=severity_filter))  # might be slower than join
            icm_vulndb_query = icm_vulndb_query.filter(
                _cpe_cls.parent.has(
                    severity=severity_filter))  # might be slower than join
        if namespace_filter:
            ipm_query = ipm_query.filter(
                ImagePackageVulnerability.vulnerability_namespace_name ==
                namespace_filter)
            icm_query = icm_query.filter(
                _cpe_cls.namespace_name == namespace_filter)
            icm_vulndb_query = icm_vulndb_query.filter(
                VulnDBCpe.namespace_name == namespace_filter)
        if affected_package_filter:
            ipm_query = ipm_query.filter(
                ImagePackageVulnerability.pkg_name == affected_package_filter)
            icm_query = icm_query.filter(
                func.lower(ImageCpe.name) == func.lower(
                    affected_package_filter))
            icm_vulndb_query = icm_vulndb_query.filter(
                func.lower(ImageCpe.name) == func.lower(
                    affected_package_filter))

        image_package_matches = ipm_query  # .all()
        image_cpe_matches = icm_query  # .all()
        image_cpe_vlndb_matches = icm_vulndb_query

        log.debug("QUERY TIME: {}".format(time.time() - start))

        start = time.time()
        if image_package_matches or image_cpe_matches or image_cpe_vlndb_matches:
            imageId_to_record = get_imageId_to_record(user_id,
                                                      dbsession=db_session)

            start = time.time()
            for image in image_package_matches:
                if vendor_only and self._check_no_advisory(
                        image, advisory_cache):
                    continue

                imageId = image.pkg_image_id
                if imageId not in ret_hash:
                    ret_hash[imageId] = {
                        "image": imageId_to_record.get(imageId, {}),
                        "vulnerable_packages": [],
                    }
                    pkg_hash[imageId] = {}

                pkg_el = {
                    "name": image.pkg_name,
                    "version": image.pkg_version,
                    "type": image.pkg_type,
                    "namespace": image.vulnerability_namespace_name,
                    "severity": image.vulnerability.severity,
                }

                ret_hash[imageId]["vulnerable_packages"].append(pkg_el)
            log.debug("IMAGEOSPKG TIME: {}".format(time.time() - start))

            for cpe_matches in [image_cpe_matches, image_cpe_vlndb_matches]:
                start = time.time()
                for image_cpe, vulnerability_cpe in cpe_matches:
                    imageId = image_cpe.image_id
                    if imageId not in ret_hash:
                        ret_hash[imageId] = {
                            "image": imageId_to_record.get(imageId, {}),
                            "vulnerable_packages": [],
                        }
                        pkg_hash[imageId] = {}
                    pkg_el = {
                        "name": image_cpe.name,
                        "version": image_cpe.version,
                        "type": image_cpe.pkg_type,
                        "namespace":
                        "{}".format(vulnerability_cpe.namespace_name),
                        "severity":
                        "{}".format(vulnerability_cpe.parent.severity),
                    }
                    phash = hashlib.sha256(
                        json.dumps(pkg_el).encode("utf-8")).hexdigest()
                    if not pkg_hash[imageId].get(phash, False):
                        ret_hash[imageId]["vulnerable_packages"].append(pkg_el)
                    pkg_hash[imageId][phash] = True

                log.debug("IMAGECPEPKG TIME: {}".format(time.time() - start))

        start = time.time()
        vulnerable_images = list(ret_hash.values())
        return_object = {"vulnerable_images": vulnerable_images}
        log.debug("RESP TIME: {}".format(time.time() - start))

        return return_object
Esempio n. 5
0
    def get_vulnerabilities(self, ids, package_name_filter,
                            package_version_filter, namespace, db_session):
        """
        Return vulnerability records with the matched criteria from the feed data.
        Copy pasted query_vulnerabilities() from synchronous_operations.py

        TODO use "with timer" for timing blocks
        TODO define message models use concretely objects instead of dictionaries

        :param ids: single string or list of string ids
        :param package_name_filter: string name to filter vulns by in the affected package list
        :param package_version_filter: version for corresponding package to filter by
        :param namespace: string or list of strings to filter namespaces by
        :param db_session: active db session to use
        :return: list of dicts
        """
        return_object = []

        return_el_template = {
            "id": None,
            "namespace": None,
            "severity": None,
            "link": None,
            "affected_packages": None,
            "description": None,
            "references": None,
            "nvd_data": None,
            "vendor_data": None,
        }

        # order_by ascending timestamp will result in dedup hash having only the latest information stored for return, if there are duplicate records for NVD
        (_nvd_cls, _cpe_cls) = select_nvd_classes(db_session)

        # Set the relationship loader for use with the queries
        loader = orm.selectinload

        # Always fetch any matching nvd records, even if namespace doesn't match, since they are used for the cvss data
        qry = (db_session.query(_nvd_cls).options(
            loader(_nvd_cls.vulnerable_cpes)).filter(
                _nvd_cls.name.in_(ids)).order_by(asc(_nvd_cls.created_at)))

        t1 = time.time()
        nvd_vulnerabilities = qry.all()
        nvd_vulnerabilities.extend(
            db_session.query(VulnDBMetadata).options(
                loader(VulnDBMetadata.cpes)).filter(
                    VulnDBMetadata.name.in_(ids)).order_by(
                        asc(VulnDBMetadata.created_at)).all())

        log.spew("Vuln query 1 timing: {}".format(time.time() - t1))

        api_endpoint = self._get_api_endpoint()

        if not namespace or ("nvdv2:cves" in namespace):
            dedupped_return_hash = {}
            t1 = time.time()

            for vulnerability in nvd_vulnerabilities:
                link = vulnerability.link
                if not link:
                    link = "{}/query/vulnerabilities?id={}".format(
                        api_endpoint, vulnerability.name)

                namespace_el = {}
                namespace_el.update(return_el_template)
                namespace_el["id"] = vulnerability.name
                namespace_el["namespace"] = vulnerability.namespace_name
                namespace_el["severity"] = vulnerability.severity
                namespace_el["link"] = link
                namespace_el["affected_packages"] = []
                namespace_el["description"] = vulnerability.description
                namespace_el["references"] = vulnerability.references
                namespace_el["nvd_data"] = vulnerability.get_cvss_data_nvd()
                namespace_el[
                    "vendor_data"] = vulnerability.get_cvss_data_vendor()

                for v_pkg in vulnerability.vulnerable_cpes:
                    if (not package_name_filter
                            or package_name_filter == v_pkg.name) and (
                                not package_version_filter
                                or package_version_filter == v_pkg.version):
                        pkg_el = {
                            "name": v_pkg.name,
                            "version": v_pkg.version,
                            "type": "*",
                        }
                        namespace_el["affected_packages"].append(pkg_el)

                if not package_name_filter or (
                        package_name_filter
                        and namespace_el["affected_packages"]):
                    dedupped_return_hash[namespace_el["id"]] = namespace_el

            log.spew("Vuln merge took {}".format(time.time() - t1))

            return_object.extend(list(dedupped_return_hash.values()))

        if namespace == ["nvdv2:cves"]:
            # Skip if requested was 'nvd'
            vulnerabilities = []
        else:
            t1 = time.time()

            qry = (db_session.query(Vulnerability).options(
                loader(Vulnerability.fixed_in)).filter(
                    Vulnerability.id.in_(ids)))

            if namespace:
                if type(namespace) == str:
                    namespace = [namespace]

                qry = qry.filter(Vulnerability.namespace_name.in_(namespace))

            vulnerabilities = qry.all()

            log.spew("Vuln query 2 timing: {}".format(time.time() - t1))

        if vulnerabilities:
            log.spew("Merging nvd data into the vulns")
            t1 = time.time()
            merged_vulns = merge_nvd_metadata(
                db_session,
                vulnerabilities,
                _nvd_cls,
                _cpe_cls,
                already_loaded_nvds=nvd_vulnerabilities,
            )
            log.spew("Vuln nvd query 2 timing: {}".format(time.time() - t1))

            for entry in merged_vulns:
                vulnerability = entry[0]
                nvds = entry[1]
                namespace_el = {}
                namespace_el.update(return_el_template)
                namespace_el["id"] = vulnerability.id
                namespace_el["namespace"] = vulnerability.namespace_name
                namespace_el["severity"] = vulnerability.severity
                namespace_el["link"] = vulnerability.link
                namespace_el["affected_packages"] = []

                namespace_el["nvd_data"] = []
                namespace_el["vendor_data"] = []

                for nvd_record in nvds:
                    namespace_el["nvd_data"].extend(
                        nvd_record.get_cvss_data_nvd())

                for v_pkg in vulnerability.fixed_in:
                    if (not package_name_filter
                            or package_name_filter == v_pkg.name) and (
                                not package_version_filter
                                or package_version_filter == v_pkg.version):
                        pkg_el = {
                            "name": v_pkg.name,
                            "version": v_pkg.version,
                            "type": v_pkg.version_format,
                        }
                        if not v_pkg.version or v_pkg.version.lower(
                        ) == "none":
                            pkg_el["version"] = "*"

                        namespace_el["affected_packages"].append(pkg_el)

                for v_pkg in vulnerability.vulnerable_in:
                    if (not package_name_filter
                            or package_name_filter == v_pkg.name) and (
                                not package_version_filter
                                or package_version_filter == v_pkg.version):
                        pkg_el = {
                            "name": v_pkg.name,
                            "version": v_pkg.version,
                            "type": v_pkg.version_format,
                        }
                        if not v_pkg.version or v_pkg.version.lower(
                        ) == "none":
                            pkg_el["version"] = "*"

                        namespace_el["affected_packages"].append(pkg_el)

                if not package_name_filter or (
                        package_name_filter
                        and namespace_el["affected_packages"]):
                    return_object.append(namespace_el)

        return return_object
Esempio n. 6
0
    def get_image_vulnerabilities(
        self,
        image: Image,
        db_session,
        vendor_only: bool = True,
        force_refresh: bool = False,
        cache: bool = True,
    ):
        # select the nvd class once and be done
        _nvd_cls, _cpe_cls = select_nvd_classes(db_session)

        # initialize the scanner
        scanner = self.__scanner__()

        user_id = image.user_id
        image_id = image.id

        results = []

        if force_refresh:
            log.info("Forcing refresh of vulnerabilities for {}/{}".format(
                user_id, image_id))
            try:
                scanner.flush_and_recompute_vulnerabilities(
                    image, db_session=db_session)
                db_session.commit()
            except Exception as e:
                log.exception(
                    "Error refreshing cve matches for image {}/{}".format(
                        user_id, image_id))
                db_session.rollback()
                return make_response_error(
                    "Error refreshing vulnerability listing for image.",
                    in_httpcode=500,
                )

            db_session = get_session()
            db_session.refresh(image)

        with timer("Image vulnerability primary lookup", log_level="debug"):
            vulns = scanner.get_vulnerabilities(image)

        # Has vulnerabilities?
        warns = []
        if not vulns:
            vulns = []
            ns = DistroNamespace.for_obj(image)
            if not have_vulnerabilities_for(ns):
                warns = [
                    "No vulnerability data available for image distro: {}".
                    format(ns.namespace_name)
                ]

        rows = []
        with timer("Image vulnerability nvd metadata merge",
                   log_level="debug"):
            vulns = merge_nvd_metadata_image_packages(db_session, vulns,
                                                      _nvd_cls, _cpe_cls)

        with timer("Image vulnerability output formatting", log_level="debug"):
            for vuln, nvd_records in vulns:
                fixed_artifact = vuln.fixed_artifact()

                # Skip the vulnerability if the vendor_only flag is set to True and the issue won't be addressed by the vendor
                if vendor_only and vuln.fix_has_no_advisory(
                        fixed_in=fixed_artifact):
                    continue

                nvd_scores = [
                    self._make_cvss_score(score) for nvd_record in nvd_records
                    for score in nvd_record.get_cvss_scores_nvd()
                ]

                results.append(
                    VulnerabilityMatch(
                        vulnerability=VulnerabilityModel(
                            vulnerability_id=vuln.vulnerability_id,
                            description="NA",
                            severity=vuln.vulnerability.severity,
                            link=vuln.vulnerability.link,
                            feed="vulnerabilities",
                            feed_group=vuln.vulnerability.namespace_name,
                            cvss_scores_nvd=nvd_scores,
                            cvss_scores_vendor=[],
                            created_at=vuln.vulnerability.created_at,
                            last_modified=vuln.vulnerability.updated_at,
                        ),
                        artifact=Artifact(
                            name=vuln.pkg_name,
                            version=vuln.package.fullversion,
                            pkg_type=vuln.pkg_type,
                            pkg_path=vuln.pkg_path,
                            cpe="None",
                            cpe23="None",
                        ),
                        fixes=[
                            FixedArtifact(
                                version=str(
                                    vuln.fixed_in(fixed_in=fixed_artifact)),
                                wont_fix=vuln.fix_has_no_advisory(
                                    fixed_in=fixed_artifact),
                                observed_at=fixed_artifact.fix_observed_at
                                if fixed_artifact else None,
                            )
                        ],
                        match=Match(detected_at=vuln.created_at),
                    ))

        # TODO move dedup here so api doesn't have to
        # cpe_vuln_listing = []
        try:
            with timer("Image vulnerabilities cpe matches", log_level="debug"):
                all_cpe_matches = scanner.get_cpe_vulnerabilities(
                    image, _nvd_cls, _cpe_cls)

                if not all_cpe_matches:
                    all_cpe_matches = []

                api_endpoint = self._get_api_endpoint()

                for image_cpe, vulnerability_cpe in all_cpe_matches:
                    link = vulnerability_cpe.parent.link
                    if not link:
                        link = "{}/query/vulnerabilities?id={}".format(
                            api_endpoint, vulnerability_cpe.vulnerability_id)

                    nvd_scores = [
                        self._make_cvss_score(score) for score in
                        vulnerability_cpe.parent.get_cvss_scores_nvd()
                    ]

                    vendor_scores = [
                        self._make_cvss_score(score) for score in
                        vulnerability_cpe.parent.get_cvss_scores_vendor()
                    ]

                    results.append(
                        VulnerabilityMatch(
                            vulnerability=VulnerabilityModel(
                                vulnerability_id=vulnerability_cpe.parent.
                                normalized_id,
                                description="NA",
                                severity=vulnerability_cpe.parent.severity,
                                link=link,
                                feed=vulnerability_cpe.feed_name,
                                feed_group=vulnerability_cpe.namespace_name,
                                cvss_scores_nvd=nvd_scores,
                                cvss_scores_vendor=vendor_scores,
                                created_at=vulnerability_cpe.parent.created_at,
                                last_modified=vulnerability_cpe.parent.
                                updated_at,
                            ),
                            artifact=Artifact(
                                name=image_cpe.name,
                                version=image_cpe.version,
                                pkg_type=image_cpe.pkg_type,
                                pkg_path=image_cpe.pkg_path,
                                cpe=image_cpe.get_cpestring(),
                                cpe23=image_cpe.get_cpe23string(),
                            ),
                            fixes=[
                                FixedArtifact(
                                    version=item,
                                    wont_fix=False,
                                    observed_at=vulnerability_cpe.created_at,
                                ) for item in vulnerability_cpe.get_fixed_in()
                            ],
                            # using vulnerability created_at to indicate the match timestamp for now
                            match=Match(
                                detected_at=vulnerability_cpe.created_at),
                        ))
        except Exception as err:
            log.exception("could not fetch CPE matches")

        import uuid

        return ImageVulnerabilitiesReport(
            account_id=image.user_id,
            image_id=image_id,
            results=results,
            metadata=VulnerabilitiesReportMetadata(
                generated_at=datetime.datetime.utcnow(),
                uuid=str(uuid.uuid4()),
                generated_by=self._get_provider_metadata(),
            ),
            problems=[],
        )