def flush_and_recompute_vulnerabilities( self, image_obj: Image, db_session: Session ) -> typing.List[ImagePackageVulnerability]: """ Wrapper for rescan_image function. """ return vulnerabilities.rescan_image(image_obj, db_session)
def rescan_images_created_between(self, from_time, to_time): """ If this was a vulnerability update (e.g. timestamps vuln feeds lies in that interval), then look for any images that were loaded in that interval and re-scan the cves for those to ensure that no ordering of transactions caused cves to be missed for an image. This is an alternative to a blocking approach by which image loading is blocked during feed syncs. :param from_time: :param to_time: :return: count of updated images """ if from_time is None or to_time is None: raise ValueError('Cannot process None timestamp') log.info('Rescanning images loaded between {} and {}'.format(from_time.isoformat(), to_time.isoformat())) count = 0 db = get_session() try: # it is critical that these tuples are in proper index order for the primary key of the Images object so that subsequent get() operation works imgs = [(x.id, x.user_id) for x in db.query(Image).filter(Image.created_at >= from_time, Image.created_at <= to_time)] log.info('Detected images: {} for rescan'.format(' ,'.join([str(x) for x in imgs]) if imgs else '[]')) finally: db.rollback() retry_max = 3 for img in imgs: for i in range(retry_max): try: # New transaction for each image to get incremental progress db = get_session() try: # If the type or ordering of 'img' tuple changes, this needs to be updated as it relies on symmetry of that tuple and the identity key of the Image entity image_obj = db.query(Image).get(img) if image_obj: log.info('Rescanning image {} post-vuln sync'.format(img)) vulns = rescan_image(image_obj, db_session=db) count += 1 else: log.warn('Failed to lookup image with tuple: {}'.format(str(img))) db.commit() finally: db.rollback() break except Exception as e: log.exception('Caught exception updating vulnerability scan results for image {}. Waiting and retrying'.format(img)) time.sleep(5) return count
def get_image_vulnerabilities(user_id, image_id, force_refresh=False, vendor_only=True): """ Return the vulnerability listing for the specified image and load from catalog if not found and specifically asked to do so. Example json output: { "multi" : { "url_column_index" : 7, "result" : { "rows" : [], "rowcount" : 0, "colcount" : 8, "header" : [ "CVE_ID", "Severity", "*Total_Affected", "Vulnerable_Package", "Fix_Available", "Fix_Images", "Rebuild_Images", "URL" ] }, "querycommand" : "/usr/lib/python2.7/site-packages/anchore/anchore-modules/multi-queries/cve-scan.py /ebs_data/anchore/querytmp/queryimages.7026386 /ebs_data/anchore/data /ebs_data/anchore/querytmp/query.59057288 all", "queryparams" : "all", "warns" : [ "0005b136f0fb (prom/prometheus:master) cannot perform CVE scan: no CVE data is currently available for the detected base distro type (busybox:unknown_version,busybox:v1.26.2)" ] } } :param user_id: user id of image to evaluate :param image_id: image id to evaluate :param force_refresh: if true, flush and recompute vulnerabilities rather than returning current values :param vendor_only: if true, filter out the vulnerabilities that vendors will explicitly not address :return: """ # Has image? db = get_session() try: img = db.query(Image).get((image_id, user_id)) vulns = [] if not img: abort(404) else: if force_refresh: log.info('Forcing refresh of vulnerabiltiies for {}/{}'.format( user_id, image_id)) try: vulns = rescan_image(img, db_session=db) db.commit() except Exception as e: log.exception( 'Error refreshing cve matches for image {}/{}'.format( user_id, image_id)) db.rollback() abort( Response( 'Error refreshing vulnerability listing for image.', 500)) db = get_session() db.refresh(img) vulns = img.vulnerabilities() # Has vulnerabilities? warns = [] if not vulns: vulns = [] ns = DistroNamespace.for_obj(img) if not have_vulnerabilities_for(ns): warns = [ 'No vulnerability data available for image distro: {}'. format(ns.namespace_name) ] rows = [] for vuln in vulns: # if vuln.vulnerability.fixed_in: # fixes_in = filter(lambda x: x.name == vuln.pkg_name or x.name == vuln.package.normalized_src_pkg, # vuln.vulnerability.fixed_in) # fix_available_in = fixes_in[0].version if fixes_in else 'None' # else: # fix_available_in = 'None' # 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(): continue rows.append([ vuln.vulnerability_id, vuln.vulnerability.severity, 1, vuln.pkg_name + '-' + vuln.package.fullversion, str(vuln.fixed_in()), vuln.pkg_image_id, 'None', # Always empty this for now vuln.vulnerability.link, vuln.pkg_type, 'vulnerabilities', vuln.vulnerability.namespace_name, vuln.pkg_name, vuln.package.fullversion, ]) vuln_listing = { 'multi': { 'url_column_index': 7, 'result': { 'header': TABLE_STYLE_HEADER_LIST, 'rowcount': len(rows), 'colcount': len(TABLE_STYLE_HEADER_LIST), 'rows': rows }, 'warns': warns } } cpe_vuln_listing = [] try: all_cpe_matches = db.query( ImageCpe, CpeVulnerability).filter(ImageCpe.image_id == image_id).filter( ImageCpe.name == CpeVulnerability.name).filter( ImageCpe.version == CpeVulnerability.version) if not all_cpe_matches: all_cpe_matches = [] cpe_hashes = {} for image_cpe, vulnerability_cpe in all_cpe_matches: cpe_vuln_el = { 'vulnerability_id': vulnerability_cpe.vulnerability_id, 'severity': vulnerability_cpe.severity, 'link': vulnerability_cpe.link, 'pkg_type': image_cpe.pkg_type, 'pkg_path': image_cpe.pkg_path, 'name': image_cpe.name, 'version': image_cpe.version, 'cpe': image_cpe.get_cpestring(), 'feed_name': vulnerability_cpe.feed_name, 'feed_namespace': vulnerability_cpe.namespace_name, } cpe_hash = hashlib.sha256( utils.ensure_bytes(json.dumps(cpe_vuln_el))).hexdigest() if not cpe_hashes.get(cpe_hash, False): cpe_vuln_listing.append(cpe_vuln_el) cpe_hashes[cpe_hash] = True except Exception as err: log.warn("could not fetch CPE matches - exception: " + str(err)) report = LegacyVulnerabilityReport.from_dict(vuln_listing) resp = ImageVulnerabilityListing(user_id=user_id, image_id=image_id, legacy_report=report, cpe_report=cpe_vuln_listing) return resp.to_dict() except HTTPException: db.rollback() raise except Exception as e: log.exception( 'Error checking image {}, {} for vulnerabiltiies. Rolling back'. format(user_id, image_id)) db.rollback() abort(500) finally: db.close()
def flush_and_recompute_vulnerabilities(self, image_obj, db_session): """ Wrapper for rescan_image function. """ vulnerabilities.rescan_image(image_obj, db_session)