import { ComponentDetails , ICve , IGraphLicense , IGraphResponse , IViolation , IVulnerability } from 'jfrog-client-js' ; import Dictionary from 'typescript-collections/dist/lib/Dictionary' ; import Set from 'typescript-collections/dist/lib/Set' ; import * as vscode from 'vscode' ; import { IIssueCacheObject } from '../types/issueCacheObject' ; import { IIssueKey } from '../types/issueKey' ; import { ILicenseCacheObject } from '../types/licenseCacheObject' ; import { ILicenseKey } from '../types/licenseKey' ; import { INodeInfo } from '../types/nodeInfo' ; import { Severity } from '../types/severity' ; import { Configuration } from '../utils/configuration' ; import { Translators } from '../utils/translators' ; import { AbstractScanLogic } from './abstractScanLogic' ; /**
 * Used in Xray >= 3.29.0.
 * Run /scan/graph REST API and populate the cache with the results.
 * When the project key is provided - only violated vulnerabilities should appear in the results. Licenses may mark as violated.
 * When the project key isn't provided - all vulnerabilities and licenses information should appear in the results.
 */ export class GraphScanLogic extends AbstractScanLogic { public async scanAndCache ( progress , componentsToScan , checkCanceled ) { progress . report ( { message : `${ componentsToScan . size ( ) } components` } ) ; let graphResponse = await this . _connectionManager . scanGraph ( componentsToScan , checkCanceled , Configuration . getProjectKey ( ) ) ; let scannedComponents = new Map ( ) ; let licenses = new Dictionary ( ) ; let issues = [ ] ; if ( graphResponse . violations ) { this . populateViolations ( graphResponse . violations , issues , licenses , scannedComponents ) ; } if ( graphResponse . vulnerabilities ) { this . populateVulnerabilities ( graphResponse . vulnerabilities , issues , scannedComponents ) ; } if ( graphResponse . licenses ) { this . populateLicenses ( graphResponse . licenses , licenses , scannedComponents ) ; } this . addMissingComponents ( componentsToScan , scannedComponents ) ; await this . _scanCacheManager . storeComponents ( scannedComponents , issues , licenses . values ( ) ) ; } private populateViolations ( violations , issues , licenses , scannedComponents ) { for ( const violation of violations ) { if ( violation . license_key && violation . license_key !== '' ) { this . populateLicense ( violation , licenses , scannedComponents , true ) ; } else { this . populateVulnerability ( violation , issues , scannedComponents ) ; } } } private populateVulnerabilities ( vulnerabilities , issues , scannedComponents ) { for ( const vuln of vulnerabilities ) { this . populateVulnerability ( vuln , issues , scannedComponents ) ; } } private populateVulnerability ( vuln , issues , scannedComponents ) { for ( let [ componentId , vulnComponent ] of Object . entries ( vuln . components ) ) { let severity = Translators . toSeverity ( vuln . severity ) ; issues . push ( { issueId : vuln . issue_id , severity : severity , summary : vuln . summary != '' ? vuln . summary : 'N/A' , fixedVersions : vulnComponent . fixed_versions , cves : this . getCves ( vuln . cves ) } as IIssueCacheObject ) ; this . addIssueToScannedComponents ( scannedComponents , this . getShortComponentId ( componentId ) , vuln . issue_id , severity ) ; } } private addIssueToScannedComponents ( scannedComponents , componentId , issueId , severity ) { let nodeInfo = scannedComponents . get ( componentId ) ; if ( ! nodeInfo ) { nodeInfo = this . createNodeInfo ( severity ) ; } else if ( severity > nodeInfo . top_severity ) { nodeInfo . top_severity = severity ; } nodeInfo . issues . push ( { component : componentId , issue_id : issueId } as IIssueKey ) ; scannedComponents . set ( componentId , nodeInfo ) ; } private populateLicenses ( scannedLicenses , licenses , scannedComponents ) { for ( const license of scannedLicenses ) { if ( ! licenses . containsKey ( license . license_key ) ) { this . populateLicense ( license , licenses , scannedComponents , false ) ; } } } private populateLicense ( scannedLicense , licenses , scannedComponents , violated ) { let moreInfoUrl = scannedLicense . references && scannedLicense . references . length > 0 ? scannedLicense . references [ 0 ] : '' ; licenses . setValue ( scannedLicense . license_key , { name : scannedLicense . license_key , fullName : scannedLicense . license_name , violated : violated , moreInfoUrl : moreInfoUrl } as ILicenseCacheObject ) ; for ( let componentId of Object . keys ( scannedLicense . components ) ) { this . addLicenseToScannedComponents ( scannedComponents , this . getShortComponentId ( componentId ) , scannedLicense . license_key , violated ) ; } } private addLicenseToScannedComponents ( scannedComponents , componentId , licenseName , violated ) { let nodeInfo = scannedComponents . get ( componentId ) || this . createNodeInfo ( Severity . Normal ) ; nodeInfo . licenses . push ( { licenseName : licenseName , violated : violated } as ILicenseKey ) ; scannedComponents . set ( componentId , nodeInfo ) ; } private addMissingComponents ( componentsToScan , scannedComponents ) { componentsToScan . forEach ( componentToScan => { let componentId = this . getShortComponentId ( componentToScan . component_id ) ; if ( ! scannedComponents . has ( componentId ) ) { scannedComponents . set ( componentId , this . createNodeInfo ( ) ) ; } } ) ; } private getCves ( graphCves ) { let cves = [ ] ; if ( ! graphCves ) { return cves ; } for ( let cve of graphCves ) { cves . push ( cve . cve ) ; } return cves ; } private getShortComponentId ( componentId ) { return componentId . substring ( componentId . indexOf ( '://' ) + 3 ) ; } private createNodeInfo ( topSeverity = Severity . Unknown ) { return { top_severity : topSeverity , issues : [ ] as IIssueKey [ ] , licenses : [ ] as ILicenseKey [ ] } as INodeInfo ; } }