Skip to content

Commit

Permalink
Merge pull request #21 from synopsys-sig/ro-optional-full-result
Browse files Browse the repository at this point in the history
Use Black Duck APIs over full-result endpoint
  • Loading branch information
rottebds authored Feb 16, 2022
2 parents 04fe703 + a615208 commit 4e623aa
Show file tree
Hide file tree
Showing 8 changed files with 13,988 additions and 13,746 deletions.
27,313 changes: 13,756 additions & 13,557 deletions dist/index.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/index.js.map

Large diffs are not rendered by default.

115 changes: 72 additions & 43 deletions src/blackduck-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,67 +4,75 @@ import { BearerCredentialHandler } from 'typed-rest-client/Handlers'
import { HttpClient } from 'typed-rest-client/HttpClient'
import { IRestResponse, RestClient } from 'typed-rest-client/RestClient'
import { APPLICATION_NAME } from './application-constants'

export interface IBlackduckView {
_meta: {
href: string
}
}

export interface IBlackduckPage<Type> extends IBlackduckView {
export interface IBlackduckItemArray<Type> extends IBlackduckView {
totalCount: number
items: Array<Type>
}

export interface IUpgradeGuidance {
version: string
shortTerm: {
version: string
versionName: string
vulnerabilityRisk: Object
shortTerm: IRecommendedVersion
longTerm: IRecommendedVersion
}

export interface IRecommendedVersion {
version: string
versionName: string
vulnerabilityRisk: Object
}

export interface IComponentSearchResult {
version: string
}

export interface IComponentVersion {
license: {
licenses: {
license: string
name: string
}[]
}
longTerm: {
version: string
versionName: string
vulnerabilityRisk: Object
_meta: {
href: string
}
}

export interface IComponentVersion {
version: string
export interface IComponentVulnerability {
vulnerabilityName: string
baseScore: number
severity: string
_meta: {
href: string
}
}

export interface IRapidScanFullResults {
export interface IRapidScanResults {
componentName: string
versionName: string
componentIdentifier: string
violatingPolicies: {
policyName: string
description: string
policySeverity: string
}[]
policyViolationVulnerabilities: {
name: string
}[]
policyViolationLicenses: {
name: string
}[]
allVulnerabilities: {
name: string
description: string
vulnSeverity: string
overallScore: number
_meta: {
href: string
}
}[]
allLicenses: {
name: string
licenseFamilyName: string
_meta: {
href: string
}
}[]
violatingPolicyNames: string[]
policyViolationVulnerabilities: IRapidScanVulnerability[]
policyViolationLicenses: IRapidScanLicense[]
_meta: {
href: string
}
}

export interface IRapidScanVulnerability {
name: string
}

export interface IRapidScanLicense {
licenseName: string
_meta: {
href: string
}
}

export class BlackduckApiService {
Expand Down Expand Up @@ -109,23 +117,44 @@ export class BlackduckApiService {
}

async getUpgradeGuidanceFor(bearerToken: string, componentVersion: IComponentVersion): Promise<IRestResponse<IUpgradeGuidance>> {
return this.get(bearerToken, `${componentVersion.version}/upgrade-guidance`)
return this.get(bearerToken, `${componentVersion._meta.href}/upgrade-guidance`)
}

async getComponentsMatching(bearerToken: string, componentIdentifier: string, limit: number = 10): Promise<IRestResponse<IBlackduckPage<IComponentVersion>>> {
async getComponentsMatching(bearerToken: string, componentIdentifier: string, limit: number = 10): Promise<IRestResponse<IBlackduckItemArray<IComponentSearchResult>>> {
const requestPath = `/api/components?q=${componentIdentifier}`

return this.requestPage(bearerToken, requestPath, 0, limit)
}

async getComponentVersion(bearerToken: string, searchResult: IComponentSearchResult) {
return this.get(bearerToken, searchResult.version)
}

async getComponentVersionMatching(bearerToken: string, componentIdentifier: string, limit: number = 10): Promise<IComponentVersion | null> {
const componentSearchResponse = await this.getComponentsMatching(bearerToken, componentIdentifier, limit)
const firstMatchingComponentVersionUrl = componentSearchResponse?.result?.items[0].version

let componentVersion = null
if (firstMatchingComponentVersionUrl !== undefined) {
const componentVersionResponse: IRestResponse<IComponentVersion> = await this.get(bearerToken, firstMatchingComponentVersionUrl)
componentVersion = componentVersionResponse?.result
}

return componentVersion
}

async getComponentVulnerabilties(bearerToken: string, componentVersion: IComponentVersion): Promise<IRestResponse<IBlackduckItemArray<IComponentVulnerability>>> {
return this.get(bearerToken, `${componentVersion._meta.href}/vulnerabilities`)
}

async getPolicies(bearerToken: string, limit: number = 10, enabled?: boolean) {
const enabledFilter = enabled === undefined || enabled === null ? '' : `filter=policyRuleEnabled%3A${enabled}`
const requestPath = `/api/policy-rules?${enabledFilter}`

return this.requestPage(bearerToken, requestPath, 0, limit)
}

async requestPage(bearerToken: string, requestPath: string, offset: number, limit: number): Promise<IRestResponse<IBlackduckPage<any>>> {
async requestPage(bearerToken: string, requestPath: string, offset: number, limit: number): Promise<IRestResponse<IBlackduckItemArray<any>>> {
return this.get(bearerToken, `${this.blackduckUrl}${requestPath}&offset=${offset}&limit=${limit}`)
}

Expand Down
141 changes: 141 additions & 0 deletions src/detect/report.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
import { warning } from '@actions/core'
import { BlackduckApiService, IComponentVersion, IComponentVulnerability, IRapidScanLicense, IRapidScanResults, IRapidScanVulnerability, IRecommendedVersion, IUpgradeGuidance } from '../blackduck-api'
import { BLACKDUCK_API_TOKEN, BLACKDUCK_URL } from '../inputs'

export async function createRapidScanReport(policyViolations: IRapidScanResults[], blackduckApiService?: BlackduckApiService): Promise<IComponentReport[]> {
const rapidScanReport: IComponentReport[] = []

if (blackduckApiService === undefined) {
blackduckApiService = new BlackduckApiService(BLACKDUCK_URL, BLACKDUCK_API_TOKEN)
}

const bearerToken = await blackduckApiService.getBearerToken()

for (const policyViolation of policyViolations) {
const componentIdentifier = policyViolation.componentIdentifier
const componentVersion = await blackduckApiService.getComponentVersionMatching(bearerToken, componentIdentifier)

let upgradeGuidance = undefined
let vulnerabilities = undefined
if (componentVersion !== null) {
upgradeGuidance = await blackduckApiService
.getUpgradeGuidanceFor(bearerToken, componentVersion)
.then(response => {
if (response.result === null) {
warning(`Could not get upgrade guidance for ${componentIdentifier}: The upgrade guidance result was empty`)
return undefined
}

return response.result
})
.catch(reason => {
warning(`Could not get upgrade guidance for ${componentIdentifier}: ${reason}`)
return undefined
})

const vulnerabilityResponse = await blackduckApiService.getComponentVulnerabilties(bearerToken, componentVersion)
vulnerabilities = vulnerabilityResponse?.result?.items
}

const componentVersionOrUndefined = componentVersion === null ? undefined : componentVersion
const componentReport = createComponentReport(policyViolation, componentVersionOrUndefined, upgradeGuidance, vulnerabilities)
rapidScanReport.push(componentReport)
}

return rapidScanReport
}
export interface IComponentReport {
violatedPolicies: string[]
name: string
href?: string
licenses: ILicenseReport[]
vulnerabilities: IVulnerabilityReport[]
shortTermUpgrade?: IUpgradeReport
longTermUpgrade?: IUpgradeReport
}

export function createComponentReport(violation: IRapidScanResults, componentVersion?: IComponentVersion, upgradeGuidance?: IUpgradeGuidance, vulnerabilities?: IComponentVulnerability[]): IComponentReport {
return {
violatedPolicies: violation.violatingPolicyNames,
name: `${violation.componentName} ${violation.versionName}`,
href: componentVersion?._meta.href,
licenses: createComponentLicenseReports(violation.policyViolationLicenses, componentVersion),
vulnerabilities: createComponentVulnerabilityReports(violation.policyViolationVulnerabilities, vulnerabilities),
shortTermUpgrade: createUpgradeReport(upgradeGuidance?.shortTerm),
longTermUpgrade: createUpgradeReport(upgradeGuidance?.longTerm)
}
}

export function createComponentLicenseReports(policyViolatingLicenses: IRapidScanLicense[], componentVersion?: IComponentVersion): ILicenseReport[] {
let licenseReport = []
if (componentVersion === undefined) {
licenseReport = policyViolatingLicenses.map(license => createLicenseReport(license.licenseName, license._meta.href, true))
} else {
const violatingPolicyLicenseNames = policyViolatingLicenses.map(license => license.licenseName)
licenseReport = componentVersion.license.licenses.map(license => createLicenseReport(license.name, license.license, violatingPolicyLicenseNames.includes(license.name)))
}

return licenseReport
}

export function createComponentVulnerabilityReports(policyViolatingVulnerabilities: IRapidScanVulnerability[], componentVulnerabilities?: IComponentVulnerability[]): IVulnerabilityReport[] {
let vulnerabilityReport = []
if (componentVulnerabilities === undefined) {
vulnerabilityReport = policyViolatingVulnerabilities.map(vulnerability => createVulnerabilityReport(vulnerability.name, true))
} else {
const violatingPolicyVulnerabilityNames = policyViolatingVulnerabilities.map(vulnerability => vulnerability.name)
vulnerabilityReport = componentVulnerabilities.map(vulnerability => createVulnerabilityReport(vulnerability.vulnerabilityName, violatingPolicyVulnerabilityNames.includes(vulnerability.vulnerabilityName), vulnerability._meta.href, vulnerability.baseScore, vulnerability.severity))
}

return vulnerabilityReport
}

export interface ILicenseReport {
name: string
href: string
violatesPolicy: boolean
}

export function createLicenseReport(name: string, href: string, violatesPolicy: boolean): ILicenseReport {
return {
name: name,
href: href,
violatesPolicy: violatesPolicy
}
}

export interface IVulnerabilityReport {
name: string
violatesPolicy: boolean
href?: string
cvssScore?: number
severity?: string
}

export function createVulnerabilityReport(name: string, violatesPolicy: boolean, href?: string, cvssScore?: number, severity?: string): IVulnerabilityReport {
return {
name: name,
violatesPolicy: violatesPolicy,
href: href,
cvssScore: cvssScore,
severity: severity
}
}

export interface IUpgradeReport {
name: string
href: string
vulnerabilityCount: number
}

export function createUpgradeReport(recommendedVersion?: IRecommendedVersion): IUpgradeReport | undefined {
if (recommendedVersion === undefined) {
return undefined
}

return {
name: recommendedVersion.versionName,
href: recommendedVersion.version,
vulnerabilityCount: Object.values(recommendedVersion.vulnerabilityRisk).reduce((accumulatedValues, value) => accumulatedValues + value, 0)
}
}
Loading

0 comments on commit 4e623aa

Please sign in to comment.