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
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, )
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
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
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=[], )